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