]> www.wagner.pp.ru Git - oss/ck.git/blob - ckGeometry.c
Ck console graphics toolkit
[oss/ck.git] / ckGeometry.c
1 /* 
2  * ckGeometry.c --
3  *
4  *      This file contains generic 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  * Copyright (c) 1995 Christian Werner
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14
15 #include "ckPort.h"
16 #include "ck.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     CkWindow *slave;            /* The slave window being positioned. */
26     CkWindow *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     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
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 CkWindow pointer 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                             CkEvent *eventPtr));
74 static void             MaintainSlaveProc _ANSI_ARGS_((ClientData clientData,
75                             CkEvent *eventPtr));
76 \f
77 /*
78  *--------------------------------------------------------------
79  *
80  * Ck_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
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. */
108 {
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);
114     }
115
116     winPtr->geomMgrPtr = mgrPtr;
117     winPtr->geomData = clientData;
118 }
119 \f
120 /*
121  *--------------------------------------------------------------
122  *
123  * Ck_GeometryRequest --
124  *
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.
129  *
130  * Results:
131  *      None.
132  *
133  * Side effects:
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
139  *      changed.
140  *
141  *--------------------------------------------------------------
142  */
143
144 void
145 Ck_GeometryRequest(winPtr, reqWidth, reqHeight)
146     CkWindow *winPtr;           /* Window that geometry information
147                                  * pertains to. */
148     int reqWidth, reqHeight;    /* Minimum desired dimensions for
149                                  * window, in pixels. */
150 {
151     if (reqWidth <= 0) {
152         reqWidth = 1;
153     }
154     if (reqHeight <= 0) {
155         reqHeight = 1;
156     }
157     if ((reqWidth == winPtr->reqWidth) && (reqHeight == winPtr->reqHeight)) {
158         return;
159     }
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);
165     }
166 }
167 \f
168 /*
169  *----------------------------------------------------------------------
170  *
171  * Ck_SetInternalBorder --
172  *
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.
176  *
177  * Results:
178  *      None.
179  *
180  * Side effects:
181  *      The border is recorded for the window, and all geometry
182  *      managers of all children are notified so that can re-layout, if
183  *      necessary.
184  *
185  *----------------------------------------------------------------------
186  */
187
188 void
189 Ck_SetInternalBorder(winPtr, onoff)
190     CkWindow *winPtr;           /* Window to modify. */
191     int onoff;                  /* Border flag. */
192 {
193     if ((onoff && (winPtr->flags & CK_BORDER)) ||
194         (!onoff && !(winPtr->flags & CK_BORDER)))
195         return;
196     if (onoff)
197         winPtr->flags |= CK_BORDER;
198     else
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);
204         }
205     }
206 }
207 \f
208 /*
209  *----------------------------------------------------------------------
210  *
211  * Ck_MaintainGeometry --
212  *
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.
220  *
221  * Results:
222  *      None.
223  *
224  * Side effects:
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).
231  *
232  *----------------------------------------------------------------------
233  */
234
235 void
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. */
242 {
243     Tcl_HashEntry *hPtr;
244     MaintainMaster *masterPtr;
245     register MaintainSlave *slavePtr;
246     int new, map;
247     CkWindow *ancestor, *parent;
248
249     if (!initialized) {
250         initialized = 1;
251         Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
252     }
253
254     /*
255      * See if there is already a MaintainMaster structure for the master;
256      * if not, then create one.
257      */
258
259     parent = slave->parentPtr;
260     hPtr = Tcl_CreateHashEntry(&maintainHashTable, (char *) master, &new);
261     if (!new) {
262         masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
263     } else {
264         masterPtr = (MaintainMaster *) ckalloc(sizeof(MaintainMaster));
265         masterPtr->ancestor = master;
266         masterPtr->checkScheduled = 0;
267         masterPtr->slavePtr = NULL;
268         Tcl_SetHashValue(hPtr, masterPtr);
269     }
270
271     /*
272      * Create a MaintainSlave structure for the slave if there isn't
273      * already one.
274      */
275
276     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
277             slavePtr = slavePtr->nextPtr) {
278         if (slavePtr->slave == slave) {
279             goto gotSlave;
280         }
281     }
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);
290
291     /*
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).
296      */
297
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;
305         }
306     }
307
308     /*
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.
311      */
312
313     gotSlave:
314     slavePtr->x = x;
315     slavePtr->y = y;
316     slavePtr->width = width;
317     slavePtr->height = height;
318     map = 1;
319     for (ancestor = slavePtr->master; ; ancestor = ancestor->parentPtr) {
320         if (!(ancestor->flags & CK_MAPPED) && (ancestor != parent)) {
321             map = 0;
322         }
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);
331             }
332             if (map) {
333                 Ck_MapWindow(slavePtr->slave);
334             } else {
335                 Ck_UnmapWindow(slavePtr->slave);
336             }
337             break;
338         }
339         x += ancestor->x;
340         y += ancestor->y;
341     }
342 }
343 \f
344 /*
345  *----------------------------------------------------------------------
346  *
347  * Ck_UnmaintainGeometry --
348  *
349  *      This procedure cancels a previous Ck_MaintainGeometry call,
350  *      so that the relationship between slave and master is no longer
351  *      maintained.
352  *
353  * Results:
354  *      None.
355  *
356  * Side effects:
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.
360  *
361  *----------------------------------------------------------------------
362  */
363
364 void
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. */
369 {
370     Tcl_HashEntry *hPtr;
371     MaintainMaster *masterPtr;
372     register MaintainSlave *slavePtr, *prevPtr;
373     CkWindow *ancestor;
374
375     if (!initialized) {
376         initialized = 1;
377         Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
378     }
379
380     if (!(slave->flags & CK_ALREADY_DEAD)) {
381         Ck_UnmapWindow(slave);
382     }
383     hPtr = Tcl_FindHashEntry(&maintainHashTable, (char *) master);
384     if (hPtr == NULL) {
385         return;
386     }
387     masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
388     slavePtr = masterPtr->slavePtr;
389     if (slavePtr->slave == slave) {
390         masterPtr->slavePtr = slavePtr->nextPtr;
391     } else {
392         for (prevPtr = slavePtr, slavePtr = slavePtr->nextPtr; ;
393                 prevPtr = slavePtr, slavePtr = slavePtr->nextPtr) {
394             if (slavePtr == NULL) {
395                 return;
396             }
397             if (slavePtr->slave == slave) {
398                 prevPtr->nextPtr = slavePtr->nextPtr;
399                 break;
400             }
401         }
402     }
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) {
414                     break;
415                 }
416             }
417         }
418         if (masterPtr->checkScheduled) {
419             Tk_CancelIdleCall(MaintainCheckProc, (ClientData) masterPtr);
420         }
421         Tcl_DeleteHashEntry(hPtr);
422         ckfree((char *) masterPtr);
423     }
424 }
425 \f
426 /*
427  *----------------------------------------------------------------------
428  *
429  * MaintainMasterProc --
430  *
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.
434  *
435  * Results:
436  *      None.
437  *
438  * Side effects:
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.
443  *
444  *----------------------------------------------------------------------
445  */
446
447 static void
448 MaintainMasterProc(clientData, eventPtr)
449     ClientData clientData;              /* Pointer to MaintainMaster structure
450                                          * for the master window. */
451     CkEvent *eventPtr;                  /* Describes what just happened. */
452 {
453     MaintainMaster *masterPtr = (MaintainMaster *) clientData;
454     MaintainSlave *slavePtr;
455     int done;
456
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);
463         }
464     } else if (eventPtr->type == CK_EV_DESTROY) {
465         /*
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.
469          */
470
471         done = 0;
472         do {
473             slavePtr = masterPtr->slavePtr;
474             if (slavePtr->nextPtr == NULL) {
475                 done = 1;
476             }
477             Ck_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
478         } while (!done);
479     }
480 }
481 \f
482 /*
483  *----------------------------------------------------------------------
484  *
485  * MaintainSlaveProc --
486  *
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.
490  *
491  * Results:
492  *      None.
493  *
494  * Side effects:
495  *      If the event is a DestroyNotify event then the Maintain state
496  *      and event handlers for this slave are deleted.
497  *
498  *----------------------------------------------------------------------
499  */
500
501 static void
502 MaintainSlaveProc(clientData, eventPtr)
503     ClientData clientData;              /* Pointer to MaintainSlave structure
504                                          * for master-slave pair. */
505     CkEvent *eventPtr;                  /* Describes what just happened. */
506 {
507     MaintainSlave *slavePtr = (MaintainSlave *) clientData;
508
509     if (eventPtr->type == CK_EV_DESTROY) {
510         Ck_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
511     }
512 }
513 \f
514 /*
515  *----------------------------------------------------------------------
516  *
517  * MaintainCheckProc --
518  *
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
524  *      the master.
525  *
526  * Results:
527  *      None.
528  *
529  * Side effects:
530  *      Slaves can get repositioned, mapped, or unmapped.
531  *
532  *----------------------------------------------------------------------
533  */
534
535 static void
536 MaintainCheckProc(clientData)
537     ClientData clientData;              /* Pointer to MaintainMaster structure
538                                          * for the master window. */
539 {
540     MaintainMaster *masterPtr = (MaintainMaster *) clientData;
541     MaintainSlave *slavePtr;
542     CkWindow *ancestor, *parent;
543     int x, y, map;
544
545     masterPtr->checkScheduled = 0;
546     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
547             slavePtr = slavePtr->nextPtr) {
548         parent = slavePtr->slave->parentPtr;
549         x = slavePtr->x;
550         y = slavePtr->y;
551         map = 1;
552         for (ancestor = slavePtr->master; ; ancestor = ancestor->parentPtr) {
553             if (!(ancestor->flags & CK_MAPPED) && (ancestor != parent)) {
554                 map = 0;
555             }
556             if (ancestor == parent) {
557                 if ((x != slavePtr->slave->x)
558                         || (y != slavePtr->slave->y)) {
559                     Ck_MoveWindow(slavePtr->slave, x, y);
560                 }
561                 if (map) {
562                     Ck_MapWindow(slavePtr->slave);
563                 } else {
564                     Ck_UnmapWindow(slavePtr->slave);
565                 }
566                 break;
567             }
568             x += ancestor->x;
569             y += ancestor->y;
570         }
571     }
572 }