summaryrefslogtreecommitdiffstats
path: root/generic/tkGeometry.c
blob: 1851965aa1f6a6c0c8150970969fd05bbf9cf695 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/* 
 * tkGeometry.c --
 *
 *	This file contains generic Tk code for geometry management
 *	(stuff that's used by all geometry managers).
 *
 * Copyright (c) 1990-1994 The Regents of the University of California.
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkGeometry.c,v 1.3 1999/04/16 01:51:14 stanton Exp $
 */

#include "tkPort.h"
#include "tkInt.h"

/*
 * Data structures of the following type are used by Tk_MaintainGeometry.
 * For each slave managed by Tk_MaintainGeometry, there is one of these
 * structures associated with its master.
 */

typedef struct MaintainSlave {
    Tk_Window slave;		/* The slave window being positioned. */
    Tk_Window master;		/* The master that determines slave's
				 * position; it must be a descendant of
				 * slave's parent. */
    int x, y;			/* Desired position of slave relative to
				 * master. */
    int width, height;		/* Desired dimensions of slave. */
    struct MaintainSlave *nextPtr;
				/* Next in list of Maintains associated
				 * with master. */
} MaintainSlave;

/*
 * For each window that has been specified as a master to
 * Tk_MaintainGeometry, there is a structure of the following type:
 */

typedef struct MaintainMaster {
    Tk_Window ancestor;		/* The lowest ancestor of this window
				 * for which we have *not* created a
				 * StructureNotify handler.  May be the
				 * same as the window itself. */
    int checkScheduled;		/* Non-zero means that there is already a
				 * call to MaintainCheckProc scheduled as
				 * an idle handler. */
    MaintainSlave *slavePtr;	/* First in list of all slaves associated
				 * with this master. */
} MaintainMaster;

/*
 * Prototypes for static procedures in this file:
 */

static void		MaintainCheckProc _ANSI_ARGS_((ClientData clientData));
static void		MaintainMasterProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));
static void		MaintainSlaveProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));

/*
 *--------------------------------------------------------------
 *
 * Tk_ManageGeometry --
 *
 *	Arrange for a particular procedure to manage the geometry
 *	of a given slave window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Proc becomes the new geometry manager for tkwin, replacing
 *	any previous geometry manager.  The geometry manager will
 *	be notified (by calling procedures in *mgrPtr) when interesting
 *	things happen in the future.  If there was an existing geometry
 *	manager for tkwin different from the new one, it is notified
 *	by calling its lostSlaveProc.
 *
 *--------------------------------------------------------------
 */

void
Tk_ManageGeometry(tkwin, mgrPtr, clientData)
    Tk_Window tkwin;		/* Window whose geometry is to
				 * be managed by proc.  */
    Tk_GeomMgr *mgrPtr;		/* Static structure describing the
				 * geometry manager.  This structure
				 * must never go away. */
    ClientData clientData;	/* Arbitrary one-word argument to
				 * pass to geometry manager procedures. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    if ((winPtr->geomMgrPtr != NULL) && (mgrPtr != NULL)
	    && ((winPtr->geomMgrPtr != mgrPtr)
		|| (winPtr->geomData != clientData))
	    && (winPtr->geomMgrPtr->lostSlaveProc != NULL)) {
	(*winPtr->geomMgrPtr->lostSlaveProc)(winPtr->geomData, tkwin);
    }

    winPtr->geomMgrPtr = mgrPtr;
    winPtr->geomData = clientData;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_GeometryRequest --
 *
 *	This procedure is invoked by widget code to indicate
 *	its preferences about the size of a window it manages.
 *	In general, widget code should call this procedure
 *	rather than Tk_ResizeWindow.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The geometry manager for tkwin (if any) is invoked to
 *	handle the request.  If possible, it will reconfigure
 *	tkwin and/or other windows to satisfy the request.  The
 *	caller gets no indication of success or failure, but it
 *	will get X events if the window size was actually
 *	changed.
 *
 *--------------------------------------------------------------
 */

void
Tk_GeometryRequest(tkwin, reqWidth, reqHeight)
    Tk_Window tkwin;		/* Window that geometry information
				 * pertains to. */
    int reqWidth, reqHeight;	/* Minimum desired dimensions for
				 * window, in pixels. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    /*
     * X gets very upset if a window requests a width or height of
     * zero, so rounds requested sizes up to at least 1.
     */

    if (reqWidth <= 0) {
	reqWidth = 1;
    }
    if (reqHeight <= 0) {
	reqHeight = 1;
    }
    if ((reqWidth == winPtr->reqWidth) && (reqHeight == winPtr->reqHeight)) {
	return;
    }
    winPtr->reqWidth = reqWidth;
    winPtr->reqHeight = reqHeight;
    if ((winPtr->geomMgrPtr != NULL)
	    && (winPtr->geomMgrPtr->requestProc != NULL)) {
	(*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, tkwin);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_SetInternalBorder --
 *
 *	Notify relevant geometry managers that a window has an internal
 *	border of a given width and that child windows should not be
 *	placed on that border.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The border width is recorded for the window, and all geometry
 *	managers of all children are notified so that can re-layout, if
 *	necessary.
 *
 *----------------------------------------------------------------------
 */

void
Tk_SetInternalBorder(tkwin, width)
    Tk_Window tkwin;		/* Window that will have internal border. */
    int width;			/* Width of internal border, in pixels. */
{
    register TkWindow *winPtr = (TkWindow *) tkwin;

    if (width == winPtr->internalBorderWidth) {
	return;
    }
    if (width < 0) {
	width = 0;
    }
    winPtr->internalBorderWidth = width;

    /*
     * All the slaves for which this is the master window must now be
     * repositioned to take account of the new internal border width.
     * To signal all the geometry managers to do this, just resize the
     * window to its current size.  The ConfigureNotify event will
     * cause geometry managers to recompute everything.
     */

    Tk_ResizeWindow(tkwin, Tk_Width(tkwin), Tk_Height(tkwin));
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_MaintainGeometry --
 *
 *	This procedure is invoked by geometry managers to handle slaves
 *	whose master's are not their parents.  It translates the desired
 *	geometry for the slave into the coordinate system of the parent
 *	and respositions the slave if it isn't already at the right place.
 *	Furthermore, it sets up event handlers so that if the master (or
 *	any of its ancestors up to the slave's parent) is mapped, unmapped,
 *	or moved, then the slave will be adjusted to match.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Event handlers are created and state is allocated to keep track
 *	of slave.  Note:  if slave was already managed for master by
 *	Tk_MaintainGeometry, then the previous information is replaced
 *	with the new information.  The caller must eventually call
 *	Tk_UnmaintainGeometry to eliminate the correspondence (or, the
 *	state is automatically freed when either window is destroyed).
 *
 *----------------------------------------------------------------------
 */

void
Tk_MaintainGeometry(slave, master, x, y, width, height)
    Tk_Window slave;		/* Slave for geometry management. */
    Tk_Window master;		/* Master for slave; must be a descendant
				 * of slave's parent. */
    int x, y;			/* Desired position of slave within master. */
    int width, height;		/* Desired dimensions for slave. */
{
    Tcl_HashEntry *hPtr;
    MaintainMaster *masterPtr;
    register MaintainSlave *slavePtr;
    int new, map;
    Tk_Window ancestor, parent;
    TkDisplay *dispPtr = ((TkWindow *) master)->dispPtr;

    if (!dispPtr->geomInit) {
	dispPtr->geomInit = 1;
	Tcl_InitHashTable(&dispPtr->maintainHashTable, TCL_ONE_WORD_KEYS);
    }

    /*
     * See if there is already a MaintainMaster structure for the master;
     * if not, then create one.
     */

    parent = Tk_Parent(slave);
    hPtr = Tcl_CreateHashEntry(&dispPtr->maintainHashTable, 
            (char *) master, &new);
    if (!new) {
	masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
    } else {
	masterPtr = (MaintainMaster *) ckalloc(sizeof(MaintainMaster));
	masterPtr->ancestor = master;
	masterPtr->checkScheduled = 0;
	masterPtr->slavePtr = NULL;
	Tcl_SetHashValue(hPtr, masterPtr);
    }

    /*
     * Create a MaintainSlave structure for the slave if there isn't
     * already one.
     */

    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
	    slavePtr = slavePtr->nextPtr) {
	if (slavePtr->slave == slave) {
	    goto gotSlave;
	}
    }
    slavePtr = (MaintainSlave *) ckalloc(sizeof(MaintainSlave));
    slavePtr->slave = slave;
    slavePtr->master = master;
    slavePtr->nextPtr = masterPtr->slavePtr;
    masterPtr->slavePtr = slavePtr;
    Tk_CreateEventHandler(slave, StructureNotifyMask, MaintainSlaveProc,
	    (ClientData) slavePtr);

    /*
     * Make sure that there are event handlers registered for all
     * the windows between master and slave's parent (including master
     * but not slave's parent).  There may already be handlers for master
     * and some of its ancestors (masterPtr->ancestor tells how many).
     */

    for (ancestor = master; ancestor != parent;
	    ancestor = Tk_Parent(ancestor)) {
	if (ancestor == masterPtr->ancestor) {
	    Tk_CreateEventHandler(ancestor, StructureNotifyMask,
		    MaintainMasterProc, (ClientData) masterPtr);
	    masterPtr->ancestor = Tk_Parent(ancestor);
	}
    }

    /*
     * Fill in up-to-date information in the structure, then update the
     * window if it's not currently in the right place or state.
     */

    gotSlave:
    slavePtr->x = x;
    slavePtr->y = y;
    slavePtr->width = width;
    slavePtr->height = height;
    map = 1;
    for (ancestor = slavePtr->master; ; ancestor = Tk_Parent(ancestor)) {
	if (!Tk_IsMapped(ancestor) && (ancestor != parent)) {
	    map = 0;
	}
	if (ancestor == parent) {
	    if ((x != Tk_X(slavePtr->slave))
		    || (y != Tk_Y(slavePtr->slave))
		    || (width != Tk_Width(slavePtr->slave))
		    || (height != Tk_Height(slavePtr->slave))) {
		Tk_MoveResizeWindow(slavePtr->slave, x, y, width, height);
	    }
	    if (map) {
		Tk_MapWindow(slavePtr->slave);
	    } else {
		Tk_UnmapWindow(slavePtr->slave);
	    }
	    break;
	}
	x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
	y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_UnmaintainGeometry --
 *
 *	This procedure cancels a previous Tk_MaintainGeometry call,
 *	so that the relationship between slave and master is no longer
 *	maintained.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The slave is unmapped and state is released, so that slave won't
 *	track master any more.  If we weren't previously managing slave
 *	relative to master, then this procedure has no effect.
 *
 *----------------------------------------------------------------------
 */

void
Tk_UnmaintainGeometry(slave, master)
    Tk_Window slave;		/* Slave for geometry management. */
    Tk_Window master;		/* Master for slave; must be a descendant
				 * of slave's parent. */
{
    Tcl_HashEntry *hPtr;
    MaintainMaster *masterPtr;
    register MaintainSlave *slavePtr, *prevPtr;
    Tk_Window ancestor;
    TkDisplay *dispPtr = ((TkWindow *) slave)->dispPtr;

    if (!dispPtr->geomInit) {
	dispPtr->geomInit = 1;
	Tcl_InitHashTable(&dispPtr->maintainHashTable, TCL_ONE_WORD_KEYS);
    }

    if (!(((TkWindow *) slave)->flags & TK_ALREADY_DEAD)) {
	Tk_UnmapWindow(slave);
    }
    hPtr = Tcl_FindHashEntry(&dispPtr->maintainHashTable, (char *) master);
    if (hPtr == NULL) {
	return;
    }
    masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
    slavePtr = masterPtr->slavePtr;
    if (slavePtr->slave == slave) {
	masterPtr->slavePtr = slavePtr->nextPtr;
    } else {
	for (prevPtr = slavePtr, slavePtr = slavePtr->nextPtr; ;
		prevPtr = slavePtr, slavePtr = slavePtr->nextPtr) {
	    if (slavePtr == NULL) {
		return;
	    }
	    if (slavePtr->slave == slave) {
		prevPtr->nextPtr = slavePtr->nextPtr;
		break;
	    }
	}
    }
    Tk_DeleteEventHandler(slavePtr->slave, StructureNotifyMask,
	    MaintainSlaveProc, (ClientData) slavePtr);
    ckfree((char *) slavePtr);
    if (masterPtr->slavePtr == NULL) {
	if (masterPtr->ancestor != NULL) {
	    for (ancestor = master; ; ancestor = Tk_Parent(ancestor)) {
		Tk_DeleteEventHandler(ancestor, StructureNotifyMask,
			MaintainMasterProc, (ClientData) masterPtr);
		if (ancestor == masterPtr->ancestor) {
		    break;
		}
	    }
	}
	if (masterPtr->checkScheduled) {
	    Tcl_CancelIdleCall(MaintainCheckProc, (ClientData) masterPtr);
	}
	Tcl_DeleteHashEntry(hPtr);
	ckfree((char *) masterPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MaintainMasterProc --
 *
 *	This procedure is invoked by the Tk event dispatcher in
 *	response to StructureNotify events on the master or one
 *	of its ancestors, on behalf of Tk_MaintainGeometry.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	It schedules a call to MaintainCheckProc, which will eventually
 *	caused the postions and mapped states to be recalculated for all
 *	the maintained slaves of the master.  Or, if the master window is
 *	being deleted then state is cleaned up.
 *
 *----------------------------------------------------------------------
 */

static void
MaintainMasterProc(clientData, eventPtr)
    ClientData clientData;		/* Pointer to MaintainMaster structure
					 * for the master window. */
    XEvent *eventPtr;			/* Describes what just happened. */
{
    MaintainMaster *masterPtr = (MaintainMaster *) clientData;
    MaintainSlave *slavePtr;
    int done;

    if ((eventPtr->type == ConfigureNotify)
	    || (eventPtr->type == MapNotify)
	    || (eventPtr->type == UnmapNotify)) {
	if (!masterPtr->checkScheduled) {
	    masterPtr->checkScheduled = 1;
	    Tcl_DoWhenIdle(MaintainCheckProc, (ClientData) masterPtr);
	}
    } else if (eventPtr->type == DestroyNotify) {
	/*
	 * Delete all of the state associated with this master, but
	 * be careful not to use masterPtr after the last slave is
	 * deleted, since its memory will have been freed.
	 */

	done = 0;
	do {
	    slavePtr = masterPtr->slavePtr;
	    if (slavePtr->nextPtr == NULL) {
		done = 1;
	    }
	    Tk_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
	} while (!done);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MaintainSlaveProc --
 *
 *	This procedure is invoked by the Tk event dispatcher in
 *	response to StructureNotify events on a slave being managed
 *	by Tk_MaintainGeometry.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If the event is a DestroyNotify event then the Maintain state
 *	and event handlers for this slave are deleted.
 *
 *----------------------------------------------------------------------
 */

static void
MaintainSlaveProc(clientData, eventPtr)
    ClientData clientData;		/* Pointer to MaintainSlave structure
					 * for master-slave pair. */
    XEvent *eventPtr;			/* Describes what just happened. */
{
    MaintainSlave *slavePtr = (MaintainSlave *) clientData;

    if (eventPtr->type == DestroyNotify) {
	Tk_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MaintainCheckProc --
 *
 *	This procedure is invoked by the Tk event dispatcher as an
 *	idle handler, when a master or one of its ancestors has been
 *	reconfigured, mapped, or unmapped.  Its job is to scan all of
 *	the slaves for the master and reposition them, map them, or
 *	unmap them as needed to maintain their geometry relative to
 *	the master.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Slaves can get repositioned, mapped, or unmapped.
 *
 *----------------------------------------------------------------------
 */

static void
MaintainCheckProc(clientData)
    ClientData clientData;		/* Pointer to MaintainMaster structure
					 * for the master window. */
{
    MaintainMaster *masterPtr = (MaintainMaster *) clientData;
    MaintainSlave *slavePtr;
    Tk_Window ancestor, parent;
    int x, y, map;

    masterPtr->checkScheduled = 0;
    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
	    slavePtr = slavePtr->nextPtr) {
	parent = Tk_Parent(slavePtr->slave);
	x = slavePtr->x;
	y = slavePtr->y;
	map = 1;
	for (ancestor = slavePtr->master; ; ancestor = Tk_Parent(ancestor)) {
	    if (!Tk_IsMapped(ancestor) && (ancestor != parent)) {
		map = 0;
	    }
	    if (ancestor == parent) {
		if ((x != Tk_X(slavePtr->slave))
			|| (y != Tk_Y(slavePtr->slave))) {
		    Tk_MoveWindow(slavePtr->slave, x, y);
		}
		if (map) {
		    Tk_MapWindow(slavePtr->slave);
		} else {
		    Tk_UnmapWindow(slavePtr->slave);
		}
		break;
	    }
	    x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
	    y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
	}
    }
}