1 /*
2 * tkPlace.c --
3 *
4 * This file contains code to implement a simple geometry manager
5 * for Tk 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-1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tkPlace.c,v 1.13 2002/11/07 19:10:30 pspjuth Exp $
14 */
15
16 #include "tkPort.h"
17 #include "tkInt.h"
18
19
20 /*
21 * Border modes for relative placement:
22 *
23 * BM_INSIDE: relative distances computed using area inside
24 * all borders of master window.
25 * BM_OUTSIDE: relative distances computed using outside area
26 * that includes all borders of master.
27 * BM_IGNORE: border issues are ignored: place relative to
28 * master's actual window size.
29 */
30
31 static char *borderModeStrings[] = {
32 "inside", "outside", "ignore", (char *) NULL
33 };
34
35 typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode;
36
37 /*
38 * For each window whose geometry is managed by the placer there is
39 * a structure of the following type:
40 */
41
42 typedef struct Slave {
43 Tk_Window tkwin; /* Tk's token for window. */
44 Tk_Window inTkwin; /* Token for the -in window. */
45 struct Master *masterPtr; /* Pointer to information for window
46 * relative to which tkwin is placed.
47 * This isn't necessarily the logical
48 * parent of tkwin. NULL means the
49 * master was deleted or never assigned. */
50 struct Slave *nextPtr; /* Next in list of windows placed relative
51 * to same master (NULL for end of list). */
52 /*
53 * Geometry information for window; where there are both relative
54 * and absolute values for the same attribute (e.g. x and relX) only
55 * one of them is actually used, depending on flags.
56 */
57
58 int x, y; /* X and Y pixel coordinates for tkwin. */
59 Tcl_Obj *xPtr, *yPtr; /* Tcl_Obj rep's of x, y coords, to keep
60 * pixel spec. information */
61 double relX, relY; /* X and Y coordinates relative to size of
62 * master. */
63 int width, height; /* Absolute dimensions for tkwin. */
64 Tcl_Obj *widthPtr; /* Tcl_Obj rep of width, to keep pixel spec */
65 Tcl_Obj *heightPtr; /* Tcl_Obj rep of height, to keep pixel spec */
66 double relWidth, relHeight; /* Dimensions for tkwin relative to size of
67 * master. */
68 Tcl_Obj *relWidthPtr;
69 Tcl_Obj *relHeightPtr;
70 Tk_Anchor anchor; /* Which point on tkwin is placed at the
71 * given position. */
72 BorderMode borderMode; /* How to treat borders of master window. */
73 int flags; /* Various flags; see below for bit
74 * definitions. */
75 } Slave;
76
77 /*
78 * Type masks for options:
79 */
80 #define IN_MASK 1
81
82 static Tk_OptionSpec optionSpecs[] = {
83 {TK_OPTION_ANCHOR, "-anchor", NULL, NULL, "nw", -1,
84 Tk_Offset(Slave, anchor), 0, 0, 0},
85 {TK_OPTION_STRING_TABLE, "-bordermode", NULL, NULL, "inside", -1,
86 Tk_Offset(Slave, borderMode), 0, (ClientData) borderModeStrings, 0},
87 {TK_OPTION_PIXELS, "-height", NULL, NULL, "", Tk_Offset(Slave, heightPtr),
88 Tk_Offset(Slave, height), TK_OPTION_NULL_OK, 0, 0},
89 {TK_OPTION_WINDOW, "-in", NULL, NULL, "", -1, Tk_Offset(Slave, inTkwin),
90 0, 0, IN_MASK},
91 {TK_OPTION_DOUBLE, "-relheight", NULL, NULL, "",
92 Tk_Offset(Slave, relHeightPtr), Tk_Offset(Slave, relHeight),
93 TK_OPTION_NULL_OK, 0, 0},
94 {TK_OPTION_DOUBLE, "-relwidth", NULL, NULL, "",
95 Tk_Offset(Slave, relWidthPtr), Tk_Offset(Slave, relWidth),
96 TK_OPTION_NULL_OK, 0, 0},
97 {TK_OPTION_DOUBLE, "-relx", NULL, NULL, "0", -1,
98 Tk_Offset(Slave, relX), 0, 0, 0},
99 {TK_OPTION_DOUBLE, "-rely", NULL, NULL, "0", -1,
100 Tk_Offset(Slave, relY), 0, 0, 0},
101 {TK_OPTION_PIXELS, "-width", NULL, NULL, "", Tk_Offset(Slave, widthPtr),
102 Tk_Offset(Slave, width), TK_OPTION_NULL_OK, 0, 0},
103 {TK_OPTION_PIXELS, "-x", NULL, NULL, "0", Tk_Offset(Slave, xPtr),
104 Tk_Offset(Slave, x), TK_OPTION_NULL_OK, 0, 0},
105 {TK_OPTION_PIXELS, "-y", NULL, NULL, "0", Tk_Offset(Slave, yPtr),
106 Tk_Offset(Slave, y), TK_OPTION_NULL_OK, 0, 0},
107 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
108 (char *) NULL, 0, -1, 0, 0, 0}
109 };
110
111 /*
112 * Flag definitions for Slave structures:
113 *
114 * CHILD_WIDTH - 1 means -width was specified;
115 * CHILD_REL_WIDTH - 1 means -relwidth was specified.
116 * CHILD_HEIGHT - 1 means -height was specified;
117 * CHILD_REL_HEIGHT - 1 means -relheight was specified.
118 */
119
120 #define CHILD_WIDTH 1
121 #define CHILD_REL_WIDTH 2
122 #define CHILD_HEIGHT 4
123 #define CHILD_REL_HEIGHT 8
124
125 /*
126 * For each master window that has a slave managed by the placer there
127 * is a structure of the following form:
128 */
129
130 typedef struct Master {
131 Tk_Window tkwin; /* Tk's token for master window. */
132 struct Slave *slavePtr; /* First in linked list of slaves
133 * placed relative to this master. */
134 int flags; /* See below for bit definitions. */
135 } Master;
136
137 /*
138 * Flag definitions for masters:
139 *
140 * PARENT_RECONFIG_PENDING - 1 means that a call to RecomputePlacement
141 * is already pending via a Do_When_Idle handler.
142 */
143
144 #define PARENT_RECONFIG_PENDING 1
145
146 /*
147 * The following structure is the official type record for the
148 * placer:
149 */
150
151 static void PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
152 Tk_Window tkwin));
153 static void PlaceLostSlaveProc _ANSI_ARGS_((ClientData clientData,
154 Tk_Window tkwin));
155
156 static Tk_GeomMgr placerType = {
157 "place", /* name */
158 PlaceRequestProc, /* requestProc */
159 PlaceLostSlaveProc, /* lostSlaveProc */
160 };
161
162 /*
163 * Forward declarations for procedures defined later in this file:
164 */
165
166 static void SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
167 XEvent *eventPtr));
168 static int ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
169 Tk_Window tkwin, Tk_OptionTable table,
170 int objc, Tcl_Obj *CONST objv[]));
171 static int PlaceInfoCommand _ANSI_ARGS_((Tcl_Interp *interp,
172 Tk_Window tkwin));
173 static Slave * CreateSlave _ANSI_ARGS_((Tk_Window tkwin));
174 static Slave * FindSlave _ANSI_ARGS_((Tk_Window tkwin));
175 static Master * CreateMaster _ANSI_ARGS_((Tk_Window tkwin));
176 static Master * FindMaster _ANSI_ARGS_((Tk_Window tkwin));
177 static void MasterStructureProc _ANSI_ARGS_((ClientData clientData,
178 XEvent *eventPtr));
179 static void RecomputePlacement _ANSI_ARGS_((ClientData clientData));
180 static void UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
181
182 /*
183 *--------------------------------------------------------------
184 *
185 * Tk_PlaceObjCmd --
186 *
187 * This procedure is invoked to process the "place" Tcl
188 * commands. See the user documentation for details on
189 * what it does.
190 *
191 * Results:
192 * A standard Tcl result.
193 *
194 * Side effects:
195 * See the user documentation.
196 *
197 *--------------------------------------------------------------
198 */
199
200 int
Tk_PlaceObjCmd(clientData,interp,objc,objv)201 Tk_PlaceObjCmd(clientData, interp, objc, objv)
202 ClientData clientData; /* NULL. */
203 Tcl_Interp *interp; /* Current interpreter. */
204 int objc; /* Number of arguments. */
205 Tcl_Obj *CONST objv[]; /* Argument objects. */
206 {
207 Tk_Window tkwin;
208 Slave *slavePtr;
209 char *string;
210 TkDisplay *dispPtr;
211 Tk_OptionTable optionTable;
212 static CONST char *optionStrings[] = {
213 "configure", "forget", "info", "slaves", (char *) NULL
214 };
215 enum options { PLACE_CONFIGURE, PLACE_FORGET, PLACE_INFO, PLACE_SLAVES };
216 int index;
217
218
219 if (objc < 3) {
220 Tcl_WrongNumArgs(interp, 1, objv, "option|pathName args");
221 return TCL_ERROR;
222 }
223
224 /*
225 * Create the option table for this widget class. If it has already
226 * been created, the cached pointer will be returned.
227 */
228
229 optionTable = Tk_CreateOptionTable(interp, optionSpecs);
230
231 /*
232 * Handle special shortcut where window name is first argument.
233 */
234
235 string = Tcl_GetString(objv[1]);
236 if (string[0] == '.') {
237 tkwin = Tk_NameToWindow(interp, string, Tk_MainWindow(interp));
238 if (tkwin == NULL) {
239 return TCL_ERROR;
240 }
241
242 /*
243 * Initialize, if that hasn't been done yet.
244 */
245
246 dispPtr = ((TkWindow *) tkwin)->dispPtr;
247 if (!dispPtr->placeInit) {
248 Tcl_InitHashTable(&dispPtr->masterTable, TCL_ONE_WORD_KEYS);
249 Tcl_InitHashTable(&dispPtr->slaveTable, TCL_ONE_WORD_KEYS);
250 dispPtr->placeInit = 1;
251 }
252
253 return ConfigureSlave(interp, tkwin, optionTable, objc-2, objv+2);
254 }
255
256 /*
257 * Handle more general case of option followed by window name followed
258 * by possible additional arguments.
259 */
260
261 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]),
262 Tk_MainWindow(interp));
263 if (tkwin == NULL) {
264 return TCL_ERROR;
265 }
266
267 /*
268 * Initialize, if that hasn't been done yet.
269 */
270
271 dispPtr = ((TkWindow *) tkwin)->dispPtr;
272 if (!dispPtr->placeInit) {
273 Tcl_InitHashTable(&dispPtr->masterTable, TCL_ONE_WORD_KEYS);
274 Tcl_InitHashTable(&dispPtr->slaveTable, TCL_ONE_WORD_KEYS);
275 dispPtr->placeInit = 1;
276 }
277
278 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
279 &index) != TCL_OK) {
280 return TCL_ERROR;
281 }
282
283 switch ((enum options) index) {
284 case PLACE_CONFIGURE: {
285 Tcl_Obj *objPtr;
286 if (objc == 3 || objc == 4) {
287 slavePtr = FindSlave(tkwin);
288 if (slavePtr == NULL) {
289 return TCL_OK;
290 }
291 objPtr = Tk_GetOptionInfo(interp, (char *) slavePtr,
292 optionTable,
293 (objc == 4) ? objv[3] : (Tcl_Obj *) NULL, tkwin);
294 if (objPtr == NULL) {
295 return TCL_ERROR;
296 } else {
297 Tcl_SetObjResult(interp, objPtr);
298 return TCL_OK;
299 }
300 } else {
301 return ConfigureSlave(interp, tkwin, optionTable, objc-3,
302 objv+3);
303 }
304 }
305
306 case PLACE_FORGET: {
307 if (objc != 3) {
308 Tcl_WrongNumArgs(interp, 2, objv, "pathName");
309 return TCL_ERROR;
310 }
311 slavePtr = FindSlave(tkwin);
312 if (slavePtr == NULL) {
313 return TCL_OK;
314 }
315 if ((slavePtr->masterPtr != NULL) &&
316 (slavePtr->masterPtr->tkwin !=
317 Tk_Parent(slavePtr->tkwin))) {
318 Tk_UnmaintainGeometry(slavePtr->tkwin,
319 slavePtr->masterPtr->tkwin);
320 }
321 UnlinkSlave(slavePtr);
322 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->slaveTable,
323 (char *) tkwin));
324 Tk_DeleteEventHandler(tkwin, StructureNotifyMask,
325 SlaveStructureProc, (ClientData) slavePtr);
326 Tk_ManageGeometry(tkwin, (Tk_GeomMgr *) NULL, (ClientData) NULL);
327 Tk_UnmapWindow(tkwin);
328 ckfree((char *) slavePtr);
329 break;
330 }
331
332 case PLACE_INFO: {
333 if (objc != 3) {
334 Tcl_WrongNumArgs(interp, 2, objv, "pathName");
335 return TCL_ERROR;
336 }
337 return PlaceInfoCommand(interp, tkwin);
338 }
339
340 case PLACE_SLAVES: {
341 Master *masterPtr;
342 Tcl_Obj *listPtr;
343 if (objc != 3) {
344 Tcl_WrongNumArgs(interp, 2, objv, "pathName");
345 return TCL_ERROR;
346 }
347 masterPtr = FindMaster(tkwin);
348 if (masterPtr != NULL) {
349 listPtr = Tcl_NewObj();
350 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
351 slavePtr = slavePtr->nextPtr) {
352 /*Tcl_ListObjAppendElement(interp, listPtr,
353 Tcl_NewStringObj(Tk_PathName(slavePtr->tkwin),-1));*/
354 Tcl_ListObjAppendElement(interp, listPtr,
355 LangWidgetObj(interp,slavePtr->tkwin));
356 }
357 Tcl_SetObjResult(interp, listPtr);
358 }
359 break;
360 }
361 }
362
363 return TCL_OK;
364 }
365
366 /*
367 *----------------------------------------------------------------------
368 *
369 * CreateSlave --
370 *
371 * Given a Tk_Window token, find the Slave structure corresponding
372 * to that token, creating a new one if necessary.
373 *
374 * Results:
375 * Pointer to the Slave structure.
376 *
377 * Side effects:
378 * A new Slave structure may be created.
379 *
380 *----------------------------------------------------------------------
381 */
382
383 static Slave *
CreateSlave(tkwin)384 CreateSlave(tkwin)
385 Tk_Window tkwin; /* Token for desired slave. */
386 {
387 Tcl_HashEntry *hPtr;
388 register Slave *slavePtr;
389 int new;
390 TkDisplay * dispPtr = ((TkWindow *) tkwin)->dispPtr;
391
392 hPtr = Tcl_CreateHashEntry(&dispPtr->slaveTable, (char *) tkwin, &new);
393 if (new) {
394 slavePtr = (Slave *) ckalloc(sizeof(Slave));
395 memset(slavePtr, 0, sizeof(Slave));
396 slavePtr->tkwin = tkwin;
397 slavePtr->inTkwin = None;
398 slavePtr->anchor = TK_ANCHOR_NW;
399 slavePtr->borderMode = BM_INSIDE;
400 Tcl_SetHashValue(hPtr, slavePtr);
401 Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
402 (ClientData) slavePtr);
403 Tk_ManageGeometry(tkwin, &placerType, (ClientData) slavePtr);
404 } else {
405 slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
406 }
407 return slavePtr;
408 }
409
410 /*
411 *----------------------------------------------------------------------
412 *
413 * FindSlave --
414 *
415 * Given a Tk_Window token, find the Slave structure corresponding
416 * to that token. This is purely a lookup function; it will not
417 * create a record if one does not yet exist.
418 *
419 * Results:
420 * Pointer to Slave structure; NULL if none exists.
421 *
422 * Side effects:
423 * None.
424 *
425 *----------------------------------------------------------------------
426 */
427
428 static Slave *
FindSlave(tkwin)429 FindSlave(tkwin)
430 Tk_Window tkwin; /* Token for desired slave. */
431 {
432 Tcl_HashEntry *hPtr;
433 register Slave *slavePtr;
434 TkDisplay * dispPtr = ((TkWindow *) tkwin)->dispPtr;
435
436 hPtr = Tcl_FindHashEntry(&dispPtr->slaveTable, (char *) tkwin);
437 if (hPtr == NULL) {
438 return NULL;
439 }
440 slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
441 return slavePtr;
442 }
443
444 /*
445 *----------------------------------------------------------------------
446 *
447 * UnlinkSlave --
448 *
449 * This procedure removes a slave window from the chain of slaves
450 * in its master.
451 *
452 * Results:
453 * None.
454 *
455 * Side effects:
456 * The slave list of slavePtr's master changes.
457 *
458 *----------------------------------------------------------------------
459 */
460
461 static void
UnlinkSlave(slavePtr)462 UnlinkSlave(slavePtr)
463 Slave *slavePtr; /* Slave structure to be unlinked. */
464 {
465 register Master *masterPtr;
466 register Slave *prevPtr;
467
468 masterPtr = slavePtr->masterPtr;
469 if (masterPtr == NULL) {
470 return;
471 }
472 if (masterPtr->slavePtr == slavePtr) {
473 masterPtr->slavePtr = slavePtr->nextPtr;
474 } else {
475 for (prevPtr = masterPtr->slavePtr; ;
476 prevPtr = prevPtr->nextPtr) {
477 if (prevPtr == NULL) {
478 panic("UnlinkSlave couldn't find slave to unlink");
479 }
480 if (prevPtr->nextPtr == slavePtr) {
481 prevPtr->nextPtr = slavePtr->nextPtr;
482 break;
483 }
484 }
485 }
486 slavePtr->masterPtr = NULL;
487 }
488
489 /*
490 *----------------------------------------------------------------------
491 *
492 * CreateMaster --
493 *
494 * Given a Tk_Window token, find the Master structure corresponding
495 * to that token, creating a new one if necessary.
496 *
497 * Results:
498 * Pointer to the Master structure.
499 *
500 * Side effects:
501 * A new Master structure may be created.
502 *
503 *----------------------------------------------------------------------
504 */
505
506 static Master *
CreateMaster(tkwin)507 CreateMaster(tkwin)
508 Tk_Window tkwin; /* Token for desired master. */
509 {
510 Tcl_HashEntry *hPtr;
511 register Master *masterPtr;
512 int new;
513 TkDisplay * dispPtr = ((TkWindow *) tkwin)->dispPtr;
514
515 hPtr = Tcl_CreateHashEntry(&dispPtr->masterTable, (char *) tkwin, &new);
516 if (new) {
517 masterPtr = (Master *) ckalloc(sizeof(Master));
518 masterPtr->tkwin = tkwin;
519 masterPtr->slavePtr = NULL;
520 masterPtr->flags = 0;
521 Tcl_SetHashValue(hPtr, masterPtr);
522 Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask,
523 MasterStructureProc, (ClientData) masterPtr);
524 } else {
525 masterPtr = (Master *) Tcl_GetHashValue(hPtr);
526 }
527 return masterPtr;
528 }
529
530 /*
531 *----------------------------------------------------------------------
532 *
533 * FindMaster --
534 *
535 * Given a Tk_Window token, find the Master structure corresponding
536 * to that token. This is simply a lookup procedure; a new record
537 * will not be created if one does not already exist.
538 *
539 * Results:
540 * Pointer to the Master structure; NULL if one does not exist for
541 * the given Tk_Window token.
542 *
543 * Side effects:
544 * None.
545 *
546 *----------------------------------------------------------------------
547 */
548
549 static Master *
FindMaster(tkwin)550 FindMaster(tkwin)
551 Tk_Window tkwin; /* Token for desired master. */
552 {
553 Tcl_HashEntry *hPtr;
554 register Master *masterPtr;
555 TkDisplay * dispPtr = ((TkWindow *) tkwin)->dispPtr;
556
557 hPtr = Tcl_FindHashEntry(&dispPtr->masterTable, (char *) tkwin);
558 if (hPtr == NULL) {
559 return NULL;
560 }
561 masterPtr = (Master *) Tcl_GetHashValue(hPtr);
562 return masterPtr;
563 }
564
565 /*
566 *----------------------------------------------------------------------
567 *
568 * ConfigureSlave --
569 *
570 * This procedure is called to process an argv/argc list to
571 * reconfigure the placement of a window.
572 *
573 * Results:
574 * A standard Tcl result. If an error occurs then a message is
575 * left in the interp's result.
576 *
577 * Side effects:
578 * Information in slavePtr may change, and slavePtr's master is
579 * scheduled for reconfiguration.
580 *
581 *----------------------------------------------------------------------
582 */
583
584 static int
ConfigureSlave(interp,tkwin,table,objc,objv)585 ConfigureSlave(interp, tkwin, table, objc, objv)
586 Tcl_Interp *interp; /* Used for error reporting. */
587 Tk_Window tkwin; /* Token for the window to manipulate. */
588 Tk_OptionTable table; /* Token for option table. */
589 int objc; /* Number of config arguments. */
590 Tcl_Obj *CONST objv[]; /* Object values for arguments. */
591 {
592 register Master *masterPtr;
593 Tk_SavedOptions savedOptions;
594 int mask;
595 int result = TCL_OK;
596 Slave *slavePtr;
597
598 if (Tk_TopWinHierarchy(tkwin)) {
599 Tcl_AppendResult(interp, "can't use placer on top-level window \"",
600 Tk_PathName(tkwin), "\"; use wm command instead",
601 (char *) NULL);
602 return TCL_ERROR;
603 }
604
605 slavePtr = CreateSlave(tkwin);
606
607 if (Tk_SetOptions(interp, (char *)slavePtr, table, objc, objv,
608 slavePtr->tkwin, &savedOptions, &mask) != TCL_OK) {
609 Tk_RestoreSavedOptions(&savedOptions);
610 result = TCL_ERROR;
611 goto done;
612 }
613
614 if (mask & IN_MASK) {
615 /* -in changed */
616 Tk_Window tkwin;
617 Tk_Window ancestor;
618
619 tkwin = slavePtr->inTkwin;
620
621 /*
622 * Make sure that the new master is either the logical parent
623 * of the slave or a descendant of that window, and that the
624 * master and slave aren't the same.
625 */
626
627 for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) {
628 if (ancestor == Tk_Parent(slavePtr->tkwin)) {
629 break;
630 }
631 if (Tk_TopWinHierarchy(ancestor)) {
632 Tcl_AppendResult(interp, "can't place ",
633 Tk_PathName(slavePtr->tkwin), " relative to ",
634 Tk_PathName(tkwin), (char *) NULL);
635 result = TCL_ERROR;
636 Tk_RestoreSavedOptions(&savedOptions);
637 goto done;
638 }
639 }
640 if (slavePtr->tkwin == tkwin) {
641 Tcl_AppendResult(interp, "can't place ",
642 Tk_PathName(slavePtr->tkwin), " relative to itself",
643 (char *) NULL);
644 result = TCL_ERROR;
645 Tk_RestoreSavedOptions(&savedOptions);
646 goto done;
647 }
648 if ((slavePtr->masterPtr != NULL)
649 && (slavePtr->masterPtr->tkwin == tkwin)) {
650 /*
651 * Re-using same old master. Nothing to do.
652 */
653 } else {
654 if ((slavePtr->masterPtr != NULL)
655 && (slavePtr->masterPtr->tkwin
656 != Tk_Parent(slavePtr->tkwin))) {
657 Tk_UnmaintainGeometry(slavePtr->tkwin,
658 slavePtr->masterPtr->tkwin);
659 }
660 UnlinkSlave(slavePtr);
661 slavePtr->masterPtr = CreateMaster(tkwin);
662 slavePtr->nextPtr = slavePtr->masterPtr->slavePtr;
663 slavePtr->masterPtr->slavePtr = slavePtr;
664 }
665 }
666
667 /*
668 * Set slave flags. First clear the field, then add bits as needed.
669 */
670
671 slavePtr->flags = 0;
672 if (slavePtr->heightPtr) {
673 slavePtr->flags |= CHILD_HEIGHT;
674 }
675
676 if (slavePtr->relHeightPtr) {
677 slavePtr->flags |= CHILD_REL_HEIGHT;
678 }
679
680 if (slavePtr->relWidthPtr) {
681 slavePtr->flags |= CHILD_REL_WIDTH;
682 }
683
684 if (slavePtr->widthPtr) {
685 slavePtr->flags |= CHILD_WIDTH;
686 }
687
688 /*
689 * If there's no master specified for this slave, use its Tk_Parent.
690 * Then arrange for a placement recalculation in the master.
691 */
692
693 Tk_FreeSavedOptions(&savedOptions);
694 done:
695 masterPtr = slavePtr->masterPtr;
696 if (masterPtr == NULL) {
697 masterPtr = CreateMaster(Tk_Parent(slavePtr->tkwin));
698 slavePtr->masterPtr = masterPtr;
699 slavePtr->nextPtr = masterPtr->slavePtr;
700 masterPtr->slavePtr = slavePtr;
701 }
702 slavePtr->inTkwin = masterPtr->tkwin;
703 if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
704 masterPtr->flags |= PARENT_RECONFIG_PENDING;
705 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
706 }
707 return result;
708 }
709
710 /*
711 *----------------------------------------------------------------------
712 *
713 * PlaceInfoCommand --
714 *
715 * Implementation of the [place info] subcommand. See the user
716 * documentation for information on what it does.
717 *
718 * Results:
719 * Standard Tcl result.
720 *
721 * Side effects:
722 * If the given tkwin is managed by the placer, this function will
723 * put information about that placement in the interp's result.
724 *
725 *----------------------------------------------------------------------
726 */
727
728 static int
PlaceInfoCommand(interp,tkwin)729 PlaceInfoCommand(interp, tkwin)
730 Tcl_Interp *interp; /* Interp into which to place result. */
731 Tk_Window tkwin; /* Token for the window to get info on. */
732 {
733 char buffer[32 + TCL_INTEGER_SPACE];
734 Slave *slavePtr;
735
736 #if 0 /* FIXME */
737 slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
738 Tcl_AppendElement(interp,"-x");
739 Tcl_IntResults(interp,1,1, slavePtr->x);
740 Tcl_AppendElement(interp,"-relx");
741 Tcl_IntResults(interp,1,1, slavePtr->relX);
742 Tcl_AppendElement(interp,"-y");
743 Tcl_IntResults(interp,1,1, slavePtr->y);
744 Tcl_AppendElement(interp,"-rely");
745 Tcl_IntResults(interp,1,1, slavePtr->relY);
746 if (slavePtr->flags & CHILD_WIDTH) {
747 Tcl_AppendElement(interp,"-width");
748 Tcl_IntResults(interp,1,1, slavePtr->width);
749 } else {
750 Tcl_AppendElement(interp,"-width");
751 Tcl_AppendElement(interp,"");
752 }
753 if (slavePtr->flags & CHILD_REL_WIDTH) {
754 Tcl_AppendElement(interp,"-relwidth");
755 Tcl_IntResults(interp,1,1, slavePtr->relWidth);
756 } else {
757 Tcl_AppendElement(interp,"-relwidth");
758 Tcl_AppendElement(interp,"");
759 }
760 if (slavePtr->flags & CHILD_HEIGHT) {
761 Tcl_AppendElement(interp,"-height");
762 Tcl_IntResults(interp,1,1, slavePtr->height);
763 } else {
764 Tcl_AppendElement(interp,"-height");
765 Tcl_AppendElement(interp,"");
766 #endif
767
768 slavePtr = FindSlave(tkwin);
769 if (slavePtr == NULL) {
770 return TCL_OK;
771 }
772 if (slavePtr->masterPtr != NULL) {
773 Tcl_AppendElement(interp, "-in");
774 Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
775 }
776 sprintf(buffer, " -x %d", slavePtr->x);
777 Tcl_AppendResult(interp, buffer, (char *) NULL);
778 sprintf(buffer, " -relx %.4g", slavePtr->relX);
779 Tcl_AppendResult(interp, buffer, (char *) NULL);
780 sprintf(buffer, " -y %d", slavePtr->y);
781 Tcl_AppendResult(interp, buffer, (char *) NULL);
782 sprintf(buffer, " -rely %.4g", slavePtr->relY);
783 Tcl_AppendResult(interp, buffer, (char *) NULL);
784 if (slavePtr->flags & CHILD_WIDTH) {
785 sprintf(buffer, " -width %d", slavePtr->width);
786 Tcl_AppendResult(interp, buffer, (char *) NULL);
787 } else {
788 Tcl_AppendResult(interp, " -width {}", (char *) NULL);
789 }
790 if (slavePtr->flags & CHILD_REL_WIDTH) {
791 sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
792 Tcl_AppendResult(interp, buffer, (char *) NULL);
793 } else {
794 Tcl_AppendResult(interp, " -relwidth {}", (char *) NULL);
795 }
796 if (slavePtr->flags & CHILD_HEIGHT) {
797 sprintf(buffer, " -height %d", slavePtr->height);
798 Tcl_AppendResult(interp, buffer, (char *) NULL);
799 } else {
800 Tcl_AppendResult(interp, " -height {}", (char *) NULL);
801 }
802 if (slavePtr->flags & CHILD_REL_HEIGHT) {
803 sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
804 Tcl_AppendResult(interp, buffer, (char *) NULL);
805 } else {
806 Tcl_AppendResult(interp, " -relheight {}", (char *) NULL);
807 }
808
809 Tcl_AppendElement(interp, "-anchor");
810 Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor));
811 Tcl_AppendElement(interp, "-bordermode");
812 Tcl_AppendElement(interp, borderModeStrings[slavePtr->borderMode]);
813 return TCL_OK;
814 }
815
816 /*
817 *----------------------------------------------------------------------
818 *
819 * RecomputePlacement --
820 *
821 * This procedure is called as a when-idle handler. It recomputes
822 * the geometries of all the slaves of a given master.
823 *
824 * Results:
825 * None.
826 *
827 * Side effects:
828 * Windows may change size or shape.
829 *
830 *----------------------------------------------------------------------
831 */
832
833 static void
RecomputePlacement(clientData)834 RecomputePlacement(clientData)
835 ClientData clientData; /* Pointer to Master record. */
836 {
837 register Master *masterPtr = (Master *) clientData;
838 register Slave *slavePtr;
839 int x, y, width, height, tmp;
840 int masterWidth, masterHeight, masterX, masterY;
841 double x1, y1, x2, y2;
842
843 masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
844
845 /*
846 * Iterate over all the slaves for the master. Each slave's
847 * geometry can be computed independently of the other slaves.
848 */
849
850 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
851 slavePtr = slavePtr->nextPtr) {
852 /*
853 * Step 1: compute size and borderwidth of master, taking into
854 * account desired border mode.
855 */
856
857 masterX = masterY = 0;
858 masterWidth = Tk_Width(masterPtr->tkwin);
859 masterHeight = Tk_Height(masterPtr->tkwin);
860 if (slavePtr->borderMode == BM_INSIDE) {
861 masterX = Tk_InternalBorderLeft(masterPtr->tkwin);
862 masterY = Tk_InternalBorderTop(masterPtr->tkwin);
863 masterWidth -= masterX + Tk_InternalBorderRight(masterPtr->tkwin);
864 masterHeight -= masterY +
865 Tk_InternalBorderBottom(masterPtr->tkwin);
866 } else if (slavePtr->borderMode == BM_OUTSIDE) {
867 masterX = masterY = -Tk_Changes(masterPtr->tkwin)->border_width;
868 masterWidth -= 2 * masterX;
869 masterHeight -= 2 * masterY;
870 }
871
872 /*
873 * Step 2: compute size of slave (outside dimensions including
874 * border) and location of anchor point within master.
875 */
876
877 x1 = slavePtr->x + masterX + (slavePtr->relX*masterWidth);
878 x = (int) (x1 + ((x1 > 0) ? 0.5 : -0.5));
879 y1 = slavePtr->y + masterY + (slavePtr->relY*masterHeight);
880 y = (int) (y1 + ((y1 > 0) ? 0.5 : -0.5));
881 if (slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) {
882 width = 0;
883 if (slavePtr->flags & CHILD_WIDTH) {
884 width += slavePtr->width;
885 }
886 if (slavePtr->flags & CHILD_REL_WIDTH) {
887 /*
888 * The code below is a bit tricky. In order to round
889 * correctly when both relX and relWidth are specified,
890 * compute the location of the right edge and round that,
891 * then compute width. If we compute the width and round
892 * it, rounding errors in relX and relWidth accumulate.
893 */
894
895 x2 = x1 + (slavePtr->relWidth*masterWidth);
896 tmp = (int) (x2 + ((x2 > 0) ? 0.5 : -0.5));
897 width += tmp - x;
898 }
899 } else {
900 width = Tk_ReqWidth(slavePtr->tkwin)
901 + 2*Tk_Changes(slavePtr->tkwin)->border_width;
902 }
903 if (slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) {
904 height = 0;
905 if (slavePtr->flags & CHILD_HEIGHT) {
906 height += slavePtr->height;
907 }
908 if (slavePtr->flags & CHILD_REL_HEIGHT) {
909 /*
910 * See note above for rounding errors in width computation.
911 */
912
913 y2 = y1 + (slavePtr->relHeight*masterHeight);
914 tmp = (int) (y2 + ((y2 > 0) ? 0.5 : -0.5));
915 height += tmp - y;
916 }
917 } else {
918 height = Tk_ReqHeight(slavePtr->tkwin)
919 + 2*Tk_Changes(slavePtr->tkwin)->border_width;
920 }
921
922 /*
923 * Step 3: adjust the x and y positions so that the desired
924 * anchor point on the slave appears at that position. Also
925 * adjust for the border mode and master's border.
926 */
927
928 switch (slavePtr->anchor) {
929 case TK_ANCHOR_N:
930 x -= width/2;
931 break;
932 case TK_ANCHOR_NE:
933 x -= width;
934 break;
935 case TK_ANCHOR_E:
936 x -= width;
937 y -= height/2;
938 break;
939 case TK_ANCHOR_SE:
940 x -= width;
941 y -= height;
942 break;
943 case TK_ANCHOR_S:
944 x -= width/2;
945 y -= height;
946 break;
947 case TK_ANCHOR_SW:
948 y -= height;
949 break;
950 case TK_ANCHOR_W:
951 y -= height/2;
952 break;
953 case TK_ANCHOR_NW:
954 break;
955 case TK_ANCHOR_CENTER:
956 x -= width/2;
957 y -= height/2;
958 break;
959 }
960
961 /*
962 * Step 4: adjust width and height again to reflect inside dimensions
963 * of window rather than outside. Also make sure that the width and
964 * height aren't zero.
965 */
966
967 width -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
968 height -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
969 if (width <= 0) {
970 width = 1;
971 }
972 if (height <= 0) {
973 height = 1;
974 }
975
976 /*
977 * Step 5: reconfigure the window and map it if needed. If the
978 * slave is a child of the master, we do this ourselves. If the
979 * slave isn't a child of the master, let Tk_MaintainGeometry do
980 * the work (it will re-adjust things as relevant windows map,
981 * unmap, and move).
982 */
983
984 if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
985 if ((x != Tk_X(slavePtr->tkwin))
986 || (y != Tk_Y(slavePtr->tkwin))
987 || (width != Tk_Width(slavePtr->tkwin))
988 || (height != Tk_Height(slavePtr->tkwin))) {
989 Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
990 }
991
992 /*
993 * Don't map the slave unless the master is mapped: the slave
994 * will get mapped later, when the master is mapped.
995 */
996
997 if (Tk_IsMapped(masterPtr->tkwin)) {
998 Tk_MapWindow(slavePtr->tkwin);
999 }
1000 } else {
1001 if ((width <= 0) || (height <= 0)) {
1002 Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
1003 Tk_UnmapWindow(slavePtr->tkwin);
1004 } else {
1005 Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
1006 x, y, width, height);
1007 }
1008 }
1009 }
1010 }
1011
1012 /*
1013 *----------------------------------------------------------------------
1014 *
1015 * MasterStructureProc --
1016 *
1017 * This procedure is invoked by the Tk event handler when
1018 * StructureNotify events occur for a master window.
1019 *
1020 * Results:
1021 * None.
1022 *
1023 * Side effects:
1024 * Structures get cleaned up if the window was deleted. If the
1025 * window was resized then slave geometries get recomputed.
1026 *
1027 *----------------------------------------------------------------------
1028 */
1029
1030 static void
MasterStructureProc(clientData,eventPtr)1031 MasterStructureProc(clientData, eventPtr)
1032 ClientData clientData; /* Pointer to Master structure for window
1033 * referred to by eventPtr. */
1034 XEvent *eventPtr; /* Describes what just happened. */
1035 {
1036 register Master *masterPtr = (Master *) clientData;
1037 register Slave *slavePtr, *nextPtr;
1038 TkDisplay *dispPtr = ((TkWindow *) masterPtr->tkwin)->dispPtr;
1039
1040 if (eventPtr->type == ConfigureNotify) {
1041 if ((masterPtr->slavePtr != NULL)
1042 && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
1043 masterPtr->flags |= PARENT_RECONFIG_PENDING;
1044 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
1045 }
1046 } else if (eventPtr->type == DestroyNotify) {
1047 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1048 slavePtr = nextPtr) {
1049 slavePtr->masterPtr = NULL;
1050 nextPtr = slavePtr->nextPtr;
1051 slavePtr->nextPtr = NULL;
1052 }
1053 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->masterTable,
1054 (char *) masterPtr->tkwin));
1055 if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
1056 Tcl_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
1057 }
1058 masterPtr->tkwin = NULL;
1059 ckfree((char *) masterPtr);
1060 } else if (eventPtr->type == MapNotify) {
1061 /*
1062 * When a master gets mapped, must redo the geometry computation
1063 * so that all of its slaves get remapped.
1064 */
1065
1066 if ((masterPtr->slavePtr != NULL)
1067 && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
1068 masterPtr->flags |= PARENT_RECONFIG_PENDING;
1069 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
1070 }
1071 } else if (eventPtr->type == UnmapNotify) {
1072 /*
1073 * Unmap all of the slaves when the master gets unmapped,
1074 * so that they don't keep redisplaying themselves.
1075 */
1076
1077 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1078 slavePtr = slavePtr->nextPtr) {
1079 Tk_UnmapWindow(slavePtr->tkwin);
1080 }
1081 }
1082 }
1083
1084 /*
1085 *----------------------------------------------------------------------
1086 *
1087 * SlaveStructureProc --
1088 *
1089 * This procedure is invoked by the Tk event handler when
1090 * StructureNotify events occur for a slave window.
1091 *
1092 * Results:
1093 * None.
1094 *
1095 * Side effects:
1096 * Structures get cleaned up if the window was deleted.
1097 *
1098 *----------------------------------------------------------------------
1099 */
1100
1101 static void
SlaveStructureProc(clientData,eventPtr)1102 SlaveStructureProc(clientData, eventPtr)
1103 ClientData clientData; /* Pointer to Slave structure for window
1104 * referred to by eventPtr. */
1105 XEvent *eventPtr; /* Describes what just happened. */
1106 {
1107 register Slave *slavePtr = (Slave *) clientData;
1108 TkDisplay * dispPtr = ((TkWindow *) slavePtr->tkwin)->dispPtr;
1109
1110 if (eventPtr->type == DestroyNotify) {
1111 UnlinkSlave(slavePtr);
1112 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->slaveTable,
1113 (char *) slavePtr->tkwin));
1114 ckfree((char *) slavePtr);
1115 }
1116 }
1117
1118 /*
1119 *----------------------------------------------------------------------
1120 *
1121 * PlaceRequestProc --
1122 *
1123 * This procedure is invoked by Tk whenever a slave managed by us
1124 * changes its requested geometry.
1125 *
1126 * Results:
1127 * None.
1128 *
1129 * Side effects:
1130 * The window will get relayed out, if its requested size has
1131 * anything to do with its actual size.
1132 *
1133 *----------------------------------------------------------------------
1134 */
1135
1136 /* ARGSUSED */
1137 static void
PlaceRequestProc(clientData,tkwin)1138 PlaceRequestProc(clientData, tkwin)
1139 ClientData clientData; /* Pointer to our record for slave. */
1140 Tk_Window tkwin; /* Window that changed its desired
1141 * size. */
1142 {
1143 Slave *slavePtr = (Slave *) clientData;
1144 Master *masterPtr;
1145
1146 if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
1147 && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
1148 return;
1149 }
1150 masterPtr = slavePtr->masterPtr;
1151 if (masterPtr == NULL) {
1152 return;
1153 }
1154 if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
1155 masterPtr->flags |= PARENT_RECONFIG_PENDING;
1156 Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
1157 }
1158 }
1159
1160 /*
1161 *--------------------------------------------------------------
1162 *
1163 * PlaceLostSlaveProc --
1164 *
1165 * This procedure is invoked by Tk whenever some other geometry
1166 * claims control over a slave that used to be managed by us.
1167 *
1168 * Results:
1169 * None.
1170 *
1171 * Side effects:
1172 * Forgets all placer-related information about the slave.
1173 *
1174 *--------------------------------------------------------------
1175 */
1176
1177 /* ARGSUSED */
1178 static void
PlaceLostSlaveProc(clientData,tkwin)1179 PlaceLostSlaveProc(clientData, tkwin)
1180 ClientData clientData; /* Slave structure for slave window that
1181 * was stolen away. */
1182 Tk_Window tkwin; /* Tk's handle for the slave window. */
1183 {
1184 register Slave *slavePtr = (Slave *) clientData;
1185 TkDisplay * dispPtr = ((TkWindow *) slavePtr->tkwin)->dispPtr;
1186
1187 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
1188 Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
1189 }
1190 Tk_UnmapWindow(tkwin);
1191 UnlinkSlave(slavePtr);
1192 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->slaveTable,
1193 (char *) tkwin));
1194 Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
1195 (ClientData) slavePtr);
1196 ckfree((char *) slavePtr);
1197 }
1198
1199