OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains3x.git] / tk / generic / tkTextWind.c
1 /* 
2  * tkTextWind.c --
3  *
4  *      This file contains code that allows arbitrary windows to be
5  *      nested inside text widgets.  It also implements the "window"
6  *      widget command for texts.
7  *
8  * Copyright (c) 1994 The Regents of the University of California.
9  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  *
14  * RCS: @(#) $Id$
15  */
16
17 #include "tk.h"
18 #include "tkText.h"
19 #include "tkPort.h"
20
21 /*
22  * The following structure is the official type record for the
23  * embedded window geometry manager:
24  */
25
26 static void             EmbWinRequestProc _ANSI_ARGS_((ClientData clientData,
27                             Tk_Window tkwin));
28 static void             EmbWinLostSlaveProc _ANSI_ARGS_((ClientData clientData,
29                             Tk_Window tkwin));
30
31 static Tk_GeomMgr textGeomType = {
32     "text",                     /* name */
33     EmbWinRequestProc,          /* requestProc */
34     EmbWinLostSlaveProc,        /* lostSlaveProc */
35 };
36
37 /*
38  * Definitions for alignment values:
39  */
40
41 #define ALIGN_BOTTOM            0
42 #define ALIGN_CENTER            1
43 #define ALIGN_TOP               2
44 #define ALIGN_BASELINE          3
45
46 /*
47  * Macro that determines the size of an embedded window segment:
48  */
49
50 #define EW_SEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
51         + sizeof(TkTextEmbWindow)))
52
53 /*
54  * Prototypes for procedures defined in this file:
55  */
56
57 static int              AlignParseProc _ANSI_ARGS_((ClientData clientData,
58                             Tcl_Interp *interp, Tk_Window tkwin, char *value,
59                             char *widgRec, int offset));
60 static char *           AlignPrintProc _ANSI_ARGS_((ClientData clientData,
61                             Tk_Window tkwin, char *widgRec, int offset,
62                             Tcl_FreeProc **freeProcPtr));
63 static TkTextSegment *  EmbWinCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
64                             TkTextLine *linePtr));
65 static void             EmbWinCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
66                             TkTextLine *linePtr));
67 static void             EmbWinBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
68                             int index, int y, int lineHeight, int baseline,
69                             int *xPtr, int *yPtr, int *widthPtr,
70                             int *heightPtr));
71 static int              EmbWinConfigure _ANSI_ARGS_((TkText *textPtr,
72                             TkTextSegment *ewPtr, int argc, char **argv));
73 static void             EmbWinDelayedUnmap _ANSI_ARGS_((
74                             ClientData clientData));
75 static int              EmbWinDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
76                             TkTextLine *linePtr, int treeGone));
77 static void             EmbWinDisplayProc _ANSI_ARGS_((
78                             TkTextDispChunk *chunkPtr, int x, int y,
79                             int lineHeight, int baseline, Display *display,
80                             Drawable dst, int screenY));
81 static int              EmbWinLayoutProc _ANSI_ARGS_((TkText *textPtr,
82                             TkTextIndex *indexPtr, TkTextSegment *segPtr,
83                             int offset, int maxX, int maxChars,
84                             int noCharsYet, Tk_Uid wrapMode,
85                             TkTextDispChunk *chunkPtr));
86 static void             EmbWinStructureProc _ANSI_ARGS_((ClientData clientData,
87                             XEvent *eventPtr));
88 static void             EmbWinUndisplayProc _ANSI_ARGS_((TkText *textPtr,
89                             TkTextDispChunk *chunkPtr));
90
91 /*
92  * The following structure declares the "embedded window" segment type.
93  */
94
95 static Tk_SegType tkTextEmbWindowType = {
96     "window",                                   /* name */
97     0,                                          /* leftGravity */
98     (Tk_SegSplitProc *) NULL,                   /* splitProc */
99     EmbWinDeleteProc,                           /* deleteProc */
100     EmbWinCleanupProc,                          /* cleanupProc */
101     (Tk_SegLineChangeProc *) NULL,              /* lineChangeProc */
102     EmbWinLayoutProc,                           /* layoutProc */
103     EmbWinCheckProc                             /* checkProc */
104 };
105
106 /*
107  * Information used for parsing window configuration options:
108  */
109
110 static Tk_CustomOption alignOption = {AlignParseProc, AlignPrintProc,
111         (ClientData) NULL};
112
113 static Tk_ConfigSpec configSpecs[] = {
114     {TK_CONFIG_CUSTOM, "-align", (char *) NULL, (char *) NULL,
115         "center", 0, TK_CONFIG_DONT_SET_DEFAULT, &alignOption},
116     {TK_CONFIG_STRING, "-create", (char *) NULL, (char *) NULL,
117         (char *) NULL, Tk_Offset(TkTextEmbWindow, create),
118         TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
119     {TK_CONFIG_INT, "-padx", (char *) NULL, (char *) NULL,
120         "0", Tk_Offset(TkTextEmbWindow, padX),
121         TK_CONFIG_DONT_SET_DEFAULT},
122     {TK_CONFIG_INT, "-pady", (char *) NULL, (char *) NULL,
123         "0", Tk_Offset(TkTextEmbWindow, padY),
124         TK_CONFIG_DONT_SET_DEFAULT},
125     {TK_CONFIG_BOOLEAN, "-stretch", (char *) NULL, (char *) NULL,
126         "0", Tk_Offset(TkTextEmbWindow, stretch),
127         TK_CONFIG_DONT_SET_DEFAULT},
128     {TK_CONFIG_WINDOW, "-window", (char *) NULL, (char *) NULL,
129         (char *) NULL, Tk_Offset(TkTextEmbWindow, tkwin),
130         TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
131     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
132         (char *) NULL, 0, 0}
133 };
134 \f
135 /*
136  *--------------------------------------------------------------
137  *
138  * TkTextWindowCmd --
139  *
140  *      This procedure implements the "window" widget command
141  *      for text widgets.  See the user documentation for details
142  *      on what it does.
143  *
144  * Results:
145  *      A standard Tcl result or error.
146  *
147  * Side effects:
148  *      See the user documentation.
149  *
150  *--------------------------------------------------------------
151  */
152
153 int
154 TkTextWindowCmd(textPtr, interp, argc, argv)
155     register TkText *textPtr;   /* Information about text widget. */
156     Tcl_Interp *interp;         /* Current interpreter. */
157     int argc;                   /* Number of arguments. */
158     char **argv;                /* Argument strings.  Someone else has already
159                                  * parsed this command enough to know that
160                                  * argv[1] is "window". */
161 {
162     size_t length;
163     register TkTextSegment *ewPtr;
164
165     if (argc < 3) {
166         Tcl_AppendResult(interp, "wrong # args: should be \"",
167                 argv[0], " window option ?arg arg ...?\"", (char *) NULL);
168         return TCL_ERROR;
169     }
170     length = strlen(argv[2]);
171     if ((strncmp(argv[2], "cget", length) == 0) && (length >= 2)) {
172         TkTextIndex index;
173         TkTextSegment *ewPtr;
174
175         if (argc != 5) {
176             Tcl_AppendResult(interp, "wrong # args: should be \"",
177                     argv[0], " window cget index option\"",
178                     (char *) NULL);
179             return TCL_ERROR;
180         }
181         if (TkTextGetIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
182             return TCL_ERROR;
183         }
184         ewPtr = TkTextIndexToSeg(&index, (int *) NULL);
185         if (ewPtr->typePtr != &tkTextEmbWindowType) {
186             Tcl_AppendResult(interp, "no embedded window at index \"",
187                     argv[3], "\"", (char *) NULL);
188             return TCL_ERROR;
189         }
190         return Tk_ConfigureValue(interp, textPtr->tkwin, configSpecs,
191                 (char *) &ewPtr->body.ew, argv[4], 0);
192     } else if ((strncmp(argv[2], "configure", length) == 0) && (length >= 2)) {
193         TkTextIndex index;
194         TkTextSegment *ewPtr;
195
196         if (argc < 4) {
197             Tcl_AppendResult(interp, "wrong # args: should be \"",
198                     argv[0], " window configure index ?option value ...?\"",
199                     (char *) NULL);
200             return TCL_ERROR;
201         }
202         if (TkTextGetIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
203             return TCL_ERROR;
204         }
205         ewPtr = TkTextIndexToSeg(&index, (int *) NULL);
206         if (ewPtr->typePtr != &tkTextEmbWindowType) {
207             Tcl_AppendResult(interp, "no embedded window at index \"",
208                     argv[3], "\"", (char *) NULL);
209             return TCL_ERROR;
210         }
211         if (argc == 4) {
212             return Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
213                     (char *) &ewPtr->body.ew, (char *) NULL, 0);
214         } else if (argc == 5) {
215             return Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
216                     (char *) &ewPtr->body.ew, argv[4], 0);
217         } else {
218             TkTextChanged(textPtr, &index, &index);
219             return EmbWinConfigure(textPtr, ewPtr, argc-4, argv+4);
220         }
221     } else if ((strncmp(argv[2], "create", length) == 0) && (length >= 2)) {
222         TkTextIndex index;
223         int lineIndex;
224
225         /*
226          * Add a new window.  Find where to put the new window, and
227          * mark that position for redisplay.
228          */
229
230         if (argc < 4) {
231             Tcl_AppendResult(interp, "wrong # args: should be \"",
232                     argv[0], " window create index ?option value ...?\"",
233                     (char *) NULL);
234             return TCL_ERROR;
235         }
236         if (TkTextGetIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
237             return TCL_ERROR;
238         }
239
240         /*
241          * Don't allow insertions on the last (dummy) line of the text.
242          */
243     
244         lineIndex = TkBTreeLineIndex(index.linePtr);
245         if (lineIndex == TkBTreeNumLines(textPtr->tree)) {
246             lineIndex--;
247             TkTextMakeIndex(textPtr->tree, lineIndex, 1000000, &index);
248         }
249
250         /*
251          * Create the new window segment and initialize it.
252          */
253
254         ewPtr = (TkTextSegment *) ckalloc(EW_SEG_SIZE);
255         ewPtr->typePtr = &tkTextEmbWindowType;
256         ewPtr->size = 1;
257         ewPtr->body.ew.textPtr = textPtr;
258         ewPtr->body.ew.linePtr = NULL;
259         ewPtr->body.ew.tkwin = NULL;
260         ewPtr->body.ew.create = NULL;
261         ewPtr->body.ew.align = ALIGN_CENTER;
262         ewPtr->body.ew.padX = ewPtr->body.ew.padY = 0;
263         ewPtr->body.ew.stretch = 0;
264         ewPtr->body.ew.chunkCount = 0;
265         ewPtr->body.ew.displayed = 0;
266
267         /*
268          * Link the segment into the text widget, then configure it (delete
269          * it again if the configuration fails).
270          */
271
272         TkTextChanged(textPtr, &index, &index);
273         TkBTreeLinkSegment(ewPtr, &index);
274         if (EmbWinConfigure(textPtr, ewPtr, argc-4, argv+4) != TCL_OK) {
275             TkTextIndex index2;
276
277             TkTextIndexForwChars(&index, 1, &index2);
278             TkBTreeDeleteChars(&index, &index2);
279             return TCL_ERROR;
280         }
281     } else if (strncmp(argv[2], "names", length) == 0) {
282         Tcl_HashSearch search;
283         Tcl_HashEntry *hPtr;
284
285         if (argc != 3) {
286             Tcl_AppendResult(interp, "wrong # args: should be \"",
287                     argv[0], " window names\"", (char *) NULL);
288             return TCL_ERROR;
289         }
290         for (hPtr = Tcl_FirstHashEntry(&textPtr->windowTable, &search);
291                 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
292             Tcl_AppendElement(interp,
293                     Tcl_GetHashKey(&textPtr->markTable, hPtr));
294         }
295     } else {
296         Tcl_AppendResult(interp, "bad window option \"", argv[2],
297                 "\": must be cget, configure, create, or names",
298                 (char *) NULL);
299         return TCL_ERROR;
300     }
301     return TCL_OK;
302 }
303 \f
304 /*
305  *--------------------------------------------------------------
306  *
307  * EmbWinConfigure --
308  *
309  *      This procedure is called to handle configuration options
310  *      for an embedded window, using an argc/argv list.
311  *
312  * Results:
313  *      The return value is a standard Tcl result.  If TCL_ERROR is
314  *      returned, then interp->result contains an error message..
315  *
316  * Side effects:
317  *      Configuration information for the embedded window changes,
318  *      such as alignment, stretching, or name of the embedded
319  *      window.
320  *
321  *--------------------------------------------------------------
322  */
323
324 static int
325 EmbWinConfigure(textPtr, ewPtr, argc, argv)
326     TkText *textPtr;            /* Information about text widget that
327                                  * contains embedded window. */
328     TkTextSegment *ewPtr;       /* Embedded window to be configured. */
329     int argc;                   /* Number of strings in argv. */
330     char **argv;                /* Array of strings describing configuration
331                                  * options. */
332 {
333     Tk_Window oldWindow;
334     Tcl_HashEntry *hPtr;
335     int new;
336
337     oldWindow = ewPtr->body.ew.tkwin;
338     if (Tk_ConfigureWidget(textPtr->interp, textPtr->tkwin, configSpecs,
339             argc, argv, (char *) &ewPtr->body.ew, TK_CONFIG_ARGV_ONLY)
340             != TCL_OK) {
341         return TCL_ERROR;
342     }
343     if (oldWindow != ewPtr->body.ew.tkwin) {
344         if (oldWindow != NULL) {
345             Tcl_DeleteHashEntry(Tcl_FindHashEntry(&textPtr->windowTable,
346                     Tk_PathName(oldWindow)));
347             Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
348                     EmbWinStructureProc, (ClientData) ewPtr);
349             Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
350                     (ClientData) NULL);
351             if (textPtr->tkwin != Tk_Parent(oldWindow)) {
352                 Tk_UnmaintainGeometry(oldWindow, textPtr->tkwin);
353             } else {
354                 Tk_UnmapWindow(oldWindow);
355             }
356         }
357         if (ewPtr->body.ew.tkwin != NULL) {
358             Tk_Window ancestor, parent;
359
360             /*
361              * Make sure that the text is either the parent of the
362              * embedded window or a descendant of that parent.  Also,
363              * don't allow a top-level window to be managed inside
364              * a text.
365              */
366
367             parent = Tk_Parent(ewPtr->body.ew.tkwin);
368             for (ancestor = textPtr->tkwin; ;
369                     ancestor = Tk_Parent(ancestor)) {
370                 if (ancestor == parent) {
371                     break;
372                 }
373                 if (Tk_IsTopLevel(ancestor)) {
374                     badMaster:
375                     Tcl_AppendResult(textPtr->interp, "can't embed ",
376                             Tk_PathName(ewPtr->body.ew.tkwin), " in ",
377                             Tk_PathName(textPtr->tkwin), (char *) NULL);
378                     ewPtr->body.ew.tkwin = NULL;
379                     return TCL_ERROR;
380                 }
381             }
382             if (Tk_IsTopLevel(ewPtr->body.ew.tkwin)
383                     || (ewPtr->body.ew.tkwin == textPtr->tkwin)) {
384                 goto badMaster;
385             }
386
387             /*
388              * Take over geometry management for the window, plus create
389              * an event handler to find out when it is deleted.
390              */
391
392             Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
393                     (ClientData) ewPtr);
394             Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
395                     EmbWinStructureProc, (ClientData) ewPtr);
396
397             /*
398              * Special trick!  Must enter into the hash table *after*
399              * calling Tk_ManageGeometry:  if the window was already managed
400              * elsewhere in this text, the Tk_ManageGeometry call will cause
401              * the entry to be removed, which could potentially lose the new
402              * entry.
403              */
404
405             hPtr = Tcl_CreateHashEntry(&textPtr->windowTable,
406                     Tk_PathName(ewPtr->body.ew.tkwin), &new);
407             Tcl_SetHashValue(hPtr, ewPtr);
408
409         }
410     }
411     return TCL_OK;
412 }
413 \f
414 /*
415  *--------------------------------------------------------------
416  *
417  * AlignParseProc --
418  *
419  *      This procedure is invoked by Tk_ConfigureWidget during
420  *      option processing to handle "-align" options for embedded
421  *      windows.
422  *
423  * Results:
424  *      A standard Tcl return value.
425  *
426  * Side effects:
427  *      The alignment for the embedded window may change.
428  *
429  *--------------------------------------------------------------
430  */
431
432         /* ARGSUSED */
433 static int
434 AlignParseProc(clientData, interp, tkwin, value, widgRec, offset)
435     ClientData clientData;              /* Not used.*/
436     Tcl_Interp *interp;                 /* Used for reporting errors. */
437     Tk_Window tkwin;                    /* Window for text widget. */
438     char *value;                        /* Value of option. */
439     char *widgRec;                      /* Pointer to TkTextEmbWindow
440                                          * structure. */
441     int offset;                         /* Offset into item (ignored). */
442 {
443     register TkTextEmbWindow *embPtr = (TkTextEmbWindow *) widgRec;
444
445     if (strcmp(value, "baseline") == 0) {
446         embPtr->align = ALIGN_BASELINE;
447     } else if (strcmp(value, "bottom") == 0) {
448         embPtr->align = ALIGN_BOTTOM;
449     } else if (strcmp(value, "center") == 0) {
450         embPtr->align = ALIGN_CENTER;
451     } else if (strcmp(value, "top") == 0) {
452         embPtr->align = ALIGN_TOP;
453     } else {
454         Tcl_AppendResult(interp, "bad alignment \"", value,
455                 "\": must be baseline, bottom, center, or top",
456                 (char *) NULL);
457         return TCL_ERROR;
458     }
459     return TCL_OK;
460 }
461 \f
462 /*
463  *--------------------------------------------------------------
464  *
465  * AlignPrintProc --
466  *
467  *      This procedure is invoked by the Tk configuration code
468  *      to produce a printable string for the "-align" configuration
469  *      option for embedded windows.
470  *
471  * Results:
472  *      The return value is a string describing the embedded
473  *      window's current alignment.
474  *
475  * Side effects:
476  *      None.
477  *
478  *--------------------------------------------------------------
479  */
480
481         /* ARGSUSED */
482 static char *
483 AlignPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
484     ClientData clientData;              /* Ignored. */
485     Tk_Window tkwin;                    /* Window for text widget. */
486     char *widgRec;                      /* Pointer to TkTextEmbWindow
487                                          * structure. */
488     int offset;                         /* Ignored. */
489     Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with
490                                          * information about how to reclaim
491                                          * storage for return string. */
492 {
493     switch (((TkTextEmbWindow *) widgRec)->align) {
494         case ALIGN_BASELINE:
495             return "baseline";
496         case ALIGN_BOTTOM:
497             return "bottom";
498         case ALIGN_CENTER:
499             return "center";
500         case ALIGN_TOP:
501             return "top";
502         default:
503             return "??";
504     }
505 }
506 \f
507 /*
508  *--------------------------------------------------------------
509  *
510  * EmbWinStructureProc --
511  *
512  *      This procedure is invoked by the Tk event loop whenever
513  *      StructureNotify events occur for a window that's embedded
514  *      in a text widget.  This procedure's only purpose is to
515  *      clean up when windows are deleted.
516  *
517  * Results:
518  *      None.
519  *
520  * Side effects:
521  *      The window is disassociated from the window segment, and
522  *      the portion of the text is redisplayed.
523  *
524  *--------------------------------------------------------------
525  */
526
527 static void
528 EmbWinStructureProc(clientData, eventPtr)
529     ClientData clientData;      /* Pointer to record describing window item. */
530     XEvent *eventPtr;           /* Describes what just happened. */
531 {
532     register TkTextSegment *ewPtr = (TkTextSegment *) clientData;
533     TkTextIndex index;
534
535     if (eventPtr->type != DestroyNotify) {
536         return;
537     }
538
539     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
540             Tk_PathName(ewPtr->body.ew.tkwin)));
541     ewPtr->body.ew.tkwin = NULL;
542     index.tree = ewPtr->body.ew.textPtr->tree;
543     index.linePtr = ewPtr->body.ew.linePtr;
544     index.charIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
545     TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
546 }
547 \f
548 /*
549  *--------------------------------------------------------------
550  *
551  * EmbWinRequestProc --
552  *
553  *      This procedure is invoked whenever a window that's associated
554  *      with a window canvas item changes its requested dimensions.
555  *
556  * Results:
557  *      None.
558  *
559  * Side effects:
560  *      The size and location on the screen of the window may change,
561  *      depending on the options specified for the window item.
562  *
563  *--------------------------------------------------------------
564  */
565
566         /* ARGSUSED */
567 static void
568 EmbWinRequestProc(clientData, tkwin)
569     ClientData clientData;              /* Pointer to record for window item. */
570     Tk_Window tkwin;                    /* Window that changed its desired
571                                          * size. */
572 {
573     TkTextSegment *ewPtr = (TkTextSegment *) clientData;
574     TkTextIndex index;
575
576     index.tree = ewPtr->body.ew.textPtr->tree;
577     index.linePtr = ewPtr->body.ew.linePtr;
578     index.charIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
579     TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
580 }
581 \f
582 /*
583  *--------------------------------------------------------------
584  *
585  * EmbWinLostSlaveProc --
586  *
587  *      This procedure is invoked by the Tk geometry manager when
588  *      a slave window managed by a text widget is claimed away
589  *      by another geometry manager.
590  *
591  * Results:
592  *      None.
593  *
594  * Side effects:
595  *      The window is disassociated from the window segment, and
596  *      the portion of the text is redisplayed.
597  *
598  *--------------------------------------------------------------
599  */
600
601 static void
602 EmbWinLostSlaveProc(clientData, tkwin)
603     ClientData clientData;      /* Pointer to record describing window item. */
604     Tk_Window tkwin;            /* Window that was claimed away by another
605                                  * geometry manager. */
606 {
607     register TkTextSegment *ewPtr = (TkTextSegment *) clientData;
608     TkTextIndex index;
609
610     Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
611             EmbWinStructureProc, (ClientData) ewPtr);
612     Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
613     if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(tkwin)) {
614         Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin);
615     } else {
616         Tk_UnmapWindow(tkwin);
617     }
618     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
619             Tk_PathName(ewPtr->body.ew.tkwin)));
620     ewPtr->body.ew.tkwin = NULL;
621     index.tree = ewPtr->body.ew.textPtr->tree;
622     index.linePtr = ewPtr->body.ew.linePtr;
623     index.charIndex = TkTextSegToOffset(ewPtr, ewPtr->body.ew.linePtr);
624     TkTextChanged(ewPtr->body.ew.textPtr, &index, &index);
625 }
626 \f
627 /*
628  *--------------------------------------------------------------
629  *
630  * EmbWinDeleteProc --
631  *
632  *      This procedure is invoked by the text B-tree code whenever
633  *      an embedded window lies in a range of characters being deleted.
634  *
635  * Results:
636  *      Returns 0 to indicate that the deletion has been accepted.
637  *
638  * Side effects:
639  *      The embedded window is deleted, if it exists, and any resources
640  *      associated with it are released.
641  *
642  *--------------------------------------------------------------
643  */
644
645         /* ARGSUSED */
646 static int
647 EmbWinDeleteProc(ewPtr, linePtr, treeGone)
648     TkTextSegment *ewPtr;               /* Segment being deleted. */
649     TkTextLine *linePtr;                /* Line containing segment. */
650     int treeGone;                       /* Non-zero means the entire tree is
651                                          * being deleted, so everything must
652                                          * get cleaned up. */
653 {
654     Tcl_HashEntry *hPtr;
655
656     if (ewPtr->body.ew.tkwin != NULL) {
657         hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.textPtr->windowTable,
658                 Tk_PathName(ewPtr->body.ew.tkwin));
659         if (hPtr != NULL) {
660             /*
661              * (It's possible for there to be no hash table entry for this
662              * window, if an error occurred while creating the window segment
663              * but before the window got added to the table)
664              */
665
666             Tcl_DeleteHashEntry(hPtr);
667         }
668
669         /*
670          * Delete the event handler for the window before destroying
671          * the window, so that EmbWinStructureProc doesn't get called
672          * (we'll already do everything that it would have done, and
673          * it will just get confused).
674          */
675
676         Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
677                 EmbWinStructureProc, (ClientData) ewPtr);
678         Tk_DestroyWindow(ewPtr->body.ew.tkwin);
679     }
680     Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
681     Tk_FreeOptions(configSpecs, (char *) &ewPtr->body.ew,
682             ewPtr->body.ew.textPtr->display, 0);
683     ckfree((char *) ewPtr);
684     return 0;
685 }
686 \f
687 /*
688  *--------------------------------------------------------------
689  *
690  * EmbWinCleanupProc --
691  *
692  *      This procedure is invoked by the B-tree code whenever a
693  *      segment containing an embedded window is moved from one
694  *      line to another.
695  *
696  * Results:
697  *      None.
698  *
699  * Side effects:
700  *      The linePtr field of the segment gets updated.
701  *
702  *--------------------------------------------------------------
703  */
704
705 static TkTextSegment *
706 EmbWinCleanupProc(ewPtr, linePtr)
707     TkTextSegment *ewPtr;               /* Mark segment that's being moved. */
708     TkTextLine *linePtr;                /* Line that now contains segment. */
709 {
710     ewPtr->body.ew.linePtr = linePtr;
711     return ewPtr;
712 }
713 \f
714 /*
715  *--------------------------------------------------------------
716  *
717  * EmbWinLayoutProc --
718  *
719  *      This procedure is the "layoutProc" for embedded window
720  *      segments.
721  *
722  * Results:
723  *      1 is returned to indicate that the segment should be
724  *      displayed.  The chunkPtr structure is filled in.
725  *
726  * Side effects:
727  *      None, except for filling in chunkPtr.
728  *
729  *--------------------------------------------------------------
730  */
731
732         /*ARGSUSED*/
733 static int
734 EmbWinLayoutProc(textPtr, indexPtr, ewPtr, offset, maxX, maxChars,
735         noCharsYet, wrapMode, chunkPtr)
736     TkText *textPtr;            /* Text widget being layed out. */
737     TkTextIndex *indexPtr;      /* Identifies first character in chunk. */
738     TkTextSegment *ewPtr;       /* Segment corresponding to indexPtr. */
739     int offset;                 /* Offset within segPtr corresponding to
740                                  * indexPtr (always 0). */
741     int maxX;                   /* Chunk must not occupy pixels at this
742                                  * position or higher. */
743     int maxChars;               /* Chunk must not include more than this
744                                  * many characters. */
745     int noCharsYet;             /* Non-zero means no characters have been
746                                  * assigned to this line yet. */
747     Tk_Uid wrapMode;            /* Wrap mode to use for line: tkTextCharUid,
748                                  * tkTextNoneUid, or tkTextWordUid. */
749     register TkTextDispChunk *chunkPtr;
750                                 /* Structure to fill in with information
751                                  * about this chunk.  The x field has already
752                                  * been set by the caller. */
753 {
754     int width, height;
755
756     if (offset != 0) {
757         panic("Non-zero offset in EmbWinLayoutProc");
758     }
759
760     if ((ewPtr->body.ew.tkwin == NULL) && (ewPtr->body.ew.create != NULL)) {
761         int code, new;
762         Tcl_DString name;
763         Tk_Window ancestor;
764         Tcl_HashEntry *hPtr;
765
766         /*
767          * The window doesn't currently exist.  Create it by evaluating
768          * the creation script.  The script must return the window's
769          * path name:  look up that name to get back to the window
770          * token.  Then register ourselves as the geometry manager for
771          * the window.
772          */
773
774         code = Tcl_GlobalEval(textPtr->interp, ewPtr->body.ew.create);
775         if (code != TCL_OK) {
776             createError:
777             Tcl_BackgroundError(textPtr->interp);
778             goto gotWindow;
779         }
780         Tcl_DStringInit(&name);
781         Tcl_DStringAppend(&name, textPtr->interp->result, -1);
782         Tcl_ResetResult(textPtr->interp);
783         ewPtr->body.ew.tkwin = Tk_NameToWindow(textPtr->interp,
784                 Tcl_DStringValue(&name), textPtr->tkwin);
785         if (ewPtr->body.ew.tkwin == NULL) {
786             goto createError;
787         }
788         for (ancestor = textPtr->tkwin; ;
789                 ancestor = Tk_Parent(ancestor)) {
790             if (ancestor == Tk_Parent(ewPtr->body.ew.tkwin)) {
791                 break;
792             }
793             if (Tk_IsTopLevel(ancestor)) {
794                 badMaster:
795                 Tcl_AppendResult(textPtr->interp, "can't embed ",
796                         Tk_PathName(ewPtr->body.ew.tkwin), " relative to ",
797                         Tk_PathName(textPtr->tkwin), (char *) NULL);
798                 Tcl_BackgroundError(textPtr->interp);
799                 ewPtr->body.ew.tkwin = NULL;
800                 goto gotWindow;
801             }
802         }
803         if (Tk_IsTopLevel(ewPtr->body.ew.tkwin)
804                 || (textPtr->tkwin == ewPtr->body.ew.tkwin)) {
805             goto badMaster;
806         }
807         Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
808                 (ClientData) ewPtr);
809         Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
810                 EmbWinStructureProc, (ClientData) ewPtr);
811
812         /*
813          * Special trick!  Must enter into the hash table *after*
814          * calling Tk_ManageGeometry:  if the window was already managed
815          * elsewhere in this text, the Tk_ManageGeometry call will cause
816          * the entry to be removed, which could potentially lose the new
817          * entry.
818          */
819
820         hPtr = Tcl_CreateHashEntry(&textPtr->windowTable,
821                 Tk_PathName(ewPtr->body.ew.tkwin), &new);
822         Tcl_SetHashValue(hPtr, ewPtr);
823     }
824
825     /*
826      * See if there's room for this window on this line.
827      */
828
829     gotWindow:
830     if (ewPtr->body.ew.tkwin == NULL) {
831         width = 0;
832         height = 0;
833     } else {
834         width = Tk_ReqWidth(ewPtr->body.ew.tkwin) + 2*ewPtr->body.ew.padX;
835         height = Tk_ReqHeight(ewPtr->body.ew.tkwin) + 2*ewPtr->body.ew.padY;
836     }
837     if ((width > (maxX - chunkPtr->x))
838             && !noCharsYet && (textPtr->wrapMode != tkTextNoneUid)) {
839         return 0;
840     }
841
842     /*
843      * Fill in the chunk structure.
844      */
845
846     chunkPtr->displayProc = EmbWinDisplayProc;
847     chunkPtr->undisplayProc = EmbWinUndisplayProc;
848     chunkPtr->measureProc = (Tk_ChunkMeasureProc *) NULL;
849     chunkPtr->bboxProc = EmbWinBboxProc;
850     chunkPtr->numChars = 1;
851     if (ewPtr->body.ew.align == ALIGN_BASELINE) {
852         chunkPtr->minAscent = height - ewPtr->body.ew.padY;
853         chunkPtr->minDescent = ewPtr->body.ew.padY;
854         chunkPtr->minHeight = 0;
855     } else {
856         chunkPtr->minAscent = 0;
857         chunkPtr->minDescent = 0;
858         chunkPtr->minHeight = height;
859     }
860     chunkPtr->width = width;
861     chunkPtr->breakIndex = -1;
862     chunkPtr->breakIndex = 1;
863     chunkPtr->clientData = (ClientData) ewPtr;
864     ewPtr->body.ew.chunkCount += 1;
865     return 1;
866 }
867 \f
868 /*
869  *--------------------------------------------------------------
870  *
871  * EmbWinCheckProc --
872  *
873  *      This procedure is invoked by the B-tree code to perform
874  *      consistency checks on embedded windows.
875  *
876  * Results:
877  *      None.
878  *
879  * Side effects:
880  *      The procedure panics if it detects anything wrong with
881  *      the embedded window.
882  *
883  *--------------------------------------------------------------
884  */
885
886 static void
887 EmbWinCheckProc(ewPtr, linePtr)
888     TkTextSegment *ewPtr;               /* Segment to check. */
889     TkTextLine *linePtr;                /* Line containing segment. */
890 {
891     if (ewPtr->nextPtr == NULL) {
892         panic("EmbWinCheckProc: embedded window is last segment in line");
893     }
894     if (ewPtr->size != 1) {
895         panic("EmbWinCheckProc: embedded window has size %d", ewPtr->size);
896     }
897 }
898 \f
899 /*
900  *--------------------------------------------------------------
901  *
902  * EmbWinDisplayProc --
903  *
904  *      This procedure is invoked by the text displaying code
905  *      when it is time to actually draw an embedded window
906  *      chunk on the screen.
907  *
908  * Results:
909  *      None.
910  *
911  * Side effects:
912  *      The embedded window gets moved to the correct location
913  *      and mapped onto the screen.
914  *
915  *--------------------------------------------------------------
916  */
917
918 static void
919 EmbWinDisplayProc(chunkPtr, x, y, lineHeight, baseline, display, dst, screenY)
920     TkTextDispChunk *chunkPtr;          /* Chunk that is to be drawn. */
921     int x;                              /* X-position in dst at which to
922                                          * draw this chunk (differs from
923                                          * the x-position in the chunk because
924                                          * of scrolling). */
925     int y;                              /* Top of rectangular bounding box
926                                          * for line: tells where to draw this
927                                          * chunk in dst (x-position is in
928                                          * the chunk itself). */
929     int lineHeight;                     /* Total height of line. */
930     int baseline;                       /* Offset of baseline from y. */
931     Display *display;                   /* Display to use for drawing. */
932     Drawable dst;                       /* Pixmap or window in which to draw */
933     int screenY;                        /* Y-coordinate in text window that
934                                          * corresponds to y. */
935 {
936     TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
937     int lineX, windowX, windowY, width, height;
938     Tk_Window tkwin;
939
940     tkwin = ewPtr->body.ew.tkwin;
941     if (tkwin == NULL) {
942         return;
943     }
944     if ((x + chunkPtr->width) <= 0) {
945         /*
946          * The window is off-screen;  just unmap it.
947          */
948
949         if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(tkwin)) {
950             Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin);
951         } else {
952             Tk_UnmapWindow(tkwin);
953         }
954         return;
955     }
956
957     /*
958      * Compute the window's location and size in the text widget, taking
959      * into account the align and stretch values for the window.
960      */
961
962     EmbWinBboxProc(chunkPtr, 0, screenY, lineHeight, baseline, &lineX,
963             &windowY, &width, &height);
964     windowX = lineX - chunkPtr->x + x;
965
966     if (ewPtr->body.ew.textPtr->tkwin == Tk_Parent(tkwin)) {
967         if ((windowX != Tk_X(tkwin)) || (windowY != Tk_Y(tkwin))
968                 || (Tk_ReqWidth(tkwin) != Tk_Width(tkwin))
969                 || (height != Tk_Height(tkwin))) {
970             Tk_MoveResizeWindow(tkwin, windowX, windowY, width, height);
971         }
972         Tk_MapWindow(tkwin);
973     } else {
974         Tk_MaintainGeometry(tkwin, ewPtr->body.ew.textPtr->tkwin,
975                 windowX, windowY, width, height);
976     }
977
978     /*
979      * Mark the window as displayed so that it won't get unmapped.
980      */
981
982     ewPtr->body.ew.displayed = 1;
983 }
984 \f
985 /*
986  *--------------------------------------------------------------
987  *
988  * EmbWinUndisplayProc --
989  *
990  *      This procedure is called when the chunk for an embedded
991  *      window is no longer going to be displayed.  It arranges
992  *      for the window associated with the chunk to be unmapped.
993  *
994  * Results:
995  *      None.
996  *
997  * Side effects:
998  *      The window is scheduled for unmapping.
999  *
1000  *--------------------------------------------------------------
1001  */
1002
1003 static void
1004 EmbWinUndisplayProc(textPtr, chunkPtr)
1005     TkText *textPtr;                    /* Overall information about text
1006                                          * widget. */
1007     TkTextDispChunk *chunkPtr;          /* Chunk that is about to be freed. */
1008 {
1009     TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
1010
1011     ewPtr->body.ew.chunkCount--;
1012     if (ewPtr->body.ew.chunkCount == 0) {
1013         /*
1014          * Don't unmap the window immediately, since there's a good chance
1015          * that it will immediately be redisplayed, perhaps even in the
1016          * same place.  Instead, schedule the window to be unmapped later;
1017          * the call to EmbWinDelayedUnmap will be cancelled in the likely
1018          * event that the unmap becomes unnecessary.
1019          */
1020
1021         ewPtr->body.ew.displayed = 0;
1022         Tcl_DoWhenIdle(EmbWinDelayedUnmap, (ClientData) ewPtr);
1023     }
1024 }
1025 \f
1026 /*
1027  *--------------------------------------------------------------
1028  *
1029  * EmbWinBboxProc --
1030  *
1031  *      This procedure is called to compute the bounding box of
1032  *      the area occupied by an embedded window.
1033  *
1034  * Results:
1035  *      There is no return value.  *xPtr and *yPtr are filled in
1036  *      with the coordinates of the upper left corner of the
1037  *      window, and *widthPtr and *heightPtr are filled in with
1038  *      the dimensions of the window in pixels.  Note:  not all
1039  *      of the returned bbox is necessarily visible on the screen
1040  *      (the rightmost part might be off-screen to the right,
1041  *      and the bottommost part might be off-screen to the bottom).
1042  *
1043  * Side effects:
1044  *      None.
1045  *
1046  *--------------------------------------------------------------
1047  */
1048
1049 static void
1050 EmbWinBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
1051         widthPtr, heightPtr)
1052     TkTextDispChunk *chunkPtr;          /* Chunk containing desired char. */
1053     int index;                          /* Index of desired character within
1054                                          * the chunk. */
1055     int y;                              /* Topmost pixel in area allocated
1056                                          * for this line. */
1057     int lineHeight;                     /* Total height of line. */
1058     int baseline;                       /* Location of line's baseline, in
1059                                          * pixels measured down from y. */
1060     int *xPtr, *yPtr;                   /* Gets filled in with coords of
1061                                          * character's upper-left pixel. */
1062     int *widthPtr;                      /* Gets filled in with width of
1063                                          * character, in pixels. */
1064     int *heightPtr;                     /* Gets filled in with height of
1065                                          * character, in pixels. */
1066 {
1067     TkTextSegment *ewPtr = (TkTextSegment *) chunkPtr->clientData;
1068     Tk_Window tkwin;
1069
1070     tkwin = ewPtr->body.ew.tkwin;
1071     if (tkwin != NULL) {
1072         *widthPtr = Tk_ReqWidth(tkwin);
1073         *heightPtr = Tk_ReqHeight(tkwin);
1074     } else {
1075         *widthPtr = 0;
1076         *heightPtr = 0;
1077     }
1078     *xPtr = chunkPtr->x + ewPtr->body.ew.padX;
1079     if (ewPtr->body.ew.stretch) {
1080         if (ewPtr->body.ew.align == ALIGN_BASELINE) {
1081             *heightPtr = baseline - ewPtr->body.ew.padY;
1082         } else {
1083             *heightPtr = lineHeight - 2*ewPtr->body.ew.padY;
1084         }
1085     }
1086     switch (ewPtr->body.ew.align) {
1087         case ALIGN_BOTTOM:
1088             *yPtr = y + (lineHeight - *heightPtr - ewPtr->body.ew.padY);
1089             break;
1090         case ALIGN_CENTER:
1091             *yPtr = y + (lineHeight - *heightPtr)/2;
1092             break;
1093         case ALIGN_TOP:
1094             *yPtr = y + ewPtr->body.ew.padY;
1095             break;
1096         case ALIGN_BASELINE:
1097             *yPtr = y + (baseline - *heightPtr);
1098             break;
1099     }
1100 }
1101 \f
1102 /*
1103  *--------------------------------------------------------------
1104  *
1105  * EmbWinDelayedUnmap --
1106  *
1107  *      This procedure is an idle handler that does the actual
1108  *      work of unmapping an embedded window.  See the comment
1109  *      in EmbWinUndisplayProc for details.
1110  *
1111  * Results:
1112  *      None.
1113  *
1114  * Side effects:
1115  *      The window gets unmapped, unless its chunk reference count
1116  *      has become non-zero again.
1117  *
1118  *--------------------------------------------------------------
1119  */
1120
1121 static void
1122 EmbWinDelayedUnmap(clientData)
1123     ClientData clientData;              /* Token for the window to
1124                                          * be unmapped. */
1125 {
1126     TkTextSegment *ewPtr = (TkTextSegment *) clientData;
1127
1128     if (!ewPtr->body.ew.displayed && (ewPtr->body.ew.tkwin != NULL)) {
1129         if (ewPtr->body.ew.textPtr->tkwin != Tk_Parent(ewPtr->body.ew.tkwin)) {
1130             Tk_UnmaintainGeometry(ewPtr->body.ew.tkwin,
1131                     ewPtr->body.ew.textPtr->tkwin);
1132         } else {
1133             Tk_UnmapWindow(ewPtr->body.ew.tkwin);
1134         }
1135     }
1136 }
1137 \f
1138 /*
1139  *--------------------------------------------------------------
1140  *
1141  * TkTextWindowIndex --
1142  *
1143  *      Given the name of an embedded window within a text widget,
1144  *      returns an index corresponding to the window's position
1145  *      in the text.
1146  *
1147  * Results:
1148  *      The return value is 1 if there is an embedded window by
1149  *      the given name in the text widget, 0 otherwise.  If the
1150  *      window exists, *indexPtr is filled in with its index.
1151  *
1152  * Side effects:
1153  *      None.
1154  *
1155  *--------------------------------------------------------------
1156  */
1157
1158 int
1159 TkTextWindowIndex(textPtr, name, indexPtr)
1160     TkText *textPtr;            /* Text widget containing window. */
1161     char *name;                 /* Name of window. */
1162     TkTextIndex *indexPtr;      /* Index information gets stored here. */
1163 {
1164     Tcl_HashEntry *hPtr;
1165     TkTextSegment *ewPtr;
1166
1167     hPtr = Tcl_FindHashEntry(&textPtr->windowTable, name);
1168     if (hPtr == NULL) {
1169         return 0;
1170     }
1171     ewPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
1172     indexPtr->tree = textPtr->tree;
1173     indexPtr->linePtr = ewPtr->body.ew.linePtr;
1174     indexPtr->charIndex = TkTextSegToOffset(ewPtr, indexPtr->linePtr);
1175     return 1;
1176 }