1/* 2 * tkImage.c -- 3 * 4 * This file contains code that allows images to be nested inside text 5 * widgets. It also implements the "image" widget command for texts. 6 * 7 * Copyright (c) 1997 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkPort.h" 16#include "tkText.h" 17 18/* 19 * Macro that determines the size of an embedded image segment: 20 */ 21 22#define EI_SEG_SIZE \ 23 ((unsigned) (Tk_Offset(TkTextSegment, body) + sizeof(TkTextEmbImage))) 24 25/* 26 * Prototypes for functions defined in this file: 27 */ 28 29static TkTextSegment * EmbImageCleanupProc(TkTextSegment *segPtr, 30 TkTextLine *linePtr); 31static void EmbImageCheckProc(TkTextSegment *segPtr, 32 TkTextLine *linePtr); 33static void EmbImageBboxProc(TkText *textPtr, 34 TkTextDispChunk *chunkPtr, int index, int y, 35 int lineHeight, int baseline, int *xPtr, int *yPtr, 36 int *widthPtr, int *heightPtr); 37static int EmbImageConfigure(TkText *textPtr, 38 TkTextSegment *eiPtr, int objc, 39 Tcl_Obj *const objv[]); 40static int EmbImageDeleteProc(TkTextSegment *segPtr, 41 TkTextLine *linePtr, int treeGone); 42static void EmbImageDisplayProc(TkText *textPtr, 43 TkTextDispChunk *chunkPtr, int x, int y, 44 int lineHeight, int baseline, Display *display, 45 Drawable dst, int screenY); 46static int EmbImageLayoutProc(TkText *textPtr, 47 TkTextIndex *indexPtr, TkTextSegment *segPtr, 48 int offset, int maxX, int maxChars, 49 int noCharsYet, TkWrapMode wrapMode, 50 TkTextDispChunk *chunkPtr); 51static void EmbImageProc(ClientData clientData, int x, int y, 52 int width, int height, int imageWidth, 53 int imageHeight); 54 55/* 56 * The following structure declares the "embedded image" segment type. 57 */ 58 59static const Tk_SegType tkTextEmbImageType = { 60 "image", /* name */ 61 0, /* leftGravity */ 62 NULL, /* splitProc */ 63 EmbImageDeleteProc, /* deleteProc */ 64 EmbImageCleanupProc, /* cleanupProc */ 65 NULL, /* lineChangeProc */ 66 EmbImageLayoutProc, /* layoutProc */ 67 EmbImageCheckProc /* checkProc */ 68}; 69 70/* 71 * Definitions for alignment values: 72 */ 73 74static char *alignStrings[] = { 75 "baseline", "bottom", "center", "top", NULL 76}; 77 78typedef enum { 79 ALIGN_BASELINE, ALIGN_BOTTOM, ALIGN_CENTER, ALIGN_TOP 80} alignMode; 81 82/* 83 * Information used for parsing image configuration options: 84 */ 85 86static const Tk_OptionSpec optionSpecs[] = { 87 {TK_OPTION_STRING_TABLE, "-align", NULL, NULL, 88 "center", -1, Tk_Offset(TkTextEmbImage, align), 89 0, (ClientData) alignStrings, 0}, 90 {TK_OPTION_PIXELS, "-padx", NULL, NULL, 91 "0", -1, Tk_Offset(TkTextEmbImage, padX), 0, 0, 0}, 92 {TK_OPTION_PIXELS, "-pady", NULL, NULL, 93 "0", -1, Tk_Offset(TkTextEmbImage, padY), 0, 0, 0}, 94 {TK_OPTION_STRING, "-image", NULL, NULL, 95 NULL, -1, Tk_Offset(TkTextEmbImage, imageString), 96 TK_OPTION_NULL_OK, 0, 0}, 97 {TK_OPTION_STRING, "-name", NULL, NULL, 98 NULL, -1, Tk_Offset(TkTextEmbImage, imageName), 99 TK_OPTION_NULL_OK, 0, 0}, 100 {TK_OPTION_END} 101}; 102 103 104/* 105 *-------------------------------------------------------------- 106 * 107 * TkTextImageCmd -- 108 * 109 * This function implements the "image" widget command for text widgets. 110 * See the user documentation for details on what it does. 111 * 112 * Results: 113 * A standard Tcl result or error. 114 * 115 * Side effects: 116 * See the user documentation. 117 * 118 *-------------------------------------------------------------- 119 */ 120 121int 122TkTextImageCmd( 123 register TkText *textPtr, /* Information about text widget. */ 124 Tcl_Interp *interp, /* Current interpreter. */ 125 int objc, /* Number of arguments. */ 126 Tcl_Obj *const objv[]) /* Argument objects. Someone else has already 127 * parsed this command enough to know that 128 * objv[1] is "image". */ 129{ 130 int idx; 131 register TkTextSegment *eiPtr; 132 TkTextIndex index; 133 static const char *optionStrings[] = { 134 "cget", "configure", "create", "names", NULL 135 }; 136 enum opts { 137 CMD_CGET, CMD_CONF, CMD_CREATE, CMD_NAMES 138 }; 139 140 if (objc < 3) { 141 Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?"); 142 return TCL_ERROR; 143 } 144 if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, "option", 0, 145 &idx) != TCL_OK) { 146 return TCL_ERROR; 147 } 148 switch ((enum opts) idx) { 149 case CMD_CGET: { 150 Tcl_Obj *objPtr; 151 152 if (objc != 5) { 153 Tcl_WrongNumArgs(interp, 3, objv, "index option"); 154 return TCL_ERROR; 155 } 156 if (TkTextGetObjIndex(interp, textPtr, objv[3], &index) != TCL_OK) { 157 return TCL_ERROR; 158 } 159 eiPtr = TkTextIndexToSeg(&index, NULL); 160 if (eiPtr->typePtr != &tkTextEmbImageType) { 161 Tcl_AppendResult(interp, "no embedded image at index \"", 162 Tcl_GetString(objv[3]), "\"", NULL); 163 return TCL_ERROR; 164 } 165 objPtr = Tk_GetOptionValue(interp, (char *) &eiPtr->body.ei, 166 eiPtr->body.ei.optionTable, objv[4], textPtr->tkwin); 167 if (objPtr == NULL) { 168 return TCL_ERROR; 169 } else { 170 Tcl_SetObjResult(interp, objPtr); 171 return TCL_OK; 172 } 173 } 174 case CMD_CONF: 175 if (objc < 4) { 176 Tcl_WrongNumArgs(interp, 3, objv, "index ?option value ...?"); 177 return TCL_ERROR; 178 } 179 if (TkTextGetObjIndex(interp, textPtr, objv[3], &index) != TCL_OK) { 180 return TCL_ERROR; 181 } 182 eiPtr = TkTextIndexToSeg(&index, NULL); 183 if (eiPtr->typePtr != &tkTextEmbImageType) { 184 Tcl_AppendResult(interp, "no embedded image at index \"", 185 Tcl_GetString(objv[3]), "\"", NULL); 186 return TCL_ERROR; 187 } 188 if (objc <= 5) { 189 Tcl_Obj *objPtr = Tk_GetOptionInfo(interp, 190 (char *) &eiPtr->body.ei, eiPtr->body.ei.optionTable, 191 (objc == 5) ? objv[4] : NULL, textPtr->tkwin); 192 if (objPtr == NULL) { 193 return TCL_ERROR; 194 } else { 195 Tcl_SetObjResult(interp, objPtr); 196 return TCL_OK; 197 } 198 } else { 199 TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index); 200 201 /* 202 * It's probably not true that all window configuration can change 203 * the line height, so we could be more efficient here and only 204 * call this when necessary. 205 */ 206 207 TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL, 208 index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY); 209 return EmbImageConfigure(textPtr, eiPtr, objc-4, objv+4); 210 } 211 case CMD_CREATE: { 212 int lineIndex; 213 214 /* 215 * Add a new image. Find where to put the new image, and mark that 216 * position for redisplay. 217 */ 218 219 if (objc < 4) { 220 Tcl_WrongNumArgs(interp, 3, objv, "index ?option value ...?"); 221 return TCL_ERROR; 222 } 223 if (TkTextGetObjIndex(interp, textPtr, objv[3], &index) != TCL_OK) { 224 return TCL_ERROR; 225 } 226 227 /* 228 * Don't allow insertions on the last (dummy) line of the text. 229 */ 230 231 lineIndex = TkBTreeLinesTo(textPtr, index.linePtr); 232 if (lineIndex == TkBTreeNumLines(textPtr->sharedTextPtr->tree, 233 textPtr)) { 234 lineIndex--; 235 TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 236 lineIndex, 1000000, &index); 237 } 238 239 /* 240 * Create the new image segment and initialize it. 241 */ 242 243 eiPtr = (TkTextSegment *) ckalloc(EI_SEG_SIZE); 244 eiPtr->typePtr = &tkTextEmbImageType; 245 eiPtr->size = 1; 246 eiPtr->body.ei.sharedTextPtr = textPtr->sharedTextPtr; 247 eiPtr->body.ei.linePtr = NULL; 248 eiPtr->body.ei.imageName = NULL; 249 eiPtr->body.ei.imageString = NULL; 250 eiPtr->body.ei.name = NULL; 251 eiPtr->body.ei.image = NULL; 252 eiPtr->body.ei.align = ALIGN_CENTER; 253 eiPtr->body.ei.padX = eiPtr->body.ei.padY = 0; 254 eiPtr->body.ei.chunkCount = 0; 255 eiPtr->body.ei.optionTable = Tk_CreateOptionTable(interp, optionSpecs); 256 257 /* 258 * Link the segment into the text widget, then configure it (delete it 259 * again if the configuration fails). 260 */ 261 262 TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index); 263 TkBTreeLinkSegment(eiPtr, &index); 264 if (EmbImageConfigure(textPtr, eiPtr, objc-4, objv+4) != TCL_OK) { 265 TkTextIndex index2; 266 267 TkTextIndexForwChars(NULL, &index, 1, &index2, COUNT_INDICES); 268 TkBTreeDeleteIndexRange(textPtr->sharedTextPtr->tree, &index, &index2); 269 return TCL_ERROR; 270 } 271 TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL, 272 index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY); 273 return TCL_OK; 274 } 275 case CMD_NAMES: { 276 Tcl_HashSearch search; 277 Tcl_HashEntry *hPtr; 278 279 if (objc != 3) { 280 Tcl_WrongNumArgs(interp, 3, objv, NULL); 281 return TCL_ERROR; 282 } 283 for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->imageTable, 284 &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { 285 Tcl_AppendElement(interp, 286 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr)); 287 } 288 return TCL_OK; 289 } 290 default: 291 Tcl_Panic("unexpected switch fallthrough"); 292 } 293 return TCL_ERROR; 294} 295 296/* 297 *-------------------------------------------------------------- 298 * 299 * EmbImageConfigure -- 300 * 301 * This function is called to handle configuration options for an 302 * embedded image, using an objc/objv list. 303 * 304 * Results: 305 * The return value is a standard Tcl result. If TCL_ERROR is returned, 306 * then the interp's result contains an error message.. 307 * 308 * Side effects: 309 * Configuration information for the embedded image changes, such as 310 * alignment, or name of the image. 311 * 312 *-------------------------------------------------------------- 313 */ 314 315static int 316EmbImageConfigure( 317 TkText *textPtr, /* Information about text widget that contains 318 * embedded image. */ 319 TkTextSegment *eiPtr, /* Embedded image to be configured. */ 320 int objc, /* Number of strings in objv. */ 321 Tcl_Obj *const objv[]) /* Array of strings describing configuration 322 * options. */ 323{ 324 Tk_Image image; 325 Tcl_DString newName; 326 Tcl_HashEntry *hPtr; 327 Tcl_HashSearch search; 328 char *name; 329 int count = 0; /* The counter for picking a unique name */ 330 int conflict = 0; /* True if we have a name conflict */ 331 size_t len; /* length of image name */ 332 333 if (Tk_SetOptions(textPtr->interp, (char*)&eiPtr->body.ei, 334 eiPtr->body.ei.optionTable, 335 objc, objv, textPtr->tkwin, NULL, NULL) != TCL_OK) { 336 return TCL_ERROR; 337 } 338 339 /* 340 * Create the image. Save the old image around and don't free it until 341 * after the new one is allocated. This keeps the reference count from 342 * going to zero so the image doesn't have to be recreated if it hasn't 343 * changed. 344 */ 345 346 if (eiPtr->body.ei.imageString != NULL) { 347 image = Tk_GetImage(textPtr->interp, textPtr->tkwin, 348 eiPtr->body.ei.imageString, EmbImageProc, (ClientData) eiPtr); 349 if (image == NULL) { 350 return TCL_ERROR; 351 } 352 } else { 353 image = NULL; 354 } 355 if (eiPtr->body.ei.image != NULL) { 356 Tk_FreeImage(eiPtr->body.ei.image); 357 } 358 eiPtr->body.ei.image = image; 359 360 if (eiPtr->body.ei.name != NULL) { 361 return TCL_OK; 362 } 363 364 /* 365 * Find a unique name for this image. Use imageName (or imageString) if 366 * available, otherwise tack on a #nn and use it. If a name is already 367 * associated with this image, delete the name. 368 */ 369 370 name = eiPtr->body.ei.imageName; 371 if (name == NULL) { 372 name = eiPtr->body.ei.imageString; 373 } 374 if (name == NULL) { 375 Tcl_AppendResult(textPtr->interp, "Either a \"-name\" ", 376 "or a \"-image\" argument must be provided ", 377 "to the \"image create\" subcommand.", NULL); 378 return TCL_ERROR; 379 } 380 len = strlen(name); 381 for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->imageTable, 382 &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { 383 char *haveName = 384 Tcl_GetHashKey(&textPtr->sharedTextPtr->imageTable, hPtr); 385 386 if (strncmp(name, haveName, len) == 0) { 387 int newVal = 0; 388 389 sscanf(haveName+len, "#%d", &newVal); 390 if (newVal > count) { 391 count = newVal; 392 } 393 if (len == strlen(haveName)) { 394 conflict = 1; 395 } 396 } 397 } 398 399 Tcl_DStringInit(&newName); 400 Tcl_DStringAppend(&newName, name, -1); 401 402 if (conflict) { 403 char buf[4 + TCL_INTEGER_SPACE]; 404 405 sprintf(buf, "#%d", count+1); 406 Tcl_DStringAppend(&newName, buf, -1); 407 } 408 name = Tcl_DStringValue(&newName); 409 { 410 int dummy; 411 412 hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->imageTable, name, 413 &dummy); 414 } 415 Tcl_SetHashValue(hPtr, eiPtr); 416 Tcl_AppendResult(textPtr->interp, name, NULL); 417 eiPtr->body.ei.name = ckalloc((unsigned) Tcl_DStringLength(&newName)+1); 418 strcpy(eiPtr->body.ei.name, name); 419 Tcl_DStringFree(&newName); 420 421 return TCL_OK; 422} 423 424/* 425 *-------------------------------------------------------------- 426 * 427 * EmbImageDeleteProc -- 428 * 429 * This function is invoked by the text B-tree code whenever an embedded 430 * image lies in a range of characters being deleted. 431 * 432 * Results: 433 * Returns 0 to indicate that the deletion has been accepted. 434 * 435 * Side effects: 436 * The embedded image is deleted, if it exists, and any resources 437 * associated with it are released. 438 * 439 *-------------------------------------------------------------- 440 */ 441 442 /* ARGSUSED */ 443static int 444EmbImageDeleteProc( 445 TkTextSegment *eiPtr, /* Segment being deleted. */ 446 TkTextLine *linePtr, /* Line containing segment. */ 447 int treeGone) /* Non-zero means the entire tree is being 448 * deleted, so everything must get cleaned 449 * up. */ 450{ 451 Tcl_HashEntry *hPtr; 452 453 if (eiPtr->body.ei.image != NULL) { 454 hPtr = Tcl_FindHashEntry(&eiPtr->body.ei.sharedTextPtr->imageTable, 455 eiPtr->body.ei.name); 456 if (hPtr != NULL) { 457 /* 458 * (It's possible for there to be no hash table entry for this 459 * image, if an error occurred while creating the image segment 460 * but before the image got added to the table) 461 */ 462 463 Tcl_DeleteHashEntry(hPtr); 464 } 465 Tk_FreeImage(eiPtr->body.ei.image); 466 } 467 468 /* 469 * No need to supply a tkwin argument, since we have no window-specific 470 * options. 471 */ 472 473 Tk_FreeConfigOptions((char *) &eiPtr->body.ei, eiPtr->body.ei.optionTable, 474 NULL); 475 if (eiPtr->body.ei.name) { 476 ckfree(eiPtr->body.ei.name); 477 } 478 ckfree((char *) eiPtr); 479 return 0; 480} 481 482/* 483 *-------------------------------------------------------------- 484 * 485 * EmbImageCleanupProc -- 486 * 487 * This function is invoked by the B-tree code whenever a segment 488 * containing an embedded image is moved from one line to another. 489 * 490 * Results: 491 * None. 492 * 493 * Side effects: 494 * The linePtr field of the segment gets updated. 495 * 496 *-------------------------------------------------------------- 497 */ 498 499static TkTextSegment * 500EmbImageCleanupProc( 501 TkTextSegment *eiPtr, /* Mark segment that's being moved. */ 502 TkTextLine *linePtr) /* Line that now contains segment. */ 503{ 504 eiPtr->body.ei.linePtr = linePtr; 505 return eiPtr; 506} 507 508/* 509 *-------------------------------------------------------------- 510 * 511 * EmbImageLayoutProc -- 512 * 513 * This function is the "layoutProc" for embedded image segments. 514 * 515 * Results: 516 * 1 is returned to indicate that the segment should be displayed. The 517 * chunkPtr structure is filled in. 518 * 519 * Side effects: 520 * None, except for filling in chunkPtr. 521 * 522 *-------------------------------------------------------------- 523 */ 524 525 /*ARGSUSED*/ 526static int 527EmbImageLayoutProc( 528 TkText *textPtr, /* Text widget being layed out. */ 529 TkTextIndex *indexPtr, /* Identifies first character in chunk. */ 530 TkTextSegment *eiPtr, /* Segment corresponding to indexPtr. */ 531 int offset, /* Offset within segPtr corresponding to 532 * indexPtr (always 0). */ 533 int maxX, /* Chunk must not occupy pixels at this 534 * position or higher. */ 535 int maxChars, /* Chunk must not include more than this many 536 * characters. */ 537 int noCharsYet, /* Non-zero means no characters have been 538 * assigned to this line yet. */ 539 TkWrapMode wrapMode, /* Wrap mode to use for line: 540 * TEXT_WRAPMODE_CHAR, TEXT_WRAPMODE_NONE, or 541 * TEXT_WRAPMODE_WORD. */ 542 register TkTextDispChunk *chunkPtr) 543 /* Structure to fill in with information about 544 * this chunk. The x field has already been 545 * set by the caller. */ 546{ 547 int width, height; 548 549 if (offset != 0) { 550 Tcl_Panic("Non-zero offset in EmbImageLayoutProc"); 551 } 552 553 /* 554 * See if there's room for this image on this line. 555 */ 556 557 if (eiPtr->body.ei.image == NULL) { 558 width = 0; 559 height = 0; 560 } else { 561 Tk_SizeOfImage(eiPtr->body.ei.image, &width, &height); 562 width += 2*eiPtr->body.ei.padX; 563 height += 2*eiPtr->body.ei.padY; 564 } 565 if ((width > (maxX - chunkPtr->x)) 566 && !noCharsYet && (textPtr->wrapMode != TEXT_WRAPMODE_NONE)) { 567 return 0; 568 } 569 570 /* 571 * Fill in the chunk structure. 572 */ 573 574 chunkPtr->displayProc = EmbImageDisplayProc; 575 chunkPtr->undisplayProc = NULL; 576 chunkPtr->measureProc = NULL; 577 chunkPtr->bboxProc = EmbImageBboxProc; 578 chunkPtr->numBytes = 1; 579 if (eiPtr->body.ei.align == ALIGN_BASELINE) { 580 chunkPtr->minAscent = height - eiPtr->body.ei.padY; 581 chunkPtr->minDescent = eiPtr->body.ei.padY; 582 chunkPtr->minHeight = 0; 583 } else { 584 chunkPtr->minAscent = 0; 585 chunkPtr->minDescent = 0; 586 chunkPtr->minHeight = height; 587 } 588 chunkPtr->width = width; 589 chunkPtr->breakIndex = -1; 590 chunkPtr->breakIndex = 1; 591 chunkPtr->clientData = (ClientData) eiPtr; 592 eiPtr->body.ei.chunkCount += 1; 593 return 1; 594} 595 596/* 597 *-------------------------------------------------------------- 598 * 599 * EmbImageCheckProc -- 600 * 601 * This function is invoked by the B-tree code to perform consistency 602 * checks on embedded images. 603 * 604 * Results: 605 * None. 606 * 607 * Side effects: 608 * The function panics if it detects anything wrong with the embedded 609 * image. 610 * 611 *-------------------------------------------------------------- 612 */ 613 614static void 615EmbImageCheckProc( 616 TkTextSegment *eiPtr, /* Segment to check. */ 617 TkTextLine *linePtr) /* Line containing segment. */ 618{ 619 if (eiPtr->nextPtr == NULL) { 620 Tcl_Panic("EmbImageCheckProc: embedded image is last segment in line"); 621 } 622 if (eiPtr->size != 1) { 623 Tcl_Panic("EmbImageCheckProc: embedded image has size %d", 624 eiPtr->size); 625 } 626} 627 628/* 629 *-------------------------------------------------------------- 630 * 631 * EmbImageDisplayProc -- 632 * 633 * This function is invoked by the text displaying code when it is time 634 * to actually draw an embedded image chunk on the screen. 635 * 636 * Results: 637 * None. 638 * 639 * Side effects: 640 * The embedded image gets moved to the correct location and drawn onto 641 * the display. 642 * 643 *-------------------------------------------------------------- 644 */ 645 646static void 647EmbImageDisplayProc( 648 TkText *textPtr, 649 TkTextDispChunk *chunkPtr, /* Chunk that is to be drawn. */ 650 int x, /* X-position in dst at which to draw this 651 * chunk (differs from the x-position in the 652 * chunk because of scrolling). */ 653 int y, /* Top of rectangular bounding box for line: 654 * tells where to draw this chunk in dst 655 * (x-position is in the chunk itself). */ 656 int lineHeight, /* Total height of line. */ 657 int baseline, /* Offset of baseline from y. */ 658 Display *display, /* Display to use for drawing. */ 659 Drawable dst, /* Pixmap or window in which to draw */ 660 int screenY) /* Y-coordinate in text window that 661 * corresponds to y. */ 662{ 663 TkTextSegment *eiPtr = (TkTextSegment *) chunkPtr->clientData; 664 int lineX, imageX, imageY, width, height; 665 Tk_Image image; 666 667 image = eiPtr->body.ei.image; 668 if (image == NULL) { 669 return; 670 } 671 if ((x + chunkPtr->width) <= 0) { 672 return; 673 } 674 675 /* 676 * Compute the image's location and size in the text widget, taking into 677 * account the align value for the image. 678 */ 679 680 EmbImageBboxProc(textPtr, chunkPtr, 0, y, lineHeight, baseline, &lineX, 681 &imageY, &width, &height); 682 imageX = lineX - chunkPtr->x + x; 683 684 Tk_RedrawImage(image, 0, 0, width, height, dst, imageX, imageY); 685} 686 687/* 688 *-------------------------------------------------------------- 689 * 690 * EmbImageBboxProc -- 691 * 692 * This function is called to compute the bounding box of the area 693 * occupied by an embedded image. 694 * 695 * Results: 696 * There is no return value. *xPtr and *yPtr are filled in with the 697 * coordinates of the upper left corner of the image, and *widthPtr and 698 * *heightPtr are filled in with the dimensions of the image in pixels. 699 * Note: not all of the returned bbox is necessarily visible on the 700 * screen (the rightmost part might be off-screen to the right, and the 701 * bottommost part might be off-screen to the bottom). 702 * 703 * Side effects: 704 * None. 705 * 706 *-------------------------------------------------------------- 707 */ 708 709static void 710EmbImageBboxProc( 711 TkText *textPtr, 712 TkTextDispChunk *chunkPtr, /* Chunk containing desired char. */ 713 int index, /* Index of desired character within the 714 * chunk. */ 715 int y, /* Topmost pixel in area allocated for this 716 * line. */ 717 int lineHeight, /* Total height of line. */ 718 int baseline, /* Location of line's baseline, in pixels 719 * measured down from y. */ 720 int *xPtr, int *yPtr, /* Gets filled in with coords of character's 721 * upper-left pixel. */ 722 int *widthPtr, /* Gets filled in with width of image, in 723 * pixels. */ 724 int *heightPtr) /* Gets filled in with height of image, in 725 * pixels. */ 726{ 727 TkTextSegment *eiPtr = (TkTextSegment *) chunkPtr->clientData; 728 Tk_Image image; 729 730 image = eiPtr->body.ei.image; 731 if (image != NULL) { 732 Tk_SizeOfImage(image, widthPtr, heightPtr); 733 } else { 734 *widthPtr = 0; 735 *heightPtr = 0; 736 } 737 738 *xPtr = chunkPtr->x + eiPtr->body.ei.padX; 739 740 switch (eiPtr->body.ei.align) { 741 case ALIGN_BOTTOM: 742 *yPtr = y + (lineHeight - *heightPtr - eiPtr->body.ei.padY); 743 break; 744 case ALIGN_CENTER: 745 *yPtr = y + (lineHeight - *heightPtr)/2; 746 break; 747 case ALIGN_TOP: 748 *yPtr = y + eiPtr->body.ei.padY; 749 break; 750 case ALIGN_BASELINE: 751 *yPtr = y + (baseline - *heightPtr); 752 break; 753 } 754} 755 756/* 757 *-------------------------------------------------------------- 758 * 759 * TkTextImageIndex -- 760 * 761 * Given the name of an embedded image within a text widget, returns an 762 * index corresponding to the image's position in the text. 763 * 764 * Results: 765 * The return value is 1 if there is an embedded image by the given name 766 * in the text widget, 0 otherwise. If the image exists, *indexPtr is 767 * filled in with its index. 768 * 769 * Side effects: 770 * None. 771 * 772 *-------------------------------------------------------------- 773 */ 774 775int 776TkTextImageIndex( 777 TkText *textPtr, /* Text widget containing image. */ 778 const char *name, /* Name of image. */ 779 TkTextIndex *indexPtr) /* Index information gets stored here. */ 780{ 781 Tcl_HashEntry *hPtr; 782 TkTextSegment *eiPtr; 783 784 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->imageTable, name); 785 if (hPtr == NULL) { 786 return 0; 787 } 788 eiPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 789 indexPtr->tree = textPtr->sharedTextPtr->tree; 790 indexPtr->linePtr = eiPtr->body.ei.linePtr; 791 indexPtr->byteIndex = TkTextSegToOffset(eiPtr, indexPtr->linePtr); 792 return 1; 793} 794 795/* 796 *-------------------------------------------------------------- 797 * 798 * EmbImageProc -- 799 * 800 * This function is called by the image code whenever an image or its 801 * contents changes. 802 * 803 * Results: 804 * None. 805 * 806 * Side effects: 807 * The image will be redisplayed. 808 * 809 *-------------------------------------------------------------- 810 */ 811 812static void 813EmbImageProc( 814 ClientData clientData, /* Pointer to widget record. */ 815 int x, int y, /* Upper left pixel (within image) that must 816 * be redisplayed. */ 817 int width, int height, /* Dimensions of area to redisplay (may be 818 * <= 0). */ 819 int imgWidth, int imgHeight)/* New dimensions of image. */ 820 821{ 822 TkTextSegment *eiPtr = (TkTextSegment *) clientData; 823 TkTextIndex index; 824 825 index.tree = eiPtr->body.ei.sharedTextPtr->tree; 826 index.linePtr = eiPtr->body.ei.linePtr; 827 index.byteIndex = TkTextSegToOffset(eiPtr, eiPtr->body.ei.linePtr); 828 TkTextChanged(eiPtr->body.ei.sharedTextPtr, NULL, &index, &index); 829 830 /* 831 * It's probably not true that all image changes can change the line 832 * height, so we could be more efficient here and only call this when 833 * necessary. 834 */ 835 836 TkTextInvalidateLineMetrics(eiPtr->body.ei.sharedTextPtr, NULL, 837 index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY); 838} 839 840/* 841 * Local Variables: 842 * mode: c 843 * c-basic-offset: 4 844 * fill-column: 78 845 * End: 846 */ 847