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