1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkGeometry.c -- |
5 |
* |
6 |
* This file contains generic Tk code for geometry management |
7 |
* (stuff that's used by all geometry managers). |
8 |
* |
9 |
* Copyright (c) 1990-1994 The Regents of the University of California. |
10 |
* Copyright (c) 1994-1995 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: tkgeometry.c,v 1.1.1.1 2001/06/13 05:01:19 dtashley Exp $ |
16 |
*/ |
17 |
|
18 |
#include "tkPort.h" |
19 |
#include "tkInt.h" |
20 |
|
21 |
/* |
22 |
* Data structures of the following type are used by Tk_MaintainGeometry. |
23 |
* For each slave managed by Tk_MaintainGeometry, there is one of these |
24 |
* structures associated with its master. |
25 |
*/ |
26 |
|
27 |
typedef struct MaintainSlave { |
28 |
Tk_Window slave; /* The slave window being positioned. */ |
29 |
Tk_Window master; /* The master that determines slave's |
30 |
* position; it must be a descendant of |
31 |
* slave's parent. */ |
32 |
int x, y; /* Desired position of slave relative to |
33 |
* master. */ |
34 |
int width, height; /* Desired dimensions of slave. */ |
35 |
struct MaintainSlave *nextPtr; |
36 |
/* Next in list of Maintains associated |
37 |
* with master. */ |
38 |
} MaintainSlave; |
39 |
|
40 |
/* |
41 |
* For each window that has been specified as a master to |
42 |
* Tk_MaintainGeometry, there is a structure of the following type: |
43 |
*/ |
44 |
|
45 |
typedef struct MaintainMaster { |
46 |
Tk_Window ancestor; /* The lowest ancestor of this window |
47 |
* for which we have *not* created a |
48 |
* StructureNotify handler. May be the |
49 |
* same as the window itself. */ |
50 |
int checkScheduled; /* Non-zero means that there is already a |
51 |
* call to MaintainCheckProc scheduled as |
52 |
* an idle handler. */ |
53 |
MaintainSlave *slavePtr; /* First in list of all slaves associated |
54 |
* with this master. */ |
55 |
} MaintainMaster; |
56 |
|
57 |
/* |
58 |
* Prototypes for static procedures in this file: |
59 |
*/ |
60 |
|
61 |
static void MaintainCheckProc _ANSI_ARGS_((ClientData clientData)); |
62 |
static void MaintainMasterProc _ANSI_ARGS_((ClientData clientData, |
63 |
XEvent *eventPtr)); |
64 |
static void MaintainSlaveProc _ANSI_ARGS_((ClientData clientData, |
65 |
XEvent *eventPtr)); |
66 |
|
67 |
/* |
68 |
*-------------------------------------------------------------- |
69 |
* |
70 |
* Tk_ManageGeometry -- |
71 |
* |
72 |
* Arrange for a particular procedure to manage the geometry |
73 |
* of a given slave window. |
74 |
* |
75 |
* Results: |
76 |
* None. |
77 |
* |
78 |
* Side effects: |
79 |
* Proc becomes the new geometry manager for tkwin, replacing |
80 |
* any previous geometry manager. The geometry manager will |
81 |
* be notified (by calling procedures in *mgrPtr) when interesting |
82 |
* things happen in the future. If there was an existing geometry |
83 |
* manager for tkwin different from the new one, it is notified |
84 |
* by calling its lostSlaveProc. |
85 |
* |
86 |
*-------------------------------------------------------------- |
87 |
*/ |
88 |
|
89 |
void |
90 |
Tk_ManageGeometry(tkwin, mgrPtr, clientData) |
91 |
Tk_Window tkwin; /* Window whose geometry is to |
92 |
* be managed by proc. */ |
93 |
Tk_GeomMgr *mgrPtr; /* Static structure describing the |
94 |
* geometry manager. This structure |
95 |
* must never go away. */ |
96 |
ClientData clientData; /* Arbitrary one-word argument to |
97 |
* pass to geometry manager procedures. */ |
98 |
{ |
99 |
register TkWindow *winPtr = (TkWindow *) tkwin; |
100 |
|
101 |
if ((winPtr->geomMgrPtr != NULL) && (mgrPtr != NULL) |
102 |
&& ((winPtr->geomMgrPtr != mgrPtr) |
103 |
|| (winPtr->geomData != clientData)) |
104 |
&& (winPtr->geomMgrPtr->lostSlaveProc != NULL)) { |
105 |
(*winPtr->geomMgrPtr->lostSlaveProc)(winPtr->geomData, tkwin); |
106 |
} |
107 |
|
108 |
winPtr->geomMgrPtr = mgrPtr; |
109 |
winPtr->geomData = clientData; |
110 |
} |
111 |
|
112 |
/* |
113 |
*-------------------------------------------------------------- |
114 |
* |
115 |
* Tk_GeometryRequest -- |
116 |
* |
117 |
* This procedure is invoked by widget code to indicate |
118 |
* its preferences about the size of a window it manages. |
119 |
* In general, widget code should call this procedure |
120 |
* rather than Tk_ResizeWindow. |
121 |
* |
122 |
* Results: |
123 |
* None. |
124 |
* |
125 |
* Side effects: |
126 |
* The geometry manager for tkwin (if any) is invoked to |
127 |
* handle the request. If possible, it will reconfigure |
128 |
* tkwin and/or other windows to satisfy the request. The |
129 |
* caller gets no indication of success or failure, but it |
130 |
* will get X events if the window size was actually |
131 |
* changed. |
132 |
* |
133 |
*-------------------------------------------------------------- |
134 |
*/ |
135 |
|
136 |
void |
137 |
Tk_GeometryRequest(tkwin, reqWidth, reqHeight) |
138 |
Tk_Window tkwin; /* Window that geometry information |
139 |
* pertains to. */ |
140 |
int reqWidth, reqHeight; /* Minimum desired dimensions for |
141 |
* window, in pixels. */ |
142 |
{ |
143 |
register TkWindow *winPtr = (TkWindow *) tkwin; |
144 |
|
145 |
/* |
146 |
* X gets very upset if a window requests a width or height of |
147 |
* zero, so rounds requested sizes up to at least 1. |
148 |
*/ |
149 |
|
150 |
if (reqWidth <= 0) { |
151 |
reqWidth = 1; |
152 |
} |
153 |
if (reqHeight <= 0) { |
154 |
reqHeight = 1; |
155 |
} |
156 |
if ((reqWidth == winPtr->reqWidth) && (reqHeight == winPtr->reqHeight)) { |
157 |
return; |
158 |
} |
159 |
winPtr->reqWidth = reqWidth; |
160 |
winPtr->reqHeight = reqHeight; |
161 |
if ((winPtr->geomMgrPtr != NULL) |
162 |
&& (winPtr->geomMgrPtr->requestProc != NULL)) { |
163 |
(*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, tkwin); |
164 |
} |
165 |
} |
166 |
|
167 |
/* |
168 |
*---------------------------------------------------------------------- |
169 |
* |
170 |
* Tk_SetInternalBorder -- |
171 |
* |
172 |
* Notify relevant geometry managers that a window has an internal |
173 |
* border of a given width and that child windows should not be |
174 |
* placed on that border. |
175 |
* |
176 |
* Results: |
177 |
* None. |
178 |
* |
179 |
* Side effects: |
180 |
* The border width is recorded for the window, and all geometry |
181 |
* managers of all children are notified so that can re-layout, if |
182 |
* necessary. |
183 |
* |
184 |
*---------------------------------------------------------------------- |
185 |
*/ |
186 |
|
187 |
void |
188 |
Tk_SetInternalBorder(tkwin, width) |
189 |
Tk_Window tkwin; /* Window that will have internal border. */ |
190 |
int width; /* Width of internal border, in pixels. */ |
191 |
{ |
192 |
register TkWindow *winPtr = (TkWindow *) tkwin; |
193 |
|
194 |
if (width == winPtr->internalBorderWidth) { |
195 |
return; |
196 |
} |
197 |
if (width < 0) { |
198 |
width = 0; |
199 |
} |
200 |
winPtr->internalBorderWidth = width; |
201 |
|
202 |
/* |
203 |
* All the slaves for which this is the master window must now be |
204 |
* repositioned to take account of the new internal border width. |
205 |
* To signal all the geometry managers to do this, just resize the |
206 |
* window to its current size. The ConfigureNotify event will |
207 |
* cause geometry managers to recompute everything. |
208 |
*/ |
209 |
|
210 |
Tk_ResizeWindow(tkwin, Tk_Width(tkwin), Tk_Height(tkwin)); |
211 |
} |
212 |
|
213 |
/* |
214 |
*---------------------------------------------------------------------- |
215 |
* |
216 |
* Tk_MaintainGeometry -- |
217 |
* |
218 |
* This procedure is invoked by geometry managers to handle slaves |
219 |
* whose master's are not their parents. It translates the desired |
220 |
* geometry for the slave into the coordinate system of the parent |
221 |
* and respositions the slave if it isn't already at the right place. |
222 |
* Furthermore, it sets up event handlers so that if the master (or |
223 |
* any of its ancestors up to the slave's parent) is mapped, unmapped, |
224 |
* or moved, then the slave will be adjusted to match. |
225 |
* |
226 |
* Results: |
227 |
* None. |
228 |
* |
229 |
* Side effects: |
230 |
* Event handlers are created and state is allocated to keep track |
231 |
* of slave. Note: if slave was already managed for master by |
232 |
* Tk_MaintainGeometry, then the previous information is replaced |
233 |
* with the new information. The caller must eventually call |
234 |
* Tk_UnmaintainGeometry to eliminate the correspondence (or, the |
235 |
* state is automatically freed when either window is destroyed). |
236 |
* |
237 |
*---------------------------------------------------------------------- |
238 |
*/ |
239 |
|
240 |
void |
241 |
Tk_MaintainGeometry(slave, master, x, y, width, height) |
242 |
Tk_Window slave; /* Slave for geometry management. */ |
243 |
Tk_Window master; /* Master for slave; must be a descendant |
244 |
* of slave's parent. */ |
245 |
int x, y; /* Desired position of slave within master. */ |
246 |
int width, height; /* Desired dimensions for slave. */ |
247 |
{ |
248 |
Tcl_HashEntry *hPtr; |
249 |
MaintainMaster *masterPtr; |
250 |
register MaintainSlave *slavePtr; |
251 |
int new, map; |
252 |
Tk_Window ancestor, parent; |
253 |
TkDisplay *dispPtr = ((TkWindow *) master)->dispPtr; |
254 |
|
255 |
if (!dispPtr->geomInit) { |
256 |
dispPtr->geomInit = 1; |
257 |
Tcl_InitHashTable(&dispPtr->maintainHashTable, TCL_ONE_WORD_KEYS); |
258 |
} |
259 |
|
260 |
/* |
261 |
* See if there is already a MaintainMaster structure for the master; |
262 |
* if not, then create one. |
263 |
*/ |
264 |
|
265 |
parent = Tk_Parent(slave); |
266 |
hPtr = Tcl_CreateHashEntry(&dispPtr->maintainHashTable, |
267 |
(char *) master, &new); |
268 |
if (!new) { |
269 |
masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr); |
270 |
} else { |
271 |
masterPtr = (MaintainMaster *) ckalloc(sizeof(MaintainMaster)); |
272 |
masterPtr->ancestor = master; |
273 |
masterPtr->checkScheduled = 0; |
274 |
masterPtr->slavePtr = NULL; |
275 |
Tcl_SetHashValue(hPtr, masterPtr); |
276 |
} |
277 |
|
278 |
/* |
279 |
* Create a MaintainSlave structure for the slave if there isn't |
280 |
* already one. |
281 |
*/ |
282 |
|
283 |
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; |
284 |
slavePtr = slavePtr->nextPtr) { |
285 |
if (slavePtr->slave == slave) { |
286 |
goto gotSlave; |
287 |
} |
288 |
} |
289 |
slavePtr = (MaintainSlave *) ckalloc(sizeof(MaintainSlave)); |
290 |
slavePtr->slave = slave; |
291 |
slavePtr->master = master; |
292 |
slavePtr->nextPtr = masterPtr->slavePtr; |
293 |
masterPtr->slavePtr = slavePtr; |
294 |
Tk_CreateEventHandler(slave, StructureNotifyMask, MaintainSlaveProc, |
295 |
(ClientData) slavePtr); |
296 |
|
297 |
/* |
298 |
* Make sure that there are event handlers registered for all |
299 |
* the windows between master and slave's parent (including master |
300 |
* but not slave's parent). There may already be handlers for master |
301 |
* and some of its ancestors (masterPtr->ancestor tells how many). |
302 |
*/ |
303 |
|
304 |
for (ancestor = master; ancestor != parent; |
305 |
ancestor = Tk_Parent(ancestor)) { |
306 |
if (ancestor == masterPtr->ancestor) { |
307 |
Tk_CreateEventHandler(ancestor, StructureNotifyMask, |
308 |
MaintainMasterProc, (ClientData) masterPtr); |
309 |
masterPtr->ancestor = Tk_Parent(ancestor); |
310 |
} |
311 |
} |
312 |
|
313 |
/* |
314 |
* Fill in up-to-date information in the structure, then update the |
315 |
* window if it's not currently in the right place or state. |
316 |
*/ |
317 |
|
318 |
gotSlave: |
319 |
slavePtr->x = x; |
320 |
slavePtr->y = y; |
321 |
slavePtr->width = width; |
322 |
slavePtr->height = height; |
323 |
map = 1; |
324 |
for (ancestor = slavePtr->master; ; ancestor = Tk_Parent(ancestor)) { |
325 |
if (!Tk_IsMapped(ancestor) && (ancestor != parent)) { |
326 |
map = 0; |
327 |
} |
328 |
if (ancestor == parent) { |
329 |
if ((x != Tk_X(slavePtr->slave)) |
330 |
|| (y != Tk_Y(slavePtr->slave)) |
331 |
|| (width != Tk_Width(slavePtr->slave)) |
332 |
|| (height != Tk_Height(slavePtr->slave))) { |
333 |
Tk_MoveResizeWindow(slavePtr->slave, x, y, width, height); |
334 |
} |
335 |
if (map) { |
336 |
Tk_MapWindow(slavePtr->slave); |
337 |
} else { |
338 |
Tk_UnmapWindow(slavePtr->slave); |
339 |
} |
340 |
break; |
341 |
} |
342 |
x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width; |
343 |
y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width; |
344 |
} |
345 |
} |
346 |
|
347 |
/* |
348 |
*---------------------------------------------------------------------- |
349 |
* |
350 |
* Tk_UnmaintainGeometry -- |
351 |
* |
352 |
* This procedure cancels a previous Tk_MaintainGeometry call, |
353 |
* so that the relationship between slave and master is no longer |
354 |
* maintained. |
355 |
* |
356 |
* Results: |
357 |
* None. |
358 |
* |
359 |
* Side effects: |
360 |
* The slave is unmapped and state is released, so that slave won't |
361 |
* track master any more. If we weren't previously managing slave |
362 |
* relative to master, then this procedure has no effect. |
363 |
* |
364 |
*---------------------------------------------------------------------- |
365 |
*/ |
366 |
|
367 |
void |
368 |
Tk_UnmaintainGeometry(slave, master) |
369 |
Tk_Window slave; /* Slave for geometry management. */ |
370 |
Tk_Window master; /* Master for slave; must be a descendant |
371 |
* of slave's parent. */ |
372 |
{ |
373 |
Tcl_HashEntry *hPtr; |
374 |
MaintainMaster *masterPtr; |
375 |
register MaintainSlave *slavePtr, *prevPtr; |
376 |
Tk_Window ancestor; |
377 |
TkDisplay *dispPtr = ((TkWindow *) slave)->dispPtr; |
378 |
|
379 |
if (!dispPtr->geomInit) { |
380 |
dispPtr->geomInit = 1; |
381 |
Tcl_InitHashTable(&dispPtr->maintainHashTable, TCL_ONE_WORD_KEYS); |
382 |
} |
383 |
|
384 |
if (!(((TkWindow *) slave)->flags & TK_ALREADY_DEAD)) { |
385 |
Tk_UnmapWindow(slave); |
386 |
} |
387 |
hPtr = Tcl_FindHashEntry(&dispPtr->maintainHashTable, (char *) master); |
388 |
if (hPtr == NULL) { |
389 |
return; |
390 |
} |
391 |
masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr); |
392 |
slavePtr = masterPtr->slavePtr; |
393 |
if (slavePtr->slave == slave) { |
394 |
masterPtr->slavePtr = slavePtr->nextPtr; |
395 |
} else { |
396 |
for (prevPtr = slavePtr, slavePtr = slavePtr->nextPtr; ; |
397 |
prevPtr = slavePtr, slavePtr = slavePtr->nextPtr) { |
398 |
if (slavePtr == NULL) { |
399 |
return; |
400 |
} |
401 |
if (slavePtr->slave == slave) { |
402 |
prevPtr->nextPtr = slavePtr->nextPtr; |
403 |
break; |
404 |
} |
405 |
} |
406 |
} |
407 |
Tk_DeleteEventHandler(slavePtr->slave, StructureNotifyMask, |
408 |
MaintainSlaveProc, (ClientData) slavePtr); |
409 |
ckfree((char *) slavePtr); |
410 |
if (masterPtr->slavePtr == NULL) { |
411 |
if (masterPtr->ancestor != NULL) { |
412 |
for (ancestor = master; ; ancestor = Tk_Parent(ancestor)) { |
413 |
Tk_DeleteEventHandler(ancestor, StructureNotifyMask, |
414 |
MaintainMasterProc, (ClientData) masterPtr); |
415 |
if (ancestor == masterPtr->ancestor) { |
416 |
break; |
417 |
} |
418 |
} |
419 |
} |
420 |
if (masterPtr->checkScheduled) { |
421 |
Tcl_CancelIdleCall(MaintainCheckProc, (ClientData) masterPtr); |
422 |
} |
423 |
Tcl_DeleteHashEntry(hPtr); |
424 |
ckfree((char *) masterPtr); |
425 |
} |
426 |
} |
427 |
|
428 |
/* |
429 |
*---------------------------------------------------------------------- |
430 |
* |
431 |
* MaintainMasterProc -- |
432 |
* |
433 |
* This procedure is invoked by the Tk event dispatcher in |
434 |
* response to StructureNotify events on the master or one |
435 |
* of its ancestors, on behalf of Tk_MaintainGeometry. |
436 |
* |
437 |
* Results: |
438 |
* None. |
439 |
* |
440 |
* Side effects: |
441 |
* It schedules a call to MaintainCheckProc, which will eventually |
442 |
* caused the postions and mapped states to be recalculated for all |
443 |
* the maintained slaves of the master. Or, if the master window is |
444 |
* being deleted then state is cleaned up. |
445 |
* |
446 |
*---------------------------------------------------------------------- |
447 |
*/ |
448 |
|
449 |
static void |
450 |
MaintainMasterProc(clientData, eventPtr) |
451 |
ClientData clientData; /* Pointer to MaintainMaster structure |
452 |
* for the master window. */ |
453 |
XEvent *eventPtr; /* Describes what just happened. */ |
454 |
{ |
455 |
MaintainMaster *masterPtr = (MaintainMaster *) clientData; |
456 |
MaintainSlave *slavePtr; |
457 |
int done; |
458 |
|
459 |
if ((eventPtr->type == ConfigureNotify) |
460 |
|| (eventPtr->type == MapNotify) |
461 |
|| (eventPtr->type == UnmapNotify)) { |
462 |
if (!masterPtr->checkScheduled) { |
463 |
masterPtr->checkScheduled = 1; |
464 |
Tcl_DoWhenIdle(MaintainCheckProc, (ClientData) masterPtr); |
465 |
} |
466 |
} else if (eventPtr->type == DestroyNotify) { |
467 |
/* |
468 |
* Delete all of the state associated with this master, but |
469 |
* be careful not to use masterPtr after the last slave is |
470 |
* deleted, since its memory will have been freed. |
471 |
*/ |
472 |
|
473 |
done = 0; |
474 |
do { |
475 |
slavePtr = masterPtr->slavePtr; |
476 |
if (slavePtr->nextPtr == NULL) { |
477 |
done = 1; |
478 |
} |
479 |
Tk_UnmaintainGeometry(slavePtr->slave, slavePtr->master); |
480 |
} while (!done); |
481 |
} |
482 |
} |
483 |
|
484 |
/* |
485 |
*---------------------------------------------------------------------- |
486 |
* |
487 |
* MaintainSlaveProc -- |
488 |
* |
489 |
* This procedure is invoked by the Tk event dispatcher in |
490 |
* response to StructureNotify events on a slave being managed |
491 |
* by Tk_MaintainGeometry. |
492 |
* |
493 |
* Results: |
494 |
* None. |
495 |
* |
496 |
* Side effects: |
497 |
* If the event is a DestroyNotify event then the Maintain state |
498 |
* and event handlers for this slave are deleted. |
499 |
* |
500 |
*---------------------------------------------------------------------- |
501 |
*/ |
502 |
|
503 |
static void |
504 |
MaintainSlaveProc(clientData, eventPtr) |
505 |
ClientData clientData; /* Pointer to MaintainSlave structure |
506 |
* for master-slave pair. */ |
507 |
XEvent *eventPtr; /* Describes what just happened. */ |
508 |
{ |
509 |
MaintainSlave *slavePtr = (MaintainSlave *) clientData; |
510 |
|
511 |
if (eventPtr->type == DestroyNotify) { |
512 |
Tk_UnmaintainGeometry(slavePtr->slave, slavePtr->master); |
513 |
} |
514 |
} |
515 |
|
516 |
/* |
517 |
*---------------------------------------------------------------------- |
518 |
* |
519 |
* MaintainCheckProc -- |
520 |
* |
521 |
* This procedure is invoked by the Tk event dispatcher as an |
522 |
* idle handler, when a master or one of its ancestors has been |
523 |
* reconfigured, mapped, or unmapped. Its job is to scan all of |
524 |
* the slaves for the master and reposition them, map them, or |
525 |
* unmap them as needed to maintain their geometry relative to |
526 |
* the master. |
527 |
* |
528 |
* Results: |
529 |
* None. |
530 |
* |
531 |
* Side effects: |
532 |
* Slaves can get repositioned, mapped, or unmapped. |
533 |
* |
534 |
*---------------------------------------------------------------------- |
535 |
*/ |
536 |
|
537 |
static void |
538 |
MaintainCheckProc(clientData) |
539 |
ClientData clientData; /* Pointer to MaintainMaster structure |
540 |
* for the master window. */ |
541 |
{ |
542 |
MaintainMaster *masterPtr = (MaintainMaster *) clientData; |
543 |
MaintainSlave *slavePtr; |
544 |
Tk_Window ancestor, parent; |
545 |
int x, y, map; |
546 |
|
547 |
masterPtr->checkScheduled = 0; |
548 |
for (slavePtr = masterPtr->slavePtr; slavePtr != NULL; |
549 |
slavePtr = slavePtr->nextPtr) { |
550 |
parent = Tk_Parent(slavePtr->slave); |
551 |
x = slavePtr->x; |
552 |
y = slavePtr->y; |
553 |
map = 1; |
554 |
for (ancestor = slavePtr->master; ; ancestor = Tk_Parent(ancestor)) { |
555 |
if (!Tk_IsMapped(ancestor) && (ancestor != parent)) { |
556 |
map = 0; |
557 |
} |
558 |
if (ancestor == parent) { |
559 |
if ((x != Tk_X(slavePtr->slave)) |
560 |
|| (y != Tk_Y(slavePtr->slave))) { |
561 |
Tk_MoveWindow(slavePtr->slave, x, y); |
562 |
} |
563 |
if (map) { |
564 |
Tk_MapWindow(slavePtr->slave); |
565 |
} else { |
566 |
Tk_UnmapWindow(slavePtr->slave); |
567 |
} |
568 |
break; |
569 |
} |
570 |
x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width; |
571 |
y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width; |
572 |
} |
573 |
} |
574 |
} |
575 |
|
576 |
/* End of tkgeometry.c */ |