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