1/*
2 * tkTableWin.c --
3 *
4 *	This module implements embedded windows for table widgets.
5 *	Much of this code is adapted from tkGrid.c and tkTextWind.c.
6 *
7 * Copyright (c) 1998-2002 Jeffrey Hobbs
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkTableWin.c,v 1.6 2004/06/11 00:24:44 hobbs Exp $
13 */
14
15#include "tkTable.h"
16
17static int	StickyParseProc _ANSI_ARGS_((ClientData clientData,
18			Tcl_Interp *interp, Tk_Window tkwin,
19			CONST84 char *value, char *widgRec, int offset));
20static char *	StickyPrintProc _ANSI_ARGS_((ClientData clientData,
21			Tk_Window tkwin, char *widgRec, int offset,
22			Tcl_FreeProc **freeProcPtr));
23
24static void	EmbWinLostSlaveProc _ANSI_ARGS_((ClientData clientData,
25						Tk_Window tkwin));
26static void	EmbWinRequestProc _ANSI_ARGS_((ClientData clientData,
27					       Tk_Window tkwin));
28
29static void	EmbWinCleanup _ANSI_ARGS_((Table *tablePtr,
30					   TableEmbWindow *ewPtr));
31static int	EmbWinConfigure _ANSI_ARGS_((Table *tablePtr,
32					     TableEmbWindow *ewPtr,
33					     int objc, Tcl_Obj *CONST objv[]));
34static void	EmbWinStructureProc _ANSI_ARGS_((ClientData clientData,
35						 XEvent *eventPtr));
36static void	EmbWinUnmapNow _ANSI_ARGS_((Tk_Window ewTkwin,
37					    Tk_Window tkwin));
38
39static Tk_GeomMgr tableGeomType = {
40    "table",			/* name */
41    EmbWinRequestProc,		/* requestProc */
42    EmbWinLostSlaveProc,	/* lostSlaveProc */
43};
44
45/* windows subcommands */
46static CONST84 char *winCmdNames[] = {
47    "cget", "configure", "delete", "move", "names", (char *) NULL
48};
49enum winCommand {
50    WIN_CGET, WIN_CONFIGURE, WIN_DELETE, WIN_MOVE, WIN_NAMES
51};
52
53/* Flag values for "sticky"ness  The 16 combinations subsume the packer's
54 * notion of anchor and fill.
55 *
56 * STICK_NORTH  	This window sticks to the top of its cavity.
57 * STICK_EAST		This window sticks to the right edge of its cavity.
58 * STICK_SOUTH		This window sticks to the bottom of its cavity.
59 * STICK_WEST		This window sticks to the left edge of its cavity.
60 */
61
62#define STICK_NORTH	(1<<0)
63#define STICK_EAST	(1<<1)
64#define STICK_SOUTH	(1<<2)
65#define STICK_WEST	(1<<3)
66
67/*
68 * The default specification for configuring embedded windows
69 * Done like this to make the command line parsing easy
70 */
71
72static Tk_CustomOption stickyOption	= { StickyParseProc, StickyPrintProc,
73					    (ClientData) NULL };
74static Tk_CustomOption tagBdOpt		= { TableOptionBdSet, TableOptionBdGet,
75					    (ClientData) BD_TABLE_WIN };
76
77static Tk_ConfigSpec winConfigSpecs[] = {
78  {TK_CONFIG_BORDER, "-background", "background", "Background", NULL,
79   Tk_Offset(TableEmbWindow, bg),
80   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
81  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
82  {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
83  {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", "",
84   0 /* no offset */,
85   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK, &tagBdOpt },
86  {TK_CONFIG_STRING, "-create", (char *)NULL, (char *)NULL, (char *)NULL,
87   Tk_Offset(TableEmbWindow, create),
88   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
89  {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL, (char *)NULL,
90   Tk_Offset(TableEmbWindow, padX), TK_CONFIG_DONT_SET_DEFAULT },
91  {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL, (char *)NULL,
92   Tk_Offset(TableEmbWindow, padY), TK_CONFIG_DONT_SET_DEFAULT },
93  {TK_CONFIG_CUSTOM, "-sticky", (char *)NULL, (char *)NULL, (char *)NULL,
94   Tk_Offset(TableEmbWindow, sticky), TK_CONFIG_DONT_SET_DEFAULT,
95   &stickyOption},
96  {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", NULL,
97   Tk_Offset(TableEmbWindow, relief), 0 },
98  {TK_CONFIG_WINDOW, "-window", (char *)NULL, (char *)NULL, (char *)NULL,
99   Tk_Offset(TableEmbWindow, tkwin),
100   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
101  {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
102   (char *)NULL, 0, 0 }
103};
104
105/*
106 *----------------------------------------------------------------------
107 *
108 * StickyPrintProc --
109 *	Converts the internal boolean combination of "sticky" bits onto
110 *	a TCL string element containing zero or more of n, s, e, or w.
111 *
112 * Results:
113 *	A string is placed into the "result" pointer.
114 *
115 * Side effects:
116 *	none.
117 *
118 *----------------------------------------------------------------------
119 */
120static char *
121StickyPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
122    ClientData clientData;		/* Ignored. */
123    Tk_Window tkwin;			/* Window for text widget. */
124    char *widgRec;			/* Pointer to TkTextEmbWindow
125					 * structure. */
126    int offset;				/* Ignored. */
127    Tcl_FreeProc **freeProcPtr;		/* Pointer to variable to fill in with
128					 * information about how to reclaim
129					 * storage for return string. */
130{
131    int flags = ((TableEmbWindow *) widgRec)->sticky;
132    int count = 0;
133    char *result = (char *) ckalloc(5*sizeof(char));
134
135    if (flags&STICK_NORTH) result[count++] = 'n';
136    if (flags&STICK_EAST)  result[count++] = 'e';
137    if (flags&STICK_SOUTH) result[count++] = 's';
138    if (flags&STICK_WEST)  result[count++] = 'w';
139
140    *freeProcPtr = TCL_DYNAMIC;
141    result[count] = '\0';
142    return result;
143}
144
145/*
146 *----------------------------------------------------------------------
147 *
148 * StringParseProc --
149 *	Converts an ascii string representing a widgets stickyness
150 *	into the boolean result.
151 *
152 * Results:
153 *	The boolean combination of the "sticky" bits is retuned.  If an
154 *	error occurs, such as an invalid character, -1 is returned instead.
155 *
156 * Side effects:
157 *	none
158 *
159 *----------------------------------------------------------------------
160 */
161static int
162StickyParseProc(clientData, interp, tkwin, value, widgRec, offset)
163    ClientData clientData;		/* Not used.*/
164    Tcl_Interp *interp;			/* Used for reporting errors. */
165    Tk_Window tkwin;			/* Window for text widget. */
166    CONST84 char *value;		/* Value of option. */
167    char *widgRec;			/* Pointer to TkTextEmbWindow
168					 * structure. */
169    int offset;				/* Offset into item (ignored). */
170{
171    register TableEmbWindow *ewPtr = (TableEmbWindow *) widgRec;
172    int sticky = 0;
173    char c;
174
175    while ((c = *value++) != '\0') {
176	switch (c) {
177	case 'n': case 'N': sticky |= STICK_NORTH; break;
178	case 'e': case 'E': sticky |= STICK_EAST;  break;
179	case 's': case 'S': sticky |= STICK_SOUTH; break;
180	case 'w': case 'W': sticky |= STICK_WEST;  break;
181	case ' ': case ',': case '\t': case '\r': case '\n': break;
182	default:
183	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
184				   "bad sticky value \"", --value,
185				   "\": must contain n, s, e or w",
186				   (char *) NULL);
187	    return TCL_ERROR;
188	}
189    }
190    ewPtr->sticky = sticky;
191    return TCL_OK;
192}
193
194/*
195 * ckallocs space for a new embedded window structure and clears the structure
196 * returns the pointer to the new structure
197 */
198static TableEmbWindow *
199TableNewEmbWindow(Table *tablePtr)
200{
201    TableEmbWindow *ewPtr = (TableEmbWindow *) ckalloc(sizeof(TableEmbWindow));
202    memset((VOID *) ewPtr, 0, sizeof(TableEmbWindow));
203
204    /*
205     * Set the values that aren't 0/NULL by default
206     */
207    ewPtr->tablePtr	= tablePtr;
208    ewPtr->relief	= -1;
209    ewPtr->padX		= -1;
210    ewPtr->padY		= -1;
211
212    return ewPtr;
213}
214
215/*
216 *----------------------------------------------------------------------
217 *
218 * EmbWinCleanup --
219 *	Releases resources used by an embedded window before it is freed up.
220 *
221 * Results:
222 *	Window will no longer be valid.
223 *
224 * Side effects:
225 *	None.
226 *
227 *----------------------------------------------------------------------
228 */
229static void
230EmbWinCleanup(Table *tablePtr, TableEmbWindow *ewPtr)
231{
232    Tk_FreeOptions(winConfigSpecs, (char *) ewPtr, tablePtr->display, 0);
233}
234
235/*
236 *--------------------------------------------------------------
237 *
238 * EmbWinDisplay --
239 *
240 *	This procedure is invoked by TableDisplay for
241 *	mapping windows into cells.
242 *
243 * Results:
244 *	Displays or moves window on table screen.
245 *
246 * Side effects:
247 *	None.
248 *
249 *--------------------------------------------------------------
250 */
251void
252EmbWinDisplay(Table *tablePtr, Drawable window, TableEmbWindow *ewPtr,
253	      TableTag *tagPtr, int x, int y, int width, int height)
254{
255    Tk_Window tkwin = tablePtr->tkwin;
256    Tk_Window ewTkwin = ewPtr->tkwin;
257    int diffx=0;	/* Cavity width - slave width. */
258    int diffy=0;	/* Cavity hight - slave height. */
259    int sticky = ewPtr->sticky;
260    int padx, pady;
261
262    if (ewPtr->bg)		tagPtr->bg	= ewPtr->bg;
263    if (ewPtr->relief != -1)	tagPtr->relief	= ewPtr->relief;
264    if (ewPtr->borders) {
265	tagPtr->borderStr	= ewPtr->borderStr;
266	tagPtr->borders		= ewPtr->borders;
267	tagPtr->bd[0]		= ewPtr->bd[0];
268	tagPtr->bd[1]		= ewPtr->bd[1];
269	tagPtr->bd[2]		= ewPtr->bd[2];
270	tagPtr->bd[3]		= ewPtr->bd[3];
271    }
272
273    padx = (ewPtr->padX < 0) ? tablePtr->padX : ewPtr->padX;
274    pady = (ewPtr->padY < 0) ? tablePtr->padY : ewPtr->padY;
275
276    x		+= padx;
277    width	-= padx*2;
278    y		+= pady;
279    height	-= pady*2;
280
281    if (width > Tk_ReqWidth(ewPtr->tkwin)) {
282	diffx = width - Tk_ReqWidth(ewPtr->tkwin);
283	width = Tk_ReqWidth(ewPtr->tkwin);
284    }
285    if (height > Tk_ReqHeight(ewPtr->tkwin)) {
286	diffy = height - Tk_ReqHeight(ewPtr->tkwin);
287	height = Tk_ReqHeight(ewPtr->tkwin);
288    }
289    if (sticky&STICK_EAST && sticky&STICK_WEST) {
290	width += diffx;
291    }
292    if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
293	height += diffy;
294    }
295    if (!(sticky&STICK_WEST)) {
296	x += (sticky&STICK_EAST) ? diffx : diffx/2;
297    }
298    if (!(sticky&STICK_NORTH)) {
299	y += (sticky&STICK_SOUTH) ? diffy : diffy/2;
300    }
301
302    /*
303     * If we fall below a specific minimum width/height requirement,
304     * we just unmap the window
305     */
306    if (width < 2 || height < 2) {
307	if (ewPtr->displayed) {
308	    EmbWinUnmapNow(ewTkwin, tkwin);
309	}
310	return;
311    }
312
313    if (tkwin == Tk_Parent(ewTkwin)) {
314	if ((x != Tk_X(ewTkwin)) || (y != Tk_Y(ewTkwin))
315	    || (width != Tk_Width(ewTkwin))
316	    || (height != Tk_Height(ewTkwin))) {
317	    Tk_MoveResizeWindow(ewTkwin, x, y, width, height);
318	}
319	Tk_MapWindow(ewTkwin);
320    } else {
321	Tk_MaintainGeometry(ewTkwin, tkwin, x, y, width, height);
322    }
323    ewPtr->displayed = 1;
324}
325
326/*
327 *--------------------------------------------------------------
328 *
329 * EmbWinUnmapNow --
330 *	Handles unmapping the window depending on parent.
331 *	tkwin should be tablePtr->tkwin.
332 *	ewTkwin should be ewPtr->tkwin.
333 *
334 * Results:
335 *	Removes the window.
336 *
337 * Side effects:
338 *	None.
339 *
340 *--------------------------------------------------------------
341 */
342static void
343EmbWinUnmapNow(Tk_Window ewTkwin, Tk_Window tkwin)
344{
345    if (tkwin != Tk_Parent(ewTkwin)) {
346	Tk_UnmaintainGeometry(ewTkwin, tkwin);
347    }
348    Tk_UnmapWindow(ewTkwin);
349}
350
351/*
352 *--------------------------------------------------------------
353 *
354 * EmbWinUnmap --
355 *	This procedure is invoked by TableAdjustParams for
356 *	unmapping windows managed moved offscreen.
357 *	rlo, ... should be in real coords.
358 *
359 * Results:
360 *	None.
361 *
362 * Side effects:
363 *	Unmaps embedded windows.
364 *
365 *--------------------------------------------------------------
366 */
367void
368EmbWinUnmap(Table *tablePtr, int rlo, int rhi, int clo, int chi)
369{
370    register TableEmbWindow *ewPtr;
371    Tcl_HashEntry *entryPtr;
372    int row, col, trow, tcol;
373    char buf[INDEX_BUFSIZE];
374
375    /*
376     * Transform numbers from real to user user coords
377     */
378    rlo += tablePtr->rowOffset;
379    rhi += tablePtr->rowOffset;
380    clo += tablePtr->colOffset;
381    chi += tablePtr->colOffset;
382    for (row = rlo; row <= rhi; row++) {
383	for (col = clo; col <= chi; col++) {
384	    TableTrueCell(tablePtr, row, col, &trow, &tcol);
385	    TableMakeArrayIndex(trow, tcol, buf);
386	    entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf);
387	    if (entryPtr != NULL) {
388		ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
389		if (ewPtr->displayed) {
390		    ewPtr->displayed = 0;
391		    if (ewPtr->tkwin != NULL && tablePtr->tkwin != NULL) {
392			EmbWinUnmapNow(ewPtr->tkwin, tablePtr->tkwin);
393		    }
394		}
395	    }
396	}
397    }
398}
399
400/*
401 *--------------------------------------------------------------
402 *
403 * EmbWinRequestProc --
404 *	This procedure is invoked by Tk_GeometryRequest for
405 *	windows managed by the Table.
406 *
407 * Results:
408 *	None.
409 *
410 * Side effects:
411 *	Arranges for tkwin, and all its managed siblings, to
412 *	be re-arranged at the next idle point.
413 *
414 *--------------------------------------------------------------
415 */
416static void
417EmbWinRequestProc(clientData, tkwin)
418    ClientData clientData;	/* Table's information about
419				 * window that got new preferred
420				 * geometry.  */
421    Tk_Window tkwin;		/* Other Tk-related information
422				 * about the window. */
423{
424    register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
425
426    /*
427     * Resize depends on the sticky
428     */
429    if (ewPtr->displayed && ewPtr->hPtr != NULL) {
430	Table *tablePtr = ewPtr->tablePtr;
431	int row, col, x, y, width, height;
432
433	TableParseArrayIndex(&row, &col,
434			     Tcl_GetHashKey(tablePtr->winTable, ewPtr->hPtr));
435	if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
436			     col-tablePtr->colOffset, &x, &y, &width, &height,
437			     0)) {
438	    TableInvalidate(tablePtr, x, y, width, height, 0);
439	}
440    }
441}
442
443static void
444EmbWinRemove(TableEmbWindow *ewPtr)
445{
446    Table *tablePtr = ewPtr->tablePtr;
447
448    if (ewPtr->tkwin != NULL) {
449	Tk_DeleteEventHandler(ewPtr->tkwin, StructureNotifyMask,
450			      EmbWinStructureProc, (ClientData) ewPtr);
451	ewPtr->tkwin = NULL;
452    }
453    ewPtr->displayed = 0;
454    if (tablePtr->tkwin != NULL) {
455	int row, col, x, y, width, height;
456
457	TableParseArrayIndex(&row, &col,
458			     Tcl_GetHashKey(tablePtr->winTable, ewPtr->hPtr));
459	/* this will cause windows removed from the table to actually
460	 * cause the associated embdedded window hash data to be removed */
461	Tcl_DeleteHashEntry(ewPtr->hPtr);
462	if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
463			     col-tablePtr->colOffset, &x, &y, &width, &height,
464			     0))
465	    TableInvalidate(tablePtr, x, y, width, height, 1);
466    }
467    /* this will cause windows removed from the table to actually
468     * cause the associated embdedded window hash data to be removed */
469    EmbWinCleanup(tablePtr, ewPtr);
470    ckfree((char *) ewPtr);
471}
472
473/*
474 *--------------------------------------------------------------
475 *
476 * EmbWinLostSlaveProc --
477 *	This procedure is invoked by Tk whenever some other geometry
478 *	claims control over a slave that used to be managed by us.
479 *
480 * Results:
481 *	None.
482 *
483 * Side effects:
484 *	Forgets all table-related information about the slave.
485 *
486 *--------------------------------------------------------------
487 */
488
489static void
490EmbWinLostSlaveProc(clientData, tkwin)
491    ClientData clientData;	/* Table structure for slave window that
492				 * was stolen away. */
493    Tk_Window tkwin;		/* Tk's handle for the slave window. */
494{
495    register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
496
497#if 0
498    Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
499#endif
500    EmbWinUnmapNow(tkwin, ewPtr->tablePtr->tkwin);
501    EmbWinRemove(ewPtr);
502}
503
504/*
505 *--------------------------------------------------------------
506 *
507 * EmbWinStructureProc --
508 *	This procedure is invoked by the Tk event loop whenever
509 *	StructureNotify events occur for a window that's embedded
510 *	in a table widget.  This procedure's only purpose is to
511 *	clean up when windows are deleted.
512 *
513 * Results:
514 *	None.
515 *
516 * Side effects:
517 *	The window is disassociated from the window segment, and
518 *	the portion of the table is redisplayed.
519 *
520 *--------------------------------------------------------------
521 */
522static void
523EmbWinStructureProc(clientData, eventPtr)
524    ClientData clientData;	/* Pointer to record describing window item. */
525    XEvent *eventPtr;		/* Describes what just happened. */
526{
527    register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
528
529    if (eventPtr->type != DestroyNotify) {
530	return;
531    }
532
533    EmbWinRemove(ewPtr);
534}
535
536/*
537 *--------------------------------------------------------------
538 *
539 * EmbWinDelete --
540 *	This procedure is invoked by ... whenever
541 *	an embedded window is being deleted.
542 *
543 * Results:
544 *	None.
545 *
546 * Side effects:
547 *	The embedded window is deleted, if it exists, and any resources
548 *	associated with it are released.
549 *
550 *--------------------------------------------------------------
551 */
552void
553EmbWinDelete(register Table *tablePtr, TableEmbWindow *ewPtr)
554{
555    Tcl_HashEntry *entryPtr = ewPtr->hPtr;
556
557    if (ewPtr->tkwin != NULL) {
558	Tk_Window tkwin = ewPtr->tkwin;
559	/*
560	 * Delete the event handler for the window before destroying
561	 * the window, so that EmbWinStructureProc doesn't get called
562	 * (we'll already do everything that it would have done, and
563	 * it will just get confused).
564	 */
565
566	ewPtr->tkwin = NULL;
567	Tk_DeleteEventHandler(tkwin, StructureNotifyMask,
568			      EmbWinStructureProc, (ClientData) ewPtr);
569	Tk_DestroyWindow(tkwin);
570    }
571    if (tablePtr->tkwin != NULL && entryPtr != NULL) {
572	int row, col, x, y, width, height;
573	TableParseArrayIndex(&row, &col,
574			     Tcl_GetHashKey(tablePtr->winTable, entryPtr));
575	Tcl_DeleteHashEntry(entryPtr);
576
577	if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
578			     col-tablePtr->colOffset,
579			     &x, &y, &width, &height, 0))
580	    TableInvalidate(tablePtr, x, y, width, height, 0);
581    }
582#if 0
583    Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
584#endif
585    EmbWinCleanup(tablePtr, ewPtr);
586    ckfree((char *) ewPtr);
587}
588
589/*
590 *--------------------------------------------------------------
591 *
592 * EmbWinConfigure --
593 *	This procedure is called to handle configuration options
594 *	for an embedded window.
595 *
596 * Results:
597 *	The return value is a standard Tcl result.  If TCL_ERROR is
598 *	returned, then the interp's result contains an error message..
599 *
600 * Side effects:
601 *	Configuration information for the embedded window changes,
602 *	such as alignment, stretching, or name of the embedded
603 *	window.
604 *
605 *--------------------------------------------------------------
606 */
607static int
608EmbWinConfigure(tablePtr, ewPtr, objc, objv)
609     Table *tablePtr;		/* Information about table widget that
610				 * contains embedded window. */
611     TableEmbWindow *ewPtr;	/* Embedded window to be configured. */
612     int objc;			/* Number of objs in objv. */
613     Tcl_Obj *CONST objv[];	/* Obj type options. */
614{
615    Tcl_Interp *interp = tablePtr->interp;
616    Tk_Window oldWindow;
617    int i, result;
618    CONST84 char **argv;
619
620    oldWindow = ewPtr->tkwin;
621
622    /* Stringify */
623    argv = (CONST84 char **) ckalloc((objc + 1) * sizeof(char *));
624    for (i = 0; i < objc; i++)
625	argv[i] = Tcl_GetString(objv[i]);
626    argv[i] = NULL;
627    result = Tk_ConfigureWidget(interp, tablePtr->tkwin,
628	    winConfigSpecs, objc, argv, (char *) ewPtr,
629	    TK_CONFIG_ARGV_ONLY);
630    ckfree((char *) argv);
631    if (result != TCL_OK) {
632	return TCL_ERROR;
633    }
634
635    if (oldWindow != ewPtr->tkwin) {
636	ewPtr->displayed = 0;
637	if (oldWindow != NULL) {
638	    Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
639				  EmbWinStructureProc, (ClientData) ewPtr);
640	    Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
641			      (ClientData) NULL);
642	    EmbWinUnmapNow(oldWindow, tablePtr->tkwin);
643	}
644	if (ewPtr->tkwin != NULL) {
645	    Tk_Window ancestor, parent;
646
647	    /*
648	     * Make sure that the table is either the parent of the
649	     * embedded window or a descendant of that parent.  Also,
650	     * don't allow a top-level window to be managed inside
651	     * a table.
652	     */
653
654	    parent = Tk_Parent(ewPtr->tkwin);
655	    for (ancestor = tablePtr->tkwin; ;
656		 ancestor = Tk_Parent(ancestor)) {
657		if (ancestor == parent) {
658		    break;
659		}
660		if (Tk_IsTopLevel(ancestor)) {
661		badMaster:
662		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
663					   "can't embed ",
664					   Tk_PathName(ewPtr->tkwin), " in ",
665					   Tk_PathName(tablePtr->tkwin),
666					   (char *)NULL);
667		    ewPtr->tkwin = NULL;
668		    return TCL_ERROR;
669		}
670	    }
671	    if (Tk_IsTopLevel(ewPtr->tkwin) ||
672		(ewPtr->tkwin == tablePtr->tkwin)) {
673		goto badMaster;
674	    }
675
676	    /*
677	     * Take over geometry management for the window, plus create
678	     * an event handler to find out when it is deleted.
679	     */
680
681	    Tk_ManageGeometry(ewPtr->tkwin, &tableGeomType, (ClientData)ewPtr);
682	    Tk_CreateEventHandler(ewPtr->tkwin, StructureNotifyMask,
683				  EmbWinStructureProc, (ClientData) ewPtr);
684	}
685    }
686    return TCL_OK;
687}
688
689/*
690 *--------------------------------------------------------------
691 *
692 * Table_WinMove --
693 *	This procedure is invoked by ... whenever
694 *	an embedded window is being moved.
695 *
696 * Results:
697 *	A standard Tcl result.
698 *
699 * Side effects:
700 *	If an embedded window is in the dest cell, it is deleted.
701 *
702 *--------------------------------------------------------------
703 */
704int
705Table_WinMove(register Table *tablePtr, char *CONST srcPtr,
706	   char *CONST destPtr, int flags)
707{
708    int srow, scol, row, col, new;
709    Tcl_HashEntry *entryPtr;
710    TableEmbWindow *ewPtr;
711
712    if (TableGetIndex(tablePtr, srcPtr, &srow, &scol) != TCL_OK ||
713	TableGetIndex(tablePtr, destPtr, &row, &col) != TCL_OK) {
714	return TCL_ERROR;
715    }
716    entryPtr = Tcl_FindHashEntry(tablePtr->winTable, srcPtr);
717    if (entryPtr == NULL) {
718	if (flags & INV_NO_ERR_MSG) {
719	    return TCL_OK;
720	} else {
721	    Tcl_AppendStringsToObj(Tcl_GetObjResult(tablePtr->interp),
722		    "no window at index \"", srcPtr, "\"", (char *) NULL);
723	    return TCL_ERROR;
724	}
725    }
726    /* avoid moving it to the same location */
727    if (srow == row && scol == col) {
728	return TCL_OK;
729    }
730    /* get the window pointer */
731    ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
732    /* and free the old hash table entry */
733    Tcl_DeleteHashEntry(entryPtr);
734
735    entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, destPtr, &new);
736    if (!new) {
737	/* window already there - just delete it */
738	TableEmbWindow *ewPtrDel;
739	ewPtrDel = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
740	/* This prevents the deletion of it's own entry, since we need it */
741	ewPtrDel->hPtr = NULL;
742	EmbWinDelete(tablePtr, ewPtrDel);
743    }
744    /* set the new entry's value */
745    Tcl_SetHashValue(entryPtr, (ClientData) ewPtr);
746    ewPtr->hPtr = entryPtr;
747
748    if (flags & INV_FORCE) {
749	int x, y, w, h;
750	/* Invalidate old cell */
751	if (TableCellVCoords(tablePtr, srow-tablePtr->rowOffset,
752		scol-tablePtr->colOffset, &x, &y, &w, &h, 0)) {
753	    TableInvalidate(tablePtr, x, y, w, h, 0);
754	}
755	/* Invalidate new cell */
756	if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
757		col-tablePtr->colOffset, &x, &y, &w, &h, 0)) {
758	    TableInvalidate(tablePtr, x, y, w, h, 0);
759	}
760    }
761    return TCL_OK;
762}
763
764/*
765 *--------------------------------------------------------------
766 *
767 * Table_WinDelete --
768 *	This procedure is invoked by ... whenever
769 *	an embedded window is being delete.
770 *
771 * Results:
772 *	A standard Tcl result.
773 *
774 * Side effects:
775 *	Window info will be deleted.
776 *
777 *--------------------------------------------------------------
778 */
779int
780Table_WinDelete(register Table *tablePtr, char *CONST idxPtr)
781{
782    Tcl_HashEntry *entryPtr;
783
784    entryPtr = Tcl_FindHashEntry(tablePtr->winTable, idxPtr);
785    if (entryPtr != NULL) {
786	/* get the window pointer & clean up data associated with it */
787	EmbWinDelete(tablePtr, (TableEmbWindow *) Tcl_GetHashValue(entryPtr));
788    }
789    return TCL_OK;
790}
791
792/*
793 *--------------------------------------------------------------
794 *
795 * Table_WindowCmd --
796 *	This procedure is invoked to process the window method
797 *	that corresponds to a widget managed by this module.
798 *	See the user documentation for details on what it does.
799 *
800 * Results:
801 *	A standard Tcl result.
802 *
803 * Side effects:
804 *	See the user documentation.
805 *
806 *--------------------------------------------------------------
807 */
808int
809Table_WindowCmd(ClientData clientData, register Tcl_Interp *interp,
810		int objc, Tcl_Obj *CONST objv[])
811{
812    register Table *tablePtr = (Table *)clientData;
813    int result = TCL_OK, cmdIndex, row, col, x, y, width, height, i, new;
814    TableEmbWindow *ewPtr;
815    Tcl_HashEntry *entryPtr;
816    Tcl_HashSearch search;
817    char buf[INDEX_BUFSIZE], *keybuf, *winname;
818
819    if (objc < 3) {
820	Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
821	return TCL_ERROR;
822    }
823
824    /* parse the next argument */
825    if (Tcl_GetIndexFromObj(interp, objv[2], winCmdNames,
826			    "option", 0, &cmdIndex) != TCL_OK) {
827	return TCL_ERROR;
828    }
829    switch ((enum winCommand) cmdIndex) {
830    case WIN_CGET:
831	if (objc != 5) {
832	    Tcl_WrongNumArgs(interp, 3, objv, "index option");
833	    return TCL_ERROR;
834	}
835	entryPtr = Tcl_FindHashEntry(tablePtr->winTable,
836				     Tcl_GetString(objv[3]));
837	if (entryPtr == NULL) {
838	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
839				   "no window at index \"",
840				   Tcl_GetString(objv[3]), "\"", (char *)NULL);
841	    return TCL_ERROR;
842	} else {
843	    ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
844	    result = Tk_ConfigureValue(interp, tablePtr->tkwin, winConfigSpecs,
845				       (char *) ewPtr,
846				       Tcl_GetString(objv[4]), 0);
847	}
848	return result;	/* CGET */
849
850    case WIN_CONFIGURE:
851	if (objc < 4) {
852	    Tcl_WrongNumArgs(interp, 3, objv, "index ?arg arg  ...?");
853	    return TCL_ERROR;
854	}
855	if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR) {
856	    return TCL_ERROR;
857	}
858	TableMakeArrayIndex(row, col, buf);
859	entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, buf, &new);
860
861	if (new) {
862	    /* create the structure */
863	    ewPtr = TableNewEmbWindow(tablePtr);
864
865	    /* insert it into the table */
866	    Tcl_SetHashValue(entryPtr, (ClientData) ewPtr);
867	    ewPtr->hPtr = entryPtr;
868
869	    /* configure the window structure */
870	    result = EmbWinConfigure(tablePtr, ewPtr, objc-4, objv+4);
871	    if (result == TCL_ERROR) {
872		/* release the structure */
873		EmbWinCleanup(tablePtr, ewPtr);
874		ckfree((char *) ewPtr);
875
876		/* and free the hash table entry */
877		Tcl_DeleteHashEntry(entryPtr);
878	    }
879	} else {
880	    /* window exists, do a reconfig if we have enough args */
881	    /* get the window pointer from the table */
882	    ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
883
884	    /* 5 args means that there are values to replace */
885	    if (objc > 5) {
886		/* and do a reconfigure */
887		result = EmbWinConfigure(tablePtr, ewPtr, objc-4, objv+4);
888	    }
889	}
890	if (result == TCL_ERROR) {
891	    return TCL_ERROR;
892	}
893
894	/*
895	 * If there were less than 6 args, we need
896	 * to do a printout of the config, even for new windows
897	 */
898	if (objc < 6) {
899	    result = Tk_ConfigureInfo(interp, tablePtr->tkwin, winConfigSpecs,
900				      (char *) ewPtr, (objc == 5)?
901				      Tcl_GetString(objv[4]) : NULL, 0);
902	} else {
903	    /* Otherwise we reconfigured so invalidate
904	     * the table for a redraw */
905	    if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
906				 col-tablePtr->colOffset,
907				 &x, &y, &width, &height, 0)) {
908		TableInvalidate(tablePtr, x, y, width, height, 1);
909	    }
910	}
911	return result;	/* CONFIGURE */
912
913    case WIN_DELETE:
914	if (objc < 4) {
915	    Tcl_WrongNumArgs(interp, 3, objv, "index ?index ...?");
916	    return TCL_ERROR;
917	}
918	for (i = 3; i < objc; i++) {
919	    Table_WinDelete(tablePtr, Tcl_GetString(objv[i]));
920	}
921	break;
922
923    case WIN_MOVE:
924	if (objc != 5) {
925	    Tcl_WrongNumArgs(interp, 3, objv, "srcIndex destIndex");
926	    return TCL_ERROR;
927	}
928	result = Table_WinMove(tablePtr, Tcl_GetString(objv[3]),
929			       Tcl_GetString(objv[4]), INV_FORCE);
930	break;
931
932    case WIN_NAMES: {
933	Tcl_Obj *objPtr = Tcl_NewObj();
934
935	/* just print out the window names */
936	if (objc < 3 || objc > 4) {
937	    Tcl_WrongNumArgs(interp, 3, objv, "?pattern?");
938	    return TCL_ERROR;
939	}
940	winname = (objc == 4) ? Tcl_GetString(objv[3]) : NULL;
941	entryPtr = Tcl_FirstHashEntry(tablePtr->winTable, &search);
942	while (entryPtr != NULL) {
943	    keybuf = Tcl_GetHashKey(tablePtr->winTable, entryPtr);
944	    if (objc == 3 || Tcl_StringMatch(keybuf, winname)) {
945		Tcl_ListObjAppendElement(NULL, objPtr,
946					 Tcl_NewStringObj(keybuf, -1));
947	    }
948	    entryPtr = Tcl_NextHashEntry(&search);
949	}
950	Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
951	break;
952    }
953    }
954    return TCL_OK;
955}
956