diff options
Diffstat (limited to 'generic/tkBusy.c')
-rw-r--r-- | generic/tkBusy.c | 928 |
1 files changed, 928 insertions, 0 deletions
diff --git a/generic/tkBusy.c b/generic/tkBusy.c new file mode 100644 index 0000000..65248a2 --- /dev/null +++ b/generic/tkBusy.c @@ -0,0 +1,928 @@ +/* + * tkBusy.c -- + * + * This file provides functions that implement busy for Tk. + * + * Copyright 1993-1998 Lucent Technologies, Inc. + * + * The "busy" command was created by George Howlett. Adapted for + * integration into Tk by Jos Decoster and Donal K. Fellows. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkInt.h" +#include "tkBusy.h" +#include "default.h" + +/* + * Things about the busy system that may be configured. Note that currently on + * OSX/Aqua, that's nothing at all. + */ + +static const Tk_OptionSpec busyOptionSpecs[] = { +#ifndef MAC_OSX_TK + {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", + DEF_BUSY_CURSOR, -1, Tk_Offset(Busy, cursor), + TK_OPTION_NULL_OK, 0, 0}, +#endif + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0} +}; + +/* + * Forward declarations of functions defined in this file. + */ + +static void BusyEventProc(ClientData clientData, + XEvent *eventPtr); +static void BusyGeometryProc(ClientData clientData, + Tk_Window tkwin); +static void BusyCustodyProc(ClientData clientData, + Tk_Window tkwin); +static int ConfigureBusy(Tcl_Interp *interp, Busy *busyPtr, + int objc, Tcl_Obj *const objv[]); +static Busy * CreateBusy(Tcl_Interp *interp, Tk_Window tkRef); +static void DestroyBusy(void *dataPtr); +static void DoConfigureNotify(Tk_FakeWin *winPtr); +static inline Tk_Window FirstChild(Tk_Window parent); +static Busy * GetBusy(Tcl_Interp *interp, + Tcl_HashTable *busyTablePtr, + Tcl_Obj *const windowObj); +static int HoldBusy(Tcl_HashTable *busyTablePtr, + Tcl_Interp *interp, Tcl_Obj *const windowObj, + int configObjc, Tcl_Obj *const configObjv[]); +static void MakeTransparentWindowExist(Tk_Window tkwin, + Window parent); +static inline Tk_Window NextChild(Tk_Window tkwin); +static void RefWinEventProc(ClientData clientData, + register 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. + * + *---------------------------------------------------------------------- + */ + +/* ARGSUSED */ +static void +BusyCustodyProc( + ClientData clientData, /* Information about the busy window. */ + Tk_Window tkwin) /* Not used. */ +{ + Busy *busyPtr = 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. + * + *---------------------------------------------------------------------- + */ + +/* ARGSUSED */ +static void +BusyGeometryProc( + ClientData clientData, /* Information about window that got new + * preferred geometry. */ + Tk_Window tkwin) /* 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 */ + register XEvent *eventPtr) /* Event which triggered call to routine */ +{ + register Busy *busyPtr = 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 */ +{ + register Busy *busyPtr = data; + + if (busyPtr->hashPtr != NULL) { + Tcl_DeleteHashEntry(busyPtr->hashPtr); + } + Tk_DeleteEventHandler(busyPtr->tkRef, StructureNotifyMask, + RefWinEventProc, busyPtr); + + if (busyPtr->tkBusy != NULL) { + Tk_FreeConfigOptions(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 = 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); + + dispPtr = winPtr->dispPtr; + hPtr = Tcl_CreateHashEntry(&dispPtr->winTable, (char *) winPtr->window, + ¬Used); + 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; + int length, x, y; + const char *fmt; + char *name; + Tk_Window tkBusy, tkChild, tkParent; + Window parent; + Tk_FakeWin *winPtr; + + busyPtr = ckalloc(sizeof(Busy)); + x = y = 0; + length = strlen(Tk_Name(tkRef)); + name = 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 = None; + 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 != None) { + 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 == None) { + 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 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 = 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 = 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 = Tcl_GetHashValue(hPtr); + if (pattern == NULL || + Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), pattern)) { + 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: + */ |