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