OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / blt2.5 / generic / bltContainer.c
1
2 /*
3  * bltContainer.c --
4  *
5  *      This module implements a container widget for the BLT toolkit.
6  *
7  * Copyright 1998 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies or any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  *
27  *      Container widget created by George A. Howlett
28  */
29
30 #include "bltInt.h"
31
32 #ifndef NO_CONTAINER
33 #include "bltChain.h"
34 #ifndef WIN32
35 #include <X11/Xproto.h>
36 #include <X11/Xutil.h>
37 #endif
38
39 #define XDEBUG
40
41 #define SEARCH_TRIES    100     /* Maximum number of attempts to check for
42                                  * a given window before failing. */
43 #define SEARCH_INTERVAL 20      /* Number milliseconds to wait after each 
44                                  * attempt to find a window. */
45
46 #define SEARCH_TKWIN    (1<<0)  /* Search via Tk window pathname. */
47 #define SEARCH_XID      (1<<1)  /* Search via an XID 0x0000000. */
48 #define SEARCH_CMD      (1<<2)  /* Search via a command-line arguments. */
49 #define SEARCH_NAME     (1<<3)  /* Search via the application name. */
50 #define SEARCH_ALL      (SEARCH_TKWIN | SEARCH_XID | SEARCH_CMD | SEARCH_NAME)
51
52 #define CONTAINER_REDRAW                (1<<1)
53 #define CONTAINER_MAPPED                (1<<2)
54 #define CONTAINER_FOCUS                 (1<<4)
55 #define CONTAINER_INIT                  (1<<5)
56 #define CONTAINER_MOVE                  (1<<7)
57
58 #define DEF_CONTAINER_BG_MONO           STD_NORMAL_BG_MONO
59 #define DEF_CONTAINER_BACKGROUND                STD_NORMAL_BACKGROUND
60 #define DEF_CONTAINER_BORDERWIDTH       STD_BORDERWIDTH
61 #define DEF_CONTAINER_COMMAND           (char *)NULL
62 #define DEF_CONTAINER_CURSOR            (char *)NULL
63 #define DEF_CONTAINER_HEIGHT            "0"
64 #define DEF_CONTAINER_HIGHLIGHT_BACKGROUND STD_NORMAL_BACKGROUND
65 #define DEF_CONTAINER_HIGHLIGHT_BG_MONO STD_NORMAL_BG_MONO
66 #define DEF_CONTAINER_HIGHLIGHT_COLOR   RGB_BLACK
67 #define DEF_CONTAINER_HIGHLIGHT_WIDTH   "2"
68 #define DEF_CONTAINER_RELIEF            "sunken"
69 #define DEF_CONTAINER_TAKE_FOCUS        "0"
70 #define DEF_CONTAINER_TIMEOUT           "20"
71 #define DEF_CONTAINER_WIDTH             "0"
72 #define DEF_CONTAINER_WINDOW            (char *)NULL
73
74 #if (TK_MAJOR_VERSION == 4)
75 #define TK_REPARENTED                   0x2000
76 #endif
77
78 typedef struct SearchInfoStruct SearchInfo;
79 typedef void (SearchProc) _ANSI_ARGS_((Display *display, Window window, 
80        SearchInfo *searchPtr));
81
82 struct SearchInfoStruct {
83     SearchProc *proc;
84     char *pattern;              /* Search pattern. */
85
86     Window window;              /* XID of last window that matches criteria. */
87     int nMatches;               /* Number of windows that match the pattern. */
88     int saveNames;              /* Indicates to save the names of the
89                                  * window XIDs that match the search
90                                  * criteria. */
91     Tcl_DString dString;        /* Will contain the strings of the
92                                  * window XIDs matching the search
93                                  * criteria. */
94 };
95
96 typedef struct {
97     Tk_Window tkwin;            /* Window that embodies the widget.
98                                  * NULL means that the window has been
99                                  * destroyed but the data structures
100                                  * haven't yet been cleaned up.*/
101
102     Display *display;           /* Display containing widget; needed,
103                                  * among other things, to release
104                                  * resources after tkwin has already
105                                  * gone away. */
106
107     Tcl_Interp *interp;         /* Interpreter associated with widget. */
108
109     Tcl_Command cmdToken;       /* Token for widget's command. */
110
111     unsigned int flags;         /* For bit-field definitions, see above. */
112
113     int inset;                  /* Total width of borders; focus
114                                  * highlight and 3-D border. Indicates
115                                  * the offset from outside edges to
116                                  * leave room for borders. */
117
118     Tk_Cursor cursor;           /* X Cursor */
119
120     Tk_3DBorder border;         /* 3D border surrounding the adopted
121                                  * window. */
122     int borderWidth;            /* Width of 3D border. */
123     int relief;                 /* 3D border relief. */
124
125     Tk_Window tkToplevel;       /* Toplevel (wrapper) window of
126                                  * container.  It's used to track the
127                                  * location of the container. If it
128                                  * moves we need to notify the
129                                  * embedded application. */
130     /*
131      * Focus highlight ring
132      */
133     int highlightWidth;         /* Width in pixels of highlight to
134                                  * draw around widget when it has the
135                                  * focus.  <= 0 means don't draw a
136                                  * highlight. */
137     XColor *highlightBgColor;   /* Color for drawing traversal
138                                  * highlight area when highlight is
139                                  * off. */
140     XColor *highlightColor;     /* Color for drawing traversal highlight. */
141
142     GC highlightGC;             /* GC for focus highlight. */
143
144     char *takeFocus;            /* Says whether to select this widget during
145                                  * tab traveral operations.  This value isn't
146                                  * used in C code, but for the widget's Tcl
147                                  * bindings. */
148
149     int reqWidth, reqHeight;    /* Requested dimensions of the container
150                                  * window. */
151
152     Window adopted;             /* X window Id or Win32 handle of adopted 
153                                  * window contained by the widget.  If None, 
154                                  * no window has been reparented. */
155     Tk_Window tkAdopted;        /* Non-NULL if this is a Tk window that's 
156                                  * been adopted. */
157     int adoptedX, adoptedY;     /* Current position of the adopted window. */
158     int adoptedWidth;           /* Current width of the adopted window. */
159     int adoptedHeight;          /* Current height of the adopted window. */
160
161     int origX, origY;
162     int origWidth, origHeight;  /* Dimensions of the window when it
163                                  * was adopted.  When the window is
164                                  * released it's returned to it's
165                                  * original dimensions. */
166
167     int timeout;
168 } Container;
169
170
171 static Tk_OptionParseProc StringToXID;
172 static Tk_OptionPrintProc XIDToString;
173
174 static Tk_CustomOption XIDOption =
175 {
176     StringToXID, XIDToString, (ClientData)(SEARCH_TKWIN | SEARCH_XID),
177 };
178
179 #ifndef WIN32
180 static Tk_CustomOption XIDNameOption =
181 {
182     StringToXID, XIDToString, (ClientData)SEARCH_NAME,
183 };
184
185 static Tk_CustomOption XIDCmdOption =
186 {
187     StringToXID, XIDToString, (ClientData)SEARCH_CMD,
188 };
189 #endif
190
191 extern Tk_CustomOption bltDistanceOption;
192 extern Tk_CustomOption bltPositiveCountOption;
193
194 static Tk_ConfigSpec configSpecs[] =
195 {
196     {TK_CONFIG_BORDER, "-background", "background", "Background",
197         DEF_CONTAINER_BG_MONO, Tk_Offset(Container, border),
198         TK_CONFIG_MONO_ONLY},
199     {TK_CONFIG_BORDER, "-background", "background", "Background",
200         DEF_CONTAINER_BACKGROUND, Tk_Offset(Container, border),
201         TK_CONFIG_COLOR_ONLY},
202     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
203     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
204     {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
205         DEF_CONTAINER_BORDERWIDTH, Tk_Offset(Container, borderWidth),
206         TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
207 #ifndef WIN32
208     {TK_CONFIG_CUSTOM, "-command", "command", "Command",
209         DEF_CONTAINER_WINDOW, Tk_Offset(Container, adopted),
210         TK_CONFIG_DONT_SET_DEFAULT, &XIDCmdOption},
211 #endif
212     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
213         DEF_CONTAINER_CURSOR, Tk_Offset(Container, cursor), TK_CONFIG_NULL_OK},
214     {TK_CONFIG_CUSTOM, "-height", "height", "Height",
215         DEF_CONTAINER_HEIGHT, Tk_Offset(Container, reqHeight),
216         TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
217     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
218         "HighlightBackground", DEF_CONTAINER_HIGHLIGHT_BACKGROUND, 
219         Tk_Offset(Container, highlightBgColor), TK_CONFIG_COLOR_ONLY},
220     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
221         "HighlightBackground", DEF_CONTAINER_HIGHLIGHT_BG_MONO, 
222         Tk_Offset(Container, highlightBgColor), TK_CONFIG_MONO_ONLY},
223     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
224         DEF_CONTAINER_HIGHLIGHT_COLOR, 
225         Tk_Offset(Container, highlightColor), 0},
226     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
227         "HighlightThickness",
228         DEF_CONTAINER_HIGHLIGHT_WIDTH, Tk_Offset(Container, highlightWidth),
229         TK_CONFIG_DONT_SET_DEFAULT},
230 #ifndef WIN32
231     {TK_CONFIG_CUSTOM, "-name", "name", "Name",
232         DEF_CONTAINER_WINDOW, Tk_Offset(Container, adopted),
233         TK_CONFIG_DONT_SET_DEFAULT, &XIDNameOption},
234 #endif
235     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
236         DEF_CONTAINER_RELIEF, Tk_Offset(Container, relief), 0},
237     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
238         DEF_CONTAINER_TAKE_FOCUS, Tk_Offset(Container, takeFocus),
239         TK_CONFIG_NULL_OK},
240     {TK_CONFIG_CUSTOM, "-timeout", "timeout", "Timeout",
241         DEF_CONTAINER_TIMEOUT, Tk_Offset(Container, timeout),
242         TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveCountOption},
243     {TK_CONFIG_CUSTOM, "-width", "width", "Width",
244         DEF_CONTAINER_WIDTH, Tk_Offset(Container, reqWidth),
245         TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
246     {TK_CONFIG_CUSTOM, "-window", "window", "Window",
247         DEF_CONTAINER_WINDOW, Tk_Offset(Container, adopted),
248         TK_CONFIG_DONT_SET_DEFAULT, &XIDOption},
249     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
250         (char *)NULL, 0, 0}
251 };
252
253 /* Forward Declarations */
254 static Tcl_IdleProc DisplayContainer;
255 static Tcl_CmdProc ContainerInstCmd;
256 static Tcl_CmdDeleteProc ContainerInstCmdDeleteProc;
257 static Tk_EventProc ToplevelEventProc;
258 static Tk_GenericProc AdoptedWindowEventProc;
259 static Tk_EventProc ContainerEventProc;
260 static Tcl_FreeProc DestroyContainer;
261 static Tcl_CmdProc ContainerCmd;
262
263 static void EventuallyRedraw _ANSI_ARGS_((Container *cntrPtr));
264
265 #ifdef notdef
266 /*
267  *----------------------------------------------------------------------
268  *
269  * GetWindowId --
270  *
271  *      Returns the XID for the Tk_Window given.  Starting in Tk 8.0,
272  *      the toplevel widgets are wrapped by another window.
273  *      Currently there's no way to get at that window, other than
274  *      what is done here: query the X window hierarchy and grab the
275  *      parent.
276  *
277  * Results:
278  *      Returns the X Window ID of the widget.  If it's a toplevel, then
279  *      the XID of the wrapper is returned.
280  *
281  *----------------------------------------------------------------------
282  */
283 Window
284 GetXID(tkwin)
285     Tk_Window tkwin;
286 {
287     HWND hWnd;
288     TkWinWindow *twdPtr;
289
290     hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
291 #if (TK_MAJOR_VERSION > 4)
292     if (Tk_IsTopLevel(tkwin)) {
293         hWnd = GetParent(hWnd);
294     }
295 #endif /* TK_MAJOR_VERSION > 4 */
296     twdPtr = Blt_Malloc(sizeof(TkWinWindow));
297     twdPtr->handle = hWnd;
298     twdPtr->type = TWD_WINDOW;
299     twdPtr->winPtr = tkwin;
300     return (Window)twdPtr;
301 }
302 #endif
303
304 /*
305  *----------------------------------------------------------------------
306  *
307  * NameOfId --
308  *
309  *      Returns a string representing the given XID.
310  *
311  * Results:
312  *      A static string containing either the hexidecimal number or
313  *      the pathname of a Tk window.
314  *
315  *----------------------------------------------------------------------
316  */
317 static char *
318 NameOfId(display, window)
319     Display *display;           /* Display containing both the container widget
320                                  * and the adopted window. */
321     Window window;              /* XID of the adopted window. */
322 {
323     if (window != None) {
324         Tk_Window tkwin;
325         static char string[200];
326
327         /* See first if it's a window that Tk knows about.  */
328         /*
329          * Note:  If the wrapper window is reparented, Tk pretends it's
330          *        no longer connected to the toplevel, so if you look for
331          *        the child of the wrapper tkwin, it's NULL.  
332          */
333         tkwin = Tk_IdToWindow(display, window); 
334         if ((tkwin != NULL) && (Tk_PathName(tkwin) != NULL)) {
335             return Tk_PathName(tkwin); 
336         } 
337         sprintf(string, "0x%x", (unsigned int)window);
338         return string;
339     }
340     return "";                  /* Return empty string if XID is None. */
341 }
342
343 #ifndef WIN32
344 /*
345  *----------------------------------------------------------------------
346  *
347  * XGeometryErrorProc --
348  *
349  *      Flags errors generated from XGetGeometry calls to the X server.
350  *
351  * Results:
352  *      Always returns 0.
353  *
354  * Side Effects:
355  *      Sets a flag, indicating an error occurred.
356  *
357  *----------------------------------------------------------------------
358  */
359 /* ARGSUSED */
360 static int
361 XGeometryErrorProc(clientData, eventPtr)
362     ClientData clientData;
363     XErrorEvent *eventPtr;      /* Not used. */
364 {
365     int *errorPtr = clientData;
366
367     *errorPtr = TCL_ERROR;
368     return 0;
369 }
370
371 /*
372  *----------------------------------------------------------------------
373  *
374  * GetAdoptedWindowGeometry --
375  *
376  *      Computes the requested geometry of the container using the 
377  *      size of adopted window as a reference.  
378  *
379  * Results:
380  *      A standard Tcl result. 
381  *
382  * Side Effects:
383  *      Sets a flag, indicating an error occurred.
384  *
385  *----------------------------------------------------------------------
386  */
387 static int
388 GetAdoptedWindowGeometry(interp, cntrPtr)
389     Tcl_Interp *interp;
390     Container *cntrPtr;
391 {
392     int x, y, width, height, borderWidth, depth;
393     int xOffset, yOffset;
394     Window root, dummy;
395     Tk_ErrorHandler handler;
396     int result;
397     int any = -1;
398     
399     width = height = 1;
400     xOffset = yOffset = 0;
401     if (cntrPtr->adopted != None) {
402         handler = Tk_CreateErrorHandler(cntrPtr->display, any, X_GetGeometry, 
403                 any, XGeometryErrorProc, &result);
404         root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
405         XTranslateCoordinates(cntrPtr->display, cntrPtr->adopted,
406                       root, 0, 0, &xOffset, &yOffset, &dummy);
407         result = XGetGeometry(cntrPtr->display, cntrPtr->adopted, &root, 
408                 &x, &y, (unsigned int *)&width, (unsigned int *)&height,
409               (unsigned int *)&borderWidth, (unsigned int *)&depth);
410         Tk_DeleteErrorHandler(handler);
411         XSync(cntrPtr->display, False);
412         if (result == 0) {
413             Tcl_AppendResult(interp, "can't get geometry for \"", 
414                      NameOfId(cntrPtr->display, cntrPtr->adopted), "\"", 
415                      (char *)NULL);
416             return TCL_ERROR;
417         }
418         cntrPtr->origX = xOffset;
419         cntrPtr->origY = yOffset;
420         cntrPtr->origWidth = width;
421         cntrPtr->origHeight = height;
422     } else {
423         cntrPtr->origX = cntrPtr->origY = 0;
424         cntrPtr->origWidth = cntrPtr->origHeight = 0;
425     }
426     cntrPtr->adoptedX = x;
427     cntrPtr->adoptedY = y;
428     cntrPtr->adoptedWidth = width;
429     cntrPtr->adoptedHeight = height;
430     return TCL_OK;
431 }
432
433 /*
434  * ------------------------------------------------------------------------
435  *
436  *  GetChildren --
437  *
438  *      Returns a chain of the child windows according to their stacking
439  *      order.  The window ids are ordered from top to bottom.
440  *
441  * ------------------------------------------------------------------------
442  */
443 static Blt_Chain *
444 GetChildren(display, window)
445     Display *display;
446     Window window;
447 {
448     Window *children;
449     unsigned int nChildren;
450     Window dummy;
451
452     if (!XQueryTree(display, window, &dummy /*parent*/, &dummy /*root*/, 
453                    &children, &nChildren)) {
454         return NULL;
455     }
456     if (nChildren > 0) {
457         Blt_Chain *chainPtr;
458         register int i;
459
460         chainPtr = Blt_ChainCreate();
461         for (i = 0; i < nChildren; i++) {
462             /*
463              *  XQuery returns windows in bottom to top order.
464              *  We'll reverse the order.
465              */
466             Blt_ChainPrepend(chainPtr, (ClientData)children[i]);
467         }
468         if (children != NULL) {
469             XFree((char *)children);
470         }
471         return chainPtr;
472     }
473     return NULL;
474 }
475
476 /*
477  *----------------------------------------------------------------------
478  *
479  * NameSearch --
480  *
481  *      Traverses the entire window hierarchy, searching for windows 
482  *      matching the name field in the SearchInfo structure. This 
483  *      routine is recursively called for each successive level in 
484  *      the window hierarchy.
485  *
486  * Results:
487  *      None.
488  *
489  * Side Effects:
490  *      The SearchInfo structure will track the number of windows that 
491  *      match the given criteria.
492  *      
493  *----------------------------------------------------------------------
494  */
495 static void
496 NameSearch(display, window, searchPtr)
497     Display *display;
498     Window window;
499     SearchInfo *searchPtr;
500 {
501     Blt_Chain *chainPtr;
502     char *wmName;
503
504     if (XFetchName(display, window, &wmName)) {
505         /* Compare the name of the window to the search pattern. */
506         if (Tcl_StringMatch(wmName, searchPtr->pattern)) {
507             if (searchPtr->saveNames) { /* Record names of matching windows. */
508                 Tcl_DStringAppendElement(&(searchPtr->dString), 
509                          NameOfId(display, window));
510                 Tcl_DStringAppendElement(&(searchPtr->dString), wmName);
511             }
512             searchPtr->window = window;
513             searchPtr->nMatches++;
514         }
515         XFree(wmName);
516     }
517     /* Process the window's descendants. */
518     chainPtr = GetChildren(display, window);
519     if (chainPtr != NULL) {
520         Blt_ChainLink *linkPtr;
521         Window child;
522
523         for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
524              linkPtr = Blt_ChainNextLink(linkPtr)) {
525             child = (Window)Blt_ChainGetValue(linkPtr);
526             NameSearch(display, child, searchPtr);
527         }
528         Blt_ChainDestroy(chainPtr);
529     }
530 }
531
532 /*
533  *----------------------------------------------------------------------
534  *
535  * CmdSearch --
536  *
537  *      Traverses the entire window hierarchy, searching for windows 
538  *      matching the command-line specified in the SearchInfo structure.  
539  *      This routine is recursively called for each successive level
540  *      in the window hierarchy.
541  *
542  * Results:
543  *      None.
544  *
545  * Side Effects:
546  *      The SearchInfo structure will track the number of windows that 
547  *      match the given command-line.
548  *      
549  *----------------------------------------------------------------------
550  */
551 static void
552 CmdSearch(display, window, searchPtr)
553     Display *display;
554     Window window;
555     SearchInfo *searchPtr;
556 {
557     Blt_Chain *chainPtr;
558     int cmdArgc;
559     char **cmdArgv;
560
561     if (XGetCommand(display, window, &cmdArgv, &cmdArgc)) {
562         char *string;
563
564         string = Tcl_Merge(cmdArgc, cmdArgv);
565         XFreeStringList(cmdArgv);
566         if (Tcl_StringMatch(string, searchPtr->pattern)) {
567             if (searchPtr->saveNames) { /* Record names of matching windows. */
568                 Tcl_DStringAppendElement(&(searchPtr->dString), 
569                          NameOfId(display, window));
570                 Tcl_DStringAppendElement(&(searchPtr->dString), string);
571             }
572             searchPtr->window = window;
573             searchPtr->nMatches++;
574         }
575         Blt_Free(string);
576     }
577     /* Process the window's descendants. */
578     chainPtr = GetChildren(display, window);
579     if (chainPtr != NULL) {
580         Blt_ChainLink *linkPtr;
581         Window child;
582
583         for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
584              linkPtr = Blt_ChainNextLink(linkPtr)) {
585             child = (Window)Blt_ChainGetValue(linkPtr);
586             CmdSearch(display, child, searchPtr);
587         }
588         Blt_ChainDestroy(chainPtr);
589     }
590 }
591
592 /*
593  *----------------------------------------------------------------------
594  *
595  * TimeoutProc --
596  *
597  *      Procedure called when the timer event elapses.  Used to wait
598  *      between attempts checking for the designated window.
599  *
600  * Results:
601  *      None.
602  *
603  * Side Effects:
604  *      Sets a flag, indicating the timeout occurred.
605  *
606  *----------------------------------------------------------------------
607  */
608 static void
609 TimeoutProc(clientData)
610     ClientData clientData;
611 {
612     int *expirePtr = clientData;
613
614     *expirePtr = TRUE;
615 }
616
617 /*
618  *----------------------------------------------------------------------
619  *
620  * TestAndWaitForWindow --
621  *
622  *      Searches, possibly multiple times, for windows matching the
623  *      criteria given, using the search proc also given.
624  *
625  * Results:
626  *      None.
627  *
628  * Side Effects:
629  *      Sets a flag, indicating the timeout occurred.
630  *
631  *----------------------------------------------------------------------
632  */
633 static void
634 TestAndWaitForWindow(cntrPtr, searchPtr)
635     Container *cntrPtr;         /* Container widget record. */
636     SearchInfo *searchPtr;      /* Search criteria. */
637 {
638     Window root;
639     Tcl_TimerToken timerToken;
640     int expire;
641     int i;
642
643     /* Get the root window to start the search.  */
644     root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
645     timerToken = NULL;
646     for (i = 0; i < SEARCH_TRIES; i++) {
647         searchPtr->nMatches = 0;
648         (*searchPtr->proc)(cntrPtr->display, root, searchPtr);
649         if (searchPtr->nMatches > 0) {
650             if (timerToken != NULL) {
651                 Tcl_DeleteTimerHandler(timerToken);
652             }
653             return;
654         }
655         expire = FALSE;
656         /*   
657          * If the X11 application associated with the adopted window
658          * was just started (via "exec" or "bgexec"), the window may
659          * not exist yet.  We have to wait a little bit for the program
660          * to start up.  Create a timer event break us out of an wait 
661          * loop.  We'll wait for a given interval for the adopted window
662          * to appear.
663          */
664         timerToken = Tcl_CreateTimerHandler(cntrPtr->timeout, TimeoutProc, 
665                 &expire);
666         while (!expire) {
667             /* Should file events be allowed? */
668             Tcl_DoOneEvent(TCL_TIMER_EVENTS | TCL_WINDOW_EVENTS | 
669                            TCL_FILE_EVENTS);
670         }
671     }   
672 }
673 #else 
674
675
676 /*
677  * ------------------------------------------------------------------------
678  *
679  *  GetChildren --
680  *
681  *      Returns a chain of the child windows according to their stacking
682  *      order.  The window ids are ordered from top to bottom.
683  *
684  * ------------------------------------------------------------------------
685  */
686 static Blt_Chain *
687 GetChildren(Display *display, Window window)
688 {
689     Blt_Chain *chainPtr;
690     HWND hWnd;
691     HWND parent;
692
693     parent = Tk_GetHWND(window);
694     chainPtr = Blt_ChainCreate();
695     for (hWnd = GetTopWindow(parent); hWnd != NULL;
696         hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) {
697         Blt_ChainAppend(chainPtr, (ClientData)hWnd);
698     }
699     return chainPtr;
700 }
701
702
703 /*
704  *----------------------------------------------------------------------
705  *
706  * GetAdoptedWindowGeometry --
707  *
708  *      Computes the requested geometry of the container using the 
709  *      size of adopted window as a reference.  
710  *
711  * Results:
712  *      A standard Tcl result. 
713  *
714  * Side Effects:
715  *      Sets a flag, indicating an error occurred.
716  *
717  *----------------------------------------------------------------------
718  */
719 static int
720 GetAdoptedWindowGeometry(Tcl_Interp *interp, Container *cntrPtr)
721 {
722     int x, y, width, height;
723     int xOffset, yOffset;
724     Window root, dummy;
725     
726     width = height = 1;
727     xOffset = yOffset = 0;
728     x = y = 0;
729     if (cntrPtr->adopted != None) {
730         HWND hWnd;
731         RECT rect;
732
733         hWnd = Tk_GetHWND(cntrPtr->adopted);
734         if (GetWindowRect(hWnd, &rect)) {
735             x = rect.left;
736             y = rect.top;
737             width = rect.right - rect.left + 1;
738             height = rect.bottom - rect.top + 1;
739         } else {
740             Tcl_AppendResult(interp, "can't get geometry for \"", 
741                      NameOfId(cntrPtr->display, cntrPtr->adopted), "\"", 
742                      (char *)NULL);
743             return TCL_ERROR;
744         }
745         root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
746         XTranslateCoordinates(cntrPtr->display, cntrPtr->adopted,
747                       root, 0, 0, &xOffset, &yOffset, &dummy);
748         cntrPtr->origX = xOffset;
749         cntrPtr->origY = yOffset;
750         cntrPtr->origWidth = width;
751         cntrPtr->origHeight = height;
752     } else {
753         cntrPtr->origX = cntrPtr->origY = 0;
754         cntrPtr->origWidth = cntrPtr->origHeight = 0;
755     }
756     cntrPtr->adoptedX = x;
757     cntrPtr->adoptedY = y;
758     cntrPtr->adoptedWidth = width;
759     cntrPtr->adoptedHeight = height;
760     return TCL_OK;
761 }
762
763 #endif /*WIN32*/
764
765 /*
766  * ------------------------------------------------------------------------
767  *
768  *  MapTree --
769  *
770  *      Maps each window in the hierarchy.  This is needed because 
771  *
772  *  Results:
773  *      None.
774  *
775  *  Side Effects:
776  *      Each window in the hierarchy is mapped.
777  *
778  * ------------------------------------------------------------------------
779  */
780 static void
781 MapTree(display, window)
782     Display *display;
783     Window window;
784 {
785     Blt_Chain *chainPtr;
786
787     XMapWindow(display, window);
788     chainPtr = GetChildren(display, window);
789     if (chainPtr != NULL) {
790         Blt_ChainLink *linkPtr;
791         Window child;
792
793         for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
794              linkPtr = Blt_ChainNextLink(linkPtr)) {
795             child = (Window)Blt_ChainGetValue(linkPtr);
796             MapTree(display, child);
797         }
798         Blt_ChainDestroy(chainPtr);
799     }
800 }
801
802 /*
803  *----------------------------------------------------------------------
804  *
805  * StringToXID --
806  *
807  *      Converts a string into an X window Id.
808  *
809  * Results:
810  *      If the string is successfully converted, TCL_OK is returned.
811  *      Otherwise, TCL_ERROR is returned and an error message is left
812  *      in interpreter's result field.
813  *
814  *---------------------------------------------------------------------- */
815 /*ARGSUSED*/
816 static int
817 StringToXID(clientData, interp, parent, string, widgRec, offset)
818     ClientData clientData;      /* Not used. */
819     Tcl_Interp *interp;         /* Interpreter to send results back to */
820     Tk_Window parent;           /* Parent window */
821     char *string;               /* String representation. */
822     char *widgRec;              /* Widget record */
823     int offset;                 /* Offset to field in structure */
824 {
825     unsigned int flags = (int)clientData;
826     Container *cntrPtr = (Container *)widgRec;
827     Window *winPtr = (Window *) (widgRec + offset);
828     Tk_Window tkAdopted;
829     Window window;
830     char buf[200], *cp;
831
832     tkAdopted = NULL;
833     window = None;
834     if ((flags & SEARCH_TKWIN) && string && ((string[0] == '.') ||
835         ((!strncmp(string, "interp", 6)) && ((cp = strchr(string, '.')))))) {
836         Tk_Window tkwin;
837         Tcl_Interp *slave = NULL;
838
839         if (string[0] != '.') {
840             /* Accept an interp prefix before the window. */
841             strncpy(buf, string, (cp-string));
842             buf[(cp-string)] = 0;
843             string = cp;
844             slave = Tcl_GetSlave(interp, buf);
845             if (!slave) {
846                 Tcl_AppendResult(interp, "cant find slave: ", buf,
847                     (char *)NULL);
848                     return TCL_ERROR;
849             }
850         } else {
851             slave = interp;
852         }
853         tkwin = Tk_NameToWindow(slave, string, Tk_MainWindow(slave));
854         if (tkwin == NULL) {
855             return TCL_ERROR;
856         }
857         if (!Tk_IsTopLevel(tkwin)) {
858             Tcl_AppendResult(interp, "can't reparent non-toplevel Tk windows",
859                              (char *)NULL);
860             return TCL_ERROR;
861         }
862         tkAdopted = tkwin;
863         Tk_MakeWindowExist(tkwin);
864         if (!Tk_IsMapped(tkwin)) {
865             /* Forced mapping fails to get dimensions. */
866             /* Tk_MapWindow(tkwin); */
867             Tcl_AppendResult(interp, "Tk window unmapped, try tkwait visibility ",
868                             string, (char *)NULL);
869                  return TCL_ERROR;
870         }
871         window = Blt_GetRealWindowId(tkwin);
872 #ifndef WIN32
873     } else if ((flags & SEARCH_XID) && (string[0] == '0') && 
874                (string[1] == 'x')) {
875         int token;
876
877         /* Hexidecimal string specifying the Window token. */
878         if (Tcl_GetInt(interp, string, &token) != TCL_OK) {
879             return TCL_ERROR;
880         }
881         window = token;
882     } else if ((string == NULL) || (string[0] == '\0')) {
883         window = None;
884     } else {
885         SearchInfo search;
886
887         memset(&search, 0, sizeof(search));
888         if (flags & (SEARCH_NAME | SEARCH_CMD)) {
889             search.pattern = string;
890             if (flags & SEARCH_NAME) {
891                 search.proc = NameSearch;
892             } else if (flags & SEARCH_CMD) {
893                 search.proc = CmdSearch;
894             }
895             TestAndWaitForWindow(cntrPtr, &search);
896             if (search.nMatches > 1) {
897                 Tcl_AppendResult(interp, "more than one window matches \"", 
898                          string, "\"", (char *)NULL);
899                 return TCL_ERROR;
900             }
901         }
902         if (search.nMatches == 0) {
903             Tcl_AppendResult(interp, "can't find window from pattern \"", 
904                              string, "\"", (char *)NULL);
905             return TCL_ERROR;
906         }
907         window = search.window;
908 #endif /*WIN32*/
909     }
910     if (*winPtr != None) {
911         Window root;
912
913         root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
914         if (Blt_ReparentWindow(cntrPtr->display, *winPtr, root, 
915                        cntrPtr->origX, cntrPtr->origY) 
916             != TCL_OK) {
917             Tcl_AppendResult(interp, "can't restore \"", 
918                          NameOfId(cntrPtr->display, *winPtr), 
919                         "\" window to root", (char *)NULL);
920             return TCL_ERROR;
921         }
922         cntrPtr->flags &= ~CONTAINER_MAPPED;
923         if (cntrPtr->tkAdopted == NULL) {
924             /* This wasn't a Tk window.  So deselect the event mask. */
925             XSelectInput(cntrPtr->display, *winPtr, 0);
926         } else {
927             MapTree(cntrPtr->display, *winPtr);
928         }
929         XMoveResizeWindow(cntrPtr->display, *winPtr, cntrPtr->origX,
930                   cntrPtr->origY, cntrPtr->origWidth, cntrPtr->origHeight);
931     }
932     cntrPtr->tkAdopted = tkAdopted;
933     *winPtr = window;
934     return TCL_OK;
935 }
936
937
938 /*
939  *----------------------------------------------------------------------
940  *
941  * XIDToString --
942  *
943  *      Converts the Tk window back to its string representation (i.e.
944  *      its name).
945  *
946  * Results:
947  *      The name of the window is returned.
948  *
949  *----------------------------------------------------------------------
950  */
951 /*ARGSUSED*/
952 static char *
953 XIDToString(clientData, parent, widgRec, offset, freeProcPtr)
954     ClientData clientData;      /* Not used. */
955     Tk_Window parent;           /* Not used. */
956     char *widgRec;              /* Widget record */
957     int offset;                 /* Offset of field in record */
958     Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
959 {
960     Container *cntrPtr = (Container *) widgRec;
961     Window window = *(Window *)(widgRec + offset);
962
963     if (cntrPtr->tkAdopted != NULL) {
964         return Tk_PathName(cntrPtr->tkAdopted);
965     } 
966     return NameOfId(cntrPtr->display, window);
967 }
968
969
970 /*
971  *----------------------------------------------------------------------
972  *
973  * EventuallyRedraw --
974  *
975  *      Queues a request to redraw the widget at the next idle point.
976  *
977  * Results:
978  *      None.
979  *
980  * Side effects:
981  *      Information gets redisplayed.  Right now we don't do selective
982  *      redisplays:  the whole window will be redrawn.
983  *
984  *----------------------------------------------------------------------
985  */
986 static void
987 EventuallyRedraw(cntrPtr)
988     Container *cntrPtr;
989 {
990     if ((cntrPtr->tkwin != NULL) && !(cntrPtr->flags & CONTAINER_REDRAW)) {
991         cntrPtr->flags |= CONTAINER_REDRAW;
992         Tcl_DoWhenIdle(DisplayContainer, cntrPtr);
993     }
994 }
995
996 /*
997  * --------------------------------------------------------------
998  *
999  * AdoptedWindowEventProc --
1000  *
1001  *      This procedure is invoked by the Tk dispatcher for various
1002  *      events on the encapsulated window.
1003  *
1004  * Results:
1005  *      None.
1006  *
1007  * Side effects:
1008  *      When the window gets deleted, internal structures get
1009  *      cleaned up.  When it gets resized or exposed, it's redisplayed.
1010  *
1011  * --------------------------------------------------------------
1012  */
1013 static int
1014 AdoptedWindowEventProc(clientData, eventPtr)
1015     ClientData clientData;      /* Information about the tab window. */
1016     XEvent *eventPtr;           /* Information about event. */
1017 {
1018     Container *cntrPtr = (Container *) clientData;
1019
1020     if (eventPtr->xany.window != cntrPtr->adopted) {
1021         return 0;
1022     }
1023     if (eventPtr->type == DestroyNotify) {
1024         cntrPtr->adopted = None;
1025         EventuallyRedraw(cntrPtr);
1026     }
1027     return 1;
1028 }
1029
1030 /*
1031  * --------------------------------------------------------------
1032  *
1033  * ContainerEventProc --
1034  *
1035  *      This procedure is invoked by the Tk dispatcher for various
1036  *      events on container widgets.
1037  *
1038  * Results:
1039  *      None.
1040  *
1041  * Side Effects:
1042  *      When the window gets deleted, internal structures get
1043  *      cleaned up.  When it gets exposed, it is redisplayed.
1044  *
1045  * --------------------------------------------------------------
1046  */
1047 static void
1048 ContainerEventProc(clientData, eventPtr)
1049     ClientData clientData;      /* Information about window. */
1050     XEvent *eventPtr;           /* Information about event. */
1051 {
1052     Container *cntrPtr = clientData;
1053
1054     switch (eventPtr->type) {
1055     case Expose:
1056         if (eventPtr->xexpose.count == 0) {
1057             EventuallyRedraw(cntrPtr);
1058         }
1059         break;
1060
1061     case FocusIn:
1062     case FocusOut:
1063         if (eventPtr->xfocus.detail != NotifyInferior) {
1064             if (eventPtr->type == FocusIn) {
1065                 cntrPtr->flags |= CONTAINER_FOCUS;
1066             } else {
1067                 cntrPtr->flags &= ~CONTAINER_FOCUS;
1068             }
1069             EventuallyRedraw(cntrPtr);
1070         }
1071         break;
1072
1073     case ConfigureNotify:
1074         EventuallyRedraw(cntrPtr);
1075         break;
1076
1077     case DestroyNotify:
1078         if (cntrPtr->tkwin != NULL) {
1079             cntrPtr->tkwin = NULL;
1080             Tcl_DeleteCommandFromToken(cntrPtr->interp, cntrPtr->cmdToken);
1081         }
1082         if (cntrPtr->flags & CONTAINER_REDRAW) {
1083             Tcl_CancelIdleCall(DisplayContainer, cntrPtr);
1084         }
1085         Tcl_EventuallyFree(cntrPtr, DestroyContainer);
1086         break;
1087     }
1088 }
1089
1090 /*
1091  * --------------------------------------------------------------
1092  *
1093  * ToplevelEventProc --
1094  *
1095  *      Some applications assume that they are always a toplevel
1096  *      window and play tricks accordingly.  For example, Netscape
1097  *      positions menus in relation to the toplevel.  But if the
1098  *      container's toplevel is moved, this positioning is wrong.  
1099  *      So watch if the toplevel is moved.  
1100  *
1101  *      [This would be easier and cleaner if Tk toplevels weren't so
1102  *      botched by the addition of menubars.  It's not enough to
1103  *      track the )
1104  *
1105  * Results:
1106  *      None.
1107  *
1108  * --------------------------------------------------------------
1109  */
1110 static void
1111 ToplevelEventProc(clientData, eventPtr)
1112     ClientData clientData;      /* Information about the tab window. */
1113     XEvent *eventPtr;           /* Information about event. */
1114 {
1115     Container *cntrPtr = clientData;
1116
1117     if ((cntrPtr->adopted != None) && (cntrPtr->tkwin != NULL) &&
1118         (eventPtr->type == ConfigureNotify)) {
1119         cntrPtr->flags |= CONTAINER_MOVE;
1120         EventuallyRedraw(cntrPtr);
1121     }
1122 }
1123
1124 /*
1125  * ----------------------------------------------------------------------
1126  *
1127  * DestroyContainer --
1128  *
1129  *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1130  *      to clean up the internal structure of the widget at a safe
1131  *      time (when no-one is using it anymore).
1132  *
1133  * Results:
1134  *      None.
1135  *
1136  * Side Effects:
1137  *      Everything associated with the widget is freed up.
1138  *
1139  * ----------------------------------------------------------------------
1140  */
1141 static void
1142 DestroyContainer(dataPtr)
1143     DestroyData dataPtr;        /* Pointer to the widget record. */
1144 {
1145     Container *cntrPtr = (Container *) dataPtr;
1146
1147     if (cntrPtr->highlightGC != NULL) {
1148         Tk_FreeGC(cntrPtr->display, cntrPtr->highlightGC);
1149     }
1150     if (cntrPtr->flags & CONTAINER_INIT) {
1151         Tk_DeleteGenericHandler(AdoptedWindowEventProc, cntrPtr);
1152     }
1153     if (cntrPtr->tkToplevel != NULL) {
1154         Tk_DeleteEventHandler(cntrPtr->tkToplevel, StructureNotifyMask, 
1155                 ToplevelEventProc, cntrPtr);
1156     }
1157     Tk_FreeOptions(configSpecs, (char *)cntrPtr, cntrPtr->display, 0);
1158     Blt_Free(cntrPtr);
1159 }
1160
1161 /*
1162  * ----------------------------------------------------------------------
1163  *
1164  * ConfigureContainer --
1165  *
1166  *      This procedure is called to process an argv/argc list, plus
1167  *      the Tk option database, in order to configure (or reconfigure)
1168  *      the widget.
1169  *
1170  * Results:
1171  *      The return value is a standard Tcl result.  If TCL_ERROR is
1172  *      returned, then interp->result contains an error message.
1173  *
1174  * Side Effects:
1175  *      Configuration information, such as text string, colors, font,
1176  *      etc. get set for cntrPtr; old resources get freed, if there
1177  *      were any.  The widget is redisplayed.
1178  *
1179  * ----------------------------------------------------------------------
1180  */
1181 static int
1182 ConfigureContainer(interp, cntrPtr, argc, argv, flags)
1183     Tcl_Interp *interp;         /* Interpreter to report errors. */
1184     Container *cntrPtr;         /* Information about widget; may or
1185                                  * may not already have values for
1186                                  * some fields. */
1187     int argc;
1188     char **argv;
1189     int flags;
1190 {
1191     XGCValues gcValues;
1192     unsigned long gcMask;
1193     GC newGC;
1194     int width, height;
1195
1196     if (Tk_ConfigureWidget(interp, cntrPtr->tkwin, configSpecs, argc, argv,
1197             (char *)cntrPtr, flags) != TCL_OK) {
1198         return TCL_ERROR;
1199     }
1200     cntrPtr->inset = cntrPtr->borderWidth + cntrPtr->highlightWidth;
1201     if (Tk_WindowId(cntrPtr->tkwin) == None) {
1202         Tk_MakeWindowExist(cntrPtr->tkwin);
1203     }
1204     if (GetAdoptedWindowGeometry(interp, cntrPtr) != TCL_OK) {
1205         return TCL_ERROR;
1206     }
1207     if (Blt_ConfigModified(configSpecs, interp, "-window", "-name", "-command", 
1208                            (char *)NULL)) {
1209         cntrPtr->flags &= ~CONTAINER_MAPPED;
1210         if (cntrPtr->adopted != None) {
1211             if (Blt_ReparentWindow(cntrPtr->display, cntrPtr->adopted,
1212                     Tk_WindowId(cntrPtr->tkwin), cntrPtr->inset,
1213                     cntrPtr->inset) != TCL_OK) {
1214                 Tcl_AppendResult(interp, "can't adopt window \"", 
1215                          NameOfId(cntrPtr->display, cntrPtr->adopted), 
1216                          "\"", (char *)NULL);
1217                 return TCL_ERROR;
1218             }
1219             XSelectInput(cntrPtr->display, cntrPtr->adopted, 
1220                  StructureNotifyMask);
1221             if ((cntrPtr->flags & CONTAINER_INIT) == 0) {
1222                 Tk_CreateGenericHandler(AdoptedWindowEventProc, cntrPtr);
1223                 cntrPtr->flags |= CONTAINER_INIT;
1224             }
1225         }
1226     }
1227     /* Add the designated inset to the requested dimensions. */
1228     width = cntrPtr->origWidth + 2 * cntrPtr->inset; 
1229     height = cntrPtr->origHeight + 2 * cntrPtr->inset;
1230
1231     if (cntrPtr->reqWidth > 0) {
1232         width = cntrPtr->reqWidth;
1233     } 
1234     if (cntrPtr->reqHeight > 0) {
1235         height = cntrPtr->reqHeight;
1236     } 
1237     /* Set the requested width and height for the container. */
1238     if ((Tk_ReqWidth(cntrPtr->tkwin) != width) ||
1239         (Tk_ReqHeight(cntrPtr->tkwin) != height)) {
1240         Tk_GeometryRequest(cntrPtr->tkwin, width, height);
1241     }
1242
1243     /*
1244      * GC for focus highlight.
1245      */
1246     gcMask = GCForeground;
1247     gcValues.foreground = cntrPtr->highlightColor->pixel;
1248     newGC = Tk_GetGC(cntrPtr->tkwin, gcMask, &gcValues);
1249     if (cntrPtr->highlightGC != NULL) {
1250         Tk_FreeGC(cntrPtr->display, cntrPtr->highlightGC);
1251     }
1252     cntrPtr->highlightGC = newGC;
1253
1254     EventuallyRedraw(cntrPtr);
1255     return TCL_OK;
1256 }
1257
1258 /*
1259  *----------------------------------------------------------------------
1260  *
1261  * ContainerInstCmdDeleteProc --
1262  *
1263  *      This procedure can be called if the window was destroyed
1264  *      (tkwin will be NULL) and the command was deleted
1265  *      automatically.  In this case, we need to do nothing.
1266  *
1267  *      Otherwise this routine was called because the command was
1268  *      deleted.  Then we need to clean-up and destroy the widget.
1269  *
1270  * Results:
1271  *      None.
1272  *
1273  * Side Effects:
1274  *      The widget is destroyed.
1275  *
1276  *----------------------------------------------------------------------
1277  */
1278 static void
1279 ContainerInstCmdDeleteProc(clientData)
1280     ClientData clientData;      /* Pointer to widget record for widget. */
1281 {
1282     Container *cntrPtr = clientData;
1283
1284     if (cntrPtr->tkwin != NULL) {
1285         Tk_Window tkwin;
1286
1287         tkwin = cntrPtr->tkwin;
1288         cntrPtr->tkwin = NULL;
1289         Tk_DestroyWindow(tkwin);
1290 #ifdef ITCL_NAMESPACES
1291         Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
1292 #endif /* ITCL_NAMESPACES */
1293     }
1294 }
1295
1296 /*
1297  * ------------------------------------------------------------------------
1298  *
1299  * ContainerCmd --
1300  *
1301  *      This procedure is invoked to process the Tcl command that
1302  *      corresponds to a widget managed by this module. See the user
1303  *      documentation for details on what it does.
1304  *
1305  * Results:
1306  *      A standard Tcl result.
1307  *
1308  * Side Effects:
1309  *      See the user documentation.
1310  *
1311  * -----------------------------------------------------------------------
1312  */
1313 /* ARGSUSED */
1314 static int
1315 ContainerCmd(clientData, interp, argc, argv)
1316     ClientData clientData;      /* Main window associated with interpreter. */
1317     Tcl_Interp *interp;         /* Current interpreter. */
1318     int argc;                   /* Number of arguments. */
1319     char **argv;                /* Argument strings. */
1320 {
1321     Container *cntrPtr;
1322     Tk_Window tkwin;
1323     unsigned int mask;
1324
1325     if (argc < 2) {
1326         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1327             " pathName ?option value?...\"", (char *)NULL);
1328         return TCL_ERROR;
1329     }
1330     tkwin = Tk_MainWindow(interp);
1331     tkwin = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *)NULL);
1332     if (tkwin == NULL) {
1333         return TCL_ERROR;
1334     }
1335     cntrPtr = Blt_Calloc(1, sizeof(Container));
1336     assert(cntrPtr);
1337     cntrPtr->tkwin = tkwin;
1338     cntrPtr->display = Tk_Display(tkwin);
1339     cntrPtr->interp = interp;
1340     cntrPtr->flags = 0;
1341     cntrPtr->timeout = SEARCH_INTERVAL;
1342     cntrPtr->borderWidth = cntrPtr->highlightWidth = 2;
1343     cntrPtr->relief = TK_RELIEF_SUNKEN;
1344     Tk_SetClass(tkwin, "Container");
1345 #if (TK_MAJOR_VERSION > 4)
1346     Blt_SetWindowInstanceData(tkwin, cntrPtr);
1347 #endif
1348     if (ConfigureContainer(interp, cntrPtr, argc - 2, argv + 2, 0) != TCL_OK) {
1349         Tk_DestroyWindow(cntrPtr->tkwin);
1350         return TCL_ERROR;
1351     }
1352     mask = (StructureNotifyMask | ExposureMask | FocusChangeMask);
1353     Tk_CreateEventHandler(tkwin, mask, ContainerEventProc, cntrPtr);
1354     cntrPtr->cmdToken = Tcl_CreateCommand(interp, argv[1], ContainerInstCmd,
1355         cntrPtr, ContainerInstCmdDeleteProc);
1356 #ifdef ITCL_NAMESPACES
1357     Itk_SetWidgetCommand(cntrPtr->tkwin, cntrPtr->cmdToken);
1358 #endif
1359     Tk_MakeWindowExist(tkwin);
1360
1361     Tcl_SetResult(interp, Tk_PathName(cntrPtr->tkwin), TCL_VOLATILE);
1362     return TCL_OK;
1363 }
1364
1365 /*
1366  * ----------------------------------------------------------------------
1367  *
1368  * DisplayContainer --
1369  *
1370  *      This procedure is invoked to display the widget.
1371  *
1372  * Results:
1373  *      None.
1374  *
1375  * Side effects:
1376  *      The widget is redisplayed.
1377  *
1378  * ----------------------------------------------------------------------
1379  */
1380 static void
1381 DisplayContainer(clientData)
1382     ClientData clientData;      /* Information about widget. */
1383 {
1384     Container *cntrPtr = clientData;
1385     Drawable drawable;
1386     int width, height;
1387
1388     cntrPtr->flags &= ~CONTAINER_REDRAW;
1389     if (cntrPtr->tkwin == NULL) {
1390         return;                 /* Window has been destroyed. */
1391     }
1392     if (!Tk_IsMapped(cntrPtr->tkwin)) {
1393         return;
1394     }
1395     drawable = Tk_WindowId(cntrPtr->tkwin);
1396
1397 #ifndef WIN32
1398     if (cntrPtr->tkToplevel == NULL) {
1399         Window window;
1400         Tk_Window tkToplevel;
1401
1402         /* Create an event handler for the toplevel of the container. */
1403         tkToplevel = Blt_Toplevel(cntrPtr->tkwin);
1404         window = Blt_GetRealWindowId(tkToplevel);
1405         cntrPtr->tkToplevel = Tk_IdToWindow(cntrPtr->display, window); 
1406         if (cntrPtr->tkToplevel != NULL) {
1407             Tk_CreateEventHandler(cntrPtr->tkToplevel, StructureNotifyMask, 
1408                 ToplevelEventProc, cntrPtr);
1409         }
1410     }
1411 #endif /* WIN32 */
1412     if (cntrPtr->adopted != None) {
1413 #ifndef WIN32
1414         if (cntrPtr->flags & CONTAINER_MOVE) {
1415             /* 
1416              * Some applications like Netscape cache its location to
1417              * position its popup menus. But when it's reparented, it
1418              * thinks it's always at the same position.  It doesn't
1419              * know when the container's moved.  The hack here is to
1420              * force the application to update its coordinates by
1421              * moving the adopted window (over by 1 pixel and
1422              * then back in case the application is comparing the last
1423              * location).  
1424              */
1425             XMoveWindow(cntrPtr->display, cntrPtr->adopted,
1426                         cntrPtr->inset + 1, cntrPtr->inset + 1);
1427             XMoveWindow(cntrPtr->display, cntrPtr->adopted,
1428                         cntrPtr->inset, cntrPtr->inset);
1429             cntrPtr->flags &= ~CONTAINER_MOVE;
1430         }
1431 #endif /* WIN32 */
1432         /* Compute the available space inside the container. */
1433         width = Tk_Width(cntrPtr->tkwin) - (2 * cntrPtr->inset);
1434         height = Tk_Height(cntrPtr->tkwin) - (2 * cntrPtr->inset);
1435
1436         if ((cntrPtr->adoptedX != cntrPtr->inset) || 
1437             (cntrPtr->adoptedY != cntrPtr->inset) ||
1438             (cntrPtr->adoptedWidth != width) || 
1439             (cntrPtr->adoptedHeight != height)) {
1440             /* Resize the window to the new size */
1441             if (width < 1) {
1442                 width = 1;
1443             }
1444             if (height < 1) {
1445                 height = 1;
1446             }
1447             XMoveResizeWindow(cntrPtr->display, cntrPtr->adopted,
1448                 cntrPtr->inset, cntrPtr->inset, width, height);
1449             cntrPtr->adoptedWidth = width;
1450             cntrPtr->adoptedHeight = height;
1451             cntrPtr->adoptedX = cntrPtr->adoptedY = cntrPtr->inset;
1452             if (cntrPtr->tkAdopted != NULL) {
1453                 Tk_ResizeWindow(cntrPtr->tkAdopted, width, height);
1454             }
1455         }
1456 #ifndef WIN32
1457         if (!(cntrPtr->flags & CONTAINER_MAPPED)) {
1458             XMapWindow(cntrPtr->display, cntrPtr->adopted);
1459             cntrPtr->flags |= CONTAINER_MAPPED;
1460         }
1461 #endif
1462         if (cntrPtr->borderWidth > 0) {
1463             Blt_Draw3DRectangle(cntrPtr->tkwin, drawable, cntrPtr->border,
1464                 cntrPtr->highlightWidth, cntrPtr->highlightWidth,
1465                 Tk_Width(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
1466                 Tk_Height(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
1467                 cntrPtr->borderWidth, cntrPtr->relief);
1468         }
1469     } else {
1470         Blt_Fill3DRectangle(cntrPtr->tkwin, drawable, cntrPtr->border,
1471             cntrPtr->highlightWidth, cntrPtr->highlightWidth,
1472             Tk_Width(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
1473             Tk_Height(cntrPtr->tkwin) - 2 * cntrPtr->highlightWidth,
1474             cntrPtr->borderWidth, cntrPtr->relief);
1475     }
1476
1477     /* Draw focus highlight ring. */
1478     if (cntrPtr->highlightWidth > 0) {
1479         XColor *color;
1480         GC gc;
1481
1482         color = (cntrPtr->flags & CONTAINER_FOCUS)
1483             ? cntrPtr->highlightColor : cntrPtr->highlightBgColor;
1484         gc = Tk_GCForColor(color, drawable);
1485         Tk_DrawFocusHighlight(cntrPtr->tkwin, gc, cntrPtr->highlightWidth,
1486             drawable);
1487     }
1488 }
1489
1490 #ifdef notdef
1491 /*
1492  *----------------------------------------------------------------------
1493  *
1494  * SendOp --
1495  *
1496  *----------------------------------------------------------------------
1497  */
1498 /*ARGSUSED*/
1499 static int
1500 SendOp(cntrPtr, interp, argc, argv)
1501     Container *cntrPtr;
1502     Tcl_Interp *interp;
1503     int argc;                   /* Not used. */
1504     char **argv;
1505 {
1506
1507     if (cntrPtr->adopted != None) {
1508         XEvent event;
1509         char *p;
1510         KeySym symbol;
1511         int xid;
1512         Window window;
1513
1514         if (Tcl_GetInt(interp, argv[2], &xid) != TCL_OK) {
1515             return TCL_ERROR;
1516         }
1517         window = (Window)xid;
1518         event.xkey.type = KeyPress;
1519         event.xkey.serial = 0;
1520         event.xkey.display = cntrPtr->display;
1521         event.xkey.window = event.xkey.subwindow = window;
1522         event.xkey.time = CurrentTime;
1523         event.xkey.x = event.xkey.x = 100;
1524         event.xkey.root = 
1525             RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));      
1526         event.xkey.x_root = Tk_X(cntrPtr->tkwin) + cntrPtr->inset;
1527         event.xkey.x_root = Tk_Y(cntrPtr->tkwin) + cntrPtr->inset;
1528         event.xkey.state = 0;
1529         event.xkey.same_screen = TRUE;
1530         
1531         for (p = argv[3]; *p != '\0'; p++) {
1532             if (*p == '\r') {
1533                 symbol = XStringToKeysym("Return");
1534             } else if (*p == ' ') {
1535                 symbol = XStringToKeysym("space");
1536             } else {
1537                 char save;
1538
1539                 save = *(p+1);
1540                 *(p+1) = '\0';
1541                 symbol = XStringToKeysym(p);
1542                 *(p+1) = save;
1543             }
1544             event.xkey.keycode = XKeysymToKeycode(cntrPtr->display, symbol);
1545             event.xkey.type = KeyPress;
1546             if (!XSendEvent(cntrPtr->display, window, False, KeyPress, &event)) {
1547                 fprintf(stderr, "send press event failed\n");
1548             }
1549             event.xkey.type = KeyRelease;
1550             if (!XSendEvent(cntrPtr->display, window, False, KeyRelease, 
1551                             &event)) {
1552                 fprintf(stderr, "send release event failed\n");
1553             }
1554         }
1555     }
1556     return TCL_OK;
1557 }
1558 #endif
1559
1560 #ifndef WIN32
1561 /*
1562  *----------------------------------------------------------------------
1563  *
1564  * FindOp --
1565  *
1566  *----------------------------------------------------------------------
1567  */
1568 /*ARGSUSED*/
1569 static int
1570 FindOp(cntrPtr, interp, argc, argv)
1571     Container *cntrPtr;
1572     Tcl_Interp *interp;
1573     int argc;                   /* Not used. */
1574     char **argv;
1575 {
1576     Window root;
1577     SearchInfo search;
1578
1579     memset(&search, 0, sizeof(search));
1580     search.pattern = argv[3];
1581     Tcl_DStringInit(&(search.dString));
1582     search.saveNames = TRUE;    /* Indicates to record all matching XIDs. */
1583     if (strcmp(argv[2], "-name") == 0) {
1584         search.proc = NameSearch;
1585     } else if (strcmp(argv[2], "-command") == 0) {
1586         search.proc = CmdSearch;
1587     } else {
1588         Tcl_AppendResult(interp, "missing \"-name\" or \"-command\" switch",
1589                          (char *)NULL);
1590         return TCL_ERROR;
1591     }
1592     root = RootWindow(cntrPtr->display, Tk_ScreenNumber(cntrPtr->tkwin));
1593     (*search.proc)(cntrPtr->display, root, &search);
1594     Tcl_DStringResult(interp, &search.dString);
1595     return TCL_OK;
1596 }
1597 #endif /*WIN32*/
1598
1599 /*
1600  *----------------------------------------------------------------------
1601  *
1602  * CgetOp --
1603  *
1604  *----------------------------------------------------------------------
1605  */
1606 /*ARGSUSED*/
1607 static int
1608 CgetOp(cntrPtr, interp, argc, argv)
1609     Container *cntrPtr;
1610     Tcl_Interp *interp;
1611     int argc;                   /* Not used. */
1612     char **argv;
1613 {
1614     return Tk_ConfigureValue(interp, cntrPtr->tkwin, configSpecs,
1615         (char *)cntrPtr, argv[2], 0);
1616 }
1617
1618 /*
1619  *----------------------------------------------------------------------
1620  *
1621  * ConfigureOp --
1622  *
1623  *      This procedure is called to process an argv/argc list, plus
1624  *      the Tk option database, in order to configure (or reconfigure)
1625  *      the widget.
1626  *
1627  * Results:
1628  *      A standard Tcl result.  If TCL_ERROR is returned, then
1629  *      interp->result contains an error message.
1630  *
1631  * Side Effects:
1632  *      Configuration information, such as text string, colors, font,
1633  *      etc. get set for cntrPtr; old resources get freed, if there
1634  *      were any.  The widget is redisplayed.
1635  *
1636  *----------------------------------------------------------------------
1637  */
1638 static int
1639 ConfigureOp(cntrPtr, interp, argc, argv)
1640     Container *cntrPtr;
1641     Tcl_Interp *interp;
1642     int argc;
1643     char **argv;
1644 {
1645     if (argc == 2) {
1646         return Tk_ConfigureInfo(interp, cntrPtr->tkwin, configSpecs,
1647             (char *)cntrPtr, (char *)NULL, 0);
1648     } else if (argc == 3) {
1649         return Tk_ConfigureInfo(interp, cntrPtr->tkwin, configSpecs,
1650             (char *)cntrPtr, argv[2], 0);
1651     }
1652     if (ConfigureContainer(interp, cntrPtr, argc - 2, argv + 2,
1653             TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1654         return TCL_ERROR;
1655     }
1656     EventuallyRedraw(cntrPtr);
1657     return TCL_OK;
1658 }
1659
1660 /*
1661  * --------------------------------------------------------------
1662  *
1663  * ContainerCmd --
1664  *
1665  *      This procedure is invoked to process the "container" command.
1666  *      See the user documentation for details on what it does.
1667  *
1668  * Results:
1669  *      A standard Tcl result.
1670  *
1671  * Side effects:
1672  *      See the user documentation.
1673  *
1674  * --------------------------------------------------------------
1675  */
1676 static Blt_OpSpec opSpecs[] =
1677 {
1678     {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
1679     {"configure", 2, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
1680 #ifndef WIN32
1681     {"find", 1, (Blt_Op)FindOp, 3, 4, "?-command|-name? pattern",},
1682 #endif /*WIN32*/
1683 #ifdef notdef
1684     {"send", 1, (Blt_Op)SendOp, 4, 4, "window string",},
1685 #endif
1686 };
1687
1688 static int nSpecs = sizeof(opSpecs) / sizeof(Blt_OpSpec);
1689
1690 static int
1691 ContainerInstCmd(clientData, interp, argc, argv)
1692     ClientData clientData;      /* Information about the widget. */
1693     Tcl_Interp *interp;         /* Interpreter to report errors back to. */
1694     int argc;                   /* Number of arguments. */
1695     char **argv;                /* Vector of argument strings. */
1696 {
1697     Blt_Op proc;
1698     Container *cntrPtr = clientData;
1699     int result;
1700
1701     proc = Blt_GetOp(interp, nSpecs, opSpecs, BLT_OP_ARG1, argc, argv, 0);
1702     if (proc == NULL) {
1703         return TCL_ERROR;
1704     }
1705     Tcl_Preserve(cntrPtr);
1706     result = (*proc)(cntrPtr, interp, argc, argv);
1707     Tcl_Release(cntrPtr);
1708     return result;
1709 }
1710
1711 int
1712 Blt_ContainerInit(interp)
1713     Tcl_Interp *interp;
1714 {
1715     static Blt_CmdSpec cmdSpec =
1716     {
1717         "container", ContainerCmd,
1718     };
1719     if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
1720         return TCL_ERROR;
1721     }
1722     return TCL_OK;
1723 }
1724
1725 #endif /* NO_CONTAINER */