summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandreas_kupries <akupries@shaw.ca>2008-07-23 20:49:50 (GMT)
committerandreas_kupries <akupries@shaw.ca>2008-07-23 20:49:50 (GMT)
commita0900a49110c0eaf5ed791ee35c70b800d3d52ec (patch)
treeb3d4d81cfc5d42e2154a618ae44c9a9da166f612
parent16253514a3a86502bec2e5b592f4c0789641535d (diff)
downloadtcl-a0900a49110c0eaf5ed791ee35c70b800d3d52ec.zip
tcl-a0900a49110c0eaf5ed791ee35c70b800d3d52ec.tar.gz
tcl-a0900a49110c0eaf5ed791ee35c70b800d3d52ec.tar.bz2
* generic/tclBasic.c: Modified TclArgumentGet to reject pure lists
* generic/tclCmdIL.c: immediately, without search. Reworked setup * generic/tclCompile.c: of eoFramePtr, doesn't need the line * tests/info.test: information, more sensible to have everything on line 1 when eval'ing a pure list. Updated the users of the line information to special case this based on the frame type (i.e. TCL_LOCATION_EVAL_LIST). Added a testcase demonstrating the new behaviour.
-rw-r--r--ChangeLog11
-rw-r--r--generic/tclBasic.c47
-rw-r--r--generic/tclCmdIL.c4
-rw-r--r--generic/tclCompile.c8
-rw-r--r--tests/info.test28
5 files changed, 69 insertions, 29 deletions
diff --git a/ChangeLog b/ChangeLog
index a5a8a89..6ab9250 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2008-07-23 Andreas Kupries <andreask@activestate.com>
+
+ * generic/tclBasic.c: Modified TclArgumentGet to reject pure lists
+ * generic/tclCmdIL.c: immediately, without search. Reworked setup
+ * generic/tclCompile.c: of eoFramePtr, doesn't need the line
+ * tests/info.test: information, more sensible to have everything
+ on line 1 when eval'ing a pure list. Updated the users of the line
+ information to special case this based on the frame type (i.e.
+ TCL_LOCATION_EVAL_LIST). Added a testcase demonstrating the new
+ behaviour.
+
2008-07-23 Miguel Sofer <msofer@users.sf.net>
* generic/tclBasic.c (GetCommandSource): added comment with
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index 1730097..da81e9c 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -16,7 +16,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclBasic.c,v 1.329 2008/07/23 13:38:22 msofer Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.330 2008/07/23 20:49:50 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -5534,6 +5534,18 @@ TclArgumentGet(
CmdFrame *framePtr;
/*
+ * An object which either has no string rep or else is a canonical list is
+ * guaranteed to have been generated dynamically: bail out, this cannot
+ * have a usable absolute location. _Do not touch_ the information the set
+ * up by the caller. It knows better than us.
+ */
+
+ if ((!obj->bytes) || ((obj->typePtr == &tclListType) &&
+ ((List *)obj->internalRep.twoPtrValue.ptr1)->canonicalFlag)) {
+ return;
+ }
+
+ /*
* First look for location information recorded in the argument
* stack. That is nearest.
*/
@@ -5745,18 +5757,11 @@ TclNREvalObjEx(
* dynamic execution we ignore the invoker, even if known.
*/
- int line, i, nline;
- char *w;
- Tcl_Obj **elements, *copyPtr = TclListObjCopy(NULL, objPtr);
CmdFrame *eoFramePtr;
- Tcl_ListObjGetElements(NULL, copyPtr,
- &nline, &elements);
-
- eoFramePtr = (CmdFrame *) TclStackAlloc(interp,
- sizeof(CmdFrame) + nline * sizeof(int));
- eoFramePtr->nline = nline;
- eoFramePtr->line = (int *) (eoFramePtr + 1);
+ eoFramePtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame));
+ eoFramePtr->nline = 0;
+ eoFramePtr->line = NULL;
eoFramePtr->type = TCL_LOCATION_EVAL_LIST;
eoFramePtr->level = (iPtr->cmdFramePtr == NULL?
@@ -5769,21 +5774,19 @@ TclNREvalObjEx(
eoFramePtr->data.eval.path = NULL;
/*
- * TIP #280 Computes all the line numbers for the words in the
- * command.
+ * TIP #280. We do _not_ compute all the line numbers for the
+ * words in the command. For the eval of a pure list the most
+ * sensible choice is to put all words on line 1. Given that we
+ * neither need memory for them nor compute anything. 'line' is
+ * left NULL. The two places using this information (TclInfoFrame,
+ * and TclInitCompileEnv), are special-cased to use the proper
+ * line number directly instead of accessing the 'line' array.
*/
- line = 1;
- for (i=0; i < eoFramePtr->nline; i++) {
- eoFramePtr->line[i] = line;
- w = TclGetString(elements[i]);
- TclAdvanceLines(&line, w, w + strlen(w));
- }
-
iPtr->cmdFramePtr = eoFramePtr;
TclNRAddCallback(interp, TEOEx_ListCallback, objPtr, eoFramePtr,
- copyPtr, NULL);
+ NULL, NULL);
return Tcl_NREvalObj(interp, objPtr, flags);
}
}
@@ -5952,7 +5955,6 @@ TEOEx_ListCallback(
Interp *iPtr = (Interp *) interp;
Tcl_Obj *objPtr = data[0];
CmdFrame *eoFramePtr = data[1];
- Tcl_Obj *copyPtr = data[2];
/*
* Remove the cmdFrame
@@ -5960,7 +5962,6 @@ TEOEx_ListCallback(
iPtr->cmdFramePtr = eoFramePtr->nextPtr;
TclStackFree(interp, eoFramePtr);
- Tcl_DecrRefCount(copyPtr);
TclDecrRefCount(objPtr);
return result;
diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c
index 9c668c2..ab324de 100644
--- a/generic/tclCmdIL.c
+++ b/generic/tclCmdIL.c
@@ -16,7 +16,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCmdIL.c,v 1.145 2008/07/21 22:22:27 nijtmans Exp $
+ * RCS: @(#) $Id: tclCmdIL.c,v 1.146 2008/07/23 20:49:52 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -1159,7 +1159,7 @@ TclInfoFrame(
*/
ADD_PAIR("type", Tcl_NewStringObj(typeString[framePtr->type], -1));
- ADD_PAIR("line", Tcl_NewIntObj(framePtr->line[0]));
+ ADD_PAIR("line", Tcl_NewIntObj(1));
/*
* We put a duplicate of the command list obj into the result to
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index c754ef1..f484d7b 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompile.c,v 1.152 2008/07/22 22:24:21 andreas_kupries Exp $
+ * RCS: @(#) $Id: tclCompile.c,v 1.153 2008/07/23 20:49:52 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -914,9 +914,11 @@ TclInitCompileEnv(
envPtr->extCmdMapPtr->neiloc = 0;
envPtr->extCmdMapPtr->nueiloc = 0;
- if (invoker == NULL) {
+ if ((invoker == NULL) ||
+ (invoker->type == TCL_LOCATION_EVAL_LIST)) {
/*
- * Initialize the compiler for relative counting.
+ * Initialize the compiler for relative counting in case of a
+ * dynamic context.
*/
envPtr->line = 1;
diff --git a/tests/info.test b/tests/info.test
index 43fc794..7fd9ec1 100644
--- a/tests/info.test
+++ b/tests/info.test
@@ -13,7 +13,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: info.test,v 1.51 2008/07/21 22:50:36 andreas_kupries Exp $
+# RCS: @(#) $Id: info.test,v 1.52 2008/07/23 20:49:52 andreas_kupries Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest 2
@@ -1301,6 +1301,32 @@ namespace delete foo
# -------------------------------------------------------------------------
+test info-34.0 {eval pure list, single line} {
+ # Basically, counting the newline in the word seen through $foo
+ # doesn't really make sense. It makes a bit of sense if the word
+ # would have been a string literal in the command list.
+ #
+ # Problem: At the point where we see the list elements we cannot
+ # distinguish the two cases, thus we cannot switch between
+ # count/not-count, it is has to be one or the other for all
+ # cases. Of the two possibilities miguel convinced me that 'not
+ # counting' is the more proper.
+ set foo {b
+ c}
+ set cmd [list foreach $foo {x y} {
+ set res [join [lrange [etrace] 0 2] \n]
+ break
+ }]
+ eval $cmd
+ set res
+} {10 {type source line 728 file info.test cmd {info frame $level} proc ::etrace level 0}
+9 {type eval line 2 cmd etrace proc ::tcltest::RunTest}
+8 {type eval line 1 cmd foreac proc ::tcltest::RunTest}}
+
+# -------------------------------------------------------------------------
+
+# -------------------------------------------------------------------------
+
# cleanup
catch {namespace delete test_ns_info1 test_ns_info2}
::tcltest::cleanupTests
TransparentWindowExist(Tk_Window tkwin, Window parent); static inline Tk_Window NextChild(Tk_Window tkwin); static void RefWinEventProc(ClientData clientData, XEvent *eventPtr); static inline void SetWindowInstanceData(Tk_Window tkwin, ClientData instanceData); /* * The "busy" geometry manager definition. */ static Tk_GeomMgr busyMgrInfo = { "busy", /* Name of geometry manager used by winfo */ BusyGeometryProc, /* Procedure to for new geometry requests */ BusyCustodyProc, /* Procedure when window is taken away */ }; /* * Helper functions, need to check if a Tcl/Tk alternative already exists. */ static inline Tk_Window FirstChild( Tk_Window parent) { struct TkWindow *parentPtr = (struct TkWindow *) parent; return (Tk_Window) parentPtr->childList; } static inline Tk_Window NextChild( Tk_Window tkwin) { struct TkWindow *winPtr = (struct TkWindow *) tkwin; if (winPtr == NULL) { return NULL; } return (Tk_Window) winPtr->nextPtr; } static inline void SetWindowInstanceData( Tk_Window tkwin, ClientData instanceData) { struct TkWindow *winPtr = (struct TkWindow *) tkwin; winPtr->instanceData = instanceData; } /* *---------------------------------------------------------------------- * * BusyCustodyProc -- * * This procedure is invoked when the busy window has been stolen by * another geometry manager. The information and memory associated with * the busy window is released. I don't know why anyone would try to pack * a busy window, but this should keep everything sane, if it is. * * Results: * None. * * Side effects: * The Busy structure is freed at the next idle point. * *---------------------------------------------------------------------- */ static void BusyCustodyProc( ClientData clientData, /* Information about the busy window. */ TCL_UNUSED(Tk_Window)) /* Not used. */ { Busy *busyPtr = (Busy *)clientData; Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask, BusyEventProc, busyPtr); TkpHideBusyWindow(busyPtr); busyPtr->tkBusy = NULL; Tcl_EventuallyFree(busyPtr, (Tcl_FreeProc *)DestroyBusy); } /* *---------------------------------------------------------------------- * * BusyGeometryProc -- * * This procedure is invoked by Tk_GeometryRequest for busy windows. * Busy windows never request geometry, so it's unlikely that this * function will ever be called;it exists simply as a place holder for * the GeomProc in the Geometry Manager structure. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void BusyGeometryProc( TCL_UNUSED(void *), /* Information about window that got new * preferred geometry. */ TCL_UNUSED(Tk_Window)) /* Other Tk-related information about the * window. */ { /* Should never get here */ } /* *---------------------------------------------------------------------- * * DoConfigureNotify -- * * Generate a ConfigureNotify event describing the current configuration * of a window. * * Results: * None. * * Side effects: * An event is generated and processed by Tk_HandleEvent. * *---------------------------------------------------------------------- */ static void DoConfigureNotify( Tk_FakeWin *winPtr) /* Window whose configuration was just * changed. */ { XEvent event; event.type = ConfigureNotify; event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display); event.xconfigure.send_event = False; event.xconfigure.display = winPtr->display; event.xconfigure.event = winPtr->window; event.xconfigure.window = winPtr->window; event.xconfigure.x = winPtr->changes.x; event.xconfigure.y = winPtr->changes.y; event.xconfigure.width = winPtr->changes.width; event.xconfigure.height = winPtr->changes.height; event.xconfigure.border_width = winPtr->changes.border_width; if (winPtr->changes.stack_mode == Above) { event.xconfigure.above = winPtr->changes.sibling; } else { event.xconfigure.above = None; } event.xconfigure.override_redirect = winPtr->atts.override_redirect; Tk_HandleEvent(&event); } /* *---------------------------------------------------------------------- * * RefWinEventProc -- * * This procedure is invoked by the Tk dispatcher for the following * events on the reference window. If the reference and parent windows * are the same, only the first event is important. * * 1) ConfigureNotify The reference window has been resized or * moved. Move and resize the busy window to be * the same size and position of the reference * window. * * 2) DestroyNotify The reference window was destroyed. Destroy * the busy window and the free resources used. * * 3) MapNotify The reference window was (re)shown. Map the * busy window again. * * 4) UnmapNotify The reference window was hidden. Unmap the * busy window. * * Results: * None. * * Side effects: * When the reference window gets deleted, internal structures get * cleaned up. When it gets resized, the busy window is resized * accordingly. If it's displayed, the busy window is displayed. And when * it's hidden, the busy window is unmapped. * *---------------------------------------------------------------------- */ static void RefWinEventProc( ClientData clientData, /* Busy window record */ XEvent *eventPtr) /* Event which triggered call to routine */ { Busy *busyPtr = (Busy *)clientData; switch (eventPtr->type) { case ReparentNotify: case DestroyNotify: /* * Arrange for the busy structure to be removed at a proper time. */ Tcl_EventuallyFree(busyPtr, (Tcl_FreeProc *)DestroyBusy); break; case ConfigureNotify: if ((busyPtr->width != Tk_Width(busyPtr->tkRef)) || (busyPtr->height != Tk_Height(busyPtr->tkRef)) || (busyPtr->x != Tk_X(busyPtr->tkRef)) || (busyPtr->y != Tk_Y(busyPtr->tkRef))) { int x, y; busyPtr->width = Tk_Width(busyPtr->tkRef); busyPtr->height = Tk_Height(busyPtr->tkRef); busyPtr->x = Tk_X(busyPtr->tkRef); busyPtr->y = Tk_Y(busyPtr->tkRef); x = y = 0; if (busyPtr->tkParent != busyPtr->tkRef) { Tk_Window tkwin; for (tkwin = busyPtr->tkRef; (tkwin != NULL) && (!Tk_IsTopLevel(tkwin)); tkwin = Tk_Parent(tkwin)) { if (tkwin == busyPtr->tkParent) { break; } x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width; y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width; } } if (busyPtr->tkBusy != NULL) { Tk_MoveResizeWindow(busyPtr->tkBusy, x, y, busyPtr->width, busyPtr->height); TkpShowBusyWindow(busyPtr); } } break; case MapNotify: if (busyPtr->tkParent != busyPtr->tkRef) { TkpShowBusyWindow(busyPtr); } break; case UnmapNotify: if (busyPtr->tkParent != busyPtr->tkRef) { TkpHideBusyWindow(busyPtr); } break; } } /* *---------------------------------------------------------------------- * * DestroyBusy -- * * This procedure is called from the Tk event dispatcher. It releases X * resources and memory used by the busy window and updates the internal * hash table. * * Results: * None. * * Side effects: * Memory and resources are released and the Tk event handler is removed. * *---------------------------------------------------------------------- */ static void DestroyBusy( void *data) /* Busy window structure record */ { Busy *busyPtr = (Busy *)data; if (busyPtr->hashPtr != NULL) { Tcl_DeleteHashEntry(busyPtr->hashPtr); } Tk_DeleteEventHandler(busyPtr->tkRef, StructureNotifyMask, RefWinEventProc, busyPtr); if (busyPtr->tkBusy != NULL) { Tk_FreeConfigOptions((char *)data, busyPtr->optionTable, busyPtr->tkBusy); Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask, BusyEventProc, busyPtr); Tk_ManageGeometry(busyPtr->tkBusy, NULL, busyPtr); Tk_DestroyWindow(busyPtr->tkBusy); } ckfree(data); } /* *---------------------------------------------------------------------- * * BusyEventProc -- * * This procedure is invoked by the Tk dispatcher for events on the busy * window itself. We're only concerned with destroy events. * * It might be necessary (someday) to watch resize events. Right now, I * don't think there's any point in it. * * Results: * None. * * Side effects: * When a busy window is destroyed, all internal structures associated * with it released at the next idle point. * *---------------------------------------------------------------------- */ static void BusyEventProc( ClientData clientData, /* Busy window record */ XEvent *eventPtr) /* Event which triggered call to routine */ { Busy *busyPtr = (Busy *)clientData; if (eventPtr->type == DestroyNotify) { busyPtr->tkBusy = NULL; Tcl_EventuallyFree(busyPtr, (Tcl_FreeProc *)DestroyBusy); } } /* *---------------------------------------------------------------------- * * MakeTransparentWindowExist -- * * Similar to Tk_MakeWindowExist but instead creates a transparent window * to block for user events from sibling windows. * * Differences from Tk_MakeWindowExist. * * 1. This is always a "busy" window. There's never a platform-specific * class procedure to execute instead. * 2. The window is transparent and never will contain children, so * colormap information is irrelevant. * * Results: * None. * * Side effects: * When the procedure returns, the internal window associated with tkwin * is guaranteed to exist. This may require the window's ancestors to be * created too. * *---------------------------------------------------------------------- */ static void MakeTransparentWindowExist( Tk_Window tkwin, /* Token for window. */ Window parent) /* Parent window. */ { TkWindow *winPtr = (TkWindow *) tkwin; Tcl_HashEntry *hPtr; int notUsed; TkDisplay *dispPtr; if (winPtr->window != None) { return; /* Window already exists. */ } /* * Create a transparent window and put it on top. */ TkpMakeTransparentWindowExist(tkwin, parent); if (winPtr->window == None) { return; /* Platform didn't make Window. */ } dispPtr = winPtr->dispPtr; hPtr = Tcl_CreateHashEntry(&dispPtr->winTable, (char *) winPtr->window, &notUsed); Tcl_SetHashValue(hPtr, winPtr); winPtr->dirtyAtts = 0; winPtr->dirtyChanges = 0; if (!(winPtr->flags & TK_TOP_HIERARCHY)) { TkWindow *winPtr2; /* * If any siblings higher up in the stacking order have already been * created then move this window to its rightful position in the * stacking order. * * NOTE: this code ignores any changes anyone might have made to the * sibling and stack_mode field of the window's attributes, so it * really isn't safe for these to be manipulated except by calling * Tk_RestackWindow. */ for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL; winPtr2 = winPtr2->nextPtr) { if ((winPtr2->window != None) && !(winPtr2->flags & (TK_TOP_HIERARCHY|TK_REPARENTED))) { XWindowChanges changes; changes.sibling = winPtr2->window; changes.stack_mode = Below; XConfigureWindow(winPtr->display, winPtr->window, CWSibling | CWStackMode, &changes); break; } } } /* * Issue a ConfigureNotify event if there were deferred configuration * changes (but skip it if the window is being deleted; the * ConfigureNotify event could cause problems if we're being called from * Tk_DestroyWindow under some conditions). */ if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY) && !(winPtr->flags & TK_ALREADY_DEAD)) { winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY; DoConfigureNotify((Tk_FakeWin *) tkwin); } } /* *---------------------------------------------------------------------- * * CreateBusy -- * * Creates a child transparent window that obscures its parent window * thereby effectively blocking device events. The size and position of * the busy window is exactly that of the reference window. * * We want to create sibling to the window to be blocked. If the busy * window is a child of the window to be blocked, Enter/Leave events can * sneak through. Futhermore under WIN32, messages of transparent windows * are sent directly to the parent. The only exception to this are * toplevels, since we can't make a sibling. Fortunately, toplevel * windows rarely receive events that need blocking. * * Results: * Returns a pointer to the new busy window structure. * * Side effects: * When the busy window is eventually displayed, it will screen device * events (in the area of the reference window) from reaching its parent * window and its children. User feed back can be achieved by changing * the cursor. * *---------------------------------------------------------------------- */ static Busy * CreateBusy( Tcl_Interp *interp, /* Interpreter to report error to */ Tk_Window tkRef) /* Window hosting the busy window */ { Busy *busyPtr; size_t length; int x, y; const char *fmt; char *name; Tk_Window tkBusy, tkChild, tkParent; Window parent; Tk_FakeWin *winPtr; busyPtr = (Busy *)ckalloc(sizeof(Busy)); x = y = 0; length = strlen(Tk_Name(tkRef)); name = (char *)ckalloc(length + 6); if (Tk_IsTopLevel(tkRef)) { fmt = "_Busy"; /* Child */ tkParent = tkRef; } else { Tk_Window tkwin; fmt = "%s_Busy"; /* Sibling */ tkParent = Tk_Parent(tkRef); for (tkwin = tkRef; (tkwin != NULL) && !Tk_IsTopLevel(tkwin); tkwin = Tk_Parent(tkwin)) { if (tkwin == tkParent) { break; } x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width; y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width; } } for (tkChild = FirstChild(tkParent); tkChild != NULL; tkChild = NextChild(tkChild)) { Tk_MakeWindowExist(tkChild); } sprintf(name, fmt, Tk_Name(tkRef)); tkBusy = Tk_CreateWindow(interp, tkParent, name, NULL); ckfree(name); if (tkBusy == NULL) { return NULL; } Tk_MakeWindowExist(tkRef); busyPtr->display = Tk_Display(tkRef); busyPtr->interp = interp; busyPtr->tkRef = tkRef; busyPtr->tkParent = tkParent; busyPtr->tkBusy = tkBusy; busyPtr->width = Tk_Width(tkRef); busyPtr->height = Tk_Height(tkRef); busyPtr->x = Tk_X(tkRef); busyPtr->y = Tk_Y(tkRef); busyPtr->cursor = NULL; Tk_SetClass(tkBusy, "Busy"); busyPtr->optionTable = Tk_CreateOptionTable(interp, busyOptionSpecs); if (Tk_InitOptions(interp, (char *) busyPtr, busyPtr->optionTable, tkBusy) != TCL_OK) { Tk_DestroyWindow(tkBusy); return NULL; } SetWindowInstanceData(tkBusy, busyPtr); winPtr = (Tk_FakeWin *) tkRef; TkpCreateBusy(winPtr, tkRef, &parent, tkParent, busyPtr); MakeTransparentWindowExist(tkBusy, parent); Tk_MoveResizeWindow(tkBusy, x, y, busyPtr->width, busyPtr->height); /* * Only worry if the busy window is destroyed. */ Tk_CreateEventHandler(tkBusy, StructureNotifyMask, BusyEventProc, busyPtr); /* * Indicate that the busy window's geometry is being managed. This will * also notify us if the busy window is ever packed. */ Tk_ManageGeometry(tkBusy, &busyMgrInfo, busyPtr); if (busyPtr->cursor != NULL) { Tk_DefineCursor(tkBusy, busyPtr->cursor); } /* * Track the reference window to see if it is resized or destroyed. */ Tk_CreateEventHandler(tkRef, StructureNotifyMask, RefWinEventProc, busyPtr); return busyPtr; } /* *---------------------------------------------------------------------- * * ConfigureBusy -- * * This procedure is called from the Tk event dispatcher. It releases X * resources and memory used by the busy window and updates the internal * hash table. * * Results: * None. * * Side effects: * Memory and resources are released and the Tk event handler is removed. * *---------------------------------------------------------------------- */ static int ConfigureBusy( Tcl_Interp *interp, Busy *busyPtr, int objc, Tcl_Obj *const objv[]) { Tk_Cursor oldCursor = busyPtr->cursor; if (Tk_SetOptions(interp, (char *) busyPtr, busyPtr->optionTable, objc, objv, busyPtr->tkBusy, NULL, NULL) != TCL_OK) { return TCL_ERROR; } if (busyPtr->cursor != oldCursor) { if (busyPtr->cursor == NULL) { Tk_UndefineCursor(busyPtr->tkBusy); } else { Tk_DefineCursor(busyPtr->tkBusy, busyPtr->cursor); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * GetBusy -- * * Returns the busy window structure associated with the reference * window, keyed by its path name. The clientData argument is the main * window of the interpreter, used to search for the reference window in * its own window hierarchy. * * Results: * If path name represents a reference window with a busy window, a * pointer to the busy window structure is returned. Otherwise, NULL is * returned and an error message is left in interp->result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Busy * GetBusy( Tcl_Interp *interp, /* Interpreter to look up main window of. */ Tcl_HashTable *busyTablePtr,/* Busy hash table */ Tcl_Obj *const windowObj) /* Path name of parent window */ { Tcl_HashEntry *hPtr; Tk_Window tkwin; if (TkGetWindowFromObj(interp, Tk_MainWindow(interp), windowObj, &tkwin) != TCL_OK) { return NULL; } hPtr = Tcl_FindHashEntry(busyTablePtr, (char *) tkwin); if (hPtr == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "can't find busy window \"%s\"", Tcl_GetString(windowObj))); Tcl_SetErrorCode(interp, "TK", "LOOKUP", "BUSY", Tcl_GetString(windowObj), NULL); return NULL; } return (Busy *)Tcl_GetHashValue(hPtr); } /* *---------------------------------------------------------------------- * * HoldBusy -- * * Creates (if necessary) and maps a busy window, thereby preventing * device events from being be received by the parent window and its * children. * * Results: * Returns a standard TCL result. If path name represents a busy window, * it is unmapped and TCL_OK is returned. Otherwise, TCL_ERROR is * returned and an error message is left in interp->result. * * Side effects: * The busy window is created and displayed, blocking events from the * parent window and its children. * *---------------------------------------------------------------------- */ static int HoldBusy( Tcl_HashTable *busyTablePtr,/* Busy hash table. */ Tcl_Interp *interp, /* Interpreter to report errors to. */ Tcl_Obj *const windowObj, /* Window name. */ int configObjc, /* Option pairs. */ Tcl_Obj *const configObjv[]) { Tk_Window tkwin; Tcl_HashEntry *hPtr; Busy *busyPtr; int isNew, result; if (TkGetWindowFromObj(interp, Tk_MainWindow(interp), windowObj, &tkwin) != TCL_OK) { return TCL_ERROR; } hPtr = Tcl_CreateHashEntry(busyTablePtr, (char *) tkwin, &isNew); if (isNew) { busyPtr = CreateBusy(interp, tkwin); if (busyPtr == NULL) { return TCL_ERROR; } Tcl_SetHashValue(hPtr, busyPtr); busyPtr->hashPtr = hPtr; } else { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); } busyPtr->tablePtr = busyTablePtr; result = ConfigureBusy(interp, busyPtr, configObjc, configObjv); /* * Don't map the busy window unless the reference window is also currently * displayed. */ if (Tk_IsMapped(busyPtr->tkRef)) { TkpShowBusyWindow(busyPtr); } else { TkpHideBusyWindow(busyPtr); } return result; } /* *---------------------------------------------------------------------- * * Tk_BusyObjCmd -- * * This function is invoked to process the "tk busy" Tcl command. See the * user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ int Tk_BusyObjCmd( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { Tk_Window tkwin = (Tk_Window)clientData; Tcl_HashTable *busyTablePtr = &((TkWindow *) tkwin)->mainPtr->busyTable; Busy *busyPtr; Tcl_Obj *objPtr; int index, result = TCL_OK; static const char *const optionStrings[] = { "cget", "configure", "current", "forget", "hold", "status", NULL }; enum options { BUSY_CGET, BUSY_CONFIGURE, BUSY_CURRENT, BUSY_FORGET, BUSY_HOLD, BUSY_STATUS }; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "options ?arg arg ...?"); return TCL_ERROR; } /* * [tk busy <window>] command shortcut. */ if (Tcl_GetString(objv[1])[0] == '.') { if (objc%2 == 1) { Tcl_WrongNumArgs(interp, 1, objv, "window ?option value ...?"); return TCL_ERROR; } return HoldBusy(busyTablePtr, interp, objv[1], objc-2, objv+2); } if (Tcl_GetIndexFromObjStruct(interp, objv[1], optionStrings, sizeof(char *), "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum options) index) { case BUSY_CGET: if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "window option"); return TCL_ERROR; } busyPtr = GetBusy(interp, busyTablePtr, objv[2]); if (busyPtr == NULL) { return TCL_ERROR; } Tcl_Preserve(busyPtr); objPtr = Tk_GetOptionValue(interp, (char *) busyPtr, busyPtr->optionTable, objv[3], busyPtr->tkBusy); if (objPtr == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } Tcl_Release(busyPtr); return result; case BUSY_CONFIGURE: if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "window ?option? ?value ...?"); return TCL_ERROR; } busyPtr = GetBusy(interp, busyTablePtr, objv[2]); if (busyPtr == NULL) { return TCL_ERROR; } Tcl_Preserve(busyPtr); if (objc <= 4) { objPtr = Tk_GetOptionInfo(interp, (char *)busyPtr, busyPtr->optionTable, (objc == 4) ? objv[3] : NULL, busyPtr->tkBusy); if (objPtr == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } } else { result = ConfigureBusy(interp, busyPtr, objc-3, objv+3); } Tcl_Release(busyPtr); return result; case BUSY_CURRENT: { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; const char *pattern = (objc == 3 ? Tcl_GetString(objv[2]) : NULL); objPtr = Tcl_NewObj(); for (hPtr = Tcl_FirstHashEntry(busyTablePtr, &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); if (pattern == NULL || Tcl_StringCaseMatch(Tk_PathName(busyPtr->tkRef), pattern, 0)) { Tcl_ListObjAppendElement(interp, objPtr, TkNewWindowObj(busyPtr->tkRef)); } } Tcl_SetObjResult(interp, objPtr); return TCL_OK; } case BUSY_FORGET: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "window"); return TCL_ERROR; } busyPtr = GetBusy(interp, busyTablePtr, objv[2]); if (busyPtr == NULL) { return TCL_ERROR; } TkpHideBusyWindow(busyPtr); Tcl_EventuallyFree(busyPtr, (Tcl_FreeProc *)DestroyBusy); return TCL_OK; case BUSY_HOLD: if (objc < 3 || objc%2 != 1) { Tcl_WrongNumArgs(interp, 2, objv, "window ?option value ...?"); return TCL_ERROR; } return HoldBusy(busyTablePtr, interp, objv[2], objc-3, objv+3); case BUSY_STATUS: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "window"); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewBooleanObj( GetBusy(interp, busyTablePtr, objv[2]) != NULL)); return TCL_OK; } Tcl_Panic("unhandled option: %d", index); return TCL_ERROR; /* Unreachable */ } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */