]> www.wagner.pp.ru Git - oss/ck.git/blob - ckPlace.c
Ck console graphics toolkit
[oss/ck.git] / ckPlace.c
1 /* 
2  * ckPlace.c --
3  *
4  *      This file contains code to implement a simple geometry manager
5  *      for Ck based on absolute placement or "rubber-sheet" placement.
6  *
7  * Copyright (c) 1992-1994 The Regents of the University of California.
8  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9  * Copyright (c) 1995 Christian Werner
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
15 #include "ckPort.h"
16 #include "ck.h"
17
18 /*
19  * Border modes for relative placement:
20  *
21  * BM_INSIDE:           relative distances computed using area inside
22  *                      all borders of master window.
23  * BM_IGNORE:           border issues are ignored:  place relative to
24  *                      master's actual window size.
25  */
26
27 typedef enum {BM_INSIDE, BM_IGNORE} BorderMode;
28
29 /*
30  * For each window whose geometry is managed by the placer there is
31  * a structure of the following type:
32  */
33
34 typedef struct Slave {
35     CkWindow *winPtr;           /* Pointer to window. */
36     struct Master *masterPtr;   /* Pointer to information for window
37                                  * relative to which winPtr is placed.
38                                  * This isn't necessarily the logical
39                                  * parent of winPtr.  NULL means the
40                                  * master was deleted or never assigned. */
41     struct Slave *nextPtr;      /* Next in list of windows placed relative
42                                  * to same master (NULL for end of list). */
43
44     /*
45      * Geometry information for window;  where there are both relative
46      * and absolute values for the same attribute (e.g. x and relX) only
47      * one of them is actually used, depending on flags.
48      */
49
50     int x, y;                   /* X and Y coordinates for winPtr. */
51     double relX, relY;          /* X and Y coordinates relative to size of
52                                  * master. */
53     int width, height;          /* Absolute dimensions for winPtr. */
54     double relWidth, relHeight; /* Dimensions for winPtr relative to size of
55                                  * master. */
56     Ck_Anchor anchor;           /* Which point on winPtr is placed at the
57                                  * given position. */
58     BorderMode borderMode;      /* How to treat borders of master window. */
59     int flags;                  /* Various flags;  see below for bit
60                                  * definitions. */
61 } Slave;
62
63 /*
64  * Flag definitions for Slave structures:
65  *
66  * CHILD_REL_X -                1 means use relX field;  0 means use x.
67  * CHILD_REL_Y -                1 means use relY field;  0 means use y;
68  * CHILD_WIDTH -                1 means use width field;
69  * CHILD_REL_WIDTH -            1 means use relWidth;  if neither this nor
70  *                              CHILD_WIDTH is 1, use window's requested
71  *                              width.
72  * CHILD_HEIGHT -               1 means use height field;
73  * CHILD_REL_HEIGHT -           1 means use relHeight;  if neither this nor
74  *                              CHILD_HEIGHT is 1, use window's requested
75  *                              height.
76  */
77
78 #define CHILD_REL_X             1
79 #define CHILD_REL_Y             2
80 #define CHILD_WIDTH             4
81 #define CHILD_REL_WIDTH         8
82 #define CHILD_HEIGHT            0x10
83 #define CHILD_REL_HEIGHT        0x20
84
85 /*
86  * For each master window that has a slave managed by the placer there
87  * is a structure of the following form:
88  */
89
90 typedef struct Master {
91     CkWindow *winPtr;           /* Pointer to master window. */
92     struct Slave *slavePtr;     /* First in linked list of slaves
93                                  * placed relative to this master. */
94     int flags;                  /* See below for bit definitions. */
95 } Master;
96
97 /*
98  * Flag definitions for masters:
99  *
100  * PARENT_RECONFIG_PENDING -    1 means that a call to RecomputePlacement
101  *                              is already pending via a Do_When_Idle handler.
102  */
103
104 #define PARENT_RECONFIG_PENDING 1
105
106 /*
107  * The hash tables below both use CkWindow pointers as keys.  They map
108  * from CkWindows to Slave and Master structures for windows, if they
109  * exist.
110  */
111
112 static int initialized = 0;
113 static Tcl_HashTable masterTable;
114 static Tcl_HashTable slaveTable;
115
116 /*
117  * The following structure is the official type record for the
118  * placer:
119  */
120
121 static void             PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
122                             CkWindow *winPtr));
123 static void             PlaceLostSlaveProc _ANSI_ARGS_((ClientData clientData,
124                             CkWindow *winPtr));
125
126 static Ck_GeomMgr placerType = {
127     "place",                            /* name */
128     PlaceRequestProc,                   /* requestProc */
129     PlaceLostSlaveProc,                 /* lostSlaveProc */
130 };
131
132 /*
133  * Forward declarations for procedures defined later in this file:
134  */
135
136 static void             SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
137                             CkEvent *eventPtr));
138 static int              ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
139                             Slave *slavePtr, int argc, char **argv));
140 static Slave *          FindSlave _ANSI_ARGS_((CkWindow *winPtr));
141 static Master *         FindMaster _ANSI_ARGS_((CkWindow *winPtr));
142 static void             MasterStructureProc _ANSI_ARGS_((ClientData clientData,
143                             CkEvent *eventPtr));
144 static void             RecomputePlacement _ANSI_ARGS_((ClientData clientData));
145 static void             UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
146 \f
147 /*
148  *--------------------------------------------------------------
149  *
150  * Ck_PlaceCmd --
151  *
152  *      This procedure is invoked to process the "place" Tcl
153  *      commands.  See the user documentation for details on
154  *      what it does.
155  *
156  * Results:
157  *      A standard Tcl result.
158  *
159  * Side effects:
160  *      See the user documentation.
161  *
162  *--------------------------------------------------------------
163  */
164
165 int
166 Ck_PlaceCmd(clientData, interp, argc, argv)
167     ClientData clientData;      /* Main window associated with interpreter. */
168     Tcl_Interp *interp;         /* Current interpreter. */
169     int argc;                   /* Number of arguments. */
170     char **argv;                /* Argument strings. */
171 {
172     CkWindow *winPtr;
173     Slave *slavePtr;
174     Tcl_HashEntry *hPtr;
175     int length;
176     char c;
177
178     /*
179      * Initialize, if that hasn't been done yet.
180      */
181
182     if (!initialized) {
183         Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
184         Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
185         initialized = 1;
186     }
187
188     if (argc < 3) {
189         Tcl_AppendResult(interp, "wrong # args: should be \"",
190                 argv[0], " option|pathName args", (char *) NULL);
191         return TCL_ERROR;
192     }
193     c = argv[1][0];
194     length = strlen(argv[1]);
195
196     /*
197      * Handle special shortcut where window name is first argument.
198      */
199
200     if (c == '.') {
201         winPtr = Ck_NameToWindow(interp, argv[1], (CkWindow *) clientData);
202         if (winPtr == NULL) {
203             return TCL_ERROR;
204         }
205         slavePtr = FindSlave(winPtr);
206         return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
207     }
208
209     /*
210      * Handle more general case of option followed by window name followed
211      * by possible additional arguments.
212      */
213
214     winPtr = Ck_NameToWindow(interp, argv[2], (CkWindow *) clientData);
215     if (winPtr == NULL) {
216         return TCL_ERROR;
217     }
218     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
219         if (argc < 5) {
220             Tcl_AppendResult(interp, "wrong # args: should be \"",
221                     argv[0],
222                     " configure pathName option value ?option value ...?\"",
223                     (char *) NULL);
224             return TCL_ERROR;
225         }
226         slavePtr = FindSlave(winPtr);
227         return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
228     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
229         if (argc != 3) {
230             Tcl_AppendResult(interp, "wrong # args: should be \"",
231                     argv[0], " forget pathName\"", (char *) NULL);
232             return TCL_ERROR;
233         }
234         hPtr = Tcl_FindHashEntry(&slaveTable, (char *) winPtr);
235         if (hPtr == NULL) {
236             return TCL_OK;
237         }
238         slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
239         UnlinkSlave(slavePtr);
240         Tcl_DeleteHashEntry(hPtr);
241         Ck_DeleteEventHandler(winPtr, CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
242                 SlaveStructureProc, (ClientData) slavePtr);
243         Ck_ManageGeometry(winPtr, (Ck_GeomMgr *) NULL, (ClientData) NULL);
244         Ck_UnmapWindow(winPtr);
245         ckfree((char *) slavePtr);
246     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
247         char buffer[50];
248
249         if (argc != 3) {
250             Tcl_AppendResult(interp, "wrong # args: should be \"",
251                     argv[0], " info pathName\"", (char *) NULL);
252             return TCL_ERROR;
253         }
254         hPtr = Tcl_FindHashEntry(&slaveTable, (char *) winPtr);
255         if (hPtr == NULL) {
256             return TCL_OK;
257         }
258         slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
259         if (slavePtr->flags & CHILD_REL_X) {
260             sprintf(buffer, "-relx %.4g", slavePtr->relX);
261         } else {
262             sprintf(buffer, "-x %d", slavePtr->x);
263         }
264         Tcl_AppendResult(interp, buffer, (char *) NULL);
265         if (slavePtr->flags & CHILD_REL_Y) {
266             sprintf(buffer, " -rely %.4g", slavePtr->relY);
267         } else {
268             sprintf(buffer, " -y %d", slavePtr->y);
269         }
270         Tcl_AppendResult(interp, buffer, (char *) NULL);
271         if (slavePtr->flags & CHILD_REL_WIDTH) {
272             sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
273             Tcl_AppendResult(interp, buffer, (char *) NULL);
274         } else if (slavePtr->flags & CHILD_WIDTH) {
275             sprintf(buffer, " -width %d", slavePtr->width);
276             Tcl_AppendResult(interp, buffer, (char *) NULL);
277         }
278         if (slavePtr->flags & CHILD_REL_HEIGHT) {
279             sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
280             Tcl_AppendResult(interp, buffer, (char *) NULL);
281         } else if (slavePtr->flags & CHILD_HEIGHT) {
282             sprintf(buffer, " -height %d", slavePtr->height);
283             Tcl_AppendResult(interp, buffer, (char *) NULL);
284         }
285         Tcl_AppendResult(interp, " -anchor ", Ck_NameOfAnchor(slavePtr->anchor),
286                 (char *) NULL);
287         if (slavePtr->borderMode == BM_IGNORE) {
288             Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
289         }
290     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
291         if (argc != 3) {
292             Tcl_AppendResult(interp, "wrong # args: should be \"",
293                     argv[0], " slaves pathName\"", (char *) NULL);
294             return TCL_ERROR;
295         }
296         hPtr = Tcl_FindHashEntry(&masterTable, (char *) winPtr);
297         if (hPtr != NULL) {
298             Master *masterPtr;
299             masterPtr = (Master *) Tcl_GetHashValue(hPtr);
300             for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
301                     slavePtr = slavePtr->nextPtr) {
302                 Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
303             }
304         }
305     } else {
306         Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
307                 "\": must be configure, forget, info, or slaves",
308                 (char *) NULL);
309         return TCL_ERROR;
310     }
311     return TCL_OK;
312 }
313 \f
314 /*
315  *----------------------------------------------------------------------
316  *
317  * FindSlave --
318  *
319  *      Given a CkWindow *, find the Slave structure corresponding
320  *      to that window (making a new one if necessary).
321  *
322  * Results:
323  *      None.
324  *
325  * Side effects:
326  *      A new Slave structure may be created.
327  *
328  *----------------------------------------------------------------------
329  */
330
331 static Slave *
332 FindSlave(winPtr)
333     CkWindow *winPtr;           /* Pointer to desired slave. */
334 {
335     Tcl_HashEntry *hPtr;
336     Slave *slavePtr;
337     int new;
338
339     hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) winPtr, &new);
340     if (new) {
341         slavePtr = (Slave *) ckalloc(sizeof (Slave));
342         slavePtr->winPtr = winPtr;
343         slavePtr->masterPtr = NULL;
344         slavePtr->nextPtr = NULL;
345         slavePtr->x = slavePtr->y = 0;
346         slavePtr->relX = slavePtr->relY = 0.0;
347         slavePtr->width = slavePtr->height = 0;
348         slavePtr->relWidth = slavePtr->relHeight = 0.0;
349         slavePtr->anchor = CK_ANCHOR_NW;
350         slavePtr->borderMode = BM_INSIDE;
351         slavePtr->flags = 0;
352         Tcl_SetHashValue(hPtr, slavePtr);
353         Ck_CreateEventHandler(winPtr, CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
354                 SlaveStructureProc, (ClientData) slavePtr);
355         Ck_ManageGeometry(winPtr, &placerType, (ClientData) slavePtr);
356     } else {
357         slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
358     }
359     return slavePtr;
360 }
361 \f
362 /*
363  *----------------------------------------------------------------------
364  *
365  * UnlinkSlave --
366  *
367  *      This procedure removes a slave window from the chain of slaves
368  *      in its master.
369  *
370  * Results:
371  *      None.
372  *
373  * Side effects:
374  *      The slave list of slavePtr's master changes.
375  *
376  *----------------------------------------------------------------------
377  */
378
379 static void
380 UnlinkSlave(slavePtr)
381     Slave *slavePtr;            /* Slave structure to be unlinked. */
382 {
383     register Master *masterPtr;
384     register Slave *prevPtr;
385
386     masterPtr = slavePtr->masterPtr;
387     if (masterPtr == NULL) {
388         return;
389     }
390     if (masterPtr->slavePtr == slavePtr) {
391         masterPtr->slavePtr = slavePtr->nextPtr;
392     } else {
393         for (prevPtr = masterPtr->slavePtr; ;
394                 prevPtr = prevPtr->nextPtr) {
395             if (prevPtr == NULL) {
396                 panic("UnlinkSlave couldn't find slave to unlink");
397             }
398             if (prevPtr->nextPtr == slavePtr) {
399                 prevPtr->nextPtr = slavePtr->nextPtr;
400                 break;
401             }
402         }
403     }
404     slavePtr->masterPtr = NULL;
405 }
406 \f
407 /*
408  *----------------------------------------------------------------------
409  *
410  * FindMaster --
411  *
412  *      Given a CkWindow *, find the Master structure corresponding
413  *      to that window (making a new one if necessary).
414  *
415  * Results:
416  *      None.
417  *
418  * Side effects:
419  *      A new Master structure may be created.
420  *
421  *----------------------------------------------------------------------
422  */
423
424 static Master *
425 FindMaster(winPtr)
426     CkWindow *winPtr;           /* Pointer to desired master. */
427 {
428     Tcl_HashEntry *hPtr;
429     Master *masterPtr;
430     int new;
431
432     hPtr = Tcl_CreateHashEntry(&masterTable, (char *) winPtr, &new);
433     if (new) {
434         masterPtr = (Master *) ckalloc(sizeof (Master));
435         masterPtr->winPtr = winPtr;
436         masterPtr->slavePtr = NULL;
437         masterPtr->flags = 0;
438         Tcl_SetHashValue(hPtr, masterPtr);
439         /*
440          * Special case: for toplevels winPtr is NULL,
441          * therefore don't create event handler.
442          */
443         if (winPtr != NULL)
444             Ck_CreateEventHandler(masterPtr->winPtr,
445                 CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
446                 MasterStructureProc, (ClientData) masterPtr);
447     } else {
448         masterPtr = (Master *) Tcl_GetHashValue(hPtr);
449     }
450     return masterPtr;
451 }
452 \f
453 /*
454  *----------------------------------------------------------------------
455  *
456  * ConfigureSlave --
457  *
458  *      This procedure is called to process an argv/argc list to
459  *      reconfigure the placement of a window.
460  *
461  * Results:
462  *      A standard Tcl result.  If an error occurs then a message is
463  *      left in interp->result.
464  *
465  * Side effects:
466  *      Information in slavePtr may change, and slavePtr's master is
467  *      scheduled for reconfiguration.
468  *
469  *----------------------------------------------------------------------
470  */
471
472 static int
473 ConfigureSlave(interp, slavePtr, argc, argv)
474     Tcl_Interp *interp;         /* Used for error reporting. */
475     Slave *slavePtr;            /* Pointer to current information
476                                  * about slave. */
477     int argc;                   /* Number of config arguments. */
478     char **argv;                /* String values for arguments. */
479 {
480     Master *masterPtr;
481     int c, length, result;
482     double d;
483
484     result = TCL_OK;
485     for ( ; argc > 0; argc -= 2, argv += 2) {
486         if (argc < 2) {
487             Tcl_AppendResult(interp, "extra option \"", argv[0],
488                     "\" (option with no value?)", (char *) NULL);
489             result = TCL_ERROR;
490             goto done;
491         }
492         length = strlen(argv[0]);
493         c = argv[0][1];
494         if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
495             if (Ck_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
496                 result = TCL_ERROR;
497                 goto done;
498             }
499         } else if ((c == 'b')
500                 && (strncmp(argv[0], "-bordermode", length) == 0)) {
501             c = argv[1][0];
502             length = strlen(argv[1]);
503             if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
504                     && (length >= 2)) {
505                 slavePtr->borderMode = BM_IGNORE;
506             } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
507                     && (length >= 2)) {
508                 slavePtr->borderMode = BM_INSIDE;
509             } else {
510                 Tcl_AppendResult(interp, "bad border mode \"", argv[1],
511                         "\": must be ignore or inside",
512                         (char *) NULL);
513                 result = TCL_ERROR;
514                 goto done;
515             }
516         } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
517             if (argv[1][0] == 0) {
518                 slavePtr->flags &= ~(CHILD_REL_HEIGHT|CHILD_HEIGHT);
519             } else {
520                 if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
521                         &slavePtr->height) != TCL_OK) {
522                     result = TCL_ERROR;
523                     goto done;
524                 }
525                 slavePtr->flags &= ~CHILD_REL_HEIGHT;
526                 slavePtr->flags |= CHILD_HEIGHT;
527             }
528         } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
529                 && (length >= 5)) {
530             if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
531                 result = TCL_ERROR;
532                 goto done;
533             }
534             slavePtr->relHeight = d;
535             slavePtr->flags |= CHILD_REL_HEIGHT;
536             slavePtr->flags &= ~CHILD_HEIGHT;
537         } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
538                 && (length >= 5)) {
539             if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
540                 result = TCL_ERROR;
541                 goto done;
542             }
543             slavePtr->relWidth = d;
544             slavePtr->flags |= CHILD_REL_WIDTH;
545             slavePtr->flags &= ~CHILD_WIDTH;
546         } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
547                 && (length >= 5)) {
548             if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
549                 result = TCL_ERROR;
550                 goto done;
551             }
552             slavePtr->relX = d;
553             slavePtr->flags |= CHILD_REL_X;
554         } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
555                 && (length >= 5)) {
556             if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
557                 result = TCL_ERROR;
558                 goto done;
559             }
560             slavePtr->relY = d;
561             slavePtr->flags |= CHILD_REL_Y;
562         } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
563             if (argv[1][0] == 0) {
564                 slavePtr->flags &= ~(CHILD_REL_WIDTH|CHILD_WIDTH);
565             } else {
566                 if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
567                         &slavePtr->width) != TCL_OK) {
568                     result = TCL_ERROR;
569                     goto done;
570                 }
571                 slavePtr->flags &= ~CHILD_REL_WIDTH;
572                 slavePtr->flags |= CHILD_WIDTH;
573             }
574         } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
575             if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
576                     &slavePtr->x) != TCL_OK) {
577                 result = TCL_ERROR;
578                 goto done;
579             }
580             slavePtr->flags &= ~CHILD_REL_X;
581         } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
582             if (Ck_GetCoord(interp, slavePtr->winPtr, argv[1],
583                     &slavePtr->y) != TCL_OK) {
584                 result = TCL_ERROR;
585                 goto done;
586             }
587             slavePtr->flags &= ~CHILD_REL_Y;
588         } else {
589             Tcl_AppendResult(interp, "unknown or ambiguous option \"",
590                     argv[0], "\": must be -anchor, -bordermode, -height, ",
591                     "-relheight, -relwidth, -relx, -rely, -width, ",
592                     "-x, or -y", (char *) NULL);
593             result = TCL_ERROR;
594             goto done;
595         }
596     }
597
598     /*
599      * Arrange for a placement recalculation in the master.
600      */
601
602 done:
603     masterPtr = slavePtr->masterPtr;
604     if (masterPtr == NULL) {
605         masterPtr = FindMaster((slavePtr->winPtr->flags & CK_TOPLEVEL) ?
606             NULL : slavePtr->winPtr->parentPtr);
607         slavePtr->masterPtr = masterPtr;
608         slavePtr->nextPtr = masterPtr->slavePtr;
609         masterPtr->slavePtr = slavePtr;
610     }
611     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
612         masterPtr->flags |= PARENT_RECONFIG_PENDING;
613         Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
614     }
615     return result;
616 }
617 \f
618 /*
619  *----------------------------------------------------------------------
620  *
621  * RecomputePlacement --
622  *
623  *      This procedure is called as a when-idle handler.  It recomputes
624  *      the geometries of all the slaves of a given master.
625  *
626  * Results:
627  *      None.
628  *
629  * Side effects:
630  *      Windows may change size or shape.
631  *
632  *----------------------------------------------------------------------
633  */
634
635 static void
636 RecomputePlacement(clientData)
637     ClientData clientData;      /* Pointer to Master record. */
638 {
639     Master *masterPtr = (Master *) clientData;
640     Slave *slavePtr;
641     CkWindow *ancestor, *realMaster;
642     int x, y, width, height;
643     int masterWidth, masterHeight, masterBW;
644
645     masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
646
647     /*
648      * Iterate over all the slaves for the master.  Each slave's
649      * geometry can be computed independently of the other slaves.
650      */
651
652     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
653             slavePtr = slavePtr->nextPtr) {
654         /*
655          * Step 1: compute size and borderwidth of master, taking into
656          * account desired border mode.
657          */
658
659         masterBW = 0;
660
661         /*
662          * Special case: masterPtr->winPtr == NULL, use entire screen !
663          */
664
665         if (masterPtr->winPtr == NULL) {
666             masterWidth = slavePtr->winPtr->mainPtr->maxWidth;
667             masterHeight = slavePtr->winPtr->mainPtr->maxHeight;
668         } else {
669             masterWidth = masterPtr->winPtr->width;
670             masterHeight = masterPtr->winPtr->height;
671             if (slavePtr->borderMode == BM_INSIDE) {
672                 masterBW = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
673             }
674             masterWidth -= 2*masterBW;
675             masterHeight -= 2*masterBW;
676         }
677
678         /*
679          * Step 2:  compute size of slave (outside dimensions including
680          * border) and location of anchor point within master.
681          */
682
683         x = slavePtr->x;
684         if (slavePtr->flags & CHILD_REL_X) {
685             x = (int) ((slavePtr->relX*masterWidth) +
686                        ((slavePtr->relX > 0) ? 0.5 : -0.5));
687         }
688         x += masterBW;
689         y = slavePtr->y;
690         if (slavePtr->flags & CHILD_REL_Y) {
691             y = (int) ((slavePtr->relY*masterHeight) +
692                        ((slavePtr->relY > 0) ? 0.5 : -0.5));
693         }
694         y += masterBW;
695         if (slavePtr->flags & CHILD_REL_WIDTH) {
696             width = (int) ((slavePtr->relWidth*masterWidth) + 0.5);
697         } else if (slavePtr->flags & CHILD_WIDTH) {
698             width = slavePtr->width;
699         } else {
700             width = slavePtr->winPtr->reqWidth;
701         }
702         if (slavePtr->flags & CHILD_REL_HEIGHT) {
703             height = (int) ((slavePtr->relHeight*masterHeight) + 0.5);
704         } else if (slavePtr->flags & CHILD_HEIGHT) {
705             height = slavePtr->height;
706         } else {
707             height = slavePtr->winPtr->reqHeight;
708         }
709
710         /*
711          * Step 3: adjust the x and y positions so that the desired
712          * anchor point on the slave appears at that position.  Also
713          * adjust for the border mode and master's border.
714          */
715
716         switch (slavePtr->anchor) {
717             case CK_ANCHOR_N:
718                 x -= width/2;
719                 break;
720             case CK_ANCHOR_NE:
721                 x -= width;
722                 break;
723             case CK_ANCHOR_E:
724                 x -= width;
725                 y -= height/2;
726                 break;
727             case CK_ANCHOR_SE:
728                 x -= width;
729                 y -= height;
730                 break;
731             case CK_ANCHOR_S:
732                 x -= width/2;
733                 y -= height;
734                 break;
735             case CK_ANCHOR_SW:
736                 y -= height;
737                 break;
738             case CK_ANCHOR_W:
739                 y -= height/2;
740                 break;
741             case CK_ANCHOR_NW:
742                 break;
743             case CK_ANCHOR_CENTER:
744                 x -= width/2;
745                 y -= height/2;
746                 break;
747         }
748
749         /*
750          * Step 4: if masterPtr isn't actually the master of slavePtr,
751          * then translate the x and y coordinates back into the coordinate
752          * system of masterPtr.
753          */
754
755         for (ancestor = masterPtr->winPtr,
756              realMaster = slavePtr->winPtr->parentPtr;
757              ancestor != NULL && ancestor != realMaster;
758              ancestor = ancestor->parentPtr) {
759             x += ancestor->x;
760             y += ancestor->y;
761         }
762
763         /*
764          * Step 5: adjust width and height again to reflect inside dimensions
765          * of window rather than outside.  Also make sure that the width and
766          * height aren't zero.
767          */
768
769         if (width <= 0) {
770             width = 1;
771         }
772         if (height <= 0) {
773             height = 1;
774         }
775
776         /*
777          * Step 6: see if the window's size or location has changed;  if
778          * so then resize and/or move it.
779          */
780
781         if (width != slavePtr->winPtr->width ||
782             height != slavePtr->winPtr->height)
783             Ck_ResizeWindow(slavePtr->winPtr, width, height);
784         if (x != slavePtr->winPtr->x ||
785             y != slavePtr->winPtr->y)
786             Ck_MoveWindow(slavePtr->winPtr, x, y);
787         /*
788          * Temporary kludge til Ck_MoveResizeWindow available !!!
789          */
790         if (width != slavePtr->winPtr->width ||
791             height != slavePtr->winPtr->height)
792             Ck_ResizeWindow(slavePtr->winPtr, width, height);
793
794         Ck_MapWindow(slavePtr->winPtr);
795     }
796 }
797 \f
798 /*
799  *----------------------------------------------------------------------
800  *
801  * MasterStructureProc --
802  *
803  *      This procedure is invoked by the event handler when
804  *      CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events occur for
805  *      a master window.
806  *
807  * Results:
808  *      None.
809  *
810  * Side effects:
811  *      Structures get cleaned up if the window was deleted.  If the
812  *      window was resized then slave geometries get recomputed.
813  *
814  *----------------------------------------------------------------------
815  */
816
817 static void
818 MasterStructureProc(clientData, eventPtr)
819     ClientData clientData;      /* Pointer to Master structure for window
820                                  * referred to by eventPtr. */
821     CkEvent *eventPtr;          /* Describes what just happened. */
822 {
823     Master *masterPtr = (Master *) clientData;
824     Slave *slavePtr, *nextPtr;
825
826     if (eventPtr->type == CK_EV_EXPOSE ||
827         eventPtr->type == CK_EV_MAP) {
828         if ((masterPtr->slavePtr != NULL)
829                 && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
830             masterPtr->flags |= PARENT_RECONFIG_PENDING;
831             Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
832         }
833     } else if (eventPtr->type == CK_EV_DESTROY) {
834         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
835                 slavePtr = nextPtr) {
836             slavePtr->masterPtr = NULL;
837             nextPtr = slavePtr->nextPtr;
838             slavePtr->nextPtr = NULL;
839         }
840         Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
841                 (char *) masterPtr->winPtr));
842         if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
843             Tk_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
844         }
845         masterPtr->winPtr = NULL;
846         ckfree((char *) masterPtr);
847     }
848 }
849 \f
850 /*
851  *----------------------------------------------------------------------
852  *
853  * SlaveStructureProc --
854  *
855  *      This procedure is invoked by the event handler when
856  *      CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events occur for a
857  *      slave window.
858  *
859  * Results:
860  *      None.
861  *
862  * Side effects:
863  *      Structures get cleaned up if the window was deleted.
864  *
865  *----------------------------------------------------------------------
866  */
867
868 static void
869 SlaveStructureProc(clientData, eventPtr)
870     ClientData clientData;      /* Pointer to Slave structure for window
871                                  * referred to by eventPtr. */
872     CkEvent *eventPtr;          /* Describes what just happened. */
873 {
874     Slave *slavePtr = (Slave *) clientData;
875
876     if (eventPtr->type == CK_EV_DESTROY) {
877         UnlinkSlave(slavePtr);
878         Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
879                 (char *) slavePtr->winPtr));
880         ckfree((char *) slavePtr);
881     }
882 }
883 \f
884 /*
885  *----------------------------------------------------------------------
886  *
887  * PlaceRequestProc --
888  *
889  *      This procedure is invoked whenever a slave managed by us
890  *      changes its requested geometry.
891  *
892  * Results:
893  *      None.
894  *
895  * Side effects:
896  *      The window will get relayed out, if its requested size has
897  *      anything to do with its actual size.
898  *
899  *----------------------------------------------------------------------
900  */
901
902 static void
903 PlaceRequestProc(clientData, winPtr)
904     ClientData clientData;              /* Pointer to our record for slave. */
905     CkWindow *winPtr;                   /* Window that changed its desired
906                                          * size. */
907 {
908     Slave *slavePtr = (Slave *) clientData;
909     Master *masterPtr;
910
911     if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
912             && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
913         return;
914     }
915     masterPtr = slavePtr->masterPtr;
916     if (masterPtr == NULL) {
917         return;
918     }
919     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
920         masterPtr->flags |= PARENT_RECONFIG_PENDING;
921         Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
922     }
923 }
924 \f
925 /*
926  *--------------------------------------------------------------
927  *
928  * PlaceLostSlaveProc --
929  *
930  *      This procedure is invoked whenever some other geometry
931  *      claims control over a slave that used to be managed by us.
932  *
933  * Results:
934  *      None.
935  *
936  * Side effects:
937  *      Forgets all placer-related information about the slave.
938  *
939  *--------------------------------------------------------------
940  */
941
942 static void
943 PlaceLostSlaveProc(clientData, winPtr)
944     ClientData clientData;      /* Slave structure for slave window that
945                                  * was stolen away. */
946     CkWindow *winPtr;           /* Slave window. */
947 {
948     register Slave *slavePtr = (Slave *) clientData;
949
950     if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
951         Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
952     }
953     Ck_UnmapWindow(winPtr);
954     UnlinkSlave(slavePtr);
955     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable, (char *) winPtr));
956     Ck_DeleteEventHandler(winPtr, CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
957         SlaveStructureProc, (ClientData) slavePtr);
958     ckfree((char *) slavePtr);
959 }