OSDN Git Service

Add new command line switch --no-construct-floats
[pf3gnuchains/pf3gnuchains4x.git] / tk / mac / tkMacWm.c
1 /* 
2  * tkMacWm.c --
3  *
4  *      This module takes care of the interactions between a Tk-based
5  *      application and the window manager.  Among other things, it
6  *      implements the "wm" command and passes geometry information
7  *      to the window manager.
8  *
9  * Copyright (c) 1994-1997 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 <Gestalt.h>
18 #include <QDOffscreen.h>
19 #include <Windows.h>
20 #include <ToolUtils.h>
21
22 #include <tclMac.h>
23 #include "tkPort.h"
24 #include "tkInt.h"
25 #include "tkMacInt.h"
26 #include <errno.h>
27 #include "tkScrollbar.h"
28
29 /*
30  * We now require the Appearance headers.  They come with CodeWarrior Pro,
31  * and are on the SDK CD.  However, we do not require the Appearance 
32  * extension
33  */
34  
35 #include <Appearance.h>
36
37 /*
38  * A data structure of the following type holds information for
39  * each window manager protocol (such as WM_DELETE_WINDOW) for
40  * which a handler (i.e. a Tcl command) has been defined for a
41  * particular top-level window.
42  */
43
44 typedef struct ProtocolHandler {
45     Atom protocol;              /* Identifies the protocol. */
46     struct ProtocolHandler *nextPtr;
47                                 /* Next in list of protocol handlers for
48                                  * the same top-level window, or NULL for
49                                  * end of list. */
50     Tcl_Interp *interp;         /* Interpreter in which to invoke command. */
51     char command[4];            /* Tcl command to invoke when a client
52                                  * message for this protocol arrives. 
53                                  * The actual size of the structure varies
54                                  * to accommodate the needs of the actual
55                                  * command. THIS MUST BE THE LAST FIELD OF
56                                  * THE STRUCTURE. */
57 } ProtocolHandler;
58
59 #define HANDLER_SIZE(cmdLength) \
60 ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
61
62 /*
63  * A data structure of the following type holds window-manager-related
64  * information for each top-level window in an application.
65  */
66
67 typedef struct TkWmInfo {
68     TkWindow *winPtr;           /* Pointer to main Tk information for
69                                  * this window. */
70     Window reparent;            /* If the window has been reparented, this
71                                  * gives the ID of the ancestor of the window
72                                  * that is a child of the root window (may
73                                  * not be window's immediate parent).  If
74                                  * the window isn't reparented, this has the
75                                  * value None. */
76     Tk_Uid titleUid;            /* Title to display in window caption.  If
77                                  * NULL, use name of widget. */
78     Tk_Uid iconName;            /* Name to display in icon. */
79     Window master;              /* Master window for TRANSIENT_FOR property,
80                                  * or None. */
81     XWMHints hints;             /* Various pieces of information for
82                                  * window manager. */
83     char *leaderName;           /* Path name of leader of window group
84                                  * (corresponds to hints.window_group).
85                                  * Malloc-ed.  Note:  this field doesn't
86                                  * get updated if leader is destroyed. */
87     char *masterWindowName;     /* Path name of window specified as master
88                                  * in "wm transient" command, or NULL.
89                                  * Malloc-ed. Note:  this field doesn't
90                                  * get updated if masterWindowName is
91                                  * destroyed. */
92     Tk_Window icon;             /* Window to use as icon for this window,
93                                  * or NULL. */
94     Tk_Window iconFor;          /* Window for which this window is icon, or
95                                  * NULL if this isn't an icon for anyone. */
96
97     /*
98      * Information used to construct an XSizeHints structure for
99      * the window manager:
100      */
101
102     int sizeHintsFlags;         /* Flags word for XSizeHints structure.
103                                  * If the PBaseSize flag is set then the
104                                  * window is gridded;  otherwise it isn't
105                                  * gridded. */
106     int minWidth, minHeight;    /* Minimum dimensions of window, in
107                                  * grid units, not pixels. */
108     int maxWidth, maxHeight;    /* Maximum dimensions of window, in
109                                  * grid units, not pixels. */
110     Tk_Window gridWin;          /* Identifies the window that controls
111                                  * gridding for this top-level, or NULL if
112                                  * the top-level isn't currently gridded. */
113     int widthInc, heightInc;    /* Increments for size changes (# pixels
114                                  * per step). */
115     struct {
116         int x;  /* numerator */
117         int y;  /* denominator */
118     } minAspect, maxAspect;     /* Min/max aspect ratios for window. */
119     int reqGridWidth, reqGridHeight;
120                                 /* The dimensions of the window (in
121                                  * grid units) requested through
122                                  * the geometry manager. */
123     int gravity;                /* Desired window gravity. */
124
125     /*
126      * Information used to manage the size and location of a window.
127      */
128
129     int width, height;          /* Desired dimensions of window, specified
130                                  * in grid units.  These values are
131                                  * set by the "wm geometry" command and by
132                                  * ConfigureNotify events (for when wm
133                                  * resizes window).  -1 means user hasn't
134                                  * requested dimensions. */
135     int x, y;                   /* Desired X and Y coordinates for window.
136                                  * These values are set by "wm geometry",
137                                  * plus by ConfigureNotify events (when wm
138                                  * moves window).  These numbers are
139                                  * different than the numbers stored in
140                                  * winPtr->changes because (a) they could be
141                                  * measured from the right or bottom edge
142                                  * of the screen (see WM_NEGATIVE_X and
143                                  * WM_NEGATIVE_Y flags) and (b) if the window
144                                  * has been reparented then they refer to the
145                                  * parent rather than the window itself. */
146     int parentWidth, parentHeight;
147                                 /* Width and height of reparent, in pixels
148                                  * *including border*.  If window hasn't been
149                                  * reparented then these will be the outer
150                                  * dimensions of the window, including
151                                  * border. */
152     int xInParent, yInParent;   /* Offset of window within reparent,  measured
153                                  * from upper-left outer corner of parent's
154                                  * border to upper-left outer corner of child's
155                                  * border.  If not reparented then these are
156                                  * zero. */
157     int configWidth, configHeight;
158                                 /* Dimensions passed to last request that we
159                                  * issued to change geometry of window.  Used
160                                  * to eliminate redundant resize operations. */
161
162     /*
163      * Information about the virtual root window for this top-level,
164      * if there is one.
165      */
166
167     Window vRoot;               /* Virtual root window for this top-level,
168                                  * or None if there is no virtual root
169                                  * window (i.e. just use the screen's root). */
170     int vRootX, vRootY;         /* Position of the virtual root inside the
171                                  * root window.  If the WM_VROOT_OFFSET_STALE
172                                  * flag is set then this information may be
173                                  * incorrect and needs to be refreshed from
174                                  * the X server.  If vRoot is None then these
175                                  * values are both 0. */
176     unsigned int vRootWidth, vRootHeight;
177                                 /* Dimensions of the virtual root window.
178                                  * If vRoot is None, gives the dimensions
179                                  * of the containing screen.  This information
180                                  * is never stale, even though vRootX and
181                                  * vRootY can be. */
182
183     /*
184      * List of children of the toplevel which have private colormaps.
185      */
186
187     TkWindow **cmapList;        /* Array of window with private colormaps. */
188     int cmapCount;              /* Number of windows in array. */
189
190     /*
191      * Miscellaneous information.
192      */
193
194     ProtocolHandler *protPtr;   /* First in list of protocol handlers for
195                                  * this window (NULL means none). */
196     int cmdArgc;                /* Number of elements in cmdArgv below. */
197     char **cmdArgv;             /* Array of strings to store in the
198                                  * WM_COMMAND property.  NULL means nothing
199                                  * available. */
200     char *clientMachine;        /* String to store in WM_CLIENT_MACHINE
201                                  * property, or NULL. */
202     int flags;                  /* Miscellaneous flags, defined below. */
203
204     /*
205      * Macintosh information.
206      */
207     int style;                  /* Native window style. */
208     TkWindow *scrollWinPtr;     /* Ptr to scrollbar handling grow widget. */
209 } WmInfo;
210
211
212 /*
213  * Flag values for WmInfo structures:
214  *
215  * WM_NEVER_MAPPED -            non-zero means window has never been
216  *                              mapped;  need to update all info when
217  *                              window is first mapped.
218  * WM_UPDATE_PENDING -          non-zero means a call to UpdateGeometryInfo
219  *                              has already been scheduled for this
220  *                              window;  no need to schedule another one.
221  * WM_NEGATIVE_X -              non-zero means x-coordinate is measured in
222  *                              pixels from right edge of screen, rather
223  *                              than from left edge.
224  * WM_NEGATIVE_Y -              non-zero means y-coordinate is measured in
225  *                              pixels up from bottom of screen, rather than
226  *                              down from top.
227  * WM_UPDATE_SIZE_HINTS -       non-zero means that new size hints need to be
228  *                              propagated to window manager.
229  * WM_SYNC_PENDING -            set to non-zero while waiting for the window
230  *                              manager to respond to some state change.
231  * WM_VROOT_OFFSET_STALE -      non-zero means that (x,y) offset information
232  *                              about the virtual root window is stale and
233  *                              needs to be fetched fresh from the X server.
234  * WM_ABOUT_TO_MAP -            non-zero means that the window is about to
235  *                              be mapped by TkWmMapWindow.  This is used
236  *                              by UpdateGeometryInfo to modify its behavior.
237  * WM_MOVE_PENDING -            non-zero means the application has requested
238  *                              a new position for the window, but it hasn't
239  *                              been reflected through the window manager
240  *                              yet.
241  * WM_COLORMAPS_EXPLICIT -      non-zero means the colormap windows were
242  *                              set explicitly via "wm colormapwindows".
243  * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
244  *                              was called the top-level itself wasn't
245  *                              specified, so we added it implicitly at
246  *                              the end of the list.
247  * WM_WIDTH_NOT_RESIZABLE -     non-zero means that we're not supposed to
248  *                              allow the user to change the width of the
249  *                              window (controlled by "wm resizable"
250  *                              command).
251  * WM_HEIGHT_NOT_RESIZABLE -    non-zero means that we're not supposed to
252  *                              allow the user to change the height of the
253  *                              window (controlled by "wm resizable"
254  *                              command).
255  */
256
257 #define WM_NEVER_MAPPED                 1
258 #define WM_UPDATE_PENDING               2
259 #define WM_NEGATIVE_X                   4
260 #define WM_NEGATIVE_Y                   8
261 #define WM_UPDATE_SIZE_HINTS            0x10
262 #define WM_SYNC_PENDING                 0x20
263 #define WM_VROOT_OFFSET_STALE           0x40
264 #define WM_ABOUT_TO_MAP                 0x100
265 #define WM_MOVE_PENDING                 0x200
266 #define WM_COLORMAPS_EXPLICIT           0x400
267 #define WM_ADDED_TOPLEVEL_COLORMAP      0x800
268 #define WM_WIDTH_NOT_RESIZABLE          0x1000
269 #define WM_HEIGHT_NOT_RESIZABLE         0x2000
270
271 /*
272  * This is a list of all of the toplevels that have been mapped so far. It is
273  * used by the menu code to inval windows that were damaged by menus, and will
274  * eventually also be used to keep track of floating windows.
275  */
276
277 TkMacWindowList *tkMacWindowListPtr = NULL;
278
279 /*
280  * The variable below is used to enable or disable tracing in this
281  * module.  If tracing is enabled, then information is printed on
282  * standard output about interesting interactions with the window
283  * manager.
284  */
285
286 static int wmTracing = 0;
287
288 /*
289  * The following structure is the official type record for geometry
290  * management of top-level windows.
291  */
292
293 static void             TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
294         Tk_Window tkwin));
295
296 static Tk_GeomMgr wmMgrType = {
297     "wm",                               /* name */
298     TopLevelReqProc,                    /* requestProc */
299     (Tk_GeomLostSlaveProc *) NULL,      /* lostSlaveProc */
300 };
301
302 /*
303  * Hash table for Mac Window -> TkWindow mapping.
304  */
305
306 static Tcl_HashTable windowTable;
307 static int windowHashInit = false;
308
309 void tkMacMoveWindow(WindowRef window, int x, int y);
310
311 /*
312  * Forward declarations for procedures defined in this file:
313  */
314
315 static void             InitialWindowBounds _ANSI_ARGS_((TkWindow *winPtr, 
316                             Rect *geometry));
317 static int              ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
318                             char *string, TkWindow *winPtr));
319 static void             TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
320                             XEvent *eventPtr));
321 static void             TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
322                             Tk_Window tkwin));
323 static void             UpdateGeometryInfo _ANSI_ARGS_((
324                             ClientData clientData));
325 static void             UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
326 static void             UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));
327 \f
328 /*
329  *--------------------------------------------------------------
330  *
331  * TkWmNewWindow --
332  *
333  *      This procedure is invoked whenever a new top-level
334  *      window is created.  Its job is to initialize the WmInfo
335  *      structure for the window.
336  *
337  * Results:
338  *      None.
339  *
340  * Side effects:
341  *      A WmInfo structure gets allocated and initialized.
342  *
343  *--------------------------------------------------------------
344  */
345
346 void
347 TkWmNewWindow(
348     TkWindow *winPtr)           /* Newly-created top-level window. */
349 {
350     register WmInfo *wmPtr;
351         
352     wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
353     wmPtr->winPtr = winPtr;
354     wmPtr->reparent = None;
355     wmPtr->titleUid = NULL;
356     wmPtr->iconName = NULL;
357     wmPtr->master = None;
358     wmPtr->hints.flags = InputHint | StateHint;
359     wmPtr->hints.input = True;
360     wmPtr->hints.initial_state = NormalState;
361     wmPtr->hints.icon_pixmap = None;
362     wmPtr->hints.icon_window = None;
363     wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
364     wmPtr->hints.icon_mask = None;
365     wmPtr->hints.window_group = None;
366     wmPtr->leaderName = NULL;
367     wmPtr->masterWindowName = NULL;
368     wmPtr->icon = NULL;
369     wmPtr->iconFor = NULL;
370     wmPtr->sizeHintsFlags = 0;
371     wmPtr->minWidth = wmPtr->minHeight = 1;
372
373     /*
374      * Default the maximum dimensions to the size of the display, minus
375      * a guess about how space is needed for window manager decorations.
376      */
377
378     wmPtr->maxWidth = DisplayWidth(winPtr->display, winPtr->screenNum) - 15;
379     wmPtr->maxHeight = DisplayHeight(winPtr->display, winPtr->screenNum) - 30;
380     wmPtr->gridWin = NULL;
381     wmPtr->widthInc = wmPtr->heightInc = 1;
382     wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
383     wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
384     wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
385     wmPtr->gravity = NorthWestGravity;
386     wmPtr->width = -1;
387     wmPtr->height = -1;
388     wmPtr->x = winPtr->changes.x;
389     wmPtr->y = winPtr->changes.y;
390     wmPtr->parentWidth = winPtr->changes.width
391         + 2*winPtr->changes.border_width;
392     wmPtr->parentHeight = winPtr->changes.height
393         + 2*winPtr->changes.border_width;
394     wmPtr->xInParent = 0;
395     wmPtr->yInParent = 0;
396     wmPtr->cmapList = NULL;
397     wmPtr->cmapCount = 0;
398     wmPtr->configWidth = -1;
399     wmPtr->configHeight = -1;
400     wmPtr->vRoot = None;
401     wmPtr->protPtr = NULL;
402     wmPtr->cmdArgv = NULL;
403     wmPtr->clientMachine = NULL;
404     wmPtr->flags = WM_NEVER_MAPPED;
405     wmPtr->style = zoomDocProc;
406     wmPtr->scrollWinPtr = NULL;
407     winPtr->wmInfoPtr = wmPtr;
408
409     UpdateVRootGeometry(wmPtr);
410
411     /*
412      * Tk must monitor structure events for top-level windows, in order
413      * to detect size and position changes caused by window managers.
414      */
415
416     Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,
417             TopLevelEventProc, (ClientData) winPtr);
418
419     /*
420      * Arrange for geometry requests to be reflected from the window
421      * to the window manager.
422      */
423
424     Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
425 }
426 \f
427 /*
428  *--------------------------------------------------------------
429  *
430  * TkWmMapWindow --
431  *
432  *      This procedure is invoked to map a top-level window.  This
433  *      module gets a chance to update all window-manager-related
434  *      information in properties before the window manager sees
435  *      the map event and checks the properties.  It also gets to
436  *      decide whether or not to even map the window after all.
437  *
438  * Results:
439  *      None.
440  *
441  * Side effects:
442  *      Properties of winPtr may get updated to provide up-to-date
443  *      information to the window manager.  The window may also get
444  *      mapped, but it may not be if this procedure decides that
445  *      isn't appropriate (e.g. because the window is withdrawn).
446  *
447  *--------------------------------------------------------------
448  */
449
450 void
451 TkWmMapWindow(
452     TkWindow *winPtr)           /* Top-level window that's about to
453                                  * be mapped. */
454 {
455     register WmInfo *wmPtr = winPtr->wmInfoPtr;
456     Point where = {0, 0};
457     int xOffset, yOffset;           
458     int firstMap = false;
459     MacDrawable *macWin;
460
461     if (wmPtr->flags & WM_NEVER_MAPPED) {
462         wmPtr->flags &= ~WM_NEVER_MAPPED;
463         firstMap = true;
464
465         /*
466          * Create the underlying Mac window for this Tk window.
467          */
468         macWin = (MacDrawable *) winPtr->window;
469         if (!TkMacHostToplevelExists(winPtr)) {
470             TkMacMakeRealWindowExist(winPtr);
471         }
472         
473         /*
474          * Generate configure event when we first map the window.
475          */
476         LocalToGlobal(&where);
477         TkMacWindowOffset((WindowRef) TkMacGetDrawablePort((Drawable) macWin),
478                 &xOffset, &yOffset);
479         where.h -= xOffset;
480         where.v -= yOffset;
481         TkGenWMConfigureEvent((Tk_Window) winPtr, 
482                 where.h, where.v, -1, -1, TK_LOCATION_CHANGED);
483         
484         /*
485          * This is the first time this window has ever been mapped.
486          * Store all the window-manager-related information for the
487          * window.
488          */
489
490         if (wmPtr->titleUid == NULL) {
491             wmPtr->titleUid = winPtr->nameUid;
492         }
493         
494         if (!Tk_IsEmbedded(winPtr)) {
495             TkSetWMName(winPtr, wmPtr->titleUid);
496         }
497
498         TkWmSetClass(winPtr);
499     
500         if (wmPtr->iconName != NULL) {
501             XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
502         }
503     
504         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
505     }
506     if (wmPtr->hints.initial_state == WithdrawnState) {
507         return;
508     }
509     
510     /*
511      * TODO: we need to display a window if it's iconic on creation.
512      */
513
514     if (wmPtr->hints.initial_state == IconicState) {
515         return;
516     }
517     
518     /*
519      * Update geometry information.
520      */
521     wmPtr->flags |= WM_ABOUT_TO_MAP;
522     if (wmPtr->flags & WM_UPDATE_PENDING) {
523         Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
524     }
525     UpdateGeometryInfo((ClientData) winPtr);
526     wmPtr->flags &= ~WM_ABOUT_TO_MAP;
527
528     /*
529      * Map the window.
530      */
531
532     XMapWindow(winPtr->display, winPtr->window);
533     
534     /*
535      * Now that the window is visable we can determine the offset
536      * from the window's content orgin to the window's decorative
537      * orgin (structure orgin).
538      */
539     TkMacWindowOffset((WindowRef) TkMacGetDrawablePort(Tk_WindowId(winPtr)), 
540         &wmPtr->xInParent, &wmPtr->yInParent);
541 }
542 \f
543 /*
544  *--------------------------------------------------------------
545  *
546  * TkWmUnmapWindow --
547  *
548  *      This procedure is invoked to unmap a top-level window.
549  *      On the Macintosh all we do is call XUnmapWindow.
550  *
551  * Results:
552  *      None.
553  *
554  * Side effects:
555  *      Unmaps the window.
556  *
557  *--------------------------------------------------------------
558  */
559
560 void
561 TkWmUnmapWindow(
562     TkWindow *winPtr)           /* Top-level window that's about to
563                                  * be mapped. */
564 {
565     XUnmapWindow(winPtr->display, winPtr->window);
566 }
567 \f
568 /*
569  *--------------------------------------------------------------
570  *
571  * TkWmDeadWindow --
572  *
573  *      This procedure is invoked when a top-level window is
574  *      about to be deleted.  It cleans up the wm-related data
575  *      structures for the window.
576  *
577  * Results:
578  *      None.
579  *
580  * Side effects:
581  *      The WmInfo structure for winPtr gets freed up.
582  *
583  *--------------------------------------------------------------
584  */
585
586 void
587 TkWmDeadWindow(winPtr)
588     TkWindow *winPtr;           /* Top-level window that's being deleted. */
589 {
590     register WmInfo *wmPtr = winPtr->wmInfoPtr;
591     WmInfo *wmPtr2;
592
593     if (wmPtr == NULL) {
594         return;
595     }
596     if (wmPtr->hints.flags & IconPixmapHint) {
597         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
598     }
599     if (wmPtr->hints.flags & IconMaskHint) {
600         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
601     }
602     if (wmPtr->leaderName != NULL) {
603         ckfree(wmPtr->leaderName);
604     }
605     if (wmPtr->masterWindowName != NULL) {
606         ckfree(wmPtr->masterWindowName);
607     }
608     if (wmPtr->icon != NULL) {
609         wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
610         wmPtr2->iconFor = NULL;
611     }
612     if (wmPtr->iconFor != NULL) {
613         wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
614         wmPtr2->icon = NULL;
615         wmPtr2->hints.flags &= ~IconWindowHint;
616     }
617     while (wmPtr->protPtr != NULL) {
618         ProtocolHandler *protPtr;
619
620         protPtr = wmPtr->protPtr;
621         wmPtr->protPtr = protPtr->nextPtr;
622         Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
623     }
624     if (wmPtr->cmdArgv != NULL) {
625         ckfree((char *) wmPtr->cmdArgv);
626     }
627     if (wmPtr->clientMachine != NULL) {
628         ckfree((char *) wmPtr->clientMachine);
629     }
630     if (wmPtr->flags & WM_UPDATE_PENDING) {
631         Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
632     }
633     ckfree((char *) wmPtr);
634     winPtr->wmInfoPtr = NULL;
635 }
636 \f
637 /*
638  *--------------------------------------------------------------
639  *
640  * TkWmSetClass --
641  *
642  *      This procedure is invoked whenever a top-level window's
643  *      class is changed.  If the window has been mapped then this
644  *      procedure updates the window manager property for the
645  *      class.  If the window hasn't been mapped, the update is
646  *      deferred until just before the first mapping.
647  *
648  * Results:
649  *      None.
650  *
651  * Side effects:
652  *      A window property may get updated.
653  *
654  *--------------------------------------------------------------
655  */
656
657 void
658 TkWmSetClass(
659     TkWindow *winPtr)           /* Newly-created top-level window. */
660 {
661     return;
662 }
663 \f
664 /*
665  *----------------------------------------------------------------------
666  *
667  * Tk_WmCmd --
668  *
669  *      This procedure is invoked to process the "wm" Tcl command.
670  *      See the user documentation for details on what it does.
671  *
672  * Results:
673  *      A standard Tcl result.
674  *
675  * Side effects:
676  *      See the user documentation.
677  *
678  *----------------------------------------------------------------------
679  */
680
681         /* ARGSUSED */
682 int
683 Tk_WmCmd(
684     ClientData clientData,      /* Main window associated with
685                                  * interpreter. */
686     Tcl_Interp *interp,         /* Current interpreter. */
687     int argc,                   /* Number of arguments. */
688     char **argv)                /* Argument strings. */
689 {
690     Tk_Window tkwin = (Tk_Window) clientData;
691     TkWindow *winPtr;
692     register WmInfo *wmPtr;
693     int c;
694     size_t length;
695
696     if (argc < 2) {
697         wrongNumArgs:
698         Tcl_AppendResult(interp, "wrong # args: should be \"",
699                 argv[0], " option window ?arg ...?\"", (char *) NULL);
700         return TCL_ERROR;
701     }
702     c = argv[1][0];
703     length = strlen(argv[1]);
704     if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
705             && (length >= 3)) {
706         if ((argc != 2) && (argc != 3)) {
707             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
708                     argv[0], " tracing ?boolean?\"", (char *) NULL);
709             return TCL_ERROR;
710         }
711         if (argc == 2) {
712             interp->result = (wmTracing) ? "on" : "off";
713             return TCL_OK;
714         }
715         return Tcl_GetBoolean(interp, argv[2], &wmTracing);
716     }
717
718     if (argc < 3) {
719         goto wrongNumArgs;
720     }
721     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
722     if (winPtr == NULL) {
723         return TCL_ERROR;
724     }
725     if (!(winPtr->flags & TK_TOP_LEVEL)) {
726         Tcl_AppendResult(interp, "window \"", winPtr->pathName,
727                 "\" isn't a top-level window", (char *) NULL);
728         return TCL_ERROR;
729     }
730     wmPtr = winPtr->wmInfoPtr;
731     if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
732         int numer1, denom1, numer2, denom2;
733
734         if ((argc != 3) && (argc != 7)) {
735             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
736                     argv[0], " aspect window ?minNumer minDenom ",
737                     "maxNumer maxDenom?\"", (char *) NULL);
738             return TCL_ERROR;
739         }
740         if (argc == 3) {
741             if (wmPtr->sizeHintsFlags & PAspect) {
742                 sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
743                         wmPtr->minAspect.y, wmPtr->maxAspect.x,
744                         wmPtr->maxAspect.y);
745             }
746             return TCL_OK;
747         }
748         if (*argv[3] == '\0') {
749             wmPtr->sizeHintsFlags &= ~PAspect;
750         } else {
751             if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
752                     || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
753                     || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
754                     || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
755                 return TCL_ERROR;
756             }
757             if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
758                     (denom2 <= 0)) {
759                 interp->result = "aspect number can't be <= 0";
760                 return TCL_ERROR;
761             }
762             wmPtr->minAspect.x = numer1;
763             wmPtr->minAspect.y = denom1;
764             wmPtr->maxAspect.x = numer2;
765             wmPtr->maxAspect.y = denom2;
766             wmPtr->sizeHintsFlags |= PAspect;
767         }
768         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
769         goto updateGeom;
770     } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
771             && (length >= 2)) {
772         if ((argc != 3) && (argc != 4)) {
773             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
774                     argv[0], " client window ?name?\"",
775                     (char *) NULL);
776             return TCL_ERROR;
777         }
778         if (argc == 3) {
779             if (wmPtr->clientMachine != NULL) {
780                 interp->result = wmPtr->clientMachine;
781             }
782             return TCL_OK;
783         }
784         if (argv[3][0] == 0) {
785             if (wmPtr->clientMachine != NULL) {
786                 ckfree((char *) wmPtr->clientMachine);
787                 wmPtr->clientMachine = NULL;
788             }
789             return TCL_OK;
790         }
791         if (wmPtr->clientMachine != NULL) {
792             ckfree((char *) wmPtr->clientMachine);
793         }
794         wmPtr->clientMachine = (char *)
795             ckalloc((unsigned) (strlen(argv[3]) + 1));
796         strcpy(wmPtr->clientMachine, argv[3]);
797     } else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)
798             && (length >= 3)) {
799         TkWindow **cmapList;
800         TkWindow *winPtr2;
801         int i, windowArgc, gotToplevel;
802         char **windowArgv;
803
804         if ((argc != 3) && (argc != 4)) {
805             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
806                     argv[0], " colormapwindows window ?windowList?\"",
807                     (char *) NULL);
808             return TCL_ERROR;
809         }
810         if (argc == 3) {
811             Tk_MakeWindowExist((Tk_Window) winPtr);
812             for (i = 0; i < wmPtr->cmapCount; i++) {
813                 if ((i == (wmPtr->cmapCount-1))
814                         && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
815                     break;
816                 }
817                 Tcl_AppendElement(interp, wmPtr->cmapList[i]->pathName);
818             }
819             return TCL_OK;
820         }
821         if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)
822                 != TCL_OK) {
823             return TCL_ERROR;
824         }
825         cmapList = (TkWindow **) ckalloc((unsigned)
826                 ((windowArgc+1)*sizeof(TkWindow*)));
827         for (i = 0; i < windowArgc; i++) {
828             winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],
829                     tkwin);
830             if (winPtr2 == NULL) {
831                 ckfree((char *) cmapList);
832                 ckfree((char *) windowArgv);
833                 return TCL_ERROR;
834             }
835             if (winPtr2 == winPtr) {
836                 gotToplevel = 1;
837             }
838             if (winPtr2->window == None) {
839                 Tk_MakeWindowExist((Tk_Window) winPtr2);
840             }
841             cmapList[i] = winPtr2;
842         }
843         if (!gotToplevel) {
844             wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
845             cmapList[windowArgc] = winPtr;
846             windowArgc++;
847         } else {
848             wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
849         }
850         wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
851         if (wmPtr->cmapList != NULL) {
852             ckfree((char *)wmPtr->cmapList);
853         }
854         wmPtr->cmapList = cmapList;
855         wmPtr->cmapCount = windowArgc;
856         ckfree((char *) windowArgv);
857
858         /*
859          * On the Macintosh all of this is just an excercise
860          * in compatability as we don't support colormaps.  If 
861          * we did they would be installed here.
862          */
863
864         return TCL_OK;
865     } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
866             && (length >= 3)) {
867         int cmdArgc;
868         char **cmdArgv;
869
870         if ((argc != 3) && (argc != 4)) {
871             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
872                     argv[0], " command window ?value?\"",
873                     (char *) NULL);
874             return TCL_ERROR;
875         }
876         if (argc == 3) {
877             if (wmPtr->cmdArgv != NULL) {
878                 interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);
879                 interp->freeProc = (Tcl_FreeProc *) free;
880             }
881             return TCL_OK;
882         }
883         if (argv[3][0] == 0) {
884             if (wmPtr->cmdArgv != NULL) {
885                 ckfree((char *) wmPtr->cmdArgv);
886                 wmPtr->cmdArgv = NULL;
887             }
888             return TCL_OK;
889         }
890         if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
891             return TCL_ERROR;
892         }
893         if (wmPtr->cmdArgv != NULL) {
894             ckfree((char *) wmPtr->cmdArgv);
895         }
896         wmPtr->cmdArgc = cmdArgc;
897         wmPtr->cmdArgv = cmdArgv;
898     } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
899         if (argc != 3) {
900             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
901                     argv[0], " deiconify window\"", (char *) NULL);
902             return TCL_ERROR;
903         }
904         if (wmPtr->iconFor != NULL) {
905             Tcl_AppendResult(interp, "can't deiconify ", argv[2],
906                     ": it is an icon for ", winPtr->pathName, (char *) NULL);
907             return TCL_ERROR;
908         }
909         if (winPtr->flags & TK_EMBEDDED) {
910             Tcl_AppendResult(interp, "can't deiconify ", winPtr->pathName,
911                     ": it is an embedded window", (char *) NULL);
912             return TCL_ERROR;
913         }
914         
915         /*
916          * TODO: may not want to call this function - look at Map events gened.
917          */
918
919         TkpWmSetState(winPtr, NormalState);
920     } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
921             && (length >= 2)) {
922         if ((argc != 3) && (argc != 4)) {
923             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
924                     argv[0], " focusmodel window ?active|passive?\"",
925                     (char *) NULL);
926             return TCL_ERROR;
927         }
928         if (argc == 3) {
929             interp->result = wmPtr->hints.input ? "passive" : "active";
930             return TCL_OK;
931         }
932         c = argv[3][0];
933         length = strlen(argv[3]);
934         if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
935             wmPtr->hints.input = False;
936         } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
937             wmPtr->hints.input = True;
938         } else {
939             Tcl_AppendResult(interp, "bad argument \"", argv[3],
940                     "\": must be active or passive", (char *) NULL);
941             return TCL_ERROR;
942         }
943     } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
944             && (length >= 2)) {
945         Window window;
946
947         if (argc != 3) {
948             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
949                     argv[0], " frame window\"", (char *) NULL);
950             return TCL_ERROR;
951         }
952         window = wmPtr->reparent;
953         if (window == None) {
954             window = Tk_WindowId((Tk_Window) winPtr);
955         }
956         sprintf(interp->result, "0x%x", (unsigned int) window);
957     } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
958             && (length >= 2)) {
959         char xSign, ySign;
960         int width, height;
961
962         if ((argc != 3) && (argc != 4)) {
963             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
964                     argv[0], " geometry window ?newGeometry?\"",
965                     (char *) NULL);
966             return TCL_ERROR;
967         }
968         if (argc == 3) {
969             xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
970             ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
971             if (wmPtr->gridWin != NULL) {
972                 width = wmPtr->reqGridWidth + (winPtr->changes.width
973                         - winPtr->reqWidth)/wmPtr->widthInc;
974                 height = wmPtr->reqGridHeight + (winPtr->changes.height
975                         - winPtr->reqHeight)/wmPtr->heightInc;
976             } else {
977                 width = winPtr->changes.width;
978                 height = winPtr->changes.height;
979             }
980             sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
981                     xSign, wmPtr->x, ySign, wmPtr->y);
982             return TCL_OK;
983         }
984         if (*argv[3] == '\0') {
985             wmPtr->width = -1;
986             wmPtr->height = -1;
987             goto updateGeom;
988         }
989         return ParseGeometry(interp, argv[3], winPtr);
990     } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
991             && (length >= 3)) {
992         int reqWidth, reqHeight, widthInc, heightInc;
993
994         if ((argc != 3) && (argc != 7)) {
995             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
996                     argv[0], " grid window ?baseWidth baseHeight ",
997                     "widthInc heightInc?\"", (char *) NULL);
998             return TCL_ERROR;
999         }
1000         if (argc == 3) {
1001             if (wmPtr->sizeHintsFlags & PBaseSize) {
1002                 sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
1003                         wmPtr->reqGridHeight, wmPtr->widthInc,
1004                         wmPtr->heightInc);
1005             }
1006             return TCL_OK;
1007         }
1008         if (*argv[3] == '\0') {
1009             /*
1010              * Turn off gridding and reset the width and height
1011              * to make sense as ungridded numbers.
1012              */
1013
1014             wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
1015             if (wmPtr->width != -1) {
1016                 wmPtr->width = winPtr->reqWidth + (wmPtr->width
1017                         - wmPtr->reqGridWidth)*wmPtr->widthInc;
1018                 wmPtr->height = winPtr->reqHeight + (wmPtr->height
1019                         - wmPtr->reqGridHeight)*wmPtr->heightInc;
1020             }
1021             wmPtr->widthInc = 1;
1022             wmPtr->heightInc = 1;
1023         } else {
1024             if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
1025                     || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
1026                     || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
1027                     || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
1028                 return TCL_ERROR;
1029             }
1030             if (reqWidth < 0) {
1031                 interp->result = "baseWidth can't be < 0";
1032                 return TCL_ERROR;
1033             }
1034             if (reqHeight < 0) {
1035                 interp->result = "baseHeight can't be < 0";
1036                 return TCL_ERROR;
1037             }
1038             if (widthInc < 0) {
1039                 interp->result = "widthInc can't be < 0";
1040                 return TCL_ERROR;
1041             }
1042             if (heightInc < 0) {
1043                 interp->result = "heightInc can't be < 0";
1044                 return TCL_ERROR;
1045             }
1046             Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
1047                     heightInc);
1048         }
1049         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1050         goto updateGeom;
1051     } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
1052             && (length >= 3)) {
1053         Tk_Window tkwin2;
1054
1055         if ((argc != 3) && (argc != 4)) {
1056             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1057                     argv[0], " group window ?pathName?\"",
1058                     (char *) NULL);
1059             return TCL_ERROR;
1060         }
1061         if (argc == 3) {
1062             if (wmPtr->hints.flags & WindowGroupHint) {
1063                 interp->result = wmPtr->leaderName;
1064             }
1065             return TCL_OK;
1066         }
1067         if (*argv[3] == '\0') {
1068             wmPtr->hints.flags &= ~WindowGroupHint;
1069             if (wmPtr->leaderName != NULL) {
1070                 ckfree(wmPtr->leaderName);
1071             }
1072             wmPtr->leaderName = NULL;
1073         } else {
1074             tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
1075             if (tkwin2 == NULL) {
1076                 return TCL_ERROR;
1077             }
1078             Tk_MakeWindowExist(tkwin2);
1079             wmPtr->hints.window_group = Tk_WindowId(tkwin2);
1080             wmPtr->hints.flags |= WindowGroupHint;
1081             wmPtr->leaderName = ckalloc((unsigned) (strlen(argv[3])+1));
1082             strcpy(wmPtr->leaderName, argv[3]);
1083         }
1084     } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
1085             && (length >= 5)) {
1086         Pixmap pixmap;
1087
1088         if ((argc != 3) && (argc != 4)) {
1089             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1090                     argv[0], " iconbitmap window ?bitmap?\"",
1091                     (char *) NULL);
1092             return TCL_ERROR;
1093         }
1094         if (argc == 3) {
1095             if (wmPtr->hints.flags & IconPixmapHint) {
1096                 interp->result = Tk_NameOfBitmap(winPtr->display,
1097                         wmPtr->hints.icon_pixmap);
1098             }
1099             return TCL_OK;
1100         }
1101         if (*argv[3] == '\0') {
1102             if (wmPtr->hints.icon_pixmap != None) {
1103                 Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
1104             }
1105             wmPtr->hints.flags &= ~IconPixmapHint;
1106         } else {
1107             pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
1108                     Tk_GetUid(argv[3]));
1109             if (pixmap == None) {
1110                 return TCL_ERROR;
1111             }
1112             wmPtr->hints.icon_pixmap = pixmap;
1113             wmPtr->hints.flags |= IconPixmapHint;
1114         }
1115     } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
1116             && (length >= 5)) {
1117         if (argc != 3) {
1118             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1119                     argv[0], " iconify window\"", (char *) NULL);
1120             return TCL_ERROR;
1121         }
1122         if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
1123             Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
1124                     "\": override-redirect flag is set", (char *) NULL);
1125             return TCL_ERROR;
1126         }
1127         if (wmPtr->master != None) {
1128             Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
1129                     "\": it is a transient", (char *) NULL);
1130             return TCL_ERROR;
1131         }
1132         if (wmPtr->iconFor != NULL) {
1133             Tcl_AppendResult(interp, "can't iconify ", argv[2],
1134                     ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
1135                    (char *) NULL);
1136             return TCL_ERROR;
1137         }
1138         if (winPtr->flags & TK_EMBEDDED) {
1139             Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
1140                     ": it is an embedded window", (char *) NULL);
1141             return TCL_ERROR;
1142         }
1143         TkpWmSetState(winPtr, IconicState);
1144     } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
1145             && (length >= 5)) {
1146         Pixmap pixmap;
1147
1148         if ((argc != 3) && (argc != 4)) {
1149             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1150                     argv[0], " iconmask window ?bitmap?\"",
1151                     (char *) NULL);
1152             return TCL_ERROR;
1153         }
1154         if (argc == 3) {
1155             if (wmPtr->hints.flags & IconMaskHint) {
1156                 interp->result = Tk_NameOfBitmap(winPtr->display,
1157                         wmPtr->hints.icon_mask);
1158             }
1159             return TCL_OK;
1160         }
1161         if (*argv[3] == '\0') {
1162             if (wmPtr->hints.icon_mask != None) {
1163                 Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
1164             }
1165             wmPtr->hints.flags &= ~IconMaskHint;
1166         } else {
1167             pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
1168             if (pixmap == None) {
1169                 return TCL_ERROR;
1170             }
1171             wmPtr->hints.icon_mask = pixmap;
1172             wmPtr->hints.flags |= IconMaskHint;
1173         }
1174     } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
1175             && (length >= 5)) {
1176         if (argc > 4) {
1177             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1178                     argv[0], " iconname window ?newName?\"", (char *) NULL);
1179             return TCL_ERROR;
1180         }
1181         if (argc == 3) {
1182             interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
1183             return TCL_OK;
1184         } else {
1185             wmPtr->iconName = Tk_GetUid(argv[3]);
1186             if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1187                 XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
1188             }
1189         }
1190     } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
1191             && (length >= 5)) {
1192         int x, y;
1193
1194         if ((argc != 3) && (argc != 5)) {
1195             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1196                     argv[0], " iconposition window ?x y?\"",
1197                     (char *) NULL);
1198             return TCL_ERROR;
1199         }
1200         if (argc == 3) {
1201             if (wmPtr->hints.flags & IconPositionHint) {
1202                 sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
1203                         wmPtr->hints.icon_y);
1204             }
1205             return TCL_OK;
1206         }
1207         if (*argv[3] == '\0') {
1208             wmPtr->hints.flags &= ~IconPositionHint;
1209         } else {
1210             if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
1211                     || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
1212                 return TCL_ERROR;
1213             }
1214             wmPtr->hints.icon_x = x;
1215             wmPtr->hints.icon_y = y;
1216             wmPtr->hints.flags |= IconPositionHint;
1217         }
1218     } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
1219             && (length >= 5)) {
1220         Tk_Window tkwin2;
1221         WmInfo *wmPtr2;
1222
1223         if ((argc != 3) && (argc != 4)) {
1224             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1225                     argv[0], " iconwindow window ?pathName?\"",
1226                     (char *) NULL);
1227             return TCL_ERROR;
1228         }
1229         if (argc == 3) {
1230             if (wmPtr->icon != NULL) {
1231                 interp->result = Tk_PathName(wmPtr->icon);
1232             }
1233             return TCL_OK;
1234         }
1235         if (*argv[3] == '\0') {
1236             wmPtr->hints.flags &= ~IconWindowHint;
1237             if (wmPtr->icon != NULL) {
1238                 wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
1239                 wmPtr2->iconFor = NULL;
1240                 wmPtr2->hints.initial_state = WithdrawnState;
1241             }
1242             wmPtr->icon = NULL;
1243         } else {
1244             tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
1245             if (tkwin2 == NULL) {
1246                 return TCL_ERROR;
1247             }
1248             if (!Tk_IsTopLevel(tkwin2)) {
1249                 Tcl_AppendResult(interp, "can't use ", argv[3],
1250                         " as icon window: not at top level", (char *) NULL);
1251                 return TCL_ERROR;
1252             }
1253             wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
1254             if (wmPtr2->iconFor != NULL) {
1255                 Tcl_AppendResult(interp, argv[3], " is already an icon for ",
1256                         Tk_PathName(wmPtr2->iconFor), (char *) NULL);
1257                 return TCL_ERROR;
1258             }
1259             if (wmPtr->icon != NULL) {
1260                 WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
1261                 wmPtr3->iconFor = NULL;
1262             }
1263             Tk_MakeWindowExist(tkwin2);
1264             wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
1265             wmPtr->hints.flags |= IconWindowHint;
1266             wmPtr->icon = tkwin2;
1267             wmPtr2->iconFor = (Tk_Window) winPtr;
1268             if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
1269                 /*
1270                  * Don't have iconwindows on the Mac.  We just withdraw.
1271                  */
1272
1273                 Tk_UnmapWindow(tkwin2);
1274             }
1275         }
1276     } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
1277             && (length >= 2)) {
1278         int width, height;
1279         if ((argc != 3) && (argc != 5)) {
1280             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1281                     argv[0], " maxsize window ?width height?\"", (char *) NULL);
1282             return TCL_ERROR;
1283         }
1284         if (argc == 3) {
1285             sprintf(interp->result, "%d %d", wmPtr->maxWidth,
1286                     wmPtr->maxHeight);
1287             return TCL_OK;
1288         }
1289         if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
1290                 || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
1291             return TCL_ERROR;
1292         }
1293         wmPtr->maxWidth = width;
1294         wmPtr->maxHeight = height;
1295         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1296         goto updateGeom;
1297     } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
1298             && (length >= 2)) {
1299         int width, height;
1300         if ((argc != 3) && (argc != 5)) {
1301             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1302                    argv[0], " minsize window ?width height?\"", (char *) NULL);
1303             return TCL_ERROR;
1304         }
1305         if (argc == 3) {
1306             sprintf(interp->result, "%d %d", wmPtr->minWidth,
1307                     wmPtr->minHeight);
1308             return TCL_OK;
1309         }
1310         if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
1311                 || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
1312             return TCL_ERROR;
1313         }
1314         wmPtr->minWidth = width;
1315         wmPtr->minHeight = height;
1316         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1317         goto updateGeom;
1318     } else if ((c == 'o')
1319             && (strncmp(argv[1], "overrideredirect", length) == 0)) {
1320         int boolean;
1321         XSetWindowAttributes atts;
1322
1323         if ((argc != 3) && (argc != 4)) {
1324             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1325                     argv[0], " overrideredirect window ?boolean?\"",
1326                     (char *) NULL);
1327             return TCL_ERROR;
1328         }
1329         if (argc == 3) {
1330             if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
1331                 interp->result = "1";
1332             } else {
1333                 interp->result = "0";
1334             }
1335             return TCL_OK;
1336         }
1337         if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
1338             return TCL_ERROR;
1339         }
1340         atts.override_redirect = (boolean) ? True : False;
1341         Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
1342                 &atts);
1343         wmPtr->style = (boolean) ? plainDBox : documentProc;
1344     } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
1345             && (length >= 2)) {
1346         if ((argc != 3) && (argc != 4)) {
1347             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1348                     argv[0], " positionfrom window ?user/program?\"",
1349                     (char *) NULL);
1350             return TCL_ERROR;
1351         }
1352         if (argc == 3) {
1353             if (wmPtr->sizeHintsFlags & USPosition) {
1354                 interp->result = "user";
1355             } else if (wmPtr->sizeHintsFlags & PPosition) {
1356                 interp->result = "program";
1357             }
1358             return TCL_OK;
1359         }
1360         if (*argv[3] == '\0') {
1361             wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
1362         } else {
1363             c = argv[3][0];
1364             length = strlen(argv[3]);
1365             if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
1366                 wmPtr->sizeHintsFlags &= ~PPosition;
1367                 wmPtr->sizeHintsFlags |= USPosition;
1368             } else if ((c == 'p') &&
1369                     (strncmp(argv[3], "program", length) == 0)) {
1370                 wmPtr->sizeHintsFlags &= ~USPosition;
1371                 wmPtr->sizeHintsFlags |= PPosition;
1372             } else {
1373                 Tcl_AppendResult(interp, "bad argument \"", argv[3],
1374                         "\": must be program or user", (char *) NULL);
1375                 return TCL_ERROR;
1376             }
1377         }
1378         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1379         goto updateGeom;
1380     } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
1381             && (length >= 2)) {
1382         register ProtocolHandler *protPtr, *prevPtr;
1383         Atom protocol;
1384         int cmdLength;
1385
1386         if ((argc < 3) || (argc > 5)) {
1387             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1388                     argv[0], " protocol window ?name? ?command?\"",
1389                     (char *) NULL);
1390             return TCL_ERROR;
1391         }
1392         if (argc == 3) {
1393             /*
1394              * Return a list of all defined protocols for the window.
1395              */
1396             for (protPtr = wmPtr->protPtr; protPtr != NULL;
1397                                            protPtr = protPtr->nextPtr) {
1398                 Tcl_AppendElement(interp,
1399                         Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
1400             }
1401             return TCL_OK;
1402         }
1403         protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
1404         if (argc == 4) {
1405             /*
1406              * Return the command to handle a given protocol.
1407              */
1408             for (protPtr = wmPtr->protPtr; protPtr != NULL;
1409                                            protPtr = protPtr->nextPtr) {
1410                 if (protPtr->protocol == protocol) {
1411                     interp->result = protPtr->command;
1412                     return TCL_OK;
1413                 }
1414             }
1415             return TCL_OK;
1416         }
1417
1418         /*
1419          * Delete any current protocol handler, then create a new
1420          * one with the specified command, unless the command is
1421          * empty.
1422          */
1423
1424         for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
1425                prevPtr = protPtr, protPtr = protPtr->nextPtr) {
1426             if (protPtr->protocol == protocol) {
1427                 if (prevPtr == NULL) {
1428                     wmPtr->protPtr = protPtr->nextPtr;
1429                 } else {
1430                     prevPtr->nextPtr = protPtr->nextPtr;
1431                 }
1432                 Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
1433                 break;
1434             }
1435         }
1436         cmdLength = strlen(argv[4]);
1437         if (cmdLength > 0) {
1438             protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
1439             protPtr->protocol = protocol;
1440             protPtr->nextPtr = wmPtr->protPtr;
1441             wmPtr->protPtr = protPtr;
1442             protPtr->interp = interp;
1443             strcpy(protPtr->command, argv[4]);
1444         }
1445     } else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {
1446         int width, height;
1447
1448         if ((argc != 3) && (argc != 5)) {
1449             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1450                     argv[0], " resizable window ?width height?\"",
1451                     (char *) NULL);
1452             return TCL_ERROR;
1453         }
1454         if (argc == 3) {
1455             sprintf(interp->result, "%d %d",
1456                     (wmPtr->flags  & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
1457                     (wmPtr->flags  & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
1458             return TCL_OK;
1459         }
1460         if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)
1461                 || (Tcl_GetBoolean(interp, argv[4], &height) != TCL_OK)) {
1462             return TCL_ERROR;
1463         }
1464         if (width) {
1465             wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
1466         } else {
1467             wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
1468         }
1469         if (height) {
1470             wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
1471         } else {
1472             wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
1473         }
1474         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1475         if (wmPtr->scrollWinPtr != NULL) {
1476             TkScrollbarEventuallyRedraw(
1477                 (TkScrollbar *) wmPtr->scrollWinPtr->instanceData);
1478         }
1479         goto updateGeom;
1480     } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
1481             && (length >= 2)) {
1482         if ((argc != 3) && (argc != 4)) {
1483             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1484                     argv[0], " sizefrom window ?user|program?\"",
1485                     (char *) NULL);
1486             return TCL_ERROR;
1487         }
1488         if (argc == 3) {
1489             if (wmPtr->sizeHintsFlags & USSize) {
1490                 interp->result = "user";
1491             } else if (wmPtr->sizeHintsFlags & PSize) {
1492                 interp->result = "program";
1493             }
1494             return TCL_OK;
1495         }
1496         if (*argv[3] == '\0') {
1497             wmPtr->sizeHintsFlags &= ~(USSize|PSize);
1498         } else {
1499             c = argv[3][0];
1500             length = strlen(argv[3]);
1501             if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
1502                 wmPtr->sizeHintsFlags &= ~PSize;
1503                 wmPtr->sizeHintsFlags |= USSize;
1504             } else if ((c == 'p')
1505                     && (strncmp(argv[3], "program", length) == 0)) {
1506                 wmPtr->sizeHintsFlags &= ~USSize;
1507                 wmPtr->sizeHintsFlags |= PSize;
1508             } else {
1509                 Tcl_AppendResult(interp, "bad argument \"", argv[3],
1510                         "\": must be program or user", (char *) NULL);
1511                 return TCL_ERROR;
1512             }
1513         }
1514         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1515         goto updateGeom;
1516     } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
1517             && (length >= 2)) {
1518         if (argc != 3) {
1519             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1520                     argv[0], " state window\"", (char *) NULL);
1521             return TCL_ERROR;
1522         }
1523         if (wmPtr->iconFor != NULL) {
1524             interp->result = "icon";
1525         } else {
1526             switch (wmPtr->hints.initial_state) {
1527                 case NormalState:
1528                     interp->result = "normal";
1529                     break;
1530                 case IconicState:
1531                     interp->result = "iconic";
1532                     break;
1533                 case WithdrawnState:
1534                     interp->result = "withdrawn";
1535                     break;
1536                 case ZoomState:
1537                     interp->result = "zoomed";
1538                     break;
1539             }
1540         }
1541     } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
1542             && (length >= 2)) {
1543         if (argc > 4) {
1544             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1545                     argv[0], " title window ?newTitle?\"", (char *) NULL);
1546             return TCL_ERROR;
1547         }
1548         if (argc == 3) {
1549             interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid
1550                 : winPtr->nameUid;
1551             return TCL_OK;
1552         } else {
1553             wmPtr->titleUid = Tk_GetUid(argv[3]);
1554             if (!(wmPtr->flags & WM_NEVER_MAPPED) && !Tk_IsEmbedded(winPtr)) {
1555                 TkSetWMName(winPtr, wmPtr->titleUid);
1556             }
1557         }
1558     } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
1559             && (length >= 3)) {
1560         Tk_Window master;
1561
1562         if ((argc != 3) && (argc != 4)) {
1563             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1564                     argv[0], " transient window ?master?\"", (char *) NULL);
1565             return TCL_ERROR;
1566         }
1567         if (argc == 3) {
1568             if (wmPtr->master != None) {
1569                 interp->result = wmPtr->masterWindowName;
1570             }
1571             return TCL_OK;
1572         }
1573         if (argv[3][0] == '\0') {
1574             wmPtr->master = None;
1575             if (wmPtr->masterWindowName != NULL) {
1576                 ckfree(wmPtr->masterWindowName);
1577             }
1578             wmPtr->masterWindowName = NULL;
1579             wmPtr->style = documentProc;
1580         } else {
1581             master = Tk_NameToWindow(interp, argv[3], tkwin);
1582             if (master == NULL) {
1583                 return TCL_ERROR;
1584             }
1585             Tk_MakeWindowExist(master);
1586             wmPtr->master = Tk_WindowId(master);
1587             wmPtr->masterWindowName = ckalloc((unsigned) (strlen(argv[3])+1));
1588             strcpy(wmPtr->masterWindowName, argv[3]);
1589             wmPtr->style = plainDBox;
1590         }
1591     } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
1592         if (argc != 3) {
1593             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1594                     argv[0], " withdraw window\"", (char *) NULL);
1595             return TCL_ERROR;
1596         }
1597         if (wmPtr->iconFor != NULL) {
1598             Tcl_AppendResult(interp, "can't withdraw ", argv[2],
1599                     ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
1600                     (char *) NULL);
1601             return TCL_ERROR;
1602         }
1603         TkpWmSetState(winPtr, WithdrawnState);
1604     } else {
1605         Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
1606                 "\": must be aspect, client, command, deiconify, ",
1607                 "focusmodel, frame, geometry, grid, group, iconbitmap, ",
1608                 "iconify, iconmask, iconname, iconposition, ",
1609                 "iconwindow, maxsize, minsize, overrideredirect, ",
1610                 "positionfrom, protocol, resizable, sizefrom, state, title, ",
1611                 "transient, or withdraw",
1612                 (char *) NULL);
1613         return TCL_ERROR;
1614     }
1615     return TCL_OK;
1616
1617     updateGeom:
1618     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1619         Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1620         wmPtr->flags |= WM_UPDATE_PENDING;
1621     }
1622     return TCL_OK;
1623 }
1624 \f
1625 /*
1626  *----------------------------------------------------------------------
1627  *
1628  * Tk_SetGrid --
1629  *
1630  *      This procedure is invoked by a widget when it wishes to set a grid
1631  *      coordinate system that controls the size of a top-level window.
1632  *      It provides a C interface equivalent to the "wm grid" command and
1633  *      is usually asscoiated with the -setgrid option.
1634  *
1635  * Results:
1636  *      None.
1637  *
1638  * Side effects:
1639  *      Grid-related information will be passed to the window manager, so
1640  *      that the top-level window associated with tkwin will resize on
1641  *      even grid units.  If some other window already controls gridding
1642  *      for the top-level window then this procedure call has no effect.
1643  *
1644  *----------------------------------------------------------------------
1645  */
1646
1647 void
1648 Tk_SetGrid(
1649     Tk_Window tkwin,            /* Token for window.  New window mgr info
1650                                  * will be posted for the top-level window
1651                                  * associated with this window. */
1652     int reqWidth,               /* Width (in grid units) corresponding to
1653                                  * the requested geometry for tkwin. */
1654     int reqHeight,              /* Height (in grid units) corresponding to
1655                                  * the requested geometry for tkwin. */
1656     int widthInc, int heightInc)/* Pixel increments corresponding to a
1657                                  * change of one grid unit. */
1658 {
1659     TkWindow *winPtr = (TkWindow *) tkwin;
1660     register WmInfo *wmPtr;
1661
1662     /*
1663      * Find the top-level window for tkwin, plus the window manager
1664      * information.
1665      */
1666
1667     while (!(winPtr->flags & TK_TOP_LEVEL)) {
1668         winPtr = winPtr->parentPtr;
1669     }
1670     wmPtr = winPtr->wmInfoPtr;
1671
1672     if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
1673         return;
1674     }
1675
1676     if ((wmPtr->reqGridWidth == reqWidth)
1677             && (wmPtr->reqGridHeight == reqHeight)
1678             && (wmPtr->widthInc == widthInc)
1679             && (wmPtr->heightInc == heightInc)
1680             && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
1681                     == PBaseSize|PResizeInc)) {
1682         return;
1683     }
1684
1685     /*
1686      * If gridding was previously off, then forget about any window
1687      * size requests made by the user or via "wm geometry":  these are
1688      * in pixel units and there's no easy way to translate them to
1689      * grid units since the new requested size of the top-level window in
1690      * pixels may not yet have been registered yet (it may filter up
1691      * the hierarchy in DoWhenIdle handlers).  However, if the window
1692      * has never been mapped yet then just leave the window size alone:
1693      * assume that it is intended to be in grid units but just happened
1694      * to have been specified before this procedure was called.
1695      */
1696
1697     if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {
1698         wmPtr->width = -1;
1699         wmPtr->height = -1;
1700     }
1701
1702     /* 
1703      * Set the new gridding information, and start the process of passing
1704      * all of this information to the window manager.
1705      */
1706
1707     wmPtr->gridWin = tkwin;
1708     wmPtr->reqGridWidth = reqWidth;
1709     wmPtr->reqGridHeight = reqHeight;
1710     wmPtr->widthInc = widthInc;
1711     wmPtr->heightInc = heightInc;
1712     wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
1713     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1714     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1715         Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1716         wmPtr->flags |= WM_UPDATE_PENDING;
1717     }
1718 }
1719 \f
1720 /*
1721  *----------------------------------------------------------------------
1722  *
1723  * Tk_UnsetGrid --
1724  *
1725  *      This procedure cancels the effect of a previous call
1726  *      to Tk_SetGrid.
1727  *
1728  * Results:
1729  *      None.
1730  *
1731  * Side effects:
1732  *      If tkwin currently controls gridding for its top-level window,
1733  *      gridding is cancelled for that top-level window;  if some other
1734  *      window controls gridding then this procedure has no effect.
1735  *
1736  *----------------------------------------------------------------------
1737  */
1738
1739 void
1740 Tk_UnsetGrid(
1741     Tk_Window tkwin)            /* Token for window that is currently
1742                                  * controlling gridding. */
1743 {
1744     TkWindow *winPtr = (TkWindow *) tkwin;
1745     register WmInfo *wmPtr;
1746
1747     /*
1748      * Find the top-level window for tkwin, plus the window manager
1749      * information.
1750      */
1751
1752     while (!(winPtr->flags & TK_TOP_LEVEL)) {
1753         winPtr = winPtr->parentPtr;
1754     }
1755     wmPtr = winPtr->wmInfoPtr;
1756     if (tkwin != wmPtr->gridWin) {
1757         return;
1758     }
1759
1760     wmPtr->gridWin = NULL;
1761     wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
1762     if (wmPtr->width != -1) {
1763         wmPtr->width = winPtr->reqWidth + (wmPtr->width
1764                 - wmPtr->reqGridWidth)*wmPtr->widthInc;
1765         wmPtr->height = winPtr->reqHeight + (wmPtr->height
1766                 - wmPtr->reqGridHeight)*wmPtr->heightInc;
1767     }
1768     wmPtr->widthInc = 1;
1769     wmPtr->heightInc = 1;
1770
1771     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1772     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1773         Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1774         wmPtr->flags |= WM_UPDATE_PENDING;
1775     }
1776 }
1777 \f
1778 /*
1779  *----------------------------------------------------------------------
1780  *
1781  * TopLevelEventProc --
1782  *
1783  *      This procedure is invoked when a top-level (or other externally-
1784  *      managed window) is restructured in any way.
1785  *
1786  * Results:
1787  *      None.
1788  *
1789  * Side effects:
1790  *      Tk's internal data structures for the window get modified to
1791  *      reflect the structural change.
1792  *
1793  *----------------------------------------------------------------------
1794  */
1795
1796 static void
1797 TopLevelEventProc(
1798     ClientData clientData,              /* Window for which event occurred. */
1799     XEvent *eventPtr)                   /* Event that just happened. */
1800 {
1801     register TkWindow *winPtr = (TkWindow *) clientData;
1802
1803     winPtr->wmInfoPtr->flags |= WM_VROOT_OFFSET_STALE;
1804     if (eventPtr->type == DestroyNotify) {
1805         Tk_ErrorHandler handler;
1806
1807         if (!(winPtr->flags & TK_ALREADY_DEAD)) {
1808             /*
1809              * A top-level window was deleted externally (e.g., by the window
1810              * manager).  This is probably not a good thing, but cleanup as
1811              * best we can.  The error handler is needed because
1812              * Tk_DestroyWindow will try to destroy the window, but of course
1813              * it's already gone.
1814              */
1815     
1816             handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
1817                     (Tk_ErrorProc *) NULL, (ClientData) NULL);
1818             Tk_DestroyWindow((Tk_Window) winPtr);
1819             Tk_DeleteErrorHandler(handler);
1820         }
1821         if (wmTracing) {
1822             printf("TopLevelEventProc: %s deleted\n", winPtr->pathName);
1823         }
1824     } else if (eventPtr->type == ReparentNotify) {
1825         panic("recieved unwanted reparent event");
1826     }
1827 }
1828 \f
1829 /*
1830  *----------------------------------------------------------------------
1831  *
1832  * TopLevelReqProc --
1833  *
1834  *      This procedure is invoked by the geometry manager whenever
1835  *      the requested size for a top-level window is changed.
1836  *
1837  * Results:
1838  *      None.
1839  *
1840  * Side effects:
1841  *      Arrange for the window to be resized to satisfy the request
1842  *      (this happens as a when-idle action).
1843  *
1844  *----------------------------------------------------------------------
1845  */
1846
1847         /* ARGSUSED */
1848 static void
1849 TopLevelReqProc(
1850     ClientData dummy,                   /* Not used. */
1851     Tk_Window tkwin)                    /* Information about window. */
1852 {
1853     TkWindow *winPtr = (TkWindow *) tkwin;
1854     WmInfo *wmPtr;
1855
1856     wmPtr = winPtr->wmInfoPtr;
1857     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1858     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1859         Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1860         wmPtr->flags |= WM_UPDATE_PENDING;
1861     }
1862 }
1863 \f
1864 /*
1865  *----------------------------------------------------------------------
1866  *
1867  * UpdateGeometryInfo --
1868  *
1869  *      This procedure is invoked when a top-level window is first
1870  *      mapped, and also as a when-idle procedure, to bring the
1871  *      geometry and/or position of a top-level window back into
1872  *      line with what has been requested by the user and/or widgets.
1873  *      This procedure doesn't return until the window manager has
1874  *      responded to the geometry change.
1875  *
1876  * Results:
1877  *      None.
1878  *
1879  * Side effects:
1880  *      The window's size and location may change, unless the WM prevents
1881  *      that from happening.
1882  *
1883  *----------------------------------------------------------------------
1884  */
1885
1886 static void
1887 UpdateGeometryInfo(
1888     ClientData clientData)              /* Pointer to the window's record. */
1889 {
1890     register TkWindow *winPtr = (TkWindow *) clientData;
1891     register WmInfo *wmPtr = winPtr->wmInfoPtr;
1892     int x, y, width, height;
1893     unsigned long serial;
1894
1895     wmPtr->flags &= ~WM_UPDATE_PENDING;
1896
1897     /*
1898      * Compute the new size for the top-level window.  See the
1899      * user documentation for details on this, but the size
1900      * requested depends on (a) the size requested internally
1901      * by the window's widgets, (b) the size requested by the
1902      * user in a "wm geometry" command or via wm-based interactive
1903      * resizing (if any), and (c) whether or not the window is
1904      * gridded.  Don't permit sizes <= 0 because this upsets
1905      * the X server.
1906      */
1907
1908     if (wmPtr->width == -1) {
1909         width = winPtr->reqWidth;
1910     } else if (wmPtr->gridWin != NULL) {
1911         width = winPtr->reqWidth
1912                 + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
1913     } else {
1914         width = wmPtr->width;
1915     }
1916     if (width <= 0) {
1917         width = 1;
1918     }
1919     if (wmPtr->height == -1) {
1920         height = winPtr->reqHeight;
1921     } else if (wmPtr->gridWin != NULL) {
1922         height = winPtr->reqHeight
1923                 + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
1924     } else {
1925         height = wmPtr->height;
1926     }
1927     if (height <= 0) {
1928         height = 1;
1929     }
1930
1931     /*
1932      * Compute the new position for the upper-left pixel of the window's
1933      * decorative frame.  This is tricky, because we need to include the
1934      * border widths supplied by a reparented parent in this calculation,
1935      * but can't use the parent's current overall size since that may
1936      * change as a result of this code.
1937      */
1938
1939     if (wmPtr->flags & WM_NEGATIVE_X) {
1940         x = wmPtr->vRootWidth - wmPtr->x
1941             - (width + (wmPtr->parentWidth - winPtr->changes.width));
1942     } else {
1943         x =  wmPtr->x;
1944     }
1945     if (wmPtr->flags & WM_NEGATIVE_Y) {
1946         y = wmPtr->vRootHeight - wmPtr->y
1947             - (height + (wmPtr->parentHeight - winPtr->changes.height));
1948     } else {
1949         y =  wmPtr->y;
1950     }
1951
1952     /*
1953      * If the window's size is going to change and the window is
1954      * supposed to not be resizable by the user, then we have to
1955      * update the size hints.  There may also be a size-hint-update
1956      * request pending from somewhere else, too.
1957      */
1958
1959     if (((width != winPtr->changes.width)
1960             || (height != winPtr->changes.height))
1961             && (wmPtr->gridWin == NULL)
1962             && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
1963         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1964     }
1965     if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
1966         UpdateSizeHints(winPtr);
1967     }
1968
1969     /*
1970      * Reconfigure the window if it isn't already configured correctly.
1971      * A few tricky points:
1972      *
1973      * 1. If the window is embedded and the container is also in this
1974      *    process, don't actually reconfigure the window; just pass the
1975      *    desired size on to the container.  Also, zero out any position
1976      *    information, since embedded windows are not allowed to move.
1977      * 2. Sometimes the window manager will give us a different size
1978      *    than we asked for (e.g. mwm has a minimum size for windows), so
1979      *    base the size check on what we *asked for* last time, not what we
1980      *    got.
1981      * 3. Don't move window unless a new position has been requested for
1982      *    it.  This is because of "features" in some window managers (e.g.
1983      *    twm, as of 4/24/91) where they don't interpret coordinates
1984      *    according to ICCCM.  Moving a window to its current location may
1985      *    cause it to shift position on the screen.
1986      */
1987
1988     if (Tk_IsEmbedded(winPtr)) {
1989         TkWindow *contWinPtr;
1990
1991         contWinPtr = TkpGetOtherWindow(winPtr);
1992         
1993         /*
1994          * NOTE: Here we should handle out of process embedding.
1995          */
1996
1997         if (contWinPtr != NULL) {           
1998             /*
1999              * This window is embedded and the container is also in this
2000              * process, so we don't need to do anything special about the
2001              * geometry, except to make sure that the desired size is known
2002              * by the container.  Also, zero out any position information,
2003              * since embedded windows are not allowed to move.
2004              */
2005
2006             wmPtr->x = wmPtr->y = 0;
2007             wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
2008             Tk_GeometryRequest((Tk_Window) contWinPtr, width, height);
2009         }
2010         return;
2011     }
2012     serial = NextRequest(winPtr->display);
2013     if (wmPtr->flags & WM_MOVE_PENDING) {
2014         wmPtr->configWidth = width;
2015         wmPtr->configHeight = height;
2016         if (wmTracing) {
2017             printf(
2018                 "UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",
2019                     x, y, width, height);
2020         }
2021         Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, (unsigned) width,
2022                 (unsigned) height);
2023     } else if ((width != wmPtr->configWidth)
2024             || (height != wmPtr->configHeight)) {
2025         wmPtr->configWidth = width;
2026         wmPtr->configHeight = height;
2027         if (wmTracing) {
2028             printf("UpdateGeometryInfo resizing to %d x %d\n", width, height);
2029         }
2030         Tk_ResizeWindow((Tk_Window) winPtr, (unsigned) width,
2031                 (unsigned) height);
2032     } else {
2033         return;
2034     }
2035 }
2036 \f
2037 /*
2038  *--------------------------------------------------------------
2039  *
2040  * UpdateSizeHints --
2041  *
2042  *      This procedure is called to update the window manager's
2043  *      size hints information from the information in a WmInfo
2044  *      structure.
2045  *
2046  * Results:
2047  *      None.
2048  *
2049  * Side effects:
2050  *      Properties get changed for winPtr.
2051  *
2052  *--------------------------------------------------------------
2053  */
2054
2055 static void
2056 UpdateSizeHints(
2057     TkWindow *winPtr)
2058 {
2059     register WmInfo *wmPtr = winPtr->wmInfoPtr;
2060     XSizeHints *hintsPtr;
2061
2062     wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
2063
2064     hintsPtr = XAllocSizeHints();
2065     if (hintsPtr == NULL) {
2066         return;
2067     }
2068
2069     /*
2070      * Compute the pixel-based sizes for the various fields in the
2071      * size hints structure, based on the grid-based sizes in
2072      * our structure.
2073      */
2074
2075     if (wmPtr->gridWin != NULL) {
2076         hintsPtr->base_width = winPtr->reqWidth
2077                 - (wmPtr->reqGridWidth * wmPtr->widthInc);
2078         if (hintsPtr->base_width < 0) {
2079             hintsPtr->base_width = 0;
2080         }
2081         hintsPtr->base_height = winPtr->reqHeight
2082                 - (wmPtr->reqGridHeight * wmPtr->heightInc);
2083         if (hintsPtr->base_height < 0) {
2084             hintsPtr->base_height = 0;
2085         }
2086         hintsPtr->min_width = hintsPtr->base_width
2087                 + (wmPtr->minWidth * wmPtr->widthInc);
2088         hintsPtr->min_height = hintsPtr->base_height
2089                 + (wmPtr->minHeight * wmPtr->heightInc);
2090         hintsPtr->max_width = hintsPtr->base_width
2091                 + (wmPtr->maxWidth * wmPtr->widthInc);
2092         hintsPtr->max_height = hintsPtr->base_height
2093                 + (wmPtr->maxHeight * wmPtr->heightInc);
2094     } else {
2095         hintsPtr->min_width = wmPtr->minWidth;
2096         hintsPtr->min_height = wmPtr->minHeight;
2097         hintsPtr->max_width = wmPtr->maxWidth;
2098         hintsPtr->max_height = wmPtr->maxHeight;
2099         hintsPtr->base_width = 0;
2100         hintsPtr->base_height = 0;
2101     }
2102     hintsPtr->width_inc = wmPtr->widthInc;
2103     hintsPtr->height_inc = wmPtr->heightInc;
2104     hintsPtr->min_aspect.x = wmPtr->minAspect.x;
2105     hintsPtr->min_aspect.y = wmPtr->minAspect.y;
2106     hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
2107     hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
2108     hintsPtr->win_gravity = wmPtr->gravity;
2109     hintsPtr->flags = wmPtr->sizeHintsFlags | PMinSize | PMaxSize;
2110
2111     /*
2112      * If the window isn't supposed to be resizable, then set the
2113      * minimum and maximum dimensions to be the same.
2114      */
2115
2116     if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
2117         if (wmPtr->width >= 0) {
2118             hintsPtr->min_width = wmPtr->width;
2119         } else {
2120             hintsPtr->min_width = winPtr->reqWidth;
2121         }
2122         hintsPtr->max_width = hintsPtr->min_width;
2123     }
2124     if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
2125         if (wmPtr->height >= 0) {
2126             hintsPtr->min_height = wmPtr->height;
2127         } else {
2128             hintsPtr->min_height = winPtr->reqHeight;
2129         }
2130         hintsPtr->max_height = hintsPtr->min_height;
2131     }
2132
2133     XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr);
2134
2135     XFree((char *) hintsPtr);
2136 }
2137 \f
2138 /*
2139  *--------------------------------------------------------------
2140  *
2141  * ParseGeometry --
2142  *
2143  *      This procedure parses a geometry string and updates
2144  *      information used to control the geometry of a top-level
2145  *      window.
2146  *
2147  * Results:
2148  *      A standard Tcl return value, plus an error message in
2149  *      interp->result if an error occurs.
2150  *
2151  * Side effects:
2152  *      The size and/or location of winPtr may change.
2153  *
2154  *--------------------------------------------------------------
2155  */
2156
2157 static int
2158 ParseGeometry(
2159     Tcl_Interp *interp,         /* Used for error reporting. */
2160     char *string,               /* String containing new geometry.  Has the
2161                                  * standard form "=wxh+x+y". */
2162     TkWindow *winPtr)           /* Pointer to top-level window whose
2163                                  * geometry is to be changed. */
2164 {
2165     register WmInfo *wmPtr = winPtr->wmInfoPtr;
2166     int x, y, width, height, flags;
2167     char *end;
2168     register char *p = string;
2169
2170     /*
2171      * The leading "=" is optional.
2172      */
2173
2174     if (*p == '=') {
2175         p++;
2176     }
2177
2178     /*
2179      * Parse the width and height, if they are present.  Don't
2180      * actually update any of the fields of wmPtr until we've
2181      * successfully parsed the entire geometry string.
2182      */
2183
2184     width = wmPtr->width;
2185     height = wmPtr->height;
2186     x = wmPtr->x;
2187     y = wmPtr->y;
2188     flags = wmPtr->flags;
2189     if (isdigit(UCHAR(*p))) {
2190         width = strtoul(p, &end, 10);
2191         p = end;
2192         if (*p != 'x') {
2193             goto error;
2194         }
2195         p++;
2196         if (!isdigit(UCHAR(*p))) {
2197             goto error;
2198         }
2199         height = strtoul(p, &end, 10);
2200         p = end;
2201     }
2202
2203     /*
2204      * Parse the X and Y coordinates, if they are present.
2205      */
2206
2207     if (*p != '\0') {
2208         flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
2209         if (*p == '-') {
2210             flags |= WM_NEGATIVE_X;
2211         } else if (*p != '+') {
2212             goto error;
2213         }
2214         x = strtol(p+1, &end, 10);
2215         p = end;
2216         if (*p == '-') {
2217             flags |= WM_NEGATIVE_Y;
2218         } else if (*p != '+') {
2219             goto error;
2220         }
2221         y = strtol(p+1, &end, 10);
2222         if (*end != '\0') {
2223             goto error;
2224         }
2225
2226         /*
2227          * Assume that the geometry information came from the user,
2228          * unless an explicit source has been specified.  Otherwise
2229          * most window managers assume that the size hints were
2230          * program-specified and they ignore them.
2231          */
2232
2233         if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
2234             wmPtr->sizeHintsFlags |= USPosition;
2235             flags |= WM_UPDATE_SIZE_HINTS;
2236         }
2237     }
2238
2239     /*
2240      * Everything was parsed OK.  Update the fields of *wmPtr and
2241      * arrange for the appropriate information to be percolated out
2242      * to the window manager at the next idle moment.
2243      */
2244
2245     wmPtr->width = width;
2246     wmPtr->height = height;
2247     if ((x != wmPtr->x) || (y != wmPtr->y)
2248             || ((flags & (WM_NEGATIVE_X|WM_NEGATIVE_Y))
2249                     != (wmPtr->flags & (WM_NEGATIVE_X|WM_NEGATIVE_Y)))) {
2250         wmPtr->x = x;
2251         wmPtr->y = y;
2252         flags |= WM_MOVE_PENDING;
2253     }
2254     wmPtr->flags = flags;
2255
2256     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2257         Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2258         wmPtr->flags |= WM_UPDATE_PENDING;
2259     }
2260     return TCL_OK;
2261
2262     error:
2263     Tcl_AppendResult(interp, "bad geometry specifier \"",
2264             string, "\"", (char *) NULL);
2265     return TCL_ERROR;
2266 }
2267 \f
2268 /*
2269  *----------------------------------------------------------------------
2270  *
2271  * Tk_GetRootCoords --
2272  *
2273  *      Given a token for a window, this procedure traces through the
2274  *      window's lineage to find the (virtual) root-window coordinates
2275  *      corresponding to point (0,0) in the window.
2276  *
2277  * Results:
2278  *      The locations pointed to by xPtr and yPtr are filled in with
2279  *      the root coordinates of the (0,0) point in tkwin.  If a virtual
2280  *      root window is in effect for the window, then the coordinates
2281  *      in the virtual root are returned.
2282  *
2283  * Side effects:
2284  *      None.
2285  *
2286  *----------------------------------------------------------------------
2287  */
2288
2289 void
2290 Tk_GetRootCoords(
2291     Tk_Window tkwin,            /* Token for window. */
2292     int *xPtr,                  /* Where to store x-displacement of (0,0). */
2293     int *yPtr)                  /* Where to store y-displacement of (0,0). */
2294 {
2295     int x, y;
2296     register TkWindow *winPtr = (TkWindow *) tkwin;
2297
2298     /*
2299      * Search back through this window's parents all the way to a
2300      * top-level window, combining the offsets of each window within
2301      * its parent.
2302      */
2303
2304     x = y = 0;
2305     while (1) {
2306         x += winPtr->changes.x + winPtr->changes.border_width;
2307         y += winPtr->changes.y + winPtr->changes.border_width;
2308         if (winPtr->flags & TK_TOP_LEVEL) {
2309             if (!(Tk_IsEmbedded(winPtr))) {
2310                 x += winPtr->wmInfoPtr->xInParent;
2311                 y += winPtr->wmInfoPtr->yInParent;
2312                 break;          
2313             } else {
2314                 TkWindow *otherPtr;
2315                 
2316                 otherPtr = TkpGetOtherWindow(winPtr);
2317                 if (otherPtr != NULL) {
2318                     /*
2319                      * The container window is in the same application.
2320                      * Query its coordinates.
2321                      */
2322                     winPtr = otherPtr;
2323                     
2324                     /*
2325                      * Remember to offset by the container window here,
2326                      * since at the end of this if branch, we will
2327                      * pop out to the container's parent...
2328                      */
2329                      
2330                     x += winPtr->changes.x + winPtr->changes.border_width;
2331                     y += winPtr->changes.y + winPtr->changes.border_width;
2332                     
2333                 } else {
2334                     Point theOffset;
2335                     
2336                     if (gMacEmbedHandler->getOffsetProc != NULL) {
2337                         /*
2338                          * We do not require that the changes.x & changes.y for 
2339                          * a non-Tk master window be kept up to date.  So we
2340                          * first subtract off the possibly bogus values that have
2341                          * been added on at the top of this pass through the loop,
2342                          * and then call out to the getOffsetProc to give us
2343                          * the correct offset.
2344                          */
2345                          
2346                         x -= winPtr->changes.x + winPtr->changes.border_width;
2347                         y -= winPtr->changes.y + winPtr->changes.border_width;
2348                         
2349                         gMacEmbedHandler->getOffsetProc((Tk_Window) winPtr, &theOffset);
2350                         
2351                         x += theOffset.h;
2352                         y += theOffset.v;
2353                     }
2354                     break;
2355                 }
2356             }
2357         }
2358         winPtr = winPtr->parentPtr;
2359     }
2360     *xPtr = x;
2361     *yPtr = y;
2362 }
2363 \f
2364 /*
2365  *----------------------------------------------------------------------
2366  *
2367  * Tk_CoordsToWindow --
2368  *
2369  *      This is a Macintosh specific implementation of this function.
2370  *      Given the root coordinates of a point, this procedure returns
2371  *      the token for the top-most window covering that point, if
2372  *      there exists such a window in this application.
2373  *
2374  * Results:
2375  *      The return result is either a token for the window corresponding
2376  *      to rootX and rootY, or else NULL to indicate that there is no such
2377  *      window.
2378  *
2379  * Side effects:
2380  *      None.
2381  *
2382  *----------------------------------------------------------------------
2383  */
2384
2385 Tk_Window
2386 Tk_CoordsToWindow(
2387     int rootX, int rootY,       /* Coordinates of point in root window.  If
2388                                  * a virtual-root window manager is in use,
2389                                  * these coordinates refer to the virtual
2390                                  * root, not the real root. */
2391     Tk_Window tkwin)            /* Token for any window in application;
2392                                  * used to identify the display. */
2393 {
2394     WindowPtr whichWin;
2395     Point where;
2396     Window rootChild;
2397     register TkWindow *winPtr, *childPtr;
2398     TkWindow *nextPtr;          /* Coordinates of highest child found so
2399                                  * far that contains point. */
2400     int x, y;                   /* Coordinates in winPtr. */
2401     int tmpx, tmpy, bd;
2402
2403     /*
2404      * Step 1: find the top-level window that contains the desired point.
2405      */
2406      
2407     where.h = rootX;
2408     where.v = rootY;
2409     FindWindow(where, &whichWin);
2410     if (whichWin == NULL) {
2411         return NULL;
2412     }
2413     rootChild = TkMacGetXWindow(whichWin);
2414     winPtr = (TkWindow *) Tk_IdToWindow(tkDisplayList->display, rootChild);
2415     if (winPtr == NULL) {
2416         return NULL;
2417     }
2418
2419     /*
2420      * Step 2: work down through the hierarchy underneath this window.
2421      * At each level, scan through all the children to find the highest
2422      * one in the stacking order that contains the point.  Then repeat
2423      * the whole process on that child.
2424      */
2425
2426     x = rootX - winPtr->wmInfoPtr->xInParent;
2427     y = rootY - winPtr->wmInfoPtr->yInParent;
2428     while (1) {
2429         x -= winPtr->changes.x;
2430         y -= winPtr->changes.y;
2431         nextPtr = NULL;
2432         
2433         /*
2434          * Container windows cannot have children.  So if it is a container,
2435          * look there, otherwise inspect the children.
2436          */
2437          
2438         if (Tk_IsContainer(winPtr)) {
2439             childPtr = TkpGetOtherWindow(winPtr);
2440             if (childPtr != NULL) {
2441                 if (Tk_IsMapped(childPtr)) {
2442                     tmpx = x - childPtr->changes.x;
2443                     tmpy = y - childPtr->changes.y;
2444                     bd = childPtr->changes.border_width;
2445                           
2446                     if ((tmpx >= -bd) && (tmpy >= -bd)
2447                         && (tmpx < (childPtr->changes.width + bd))
2448                         && (tmpy < (childPtr->changes.height + bd))) {
2449                         nextPtr = childPtr;
2450                     }
2451                 }
2452             }
2453             
2454
2455             /*
2456              * NOTE: Here we should handle out of process embedding.
2457              */
2458         
2459         } else {
2460             for (childPtr = winPtr->childList; childPtr != NULL;
2461                     childPtr = childPtr->nextPtr) {
2462                 if (!Tk_IsMapped(childPtr) ||
2463                         (childPtr->flags & TK_TOP_LEVEL)) {
2464                     continue;
2465                 }
2466                 tmpx = x - childPtr->changes.x;
2467                 tmpy = y - childPtr->changes.y;
2468                 bd = childPtr->changes.border_width;
2469                 if ((tmpx >= -bd) && (tmpy >= -bd)
2470                         && (tmpx < (childPtr->changes.width + bd))
2471                         && (tmpy < (childPtr->changes.height + bd))) {
2472                     nextPtr = childPtr;
2473                 }
2474             }
2475         }
2476         if (nextPtr == NULL) {
2477             break;
2478         }
2479         winPtr = nextPtr;
2480     }
2481     return (Tk_Window) winPtr;
2482 }
2483 \f
2484 /*
2485  *----------------------------------------------------------------------
2486  *
2487  * Tk_TopCoordsToWindow --
2488  *
2489  *      Given a Tk Window, and coordinates of a point relative to that window
2490  *      this procedure returns the top-most child of the window (excluding
2491  *      toplevels) covering that point, if there exists such a window in this
2492  *      application.
2493  *      It also sets newX, and newY to the coords of the point relative to the
2494  *      window returned.
2495  *
2496  * Results:
2497  *      The return result is either a token for the window corresponding
2498  *      to rootX and rootY, or else NULL to indicate that there is no such
2499  *      window.  newX and newY are also set to the coords of the point relative
2500  *      to the returned window.
2501  *
2502  * Side effects:
2503  *      None.
2504  *
2505  *----------------------------------------------------------------------
2506  */
2507
2508 Tk_Window
2509 Tk_TopCoordsToWindow(
2510     Tk_Window tkwin,            /* Token for a Tk Window which defines the;
2511                                  * coordinates for rootX & rootY */
2512     int rootX, int rootY,       /* Coordinates of a point in tkWin. */
2513     int *newX, int *newY)       /* Coordinates of point in the upperMost child of
2514                                  * tkWin containing (rootX,rootY) */
2515 {
2516     register TkWindow *winPtr, *childPtr;
2517     TkWindow *nextPtr;          /* Coordinates of highest child found so
2518                                  * far that contains point. */
2519     int x, y;                   /* Coordinates in winPtr. */
2520     Window *children;           /* Children of winPtr, or NULL. */
2521
2522     winPtr = (TkWindow *) tkwin;
2523     x = rootX;
2524     y = rootY;
2525     while (1) {
2526         nextPtr = NULL;
2527         children = NULL;
2528
2529         /*
2530          * Container windows cannot have children.  So if it is a container,
2531          * look there, otherwise inspect the children.
2532          */
2533          
2534         if (Tk_IsContainer(winPtr)) {
2535             childPtr = TkpGetOtherWindow(winPtr);
2536             if (childPtr != NULL) {
2537                 if (Tk_IsMapped(childPtr) && 
2538                          (x > childPtr->changes.x && 
2539                              x < childPtr->changes.x +
2540                                  childPtr->changes.width) &&
2541                          (y > childPtr->changes.y &&
2542                              y < childPtr->changes.y +
2543                                  childPtr->changes.height)) {       
2544                     nextPtr = childPtr;
2545                 }
2546             }
2547
2548             /*
2549              * NOTE: Here we should handle out of process embedding.
2550              */
2551         
2552         } else {
2553         
2554             for (childPtr = winPtr->childList; childPtr != NULL;
2555                                            childPtr = childPtr->nextPtr) {
2556                 if (!Tk_IsMapped(childPtr) ||
2557                         (childPtr->flags & TK_TOP_LEVEL)) {
2558                     continue;
2559                 }
2560                 if (x < childPtr->changes.x || y < childPtr->changes.y) {
2561                     continue;
2562                 }
2563                 if (x > childPtr->changes.x + childPtr->changes.width ||
2564                         y > childPtr->changes.y + childPtr->changes.height) {
2565                     continue;
2566                 }
2567                 nextPtr = childPtr;
2568             }
2569         }
2570         if (nextPtr == NULL) {
2571             break;
2572         }
2573         winPtr = nextPtr;
2574         x -= winPtr->changes.x;
2575         y -= winPtr->changes.y;
2576     }
2577     *newX = x;
2578     *newY = y;
2579     return (Tk_Window) winPtr;
2580 }
2581 \f
2582 /*
2583  *----------------------------------------------------------------------
2584  *
2585  * UpdateVRootGeometry --
2586  *
2587  *      This procedure is called to update all the virtual root
2588  *      geometry information in wmPtr.
2589  *
2590  * Results:
2591  *      None.
2592  *
2593  * Side effects:
2594  *      The vRootX, vRootY, vRootWidth, and vRootHeight fields in
2595  *      wmPtr are filled with the most up-to-date information.
2596  *
2597  *----------------------------------------------------------------------
2598  */
2599
2600 static void
2601 UpdateVRootGeometry(
2602     WmInfo *wmPtr)              /* Window manager information to be
2603                                  * updated.  The wmPtr->vRoot field must
2604                                  * be valid. */
2605 {
2606     TkWindow *winPtr = wmPtr->winPtr;
2607     unsigned int bd, dummy;
2608     Window dummy2;
2609     Status status;
2610     Tk_ErrorHandler handler;
2611
2612     /*
2613      * If this isn't a virtual-root window manager, just return information
2614      * about the screen.
2615      */
2616
2617     wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;
2618     if (wmPtr->vRoot == None) {
2619         noVRoot:
2620         wmPtr->vRootX = wmPtr->vRootY = 0;
2621         wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
2622         wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
2623         return;
2624     }
2625
2626     /*
2627      * Refresh the virtual root information if it's out of date.
2628      */
2629
2630     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
2631             (Tk_ErrorProc *) NULL, (ClientData) NULL);
2632     status = XGetGeometry(winPtr->display, wmPtr->vRoot,
2633             &dummy2, &wmPtr->vRootX, &wmPtr->vRootY,
2634             &wmPtr->vRootWidth, &wmPtr->vRootHeight, &bd, &dummy);
2635     if (wmTracing) {
2636         printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",
2637                 wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);
2638         printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);
2639     }
2640     Tk_DeleteErrorHandler(handler);
2641     if (status == 0) {
2642         /*
2643          * The virtual root is gone!  Pretend that it never existed.
2644          */
2645
2646         wmPtr->vRoot = None;
2647         goto noVRoot;
2648     }
2649 }
2650 \f
2651 /*
2652  *----------------------------------------------------------------------
2653  *
2654  * Tk_GetVRootGeometry --
2655  *
2656  *      This procedure returns information about the virtual root
2657  *      window corresponding to a particular Tk window.
2658  *
2659  * Results:
2660  *      The values at xPtr, yPtr, widthPtr, and heightPtr are set
2661  *      with the offset and dimensions of the root window corresponding
2662  *      to tkwin.  If tkwin is being managed by a virtual root window
2663  *      manager these values correspond to the virtual root window being
2664  *      used for tkwin;  otherwise the offsets will be 0 and the
2665  *      dimensions will be those of the screen.
2666  *
2667  * Side effects:
2668  *      Vroot window information is refreshed if it is out of date.
2669  *
2670  *----------------------------------------------------------------------
2671  */
2672
2673 void
2674 Tk_GetVRootGeometry(
2675     Tk_Window tkwin,            /* Window whose virtual root is to be
2676                                  * queried. */
2677     int *xPtr, int *yPtr,       /* Store x and y offsets of virtual root
2678                                  * here. */
2679     int *widthPtr,              /* Store dimensions of virtual root here. */
2680     int *heightPtr)
2681 {
2682     WmInfo *wmPtr;
2683     TkWindow *winPtr = (TkWindow *) tkwin;
2684
2685     /*
2686      * Find the top-level window for tkwin, and locate the window manager
2687      * information for that window.
2688      */
2689
2690     while (!(winPtr->flags & TK_TOP_LEVEL)) {
2691         winPtr = winPtr->parentPtr;
2692     }
2693     wmPtr = winPtr->wmInfoPtr;
2694
2695     /*
2696      * Make sure that the geometry information is up-to-date, then copy
2697      * it out to the caller.
2698      */
2699
2700     if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {
2701         UpdateVRootGeometry(wmPtr);
2702     }
2703     *xPtr = wmPtr->vRootX;
2704     *yPtr = wmPtr->vRootY;
2705     *widthPtr = wmPtr->vRootWidth;
2706     *heightPtr = wmPtr->vRootHeight;
2707 }
2708 \f
2709 /*
2710  *----------------------------------------------------------------------
2711  *
2712  * Tk_MoveToplevelWindow --
2713  *
2714  *      This procedure is called instead of Tk_MoveWindow to adjust
2715  *      the x-y location of a top-level window.  It delays the actual
2716  *      move to a later time and keeps window-manager information
2717  *      up-to-date with the move
2718  *
2719  * Results:
2720  *      None.
2721  *
2722  * Side effects:
2723  *      The window is eventually moved so that its upper-left corner
2724  *      (actually, the upper-left corner of the window's decorative
2725  *      frame, if there is one) is at (x,y).
2726  *
2727  *----------------------------------------------------------------------
2728  */
2729
2730 void
2731 Tk_MoveToplevelWindow(
2732     Tk_Window tkwin,            /* Window to move. */
2733     int x, int y)               /* New location for window (within
2734                                  * parent). */
2735 {
2736     TkWindow *winPtr = (TkWindow *) tkwin;
2737     register WmInfo *wmPtr = winPtr->wmInfoPtr;
2738
2739     if (!(winPtr->flags & TK_TOP_LEVEL)) {
2740         panic("Tk_MoveToplevelWindow called with non-toplevel window");
2741     }
2742     wmPtr->x = x;
2743     wmPtr->y = y;
2744     wmPtr->flags |= WM_MOVE_PENDING;
2745     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
2746     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
2747         wmPtr->sizeHintsFlags |= USPosition;
2748         wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2749     }
2750
2751     /*
2752      * If the window has already been mapped, must bring its geometry
2753      * up-to-date immediately, otherwise an event might arrive from the
2754      * server that would overwrite wmPtr->x and wmPtr->y and lose the
2755      * new position.
2756      */
2757
2758     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
2759         if (wmPtr->flags & WM_UPDATE_PENDING) {
2760             Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
2761         }
2762         UpdateGeometryInfo((ClientData) winPtr);
2763     }
2764 }
2765 \f
2766 /*
2767  *----------------------------------------------------------------------
2768  *
2769  * TkWmProtocolEventProc --
2770  *
2771  *      This procedure is called by the Tk_HandleEvent whenever a
2772  *      ClientMessage event arrives whose type is "WM_PROTOCOLS".
2773  *      This procedure handles the message from the window manager
2774  *      in an appropriate fashion.
2775  *
2776  * Results:
2777  *      None.
2778  *
2779  * Side effects:
2780  *      Depends on what sort of handler, if any, was set up for the
2781  *      protocol.
2782  *
2783  *----------------------------------------------------------------------
2784  */
2785
2786 void
2787 TkWmProtocolEventProc(
2788     TkWindow *winPtr,           /* Window to which the event was sent. */
2789     XEvent *eventPtr)           /* X event. */
2790 {
2791     WmInfo *wmPtr;
2792     register ProtocolHandler *protPtr;
2793     Tcl_Interp *interp;
2794     Atom protocol;
2795     int result;
2796
2797     wmPtr = winPtr->wmInfoPtr;
2798     if (wmPtr == NULL) {
2799         return;
2800     }
2801     protocol = (Atom) eventPtr->xclient.data.l[0];
2802     for (protPtr = wmPtr->protPtr; protPtr != NULL;
2803                                    protPtr = protPtr->nextPtr) {
2804         if (protocol == protPtr->protocol) {
2805             Tcl_Preserve((ClientData) protPtr);
2806             interp = protPtr->interp;
2807             Tcl_Preserve((ClientData) interp);
2808             result = Tcl_GlobalEval(interp, protPtr->command);
2809             if (result != TCL_OK) {
2810                 Tcl_AddErrorInfo(interp, "\n    (command for \"");
2811                 Tcl_AddErrorInfo(interp,
2812                         Tk_GetAtomName((Tk_Window) winPtr, protocol));
2813                 Tcl_AddErrorInfo(interp, "\" window manager protocol)");
2814                 Tk_BackgroundError(interp);
2815             }
2816             Tcl_Release((ClientData) interp);
2817             Tcl_Release((ClientData) protPtr);
2818             return;
2819         }
2820     }
2821
2822     /*
2823      * No handler was present for this protocol.  If this is a
2824      * WM_DELETE_WINDOW message then just destroy the window.
2825      */
2826
2827     if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
2828         Tk_DestroyWindow((Tk_Window) winPtr);
2829     }
2830 }
2831 \f
2832 /*
2833  *----------------------------------------------------------------------
2834  *
2835  * TkWmRestackToplevel --
2836  *
2837  *      This procedure restacks a top-level window.
2838  *
2839  * Results:
2840  *      None.
2841  *
2842  * Side effects:
2843  *      WinPtr gets restacked  as specified by aboveBelow and otherPtr.
2844  *      This procedure doesn't return until the restack has taken
2845  *      effect and the ConfigureNotify event for it has been received.
2846  *
2847  *----------------------------------------------------------------------
2848  */
2849
2850 void
2851 TkWmRestackToplevel(
2852     TkWindow *winPtr,           /* Window to restack. */
2853     int aboveBelow,             /* Gives relative position for restacking;
2854                                  * must be Above or Below. */
2855     TkWindow *otherPtr)         /* Window relative to which to restack;
2856                                  * if NULL, then winPtr gets restacked
2857                                  * above or below *all* siblings. */
2858 {
2859     WmInfo *wmPtr;
2860     WindowPeek macWindow, otherMacWindow, frontWindow;
2861
2862     wmPtr = winPtr->wmInfoPtr;
2863     
2864     /*
2865      * Get the mac window.  Make sure it exists & is mapped.
2866      */
2867     
2868     if (winPtr->window == None) {
2869         Tk_MakeWindowExist((Tk_Window) winPtr);
2870     }
2871     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
2872
2873         /*
2874          * Can't set stacking order properly until the window is on the
2875          * screen (mapping it may give it a reparent window), so make sure
2876          * it's on the screen.
2877          */
2878
2879         TkWmMapWindow(winPtr);
2880     }
2881     macWindow = (WindowPeek) TkMacGetDrawablePort(winPtr->window);
2882     
2883     /*
2884      * Get the window in which a raise or lower is in relation to.
2885      */
2886     if (otherPtr != NULL) {
2887         if (otherPtr->window == None) {
2888             Tk_MakeWindowExist((Tk_Window) otherPtr);
2889         }
2890         if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
2891             TkWmMapWindow(otherPtr);
2892         }
2893         otherMacWindow = (WindowPeek) TkMacGetDrawablePort(otherPtr->window);
2894     } else {
2895         otherMacWindow = NULL;
2896     }
2897         
2898     frontWindow = (WindowPeek) FrontWindow();
2899     if (aboveBelow == Above) {
2900         if (macWindow == frontWindow) {
2901             /* 
2902              * Do nothing - it's already at the top.
2903              */
2904         } else if (otherMacWindow == frontWindow || otherMacWindow == NULL) {
2905             /*
2906              * Raise the window to the top.  If the window is visable then
2907              * we also make it the active window.
2908              */
2909
2910             if (wmPtr->hints.initial_state == WithdrawnState) {
2911                 BringToFront((WindowPtr) macWindow);
2912             } else {
2913                 SelectWindow((WindowPtr)  macWindow);
2914             }
2915         } else {
2916             /*
2917              * Find the window to be above.  (Front window will actually be the
2918              * window to be behind.)  Front window is NULL if no other windows.
2919              */
2920             while (frontWindow != NULL &&
2921                     frontWindow->nextWindow != otherMacWindow) {
2922                 frontWindow = frontWindow->nextWindow;
2923             }
2924             if (frontWindow != NULL) {
2925                 SendBehind((WindowPtr) macWindow, (WindowPtr) frontWindow);
2926             }
2927         }
2928     } else {
2929         /*
2930          * Send behind.  If it was in front find another window to make active.
2931          */
2932         if (macWindow == frontWindow) {
2933             if (macWindow->nextWindow != NULL) {
2934                 SelectWindow((WindowPtr)  macWindow->nextWindow);
2935             }
2936         }
2937         SendBehind((WindowPtr) macWindow, (WindowPtr) otherMacWindow);
2938     }
2939 }
2940 \f
2941 /*
2942  *----------------------------------------------------------------------
2943  *
2944  * TkWmAddToColormapWindows --
2945  *
2946  *      This procedure is called to add a given window to the
2947  *      WM_COLORMAP_WINDOWS property for its top-level, if it
2948  *      isn't already there.  It is invoked by the Tk code that
2949  *      creates a new colormap, in order to make sure that colormap
2950  *      information is propagated to the window manager by default.
2951  *
2952  * Results:
2953  *      None.
2954  *
2955  * Side effects:
2956  *      WinPtr's window gets added to the WM_COLORMAP_WINDOWS
2957  *      property of its nearest top-level ancestor, unless the
2958  *      colormaps have been set explicitly with the
2959  *      "wm colormapwindows" command.
2960  *
2961  *----------------------------------------------------------------------
2962  */
2963
2964 void
2965 TkWmAddToColormapWindows(
2966     TkWindow *winPtr)           /* Window with a non-default colormap.
2967                                  * Should not be a top-level window. */
2968 {
2969     TkWindow *topPtr;
2970     TkWindow **oldPtr, **newPtr;
2971     int count, i;
2972
2973     if (winPtr->window == None) {
2974         return;
2975     }
2976
2977     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
2978         if (topPtr == NULL) {
2979             /*
2980              * Window is being deleted.  Skip the whole operation.
2981              */
2982
2983             return;
2984         }
2985         if (topPtr->flags & TK_TOP_LEVEL) {
2986             break;
2987         }
2988     }
2989     if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
2990         return;
2991     }
2992
2993     /*
2994      * Make sure that the window isn't already in the list.
2995      */
2996
2997     count = topPtr->wmInfoPtr->cmapCount;
2998     oldPtr = topPtr->wmInfoPtr->cmapList;
2999
3000     for (i = 0; i < count; i++) {
3001         if (oldPtr[i] == winPtr) {
3002             return;
3003         }
3004     }
3005
3006     /*
3007      * Make a new bigger array and use it to reset the property.
3008      * Automatically add the toplevel itself as the last element
3009      * of the list.
3010      */
3011
3012     newPtr = (TkWindow **) ckalloc((unsigned) ((count+2)*sizeof(TkWindow*)));
3013     if (count > 0) {
3014         memcpy(newPtr, oldPtr, count * sizeof(TkWindow*));
3015     }
3016     if (count == 0) {
3017         count++;
3018     }
3019     newPtr[count-1] = winPtr;
3020     newPtr[count] = topPtr;
3021     if (oldPtr != NULL) {
3022         ckfree((char *) oldPtr);
3023     }
3024
3025     topPtr->wmInfoPtr->cmapList = newPtr;
3026     topPtr->wmInfoPtr->cmapCount = count+1;
3027
3028     /*
3029      * On the Macintosh all of this is just an excercise
3030      * in compatability as we don't support colormaps.  If 
3031      * we did they would be installed here.
3032      */
3033 }
3034 \f
3035 /*
3036  *----------------------------------------------------------------------
3037  *
3038  * TkWmRemoveFromColormapWindows --
3039  *
3040  *      This procedure is called to remove a given window from the
3041  *      WM_COLORMAP_WINDOWS property for its top-level.  It is invoked
3042  *      when windows are deleted.
3043  *
3044  * Results:
3045  *      None.
3046  *
3047  * Side effects:
3048  *      WinPtr's window gets removed from the WM_COLORMAP_WINDOWS
3049  *      property of its nearest top-level ancestor, unless the
3050  *      top-level itself is being deleted too.
3051  *
3052  *----------------------------------------------------------------------
3053  */
3054
3055 void
3056 TkWmRemoveFromColormapWindows(
3057     TkWindow *winPtr)           /* Window that may be present in
3058                                  * WM_COLORMAP_WINDOWS property for its
3059                                  * top-level.  Should not be a top-level
3060                                  * window. */
3061 {
3062     TkWindow *topPtr;
3063     TkWindow **oldPtr;
3064     int count, i, j;
3065
3066     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
3067         if (topPtr == NULL) {
3068             /*
3069              * Ancestors have been deleted, so skip the whole operation.
3070              * Seems like this can't ever happen?
3071              */
3072
3073             return;
3074         }
3075         if (topPtr->flags & TK_TOP_LEVEL) {
3076             break;
3077         }
3078     }
3079     if (topPtr->flags & TK_ALREADY_DEAD) {
3080         /*
3081          * Top-level is being deleted, so there's no need to cleanup
3082          * the WM_COLORMAP_WINDOWS property.
3083          */
3084
3085         return;
3086     }
3087
3088     /*
3089      * Find the window and slide the following ones down to cover
3090      * it up.
3091      */
3092
3093     count = topPtr->wmInfoPtr->cmapCount;
3094     oldPtr = topPtr->wmInfoPtr->cmapList;
3095     for (i = 0; i < count; i++) {
3096         if (oldPtr[i] == winPtr) {
3097             for (j = i ; j < count-1; j++) {
3098                 oldPtr[j] = oldPtr[j+1];
3099             }
3100             topPtr->wmInfoPtr->cmapCount = count - 1;
3101             break;
3102         }
3103     }
3104 }
3105 \f
3106 /*
3107  *----------------------------------------------------------------------
3108  *
3109  * TkGetPointerCoords --
3110  *
3111  *      Fetch the position of the mouse pointer.
3112  *
3113  * Results:
3114  *      *xPtr and *yPtr are filled in with the (virtual) root coordinates
3115  *      of the mouse pointer for tkwin's display.  If the pointer isn't
3116  *      on tkwin's screen, then -1 values are returned for both
3117  *      coordinates.  The argument tkwin must be a toplevel window.
3118  *
3119  * Side effects:
3120  *      None.
3121  *
3122  *----------------------------------------------------------------------
3123  */
3124
3125 void
3126 TkGetPointerCoords(
3127     Tk_Window tkwin,            /* Toplevel window that identifies screen
3128                                  * on which lookup is to be done. */
3129     int *xPtr, int *yPtr)       /* Store pointer coordinates here. */
3130 {
3131     Point where;
3132
3133     GetMouse(&where);
3134     LocalToGlobal(&where);
3135     *xPtr = where.h;
3136     *yPtr = where.v;
3137 }
3138 \f
3139 /*
3140  *----------------------------------------------------------------------
3141  *
3142  * InitialWindowBounds --
3143  *
3144  *      This function calculates the initial bounds for a new Mac
3145  *      toplevel window.  Unless the geometry is specified by the user
3146  *      this code will auto place the windows in a cascade diagonially
3147  *      across the main monitor of the Mac.
3148  *
3149  * Results:
3150  *      The bounds are returned in geometry.
3151  *
3152  * Side effects:
3153  *      None.
3154  *
3155  *----------------------------------------------------------------------
3156  */
3157
3158 static void
3159 InitialWindowBounds(
3160     TkWindow *winPtr,           /* Window to get initial bounds for. */
3161     Rect *geometry)             /* On return the initial bounds. */
3162 {
3163     int x, y;
3164     static int defaultX = 5;
3165     static int defaultY = 45;
3166         
3167     if (!(winPtr->wmInfoPtr->sizeHintsFlags & (USPosition | PPosition))) {
3168         /* 
3169          * We will override the program & hopefully place the
3170          * window in a "better" location.
3171          */
3172                 
3173         if (((tcl_macQdPtr->screenBits.bounds.right - defaultX) < 30) ||
3174                 ((tcl_macQdPtr->screenBits.bounds.bottom - defaultY) < 30)) {
3175             defaultX = 5;
3176             defaultY = 45;
3177         }
3178         x = defaultX;
3179         y = defaultY;
3180         defaultX += 20;
3181         defaultY += 20;
3182     } else {
3183         x = winPtr->wmInfoPtr->x;
3184         y = winPtr->wmInfoPtr->y;
3185     }
3186         
3187     geometry->left = x;
3188     geometry->top = y;
3189     geometry->right = x + winPtr->changes.width;
3190     geometry->bottom = y + winPtr->changes.height;
3191 }
3192 \f
3193 /*
3194  *----------------------------------------------------------------------
3195  *
3196  * TkMacResizable --
3197  *
3198  *      This function determines if the passed in window is part of
3199  *      a toplevel window that is resizable.  If the window is 
3200  *      resizable in the x, y or both directions, true is returned.
3201  *
3202  * Results:
3203  *      True if resizable, false otherwise.
3204  *
3205  * Side effects:
3206  *      None.
3207  *
3208  *----------------------------------------------------------------------
3209  */
3210
3211 int
3212 TkMacResizable(
3213     TkWindow *winPtr)           /* Tk window or NULL. */
3214 {
3215     WmInfo *wmPtr;
3216
3217     if (winPtr == NULL) {
3218         return false;
3219     }
3220     while (winPtr->wmInfoPtr == NULL) {
3221         winPtr = winPtr->parentPtr;
3222     }
3223     
3224     wmPtr = winPtr->wmInfoPtr;
3225     if ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) &&
3226             (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE)) {
3227         return false;
3228     } else {
3229         return true;
3230     }
3231 }
3232 \f
3233 /*
3234  *----------------------------------------------------------------------
3235  *
3236  * TkMacGrowToplevel --
3237  *
3238  *      The function is invoked when the user clicks in the grow region
3239  *      of a Tk window.  The function will handle the dragging
3240  *      procedure and not return until completed.  Finally, the function
3241  *      may place information Tk's event queue is the window was resized.
3242  *
3243  * Results:
3244  *      True if events were placed on event queue, false otherwise.
3245  *
3246  * Side effects:
3247  *      None.
3248  *
3249  *----------------------------------------------------------------------
3250  */
3251
3252 int
3253 TkMacGrowToplevel(
3254     WindowPtr whichWindow,
3255     Point start)
3256 {
3257     Point where = start;
3258
3259     GlobalToLocal(&where);
3260     if (where.h > (whichWindow->portRect.right - 16) &&
3261             where.v > (whichWindow->portRect.bottom - 16)) {
3262                 
3263         Window window;
3264         TkWindow *winPtr;
3265         WmInfo *wmPtr;
3266         Rect bounds;
3267         long growResult;
3268
3269         window = TkMacGetXWindow(whichWindow);
3270         winPtr = (TkWindow *) Tk_IdToWindow(tkDisplayList->display, window);
3271         wmPtr = winPtr->wmInfoPtr;
3272         
3273         /* TODO: handle grid size options. */
3274         if ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) &&
3275                 (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE)) {
3276             return false;
3277         }
3278         if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
3279             bounds.left = bounds.right = winPtr->changes.width;
3280         } else {
3281             bounds.left = (wmPtr->minWidth < 64) ? 64 : wmPtr->minWidth;
3282             bounds.right = (wmPtr->maxWidth < 64) ? 64 : wmPtr->maxWidth;
3283         }
3284         if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
3285             bounds.top = bounds.bottom = winPtr->changes.height;
3286         } else {
3287             bounds.top = (wmPtr->minHeight < 64) ? 64 : wmPtr->minHeight;
3288             bounds.bottom = (wmPtr->maxHeight < 64) ? 64 : wmPtr->maxHeight;
3289         }
3290         
3291         growResult = GrowWindow(whichWindow, start, &bounds);
3292
3293         if (growResult != 0) {
3294             SizeWindow(whichWindow,
3295                     LoWord(growResult), HiWord(growResult), true);
3296             SetPort(whichWindow);
3297             InvalRect(&whichWindow->portRect);  /* TODO: may not be needed */
3298             TkMacInvalClipRgns(winPtr);
3299             TkGenWMConfigureEvent((Tk_Window) winPtr, -1, -1, 
3300                     (int) LoWord(growResult), (int) HiWord(growResult),
3301                     TK_SIZE_CHANGED);
3302             return true;
3303         }
3304         return false;
3305     }
3306     return false;
3307 }
3308 \f
3309 /*
3310  *----------------------------------------------------------------------
3311  *
3312  * TkSetWMName --
3313  *
3314  *      Set the title for a toplevel window.  If the window is embedded, 
3315  *      do not change the window title.
3316  *
3317  * Results:
3318  *      None.
3319  *
3320  * Side effects:
3321  *      The title of the window is changed.
3322  *
3323  *----------------------------------------------------------------------
3324  */
3325
3326 void
3327 TkSetWMName(
3328     TkWindow *winPtr,
3329     Tk_Uid titleUid)
3330 {
3331     Str255  pTitle;
3332     GWorldPtr macWin;
3333     
3334     if (Tk_IsEmbedded(winPtr)) {
3335         return;
3336     }
3337     
3338      macWin = TkMacGetDrawablePort(winPtr->window);
3339         
3340     strcpy((char *) pTitle + 1, titleUid);
3341     pTitle[0] = strlen(titleUid);
3342     SetWTitle((WindowPtr) macWin, pTitle);
3343 }
3344
3345 void
3346 TkGenWMDestroyEvent(
3347     Tk_Window tkwin)
3348 {
3349     XEvent event;
3350     
3351     event.xany.serial = Tk_Display(tkwin)->request;
3352     event.xany.send_event = False;
3353     event.xany.display = Tk_Display(tkwin);
3354         
3355     event.xclient.window = Tk_WindowId(tkwin);
3356     event.xclient.type = ClientMessage;
3357     event.xclient.message_type = Tk_InternAtom(tkwin, "WM_PROTOCOLS");
3358     event.xclient.format = 32;
3359     event.xclient.data.l[0] = Tk_InternAtom(tkwin, "WM_DELETE_WINDOW");
3360     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
3361 }
3362 \f
3363 /*
3364  *----------------------------------------------------------------------
3365  *
3366  * TkGenWMConfigureEvent --
3367  *
3368  *      Generate a ConfigureNotify event for Tk.  Depending on the 
3369  *      value of flag the values of width/height, x/y, or both may
3370  *      be changed.
3371  *
3372  * Results:
3373  *      None.
3374  *
3375  * Side effects:
3376  *      A ConfigureNotify event is sent to Tk.
3377  *
3378  *----------------------------------------------------------------------
3379  */
3380
3381 void
3382 TkGenWMConfigureEvent(
3383     Tk_Window tkwin,
3384     int x,
3385     int y,
3386     int width,
3387     int height,
3388     int flags)
3389 {
3390     XEvent event;
3391     WmInfo *wmPtr;
3392     TkWindow *winPtr = (TkWindow *) tkwin;
3393     
3394     if (tkwin == NULL) {
3395         return;
3396     }
3397     
3398     event.type = ConfigureNotify;
3399     event.xconfigure.serial = Tk_Display(tkwin)->request;
3400     event.xconfigure.send_event = False;
3401     event.xconfigure.display = Tk_Display(tkwin);
3402     event.xconfigure.event = Tk_WindowId(tkwin);
3403     event.xconfigure.window = Tk_WindowId(tkwin);
3404     event.xconfigure.border_width = winPtr->changes.border_width;
3405     event.xconfigure.override_redirect = winPtr->atts.override_redirect;
3406     if (winPtr->changes.stack_mode == Above) {
3407         event.xconfigure.above = winPtr->changes.sibling;
3408     } else {
3409         event.xconfigure.above = None;
3410     }
3411
3412     if (flags & TK_LOCATION_CHANGED) {
3413         event.xconfigure.x = x;
3414         event.xconfigure.y = y;
3415     } else {
3416         event.xconfigure.x = Tk_X(tkwin);
3417         event.xconfigure.y = Tk_Y(tkwin);
3418         x = Tk_X(tkwin);
3419         y = Tk_Y(tkwin);
3420     }
3421     if (flags & TK_SIZE_CHANGED) {
3422         event.xconfigure.width = width;
3423         event.xconfigure.height = height;
3424     } else {
3425         event.xconfigure.width = Tk_Width(tkwin);
3426         event.xconfigure.height = Tk_Height(tkwin);
3427         width = Tk_Width(tkwin);
3428         height = Tk_Height(tkwin);
3429     }
3430     
3431     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
3432     
3433     /*
3434      * Update window manager information.
3435      */
3436     if (Tk_IsTopLevel(winPtr)) {
3437         wmPtr = winPtr->wmInfoPtr;
3438         if (flags & TK_LOCATION_CHANGED) {
3439             wmPtr->x = x;
3440             wmPtr->y = y;
3441             wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
3442         }
3443         if ((flags & TK_SIZE_CHANGED) && 
3444                 ((width != Tk_Width(tkwin)) || (height != Tk_Height(tkwin)))) {
3445             if ((wmPtr->width == -1) && (width == winPtr->reqWidth)) {
3446                 /*
3447                  * Don't set external width, since the user didn't change it
3448                  * from what the widgets asked for.
3449                  */
3450             } else {
3451                 if (wmPtr->gridWin != NULL) {
3452                     wmPtr->width = wmPtr->reqGridWidth
3453                         + (width - winPtr->reqWidth)/wmPtr->widthInc;
3454                     if (wmPtr->width < 0) {
3455                         wmPtr->width = 0;
3456                     }
3457                 } else {
3458                     wmPtr->width = width;
3459                 }
3460             }
3461             if ((wmPtr->height == -1) && (height == winPtr->reqHeight)) {
3462                 /*
3463                  * Don't set external height, since the user didn't change it
3464                  * from what the widgets asked for.
3465                  */
3466             } else {
3467                 if (wmPtr->gridWin != NULL) {
3468                     wmPtr->height = wmPtr->reqGridHeight
3469                         + (height - winPtr->reqHeight)/wmPtr->heightInc;
3470                     if (wmPtr->height < 0) {
3471                         wmPtr->height = 0;
3472                     }
3473                 } else {
3474                     wmPtr->height = height;
3475                 }
3476             }
3477             wmPtr->configWidth = width;
3478             wmPtr->configHeight = height;
3479         }
3480     }
3481     
3482     /*
3483      * Now set up the changes structure.  Under X we wait for the
3484      * ConfigureNotify to set these values.  On the Mac we know imediatly that
3485      * this is what we want - so we just set them.  However, we need to
3486      * make sure the windows clipping region is marked invalid so the
3487      * change is visable to the subwindow.
3488      */
3489     winPtr->changes.x = x;
3490     winPtr->changes.y = y;
3491     winPtr->changes.width = width;
3492     winPtr->changes.height = height;
3493     TkMacInvalClipRgns(winPtr);
3494 }
3495 \f
3496 /*
3497  *----------------------------------------------------------------------
3498  *
3499  * TkGetTransientMaster --
3500  *
3501  *      If the passed window has the TRANSIENT_FOR property set this
3502  *      will return the master window.  Otherwise it will return None.
3503  *
3504  * Results:
3505  *      The master window or None.
3506  *
3507  * Side effects:
3508  *      None.
3509  *
3510  *----------------------------------------------------------------------
3511  */
3512
3513 Window
3514 TkGetTransientMaster(
3515     TkWindow *winPtr)
3516 {
3517     if (winPtr->wmInfoPtr != NULL) {
3518         return winPtr->wmInfoPtr->master;
3519     }
3520     return None;
3521 }
3522 \f
3523 /*
3524  *----------------------------------------------------------------------
3525  *
3526  * TkMacGetXWindow --
3527  *
3528  *      Returns the X window Id associated with the given WindowRef.
3529  *
3530  * Results:
3531  *      The window id is returned.  None is returned if not a Tk window.
3532  *
3533  * Side effects:
3534  *      None.
3535  *
3536  *----------------------------------------------------------------------
3537  */
3538
3539 Window
3540 TkMacGetXWindow(
3541     WindowRef macWinPtr)
3542 {
3543     register Tcl_HashEntry *hPtr;
3544
3545     if ((macWinPtr == NULL) || !windowHashInit) {
3546         return None;
3547     }
3548     hPtr = Tcl_FindHashEntry(&windowTable, (char *) macWinPtr);
3549     if (hPtr == NULL) {
3550         return None;
3551     }
3552     return (Window) Tcl_GetHashValue(hPtr);
3553 }
3554 \f
3555 /*
3556  *----------------------------------------------------------------------
3557  *
3558  * TkMacZoomToplevel --
3559  *
3560  *      The function is invoked when the user clicks in the zoom region
3561  *      of a Tk window.  The function will handle the mouse tracking
3562  *      for the interaction.  If the window is to be zoomed the window
3563  *      size is changed and events are generated to let Tk know what
3564  *      happened.
3565  *
3566  * Results:
3567  *      True if events were placed on event queue, false otherwise.
3568  *
3569  * Side effects:
3570  *      The window may be resized & events placed on Tk's queue.
3571  *
3572  *----------------------------------------------------------------------
3573  */
3574
3575 int
3576 TkMacZoomToplevel(
3577     WindowPtr whichWindow,      /* The Macintosh window to zoom. */
3578     Point where,                /* The current mouse position. */
3579     short zoomPart)             /* Either inZoomIn or inZoomOut */
3580 {
3581     Window window;
3582     Tk_Window tkwin;
3583     Point location = {0, 0};
3584     int xOffset, yOffset;
3585     WmInfo *wmPtr;
3586
3587     SetPort(whichWindow);
3588     if (!TrackBox(whichWindow, where, zoomPart)) {
3589         return false;
3590     }
3591
3592     /*
3593      * We should now zoom the window (as long as it's one of ours).  We 
3594      * also need to generate an event to let Tk know that the window size 
3595      * has changed.
3596      */
3597     window = TkMacGetXWindow(whichWindow);
3598     tkwin = Tk_IdToWindow(tkDisplayList->display, window);
3599     if (tkwin == NULL) {
3600         return false;
3601     }
3602
3603     /*
3604      * The following block of code works around a bug in the window
3605      * definition for Apple's floating windows.  The zoom behavior is
3606      * broken - we must manually set the standard state (by default
3607      * it's something like 1x1) and we must swap the zoomPart manually
3608      * otherwise we always get the same zoomPart and nothing happens.
3609      */
3610     wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
3611     if (wmPtr->style >= floatProc && wmPtr->style <= floatSideZoomGrowProc) {
3612         if (zoomPart == inZoomIn) {
3613             Rect zoomRect = tcl_macQdPtr->screenBits.bounds;
3614             InsetRect(&zoomRect, 60, 60);
3615             SetWindowStandardState(whichWindow, &zoomRect);
3616             zoomPart = inZoomOut;
3617         } else {
3618             zoomPart = inZoomIn;
3619         }
3620     }
3621     
3622     ZoomWindow(whichWindow, zoomPart, false);
3623     InvalRect(&whichWindow->portRect);
3624     TkMacInvalClipRgns((TkWindow *) tkwin);
3625
3626     LocalToGlobal(&location);
3627     TkMacWindowOffset(whichWindow, &xOffset, &yOffset);
3628     location.h -= xOffset;
3629     location.v -= yOffset;
3630     TkGenWMConfigureEvent(tkwin, location.h, location.v, 
3631             whichWindow->portRect.right - whichWindow->portRect.left,
3632             whichWindow->portRect.bottom - whichWindow->portRect.top,
3633             TK_BOTH_CHANGED);
3634     return true;
3635 }
3636 \f
3637 /*
3638  *----------------------------------------------------------------------
3639  *
3640  * TkUnsupported1Cmd --
3641  *
3642  *      This procedure is invoked to process the "unsupported1" Tcl 
3643  *      command.  This command allows you to set the style of decoration
3644  *      for a Macintosh window.
3645  *
3646  * Results:
3647  *      A standard Tcl result.
3648  *
3649  * Side effects:
3650  *      Changes the style of a new Mac window.
3651  *
3652  *----------------------------------------------------------------------
3653  */
3654
3655         /* ARGSUSED */
3656 int
3657 TkUnsupported1Cmd(
3658     ClientData clientData,      /* Main window associated with
3659                                  * interpreter. */
3660     Tcl_Interp *interp,         /* Current interpreter. */
3661     int argc,                   /* Number of arguments. */
3662     char **argv)                /* Argument strings. */
3663 {
3664     Tk_Window tkwin = (Tk_Window) clientData;
3665     TkWindow *winPtr;
3666     register WmInfo *wmPtr;
3667     int c;
3668     size_t length;
3669
3670     if (argc < 3) {
3671         wrongNumArgs:
3672         Tcl_AppendResult(interp, "wrong # args: should be \"",
3673                 argv[0], " option window ?arg ...?\"", (char *) NULL);
3674         return TCL_ERROR;
3675     }
3676
3677     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
3678     if (winPtr == NULL) {
3679         return TCL_ERROR;
3680     }
3681     if (!(winPtr->flags & TK_TOP_LEVEL)) {
3682         Tcl_AppendResult(interp, "window \"", winPtr->pathName,
3683                 "\" isn't a top-level window", (char *) NULL);
3684         return TCL_ERROR;
3685     }
3686     wmPtr = winPtr->wmInfoPtr;
3687     c = argv[1][0];
3688     length = strlen(argv[1]);
3689     if ((c == 's')  && (strncmp(argv[1], "style", length) == 0)) {
3690         if ((argc != 3) && (argc != 4)) {
3691             Tcl_AppendResult(interp, "wrong # arguments: must be \"",
3692                     argv[0], " style window ?windowStyle?\"",
3693                     (char *) NULL);
3694             return TCL_ERROR;
3695         }
3696         if (argc == 3) {
3697             switch (wmPtr->style) {
3698                 case noGrowDocProc:
3699                 case documentProc:
3700                     interp->result = "documentProc";
3701                     break;
3702                 case dBoxProc:
3703                     interp->result = "dBoxProc";
3704                     break;
3705                 case plainDBox:
3706                     interp->result = "plainDBox";
3707                     break;
3708                 case altDBoxProc:
3709                     interp->result = "altDBoxProc";
3710                     break;
3711                 case movableDBoxProc:
3712                     interp->result = "movableDBoxProc";
3713                     break;
3714                 case zoomDocProc:
3715                 case zoomNoGrow:
3716                     interp->result = "zoomDocProc";
3717                     break;
3718                 case rDocProc:
3719                     interp->result = "rDocProc";
3720                     break;
3721                 case floatProc:
3722                 case floatGrowProc:
3723                     interp->result = "floatProc";
3724                     break;
3725                 case floatZoomProc:
3726                 case floatZoomGrowProc:
3727                     interp->result = "floatZoomProc";
3728                     break;
3729                 case floatSideProc:
3730                 case floatSideGrowProc:
3731                     interp->result = "floatSideProc";
3732                     break;
3733                 case floatSideZoomProc:
3734                 case floatSideZoomGrowProc:
3735                     interp->result = "floatSideZoomProc";
3736                     break;
3737                 default:
3738                    panic("invalid style");
3739             }
3740             return TCL_OK;
3741         }
3742         if (strcmp(argv[3], "documentProc") == 0) {
3743             wmPtr->style = documentProc;
3744         } else if (strcmp(argv[3], "noGrowDocProc") == 0) {
3745             wmPtr->style = documentProc;
3746         } else if (strcmp(argv[3], "dBoxProc") == 0) {
3747             wmPtr->style = dBoxProc;
3748         } else if (strcmp(argv[3], "plainDBox") == 0) {
3749             wmPtr->style = plainDBox;
3750         } else if (strcmp(argv[3], "altDBoxProc") == 0) {
3751             wmPtr->style = altDBoxProc;
3752         } else if (strcmp(argv[3], "movableDBoxProc") == 0) {
3753             wmPtr->style = movableDBoxProc;
3754         } else if (strcmp(argv[3], "zoomDocProc") == 0) {
3755             wmPtr->style = zoomDocProc;
3756         } else if (strcmp(argv[3], "zoomNoGrow") == 0) {
3757             wmPtr->style = zoomNoGrow;
3758         } else if (strcmp(argv[3], "rDocProc") == 0) {
3759             wmPtr->style = rDocProc;
3760         } else if (strcmp(argv[3], "floatProc") == 0) {
3761             wmPtr->style = floatGrowProc;
3762         } else if (strcmp(argv[3], "floatGrowProc") == 0) {
3763             wmPtr->style = floatGrowProc;
3764         } else if (strcmp(argv[3], "floatZoomProc") == 0) {
3765             wmPtr->style = floatZoomGrowProc;
3766         } else if (strcmp(argv[3], "floatZoomGrowProc") == 0) {
3767             wmPtr->style = floatZoomGrowProc;
3768         } else if (strcmp(argv[3], "floatSideProc") == 0) {
3769             wmPtr->style = floatSideGrowProc;
3770         } else if (strcmp(argv[3], "floatSideGrowProc") == 0) {
3771             wmPtr->style = floatSideGrowProc;
3772         } else if (strcmp(argv[3], "floatSideZoomProc") == 0) {
3773             wmPtr->style = floatSideZoomGrowProc;
3774         } else if (strcmp(argv[3], "floatSideZoomGrowProc") == 0) {
3775             wmPtr->style = floatSideZoomGrowProc;
3776         } else {
3777             Tcl_AppendResult(interp, "bad style: should be documentProc, ",
3778                     "dBoxProc, plainDBox, altDBoxProc, movableDBoxProc, ",
3779                     "zoomDocProc, rDocProc, floatProc, floatZoomProc, ",
3780                     "floatSideProc, or floatSideZoomProc",
3781                     (char *) NULL);
3782             return TCL_ERROR;
3783         }
3784     } else {
3785         Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
3786                 "\": must be style",
3787                 (char *) NULL);
3788         return TCL_ERROR;
3789     }
3790     
3791     return TCL_OK;
3792 }
3793 \f
3794 /*
3795  *----------------------------------------------------------------------
3796  *
3797  * TkpMakeMenuWindow --
3798  *
3799  *      Configure the window to be either a undecorated pull-down 
3800  *      (or pop-up) menu, or as a toplevel floating menu (palette).
3801  *
3802  * Results:
3803  *      None.
3804  *
3805  * Side effects:
3806  *      Changes the style bit used to create a new Mac toplevel.
3807  *
3808  *----------------------------------------------------------------------
3809  */
3810
3811 void
3812 TkpMakeMenuWindow(
3813     Tk_Window tkwin,            /* New window. */
3814     int transient)              /* 1 means menu is only posted briefly as
3815                                  * a popup or pulldown or cascade.  0 means
3816                                  * menu is always visible, e.g. as a 
3817                                  * floating menu. */
3818 {
3819     if (transient) {
3820         ((TkWindow *) tkwin)->wmInfoPtr->style = plainDBox;
3821     } else {
3822         ((TkWindow *) tkwin)->wmInfoPtr->style = floatProc;
3823         ((TkWindow *) tkwin)->wmInfoPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
3824         ((TkWindow *) tkwin)->wmInfoPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
3825     }
3826 }
3827 \f
3828 /*
3829  *----------------------------------------------------------------------
3830  *
3831  * TkMacMakeRealWindowExist --
3832  *
3833  *      This function finally creates the real Macintosh window that
3834  *      the Mac actually understands.  
3835  *
3836  * Results:
3837  *      None.
3838  *
3839  * Side effects:
3840  *      A new Macintosh toplevel is created.
3841  *
3842  *----------------------------------------------------------------------
3843  */
3844
3845 void 
3846 TkMacMakeRealWindowExist(
3847     TkWindow *winPtr)   /* Tk window. */
3848 {
3849     WmInfo *wmPtr = winPtr->wmInfoPtr;
3850     WindowRef newWindow = NULL;
3851     MacDrawable *macWin;
3852     Rect geometry;
3853     Tcl_HashEntry *valueHashPtr;
3854     int new;
3855     TkMacWindowList *listPtr;
3856
3857     if (TkMacHostToplevelExists(winPtr)) {
3858         return;
3859     }
3860     
3861     macWin = (MacDrawable *) winPtr->window;
3862
3863     /*
3864      * If this is embedded, make sure its container's toplevel exists,
3865      * then return... 
3866      */
3867     
3868     if (Tk_IsEmbedded(winPtr)) {
3869         TkWindow *contWinPtr;
3870
3871         contWinPtr = TkpGetOtherWindow(winPtr);
3872         if (contWinPtr != NULL) {
3873             TkMacMakeRealWindowExist(contWinPtr->privatePtr->toplevel->winPtr);
3874             macWin->flags |= TK_HOST_EXISTS;
3875             return;
3876         } else if (gMacEmbedHandler != NULL) {
3877             if (gMacEmbedHandler->containerExistProc != NULL) {
3878                 if (gMacEmbedHandler->containerExistProc((Tk_Window) winPtr) != TCL_OK) {
3879                    panic("ContainerExistProc could not make container");
3880                }
3881             }
3882             return;
3883         } else {
3884             panic("TkMacMakeRealWindowExist could not find container");
3885         }
3886
3887         /*
3888          * NOTE: Here we should handle out of process embedding.
3889          */
3890         
3891     }
3892     
3893     InitialWindowBounds(winPtr, &geometry);
3894         
3895     newWindow = NewCWindow(NULL, &geometry, "\ptemp", false, 
3896             (short) wmPtr->style, (WindowRef) -1, true, 0);
3897     if (newWindow == NULL) {
3898         panic("couldn't allocate new Mac window");
3899     }
3900     
3901     /*
3902      * Add this window to the list of toplevel windows.
3903      */
3904     
3905     listPtr = (TkMacWindowList *) ckalloc(sizeof(TkMacWindowList));
3906     listPtr->nextPtr = tkMacWindowListPtr;
3907     listPtr->winPtr = winPtr;
3908     tkMacWindowListPtr = listPtr;
3909     
3910     macWin->portPtr = (GWorldPtr) newWindow;
3911     tkMacMoveWindow(newWindow, (int) geometry.left, (int) geometry.top);
3912     SetPort((GrafPtr) newWindow);
3913         
3914     if (!windowHashInit) {
3915         Tcl_InitHashTable(&windowTable, TCL_ONE_WORD_KEYS);
3916         windowHashInit = true;
3917     }
3918     valueHashPtr = Tcl_CreateHashEntry(&windowTable,
3919             (char *) newWindow, &new);
3920     if (!new) {
3921         panic("same macintosh window allocated twice!");
3922     }
3923     Tcl_SetHashValue(valueHashPtr, macWin);
3924     
3925     macWin->flags |= TK_HOST_EXISTS;
3926 }
3927 \f
3928 /*
3929  *----------------------------------------------------------------------
3930  *
3931  * TkMacRegisterOffScreenWindow --
3932  *
3933  *      This function adds the passed in Off Screen Port to the
3934  *      hash table that maps Mac windows to root X windows.  
3935  *
3936  * Results:
3937  *      None.
3938  *
3939  * Side effects:
3940  *      An entry is added to the windowTable hash table.
3941  *
3942  *----------------------------------------------------------------------
3943  */
3944
3945 void 
3946 TkMacRegisterOffScreenWindow(
3947     Window window,      /* Window structure. */
3948     GWorldPtr portPtr)  /* Pointer to a Mac GWorld. */
3949 {
3950     WindowRef newWindow = NULL;
3951     MacDrawable *macWin;
3952     Tcl_HashEntry *valueHashPtr;
3953     int new;
3954
3955     macWin = (MacDrawable *) window;
3956     if (!windowHashInit) {
3957         Tcl_InitHashTable(&windowTable, TCL_ONE_WORD_KEYS);
3958         windowHashInit = true;
3959     }
3960     valueHashPtr = Tcl_CreateHashEntry(&windowTable,
3961             (char *) portPtr, &new);
3962     if (!new) {
3963         panic("same macintosh window allocated twice!");
3964     }
3965     Tcl_SetHashValue(valueHashPtr, macWin);
3966 }
3967 \f
3968 /*
3969  *----------------------------------------------------------------------
3970  *
3971  * TkMacUnregisterMacWindow --
3972  *
3973  *      Given a macintosh port window, this function removes the 
3974  *      association between this window and the root X window that
3975  *      Tk cares about.  
3976  *
3977  * Results:
3978  *      None.
3979  *
3980  * Side effects:
3981  *      An entry is removed from the windowTable hash table.
3982  *
3983  *----------------------------------------------------------------------
3984  */
3985
3986 void 
3987 TkMacUnregisterMacWindow(
3988     GWorldPtr portPtr)  /* Pointer to a Mac GWorld. */
3989 {
3990     if (!windowHashInit) {
3991         panic("TkMacUnregisterMacWindow: unmapping before inited");;
3992     }
3993     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&windowTable,
3994         (char *) portPtr));
3995 }
3996 \f
3997 /*
3998  *----------------------------------------------------------------------
3999  *
4000  * TkMacSetScrollbarGrow --
4001  *
4002  *      Sets a flag for a toplevel window indicating that the passed
4003  *      Tk scrollbar window will display the grow region for the 
4004  *      toplevel window.
4005  *
4006  * Results:
4007  *      None.
4008  *
4009  * Side effects:
4010  *      A flag is set int windows toplevel parent.
4011  *
4012  *----------------------------------------------------------------------
4013  */
4014
4015 void 
4016 TkMacSetScrollbarGrow(
4017     TkWindow *winPtr,           /* Tk scrollbar window. */
4018     int flag)                   /* Boolean value true or false. */
4019 {
4020     if (flag) {
4021         winPtr->privatePtr->toplevel->flags |= TK_SCROLLBAR_GROW;
4022         winPtr->privatePtr->toplevel->winPtr->wmInfoPtr->scrollWinPtr = winPtr;
4023     } else if (winPtr->privatePtr->toplevel->winPtr->wmInfoPtr->scrollWinPtr
4024             == winPtr) {
4025         winPtr->privatePtr->toplevel->flags &= ~TK_SCROLLBAR_GROW;
4026         winPtr->privatePtr->toplevel->winPtr->wmInfoPtr->scrollWinPtr = NULL;
4027     }
4028 }
4029 \f
4030 /*
4031  *----------------------------------------------------------------------
4032  *
4033  * TkMacGetScrollbarGrowWindow --
4034  *
4035  *      Tests to see if a given window's toplevel window contains a
4036  *      scrollbar that will draw the GrowIcon for the window.
4037  *
4038  * Results:
4039  *      Boolean value.
4040  *
4041  * Side effects:
4042  *      None.
4043  *
4044  *----------------------------------------------------------------------
4045  */
4046
4047 TkWindow * 
4048 TkMacGetScrollbarGrowWindow(
4049     TkWindow *winPtr)   /* Tk window. */
4050 {
4051     TkWindow *scrollWinPtr;
4052     
4053     if (winPtr == NULL) {
4054         return NULL;
4055     }
4056     scrollWinPtr =
4057         winPtr->privatePtr->toplevel->winPtr->wmInfoPtr->scrollWinPtr;
4058     if (winPtr != NULL) {
4059         /*
4060          * We need to confirm the window exists.
4061          */
4062         if ((Tk_Window) scrollWinPtr !=
4063                 Tk_IdToWindow(winPtr->display, winPtr->window)) {
4064             scrollWinPtr = NULL;
4065         }
4066     }
4067     return scrollWinPtr;
4068 }
4069 \f
4070 /*
4071  *----------------------------------------------------------------------
4072  *
4073  * TkWmFocusToplevel --
4074  *
4075  *      This is a utility procedure invoked by focus-management code. It
4076  *      exists because of the extra wrapper windows that exist under
4077  *      Unix; its job is to map from wrapper windows to the
4078  *      corresponding toplevel windows.  On PCs and Macs there are no
4079  *      wrapper windows so no mapping is necessary;  this procedure just
4080  *      determines whether a window is a toplevel or not.
4081  *
4082  * Results:
4083  *      If winPtr is a toplevel window, returns the pointer to the
4084  *      window; otherwise returns NULL.
4085  *
4086  * Side effects:
4087  *      None.
4088  *
4089  *----------------------------------------------------------------------
4090  */
4091
4092 TkWindow *
4093 TkWmFocusToplevel(
4094     TkWindow *winPtr)           /* Window that received a focus-related
4095                                  * event. */
4096 {
4097     if (!(winPtr->flags & TK_TOP_LEVEL)) {
4098         return NULL;
4099     }
4100     return winPtr;
4101 }
4102 \f
4103 /*
4104  *----------------------------------------------------------------------
4105  *
4106  * TkpGetWrapperWindow --
4107  *
4108  *      This is a utility procedure invoked by focus-management code. It
4109  *      maps to the wrapper for a top-level, which is just the same
4110  *      as the top-level on Macs and PCs.
4111  *
4112  * Results:
4113  *      If winPtr is a toplevel window, returns the pointer to the
4114  *      window; otherwise returns NULL.
4115  *
4116  * Side effects:
4117  *      None.
4118  *
4119  *----------------------------------------------------------------------
4120  */
4121
4122 TkWindow *
4123 TkpGetWrapperWindow(
4124     TkWindow *winPtr)           /* Window that received a focus-related
4125                                  * event. */
4126 {
4127     if (!(winPtr->flags & TK_TOP_LEVEL)) {
4128         return NULL;
4129     }
4130     return winPtr;
4131 }
4132 \f
4133 /*
4134  *----------------------------------------------------------------------
4135  *
4136  * TkpWmSetState --
4137  *
4138  *      Sets the window manager state for the wrapper window of a
4139  *      given toplevel window.
4140  *
4141  * Results:
4142  *      None.
4143  *
4144  * Side effects:
4145  *      May maximize, minimize, restore, or withdraw a window.
4146  *
4147  *----------------------------------------------------------------------
4148  */
4149
4150 void
4151 TkpWmSetState(winPtr, state)
4152      TkWindow *winPtr;          /* Toplevel window to operate on. */
4153      int state;                 /* One of IconicState, ZoomState, NormalState,
4154                                  * or WithdrawnState. */
4155 {
4156     WmInfo *wmPtr = winPtr->wmInfoPtr;
4157     GWorldPtr macWin;
4158     
4159     wmPtr->hints.initial_state = state;
4160     if (wmPtr->flags & WM_NEVER_MAPPED) {
4161         return;
4162     }
4163
4164     macWin = TkMacGetDrawablePort(winPtr->window);
4165
4166     if (state == WithdrawnState) {
4167         Tk_UnmapWindow((Tk_Window) winPtr);
4168     } else if (state == IconicState) {
4169         Tk_UnmapWindow((Tk_Window) winPtr);
4170         if (TkMacHaveAppearance()) {
4171             /*
4172              * The window always gets unmapped.  However, if we can show the
4173              * icon version of the window (collapsed) we make the window visable
4174              * and then collapse it.
4175              *
4176              * TODO: This approach causes flashing!
4177              */
4178
4179             if (IsWindowCollapsable((WindowRef) macWin)) {
4180                 ShowWindow((WindowRef) macWin);
4181                 CollapseWindow((WindowPtr) macWin, true);
4182             }
4183         }
4184     } else if (state == NormalState) {
4185         Tk_MapWindow((Tk_Window) winPtr);
4186         if (TkMacHaveAppearance()) {
4187             CollapseWindow((WindowPtr) macWin, false);
4188         }
4189     } else if (state == ZoomState) {
4190         /* TODO: need to support zoomed windows */
4191     }
4192 }
4193 /*
4194  *----------------------------------------------------------------------
4195  *
4196  * TkMacHaveAppearance --
4197  *
4198  *      Determine if the appearance manager is available on this Mac.
4199  *      We cache the result so future calls are fast.  Return a different
4200  *      value if 1.0.1 is present, since many interfaces were added in
4201  *      1.0.1
4202  *
4203  * Results:
4204  *      1 if the appearance manager is present, 2 if the appearance
4205  *      manager version is 1.0.1 or greater, 0 if it is not present.
4206  *
4207  * Side effects:
4208  *      Calls Gestalt to query system values.
4209  *
4210  *----------------------------------------------------------------------
4211  */
4212
4213 int
4214 TkMacHaveAppearance()
4215 {
4216     static initialized = false;
4217     static int TkMacHaveAppearance = 0;
4218     long response = 0;
4219     OSErr err = noErr;
4220     
4221     if (!initialized) {
4222         err = Gestalt(gestaltAppearanceAttr, &response);
4223         if (err == noErr) {
4224             TkMacHaveAppearance = 1;
4225         }
4226         err = Gestalt(gestaltAppearanceVersion, &response);
4227         if (err == noErr) {
4228             TkMacHaveAppearance = 2;
4229         }
4230     }
4231
4232     return TkMacHaveAppearance;
4233 }