4 * This file contains generic code for geometry management
5 * (stuff that's used by all geometry managers).
7 * Copyright (c) 1990-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9 * Copyright (c) 1995 Christian Werner
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
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.
24 typedef struct MaintainSlave {
25 CkWindow *slave; /* The slave window being positioned. */
26 CkWindow *master; /* The master that determines slave's
27 * position; it must be a descendant of
29 int x, y; /* Desired position of slave relative to
31 int width, height; /* Desired dimensions of slave. */
32 struct MaintainSlave *nextPtr;
33 /* Next in list of Maintains associated
38 * For each window that has been specified as a master to
39 * Tk_MaintainGeometry, there is a structure of the following type:
42 typedef struct MaintainMaster {
43 CkWindow *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
50 MaintainSlave *slavePtr; /* First in list of all slaves associated
51 * with this master. */
55 * Hash table that maps from a master's CkWindow pointer to a list of
56 * Maintains for that master:
59 static Tcl_HashTable maintainHashTable;
62 * Has maintainHashTable been initialized yet?
65 static int initialized = 0;
68 * Prototypes for static procedures in this file:
71 static void MaintainCheckProc _ANSI_ARGS_((ClientData clientData));
72 static void MaintainMasterProc _ANSI_ARGS_((ClientData clientData,
74 static void MaintainSlaveProc _ANSI_ARGS_((ClientData clientData,
78 *--------------------------------------------------------------
80 * Ck_ManageGeometry --
82 * Arrange for a particular procedure to manage the geometry
83 * of a given slave window.
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.
96 *--------------------------------------------------------------
100 Ck_ManageGeometry(winPtr, mgrPtr, clientData)
101 CkWindow *winPtr; /* Window whose geometry is to
102 * be managed by proc. */
103 Ck_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. */
109 if ((winPtr->geomMgrPtr != NULL) && (mgrPtr != NULL)
110 && ((winPtr->geomMgrPtr != mgrPtr)
111 || (winPtr->geomData != clientData))
112 && (winPtr->geomMgrPtr->lostSlaveProc != NULL)) {
113 (*winPtr->geomMgrPtr->lostSlaveProc)(winPtr->geomData, winPtr);
116 winPtr->geomMgrPtr = mgrPtr;
117 winPtr->geomData = clientData;
121 *--------------------------------------------------------------
123 * Ck_GeometryRequest --
125 * This procedure is invoked by widget code to indicate
126 * its preferences about the size of a window it manages.
127 * In general, widget code should call this procedure
128 * rather than Ck_ResizeWindow.
134 * The geometry manager for winPtr (if any) is invoked to
135 * handle the request. If possible, it will reconfigure
136 * winPtr and/or other windows to satisfy the request. The
137 * caller gets no indication of success or failure, but it
138 * will get events if the window size was actually
141 *--------------------------------------------------------------
145 Ck_GeometryRequest(winPtr, reqWidth, reqHeight)
146 CkWindow *winPtr; /* Window that geometry information
148 int reqWidth, reqHeight; /* Minimum desired dimensions for
149 * window, in pixels. */
154 if (reqHeight <= 0) {
157 if ((reqWidth == winPtr->reqWidth) && (reqHeight == winPtr->reqHeight)) {
160 winPtr->reqWidth = reqWidth;
161 winPtr->reqHeight = reqHeight;
162 if ((winPtr->geomMgrPtr != NULL)
163 && (winPtr->geomMgrPtr->requestProc != NULL)) {
164 (*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, winPtr);
169 *----------------------------------------------------------------------
171 * Ck_SetInternalBorder --
173 * Notify relevant geometry managers that a window has an internal
174 * border of zero or one character cells and that child windows
175 * should not be placed on that border.
181 * The border is recorded for the window, and all geometry
182 * managers of all children are notified so that can re-layout, if
185 *----------------------------------------------------------------------
189 Ck_SetInternalBorder(winPtr, onoff)
190 CkWindow *winPtr; /* Window to modify. */
191 int onoff; /* Border flag. */
193 if ((onoff && (winPtr->flags & CK_BORDER)) ||
194 (!onoff && !(winPtr->flags & CK_BORDER)))
197 winPtr->flags |= CK_BORDER;
199 winPtr->flags &= ~CK_BORDER;
200 for (winPtr = winPtr->childList; winPtr != NULL;
201 winPtr = winPtr->nextPtr) {
202 if (winPtr->geomMgrPtr != NULL) {
203 (*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, winPtr);
209 *----------------------------------------------------------------------
211 * Ck_MaintainGeometry --
213 * This procedure is invoked by geometry managers to handle slaves
214 * whose master's are not their parents. It translates the desired
215 * geometry for the slave into the coordinate system of the parent
216 * and respositions the slave if it isn't already at the right place.
217 * Furthermore, it sets up event handlers so that if the master (or
218 * any of its ancestors up to the slave's parent) is mapped, unmapped,
219 * or moved, then the slave will be adjusted to match.
225 * Event handlers are created and state is allocated to keep track
226 * of slave. Note: if slave was already managed for master by
227 * Tk_MaintainGeometry, then the previous information is replaced
228 * with the new information. The caller must eventually call
229 * Tk_UnmaintainGeometry to eliminate the correspondence (or, the
230 * state is automatically freed when either window is destroyed).
232 *----------------------------------------------------------------------
236 Ck_MaintainGeometry(slave, master, x, y, width, height)
237 CkWindow *slave; /* Slave for geometry management. */
238 CkWindow *master; /* Master for slave; must be a descendant
239 * of slave's parent. */
240 int x, y; /* Desired position of slave within master. */
241 int width, height; /* Desired dimensions for slave. */
244 MaintainMaster *masterPtr;
245 register MaintainSlave *slavePtr;
247 CkWindow *ancestor, *parent;
251 Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
255 * See if there is already a MaintainMaster structure for the master;
256 * if not, then create one.
259 parent = slave->parentPtr;
260 hPtr = Tcl_CreateHashEntry(&maintainHashTable, (char *) master, &new);
262 masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
264 masterPtr = (MaintainMaster *) ckalloc(sizeof(MaintainMaster));
265 masterPtr->ancestor = master;
266 masterPtr->checkScheduled = 0;
267 masterPtr->slavePtr = NULL;
268 Tcl_SetHashValue(hPtr, masterPtr);
272 * Create a MaintainSlave structure for the slave if there isn't
276 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
277 slavePtr = slavePtr->nextPtr) {
278 if (slavePtr->slave == slave) {
282 slavePtr = (MaintainSlave *) ckalloc(sizeof(MaintainSlave));
283 slavePtr->slave = slave;
284 slavePtr->master = master;
285 slavePtr->nextPtr = masterPtr->slavePtr;
286 masterPtr->slavePtr = slavePtr;
287 Ck_CreateEventHandler(slave,
288 CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
289 MaintainSlaveProc, (ClientData) slavePtr);
292 * Make sure that there are event handlers registered for all
293 * the windows between master and slave's parent (including master
294 * but not slave's parent). There may already be handlers for master
295 * and some of its ancestors (masterPtr->ancestor tells how many).
298 for (ancestor = master; ancestor != parent;
299 ancestor = ancestor->parentPtr) {
300 if (ancestor == masterPtr->ancestor) {
301 Ck_CreateEventHandler(ancestor,
302 CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
303 MaintainMasterProc, (ClientData) masterPtr);
304 masterPtr->ancestor = ancestor->parentPtr;
309 * Fill in up-to-date information in the structure, then update the
310 * window if it's not currently in the right place or state.
316 slavePtr->width = width;
317 slavePtr->height = height;
319 for (ancestor = slavePtr->master; ; ancestor = ancestor->parentPtr) {
320 if (!(ancestor->flags & CK_MAPPED) && (ancestor != parent)) {
323 if (ancestor == parent) {
324 if ((x != slavePtr->slave->x)
325 || (y != slavePtr->slave->y)
326 || (width != slavePtr->slave->width)
327 || (height != slavePtr->slave->height)) {
328 Ck_MoveWindow(slavePtr->slave, x, y);
329 Ck_ResizeWindow(slavePtr->slave, width, height);
330 Ck_RestackWindow(slavePtr->slave, CK_ABOVE, slavePtr->master);
333 Ck_MapWindow(slavePtr->slave);
335 Ck_UnmapWindow(slavePtr->slave);
345 *----------------------------------------------------------------------
347 * Ck_UnmaintainGeometry --
349 * This procedure cancels a previous Ck_MaintainGeometry call,
350 * so that the relationship between slave and master is no longer
357 * The slave is unmapped and state is released, so that slave won't
358 * track master any more. If we weren't previously managing slave
359 * relative to master, then this procedure has no effect.
361 *----------------------------------------------------------------------
365 Ck_UnmaintainGeometry(slave, master)
366 CkWindow *slave; /* Slave for geometry management. */
367 CkWindow *master; /* Master for slave; must be a descendant
368 * of slave's parent. */
371 MaintainMaster *masterPtr;
372 register MaintainSlave *slavePtr, *prevPtr;
377 Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
380 if (!(slave->flags & CK_ALREADY_DEAD)) {
381 Ck_UnmapWindow(slave);
383 hPtr = Tcl_FindHashEntry(&maintainHashTable, (char *) master);
387 masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
388 slavePtr = masterPtr->slavePtr;
389 if (slavePtr->slave == slave) {
390 masterPtr->slavePtr = slavePtr->nextPtr;
392 for (prevPtr = slavePtr, slavePtr = slavePtr->nextPtr; ;
393 prevPtr = slavePtr, slavePtr = slavePtr->nextPtr) {
394 if (slavePtr == NULL) {
397 if (slavePtr->slave == slave) {
398 prevPtr->nextPtr = slavePtr->nextPtr;
403 Ck_DeleteEventHandler(slavePtr->slave,
404 CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
405 MaintainSlaveProc, (ClientData) slavePtr);
406 ckfree((char *) slavePtr);
407 if (masterPtr->slavePtr == NULL) {
408 if (masterPtr->ancestor != NULL) {
409 for (ancestor = master; ; ancestor = ancestor->parentPtr) {
410 Ck_DeleteEventHandler(ancestor,
411 CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
412 MaintainMasterProc, (ClientData) masterPtr);
413 if (ancestor == masterPtr->ancestor) {
418 if (masterPtr->checkScheduled) {
419 Tk_CancelIdleCall(MaintainCheckProc, (ClientData) masterPtr);
421 Tcl_DeleteHashEntry(hPtr);
422 ckfree((char *) masterPtr);
427 *----------------------------------------------------------------------
429 * MaintainMasterProc --
431 * This procedure is invoked by the event dispatcher in
432 * response to StructureNotify events on the master or one
433 * of its ancestors, on behalf of Ck_MaintainGeometry.
439 * It schedules a call to MaintainCheckProc, which will eventually
440 * caused the postions and mapped states to be recalculated for all
441 * the maintained slaves of the master. Or, if the master window is
442 * being deleted then state is cleaned up.
444 *----------------------------------------------------------------------
448 MaintainMasterProc(clientData, eventPtr)
449 ClientData clientData; /* Pointer to MaintainMaster structure
450 * for the master window. */
451 CkEvent *eventPtr; /* Describes what just happened. */
453 MaintainMaster *masterPtr = (MaintainMaster *) clientData;
454 MaintainSlave *slavePtr;
457 if ((eventPtr->type == CK_EV_EXPOSE)
458 || (eventPtr->type == CK_EV_MAP)
459 || (eventPtr->type == CK_EV_UNMAP)) {
460 if (!masterPtr->checkScheduled) {
461 masterPtr->checkScheduled = 1;
462 Tk_DoWhenIdle(MaintainCheckProc, (ClientData) masterPtr);
464 } else if (eventPtr->type == CK_EV_DESTROY) {
466 * Delete all of the state associated with this master, but
467 * be careful not to use masterPtr after the last slave is
468 * deleted, since its memory will have been freed.
473 slavePtr = masterPtr->slavePtr;
474 if (slavePtr->nextPtr == NULL) {
477 Ck_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
483 *----------------------------------------------------------------------
485 * MaintainSlaveProc --
487 * This procedure is invoked by the Tk event dispatcher in
488 * response to StructureNotify events on a slave being managed
489 * by Tk_MaintainGeometry.
495 * If the event is a DestroyNotify event then the Maintain state
496 * and event handlers for this slave are deleted.
498 *----------------------------------------------------------------------
502 MaintainSlaveProc(clientData, eventPtr)
503 ClientData clientData; /* Pointer to MaintainSlave structure
504 * for master-slave pair. */
505 CkEvent *eventPtr; /* Describes what just happened. */
507 MaintainSlave *slavePtr = (MaintainSlave *) clientData;
509 if (eventPtr->type == CK_EV_DESTROY) {
510 Ck_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
515 *----------------------------------------------------------------------
517 * MaintainCheckProc --
519 * This procedure is invoked by the Tk event dispatcher as an
520 * idle handler, when a master or one of its ancestors has been
521 * reconfigured, mapped, or unmapped. Its job is to scan all of
522 * the slaves for the master and reposition them, map them, or
523 * unmap them as needed to maintain their geometry relative to
530 * Slaves can get repositioned, mapped, or unmapped.
532 *----------------------------------------------------------------------
536 MaintainCheckProc(clientData)
537 ClientData clientData; /* Pointer to MaintainMaster structure
538 * for the master window. */
540 MaintainMaster *masterPtr = (MaintainMaster *) clientData;
541 MaintainSlave *slavePtr;
542 CkWindow *ancestor, *parent;
545 masterPtr->checkScheduled = 0;
546 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
547 slavePtr = slavePtr->nextPtr) {
548 parent = slavePtr->slave->parentPtr;
552 for (ancestor = slavePtr->master; ; ancestor = ancestor->parentPtr) {
553 if (!(ancestor->flags & CK_MAPPED) && (ancestor != parent)) {
556 if (ancestor == parent) {
557 if ((x != slavePtr->slave->x)
558 || (y != slavePtr->slave->y)) {
559 Ck_MoveWindow(slavePtr->slave, x, y);
562 Ck_MapWindow(slavePtr->slave);
564 Ck_UnmapWindow(slavePtr->slave);