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