/* * bltBusy.c -- * * * This file has been changed to fit into the tkrat distribution. * I have among other things changed the semantics of the release command * so that it ignores errors. * * maf@dtek.chalmers.se * * * This module implements busy windows for the BLT toolkit. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "busy" command was created by George Howlett. */ #include "tcl.h" #include "tk.h" #include #include #include #include "blt.h" #ifndef CONST84 # define CONST84 #endif #define TRUE 1 #define FALSE 0 #ifndef TK_REPARENTED #define TK_REPARENTED 0 #endif typedef struct { Display *display; /* Display of busy window */ Tcl_Interp *interp; /* Interpreter where "busy" command was * created. It's used to key the * searches in the window hierarchy. See the * "windows" command. */ Tk_Window busy; /* Busy window: Transparent window used * to block delivery of events to windows * underneath it. */ Tk_Window parent; /* Parent window of the busy * window. It may be the reference * window (if the reference is a * toplevel) or a mutual ancestor of * the reference window */ Tk_Window tkwin; /* Reference window of the busy window. * It is used to manage the size and * position of the busy window. */ int x, y; /* Position of the reference window */ int width, height; /* Size of the reference window. Retained to * know if the reference window has been * reconfigured to a new size. */ int isBusy; /* Indicates whether the transparent * window should be displayed. This * can be different from what * Tk_IsMapped says because the a * sibling reference window may be * unmapped, forcing the busy window * to be also hidden. */ int menuBar; /* Menu bar flag. */ Tk_Cursor cursor; /* Cursor for the busy window. */ } Busy; #ifdef WIN32 #define DEF_BUSY_CURSOR "wait" #else #define DEF_BUSY_CURSOR "watch" #endif static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor", DEF_BUSY_CURSOR, Tk_Offset(Busy, cursor), TK_CONFIG_NULL_OK}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; static int initialized = 0; /* If non-zero, indicates to * initialize the hash table */ static Tcl_HashTable busyTable; /* Hash table of busy window * structures keyed by the address of * the reference Tk window */ static void BusyGeometryProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static void BusyCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); 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 */ }; /* Forward declarations */ static void DestroyBusy _ANSI_ARGS_((char* dataPtr)); static void BusyEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); #ifdef __STDC__ static Tk_EventProc BusyEventProc; static Tk_EventProc RefWinEventProc; static Tcl_CmdProc BusyCmd; #endif /* *---------------------------------------------------------------------- * * 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, eventPtr) ClientData clientData; /* Busy window record */ XEvent *eventPtr; /* Event which triggered call to routine */ { Busy *busyPtr = (Busy *)clientData; if (eventPtr->type == DestroyNotify) { busyPtr->busy = NULL; Tk_EventuallyFree((ClientData)busyPtr, DestroyBusy); } } /* * ---------------------------------------------------------------------------- * * 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, tkwin) ClientData clientData; /* Information about the slave window */ Tk_Window tkwin; /* Not used */ { Busy *busyPtr = (Busy *)clientData; Tk_DeleteEventHandler(busyPtr->busy, StructureNotifyMask, BusyEventProc, (ClientData)busyPtr); if (busyPtr->busy != NULL) { Tk_UnmapWindow(busyPtr->busy); busyPtr->busy = NULL; } busyPtr->isBusy = FALSE; Tk_EventuallyFree((ClientData)busyPtr, DestroyBusy); } /* * ---------------------------------------------------------------------------- * * BusyGeometryProc -- * * This procedure is invoked by Tk_GeometryRequest for busy * windows. Busy windows never request geometry, so it's * unlikely that this routine will ever be called. The routine * exists simply as a place holder for the GeomProc in the * Geometry Manager structure. * * Results: * None. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static void BusyGeometryProc(clientData, tkwin) ClientData clientData; /* Information about window that got new * preferred geometry. */ Tk_Window tkwin; /* Other Tk-related information about the * window. */ { /* Should never get here */ } /* * ------------------------------------------------------------------ * * 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, eventPtr) ClientData clientData; /* Busy window record */ register XEvent *eventPtr; /* Event which triggered call to routine */ { register Busy *busyPtr = (Busy *)clientData; switch (eventPtr->type) { case DestroyNotify: /* * Arrange for the busy structure to be removed at a proper time. */ Tk_EventuallyFree((ClientData)busyPtr, DestroyBusy); break; case ConfigureNotify: if ((busyPtr->width != Tk_Width(busyPtr->tkwin)) || (busyPtr->height != Tk_Height(busyPtr->tkwin)) || (busyPtr->x != Tk_X(busyPtr->tkwin)) || (busyPtr->y != Tk_Y(busyPtr->tkwin))) { int x, y; busyPtr->width = Tk_Width(busyPtr->tkwin); busyPtr->height = Tk_Height(busyPtr->tkwin); busyPtr->x = Tk_X(busyPtr->tkwin); busyPtr->y = Tk_Y(busyPtr->tkwin); x = y = 0; if (busyPtr->parent != busyPtr->tkwin) { Tk_Window ancestor; for (ancestor = busyPtr->tkwin; ancestor != busyPtr->parent; ancestor = Tk_Parent(ancestor)) { x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width; y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width; } } #ifdef DEBUG PurifyPrintf("2 busyPtr->width=%d, busyPtr->height=%d\n", busyPtr->width, busyPtr->height); #endif if (busyPtr->busy != NULL) { Tk_MoveResizeWindow(busyPtr->busy, x, y, busyPtr->width, busyPtr->height); } } break; case MapNotify: if ((busyPtr->parent != busyPtr->tkwin) && (busyPtr->isBusy)) { Tk_MapWindow(busyPtr->busy); } break; case UnmapNotify: if (busyPtr->parent != busyPtr->tkwin) { Tk_UnmapWindow(busyPtr->busy); } break; } } /* * ------------------------------------------------------------------ * * 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(interp, busyPtr, argc, argv) Tcl_Interp *interp; Busy *busyPtr; int argc; char **argv; { Tk_Cursor oldCursor; oldCursor = busyPtr->cursor; if (Tk_ConfigureWidget(interp, busyPtr->tkwin, configSpecs, argc, (CONST84 char**)argv, (char*)busyPtr, 0) != TCL_OK) { return TCL_ERROR; } if (busyPtr->cursor != oldCursor) { if (busyPtr->cursor == None) { Tk_UndefineCursor(busyPtr->busy); } else { Tk_DefineCursor(busyPtr->busy, busyPtr->cursor); } } return TCL_OK; } /* * ------------------------------------------------------------------ * * 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(interp, tkwin) Tcl_Interp *interp; /* Interpreter to report error to */ Tk_Window tkwin; /* Window hosting the busy window */ { Busy *busyPtr; int length; char *fmt, *name; Tk_Window busy; Window parentWin; Tk_Window parent; Tk_FakeWin *winPtr; int x, y; busyPtr = (Busy *)calloc(1, sizeof(Busy)); x = y = 0; length = strlen(Tk_Name(tkwin)); name = (char *)ckalloc(length + 6); if (Tk_IsTopLevel(tkwin)) { fmt = "_Busy"; /* Child */ parent = tkwin; } else { Tk_Window ancestor; fmt = "%s_Busy"; /* Sibling */ parent = Tk_Parent(tkwin); for (ancestor = tkwin; ancestor != parent; ancestor = Tk_Parent(ancestor)) { x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width; y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width; if (Tk_IsTopLevel(ancestor)) { break; } } } sprintf(name, fmt, Tk_Name(tkwin)); busy = Tk_CreateWindow(interp, parent, name, (char *)NULL); ckfree((char *)name); if (busy == NULL) { return NULL; } Tk_MakeWindowExist(tkwin); busyPtr->display = Tk_Display(tkwin); busyPtr->tkwin = tkwin; busyPtr->parent = parent; busyPtr->interp = interp; busyPtr->width = Tk_Width(tkwin); busyPtr->height = Tk_Height(tkwin); busyPtr->x = Tk_X(tkwin); busyPtr->y = Tk_Y(tkwin); busyPtr->cursor = None; busyPtr->busy = busy; Tk_SetClass(busy, "Busy"); #if (TK_MAJOR_VERSION >= 8) Blt_SetWindowInstanceData(busy, (ClientData)busyPtr); #endif winPtr = (Tk_FakeWin *)tkwin; if (winPtr->flags & TK_REPARENTED) { /* * This works around a bug in the implementation of menubars * for non-MacIntosh window systems (Win32 and X11). Tk * doesn't reset the pointers to the parent window when the * menu is reparented (winPtr->parentPtr points to the * wrong window). We get around this by determining the parent * via the native API calls. */ #ifdef WIN32 { HWND hWnd; RECT region; hWnd = GetParent(Tk_GetHWND(Tk_WindowId(tkwin))); parentWin = (Window)hWnd; if (GetWindowRect(hWnd, ®ion)) { busyPtr->width = region.right - region.left; busyPtr->height = region.bottom - region.top; #ifdef WINDEBUG PurifyPrintf("0. busyPtr->width=%d, busyPtr->height=%d\n", busyPtr->width, busyPtr->height); #endif } } #else parentWin = Blt_GetParent(Tk_Display(tkwin), Tk_WindowId(tkwin)); #endif } else { parentWin = Tk_WindowId(parent); #ifdef WIN32 parentWin = (Window)Tk_GetHWND(parentWin); #endif } Blt_MakeTransparentWindowExist(busy, parentWin); #ifdef WINDEBUG PurifyPrintf("1. busyPtr->width=%d, busyPtr->height=%d\n", busyPtr->width, busyPtr->height); #endif Tk_MoveResizeWindow(busy, x, y, busyPtr->width, busyPtr->height); Tk_RestackWindow(busy, Above, (Tk_Window)NULL); /* * Only worry if the busy window is destroyed. */ Tk_CreateEventHandler(busy, StructureNotifyMask, BusyEventProc, (ClientData)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(busy, &busyMgrInfo, (ClientData)busyPtr); if (busyPtr->cursor != None) { Tk_DefineCursor(busy, busyPtr->cursor); } /* Track the reference window to see if it is resized or destroyed. */ Tk_CreateEventHandler(tkwin, StructureNotifyMask, RefWinEventProc, (ClientData)busyPtr); return (busyPtr); } /* * ------------------------------------------------------------------ * * 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(char *dataPtr) { Busy *busyPtr = (Busy *)dataPtr; Tcl_HashEntry *hPtr; Tk_FreeOptions(configSpecs, (char *)busyPtr, busyPtr->display, 0); hPtr = Tcl_FindHashEntry(&busyTable, (char *)busyPtr->tkwin); if (hPtr != NULL) { Tcl_DeleteHashEntry(hPtr); } Tk_DeleteEventHandler(busyPtr->tkwin, StructureNotifyMask, RefWinEventProc, (ClientData)busyPtr); if (busyPtr->busy != NULL) { Tk_DeleteEventHandler(busyPtr->busy, StructureNotifyMask, BusyEventProc, (ClientData)busyPtr); Tk_ManageGeometry(busyPtr->busy, (Tk_GeomMgr *) NULL, (ClientData)busyPtr); Tk_DestroyWindow(busyPtr->busy); } free((char *)busyPtr); } /* * ------------------------------------------------------------------ * * 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. * * ------------------------------------------------------------------- */ static int GetBusy(clientData, interp, pathName, busyPtrPtr) ClientData clientData; /* Window used to reference search */ Tcl_Interp *interp; /* Interpreter to report errors to */ char *pathName; /* Path name of parent window */ Busy **busyPtrPtr; /* Will contain address of busy window if * found. */ { Tcl_HashEntry *hPtr; Tk_Window tkwin; tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&busyTable, (char *)tkwin); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"", (char *)NULL); return TCL_ERROR; } *busyPtrPtr = ((Busy *)Tcl_GetHashValue(hPtr)); return TCL_OK; } /* * ------------------------------------------------------------------ * * 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(clientData, interp, argc, argv) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Window name and option pairs */ { Tk_Window tkwin; Tcl_HashEntry *hPtr; Busy *busyPtr; int isNew; int result; tkwin = Tk_NameToWindow(interp, argv[0], Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_CreateHashEntry(&busyTable, (char *)tkwin, &isNew); if (isNew) { busyPtr = (Busy *)CreateBusy(interp, tkwin); if (busyPtr == NULL) { return TCL_ERROR; } Tcl_SetHashValue(hPtr, (char *)busyPtr); } else { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); /* * Raise and re-map the busy window whenever hold is reasserted. */ Tk_RestackWindow(busyPtr->busy, Above, (Tk_Window)NULL); } /* * Don't map the busy window unless the reference window is also displayed */ if (Tk_IsMapped(busyPtr->tkwin)) { Tk_MapWindow(busyPtr->busy); } else { Tk_UnmapWindow(busyPtr->busy); } busyPtr->isBusy = TRUE; Tk_Preserve((ClientData)busyPtr); result = ConfigureBusy(interp, busyPtr, argc - 1, argv + 1); Tk_Release((ClientData)busyPtr); #ifdef WIN32 { POINT point; /* * Under Win32, cursors aren't associated with windows. Tk * fakes this by watching Motion events on its windows. So Tk * will automatically change the cursor when the pointer * enters the Busy window. But Windows doesn't immediately * change the cursor; it waits for the cursor position to * change or a system call. We need to change the cursor * before the application starts processing, so set the cursor * position redundantly back to the current position. */ GetCursorPos(&point); SetCursorPos(point.x, point.y); } #endif return result; } /* * ------------------------------------------------------------------ * * StatusOp -- * * Returns the status of the busy window; whether it's blocking * events or not. * * Results: * Returns a standard TCL result. If path name represents a busy * window, the status is returned via interp->result and TCL_OK * is returned. Otherwise, TCL_ERROR is returned and an error * message is left in interp->result. * * ------------------------------------------------------------------- */ /*ARGSUSED*/ static int StatusOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of interpreter */ Tcl_Interp *interp; /* Interpreter to report error to */ int argc; /* not used */ char **argv; { Busy *busyPtr; if (GetBusy(clientData, interp, argv[2], &busyPtr) != TCL_OK) { return TCL_ERROR; } Tk_Preserve((ClientData)busyPtr); Tcl_SetResult(interp, busyPtr->isBusy ? "1" : "0", TCL_STATIC); Tk_Release((ClientData)busyPtr); return TCL_OK; } /* * ------------------------------------------------------------------ * * ForgetOp -- * * Destroys the busy window associated with the reference window and * arranges for internal resources to the released when they're * not being used anymore. * * Results: * Returns a standard TCL result. If path name represents a busy * window, it is destroyed 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 removed. Other related memory and resources * are eventually released by the Tk dispatcher. * * ------------------------------------------------------------------- */ static int ForgetOp(clientData, interp, argc, argv) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { Busy *busyPtr; register int i; Tcl_HashEntry *hPtr; Tk_Window tkwin; Tk_Window mainWindow; mainWindow = Tk_MainWindow(interp); for (i = 2; i < argc; i++) { tkwin = Tk_NameToWindow(interp, argv[i], mainWindow); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&busyTable, (char *)tkwin); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find busy window \"", argv[i], "\"", (char *)NULL); return TCL_ERROR; } busyPtr = (Busy *)Tcl_GetHashValue(hPtr); /* Unmap the window even though it will be soon destroyed */ if (busyPtr->busy != NULL) { Tk_UnmapWindow(busyPtr->busy); } busyPtr->isBusy = FALSE; Tk_EventuallyFree((ClientData)busyPtr, DestroyBusy); } return TCL_OK; } /* * ------------------------------------------------------------------ * * ReleaseOp -- * * Unmaps the busy window, thereby permitting device events * to 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 hidden, allowing the parent window and * its children to receive events again. * * ------------------------------------------------------------------- */ static int ReleaseOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { Busy *busyPtr; int i; for (i = 2; i < argc; i++) { if (GetBusy(clientData, interp, argv[i], &busyPtr) != TCL_OK) { continue; } Tk_Preserve((ClientData)busyPtr); if (busyPtr->busy != NULL) { Tk_UnmapWindow(busyPtr->busy); } busyPtr->isBusy = FALSE; Tk_Release((ClientData)busyPtr); } return TCL_OK; } /* * ------------------------------------------------------------------ * * WindowsOp -- * * Reports the names of all widgets with busy windows attached to * them, matching a given pattern. If no pattern is given, all * busy widgets are listed. * * Results: * Returns a TCL list of the names of the widget with busy windows * attached to them, regardless if the widget is currently busy * or not. * * ------------------------------------------------------------------- */ static int WindowsOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; Busy *busyPtr; for (hPtr = Tcl_FirstHashEntry(&busyTable, &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); if (busyPtr->interp == interp) { if ((argc != 3) || (Tcl_StringMatch(Tk_PathName(busyPtr->tkwin), argv[2]))) { Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkwin)); } } } return TCL_OK; } /* * ------------------------------------------------------------------ * * IsBusyOp -- * * Reports the names of all widgets with busy windows attached to * them, matching a given pattern. If no pattern is given, all * busy widgets are listed. * * Results: * Returns a TCL list of the names of the widget with busy windows * attached to them, regardless if the widget is currently busy * or not. * * ------------------------------------------------------------------- */ static int IsBusyOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; Busy *busyPtr; for (hPtr = Tcl_FirstHashEntry(&busyTable, &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); if (busyPtr->interp == interp) { if ((busyPtr->isBusy) && ((argc != 3) || (Tcl_StringMatch(Tk_PathName(busyPtr->tkwin), argv[2])))) { Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkwin)); } } } return TCL_OK; } /* * ------------------------------------------------------------------ * * HoldOp -- * * Creates (if necessary) and maps a busy window, thereby * preventing device events from being be received by the parent * window and its children. The argument vector may contain * option-value pairs of configuration options to be set. * * Results: * Returns a standard TCL result. * * Side effects: * The busy window is created and displayed, blocking events from the * parent window and its children. * * ------------------------------------------------------------------- */ static int HoldOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Window name and option pairs */ { register int i, count; if ((argv[1][0] == 'h') && (strcmp(argv[1], "hold") == 0)) { argc--, argv++; /* Command used "hold" keyword */ } for (i = 1; i < argc; i++) { /* * Find the end of the option-value pairs for this window. */ for (count = i + 1; count < argc; count += 2) { if (argv[count][0] != '-') { break; } } if (count > argc) { count = argc; } if (HoldBusy(clientData, interp, count - i, argv + i) != TCL_OK) { return TCL_ERROR; } i = count; } return TCL_OK; } /* ARGSUSED*/ static int CgetOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Widget pathname and option switch */ { Busy *busyPtr; int result; if (GetBusy(clientData, interp, argv[2], &busyPtr) != TCL_OK) { return TCL_ERROR; } Tk_Preserve((ClientData)busyPtr); result = Tk_ConfigureValue(interp, busyPtr->tkwin, configSpecs, (char *)busyPtr, argv[3], 0); Tk_Release((ClientData)busyPtr); return result; } /* *---------------------------------------------------------------------- * * ConfigureOp -- * * This procedure is called to process an argv/argc list in order * to configure (or reconfigure) a busy window. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then interp->result contains an error message. * * Side effects: * Configuration information get set for busyPtr; old resources * get freed, if there were any. The busy window destroyed and * recreated in a new parent window. * *---------------------------------------------------------------------- */ static int ConfigureOp(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Reference window path name and options */ { Busy *busyPtr; int result; if (GetBusy(clientData, interp, argv[2], &busyPtr) != TCL_OK) { return TCL_ERROR; } Tk_Preserve((ClientData)busyPtr); if (argc == 3) { result = Tk_ConfigureInfo(interp, busyPtr->tkwin, configSpecs, (char *)busyPtr, (char *)NULL, 0); } else if (argc == 4) { result = Tk_ConfigureInfo(interp, busyPtr->tkwin, configSpecs, (char *)busyPtr, argv[3], 0); } else { result = ConfigureBusy(interp, busyPtr, argc - 3, argv + 3); } Tk_Release((ClientData)busyPtr); return result; } /* *-------------------------------------------------------------- * * Busy Sub-command specification: * * - Name of the sub-command. * - Minimum number of characters needed to unambiguously * recognize the sub-command. * - Pointer to the function to be called for the sub-command. * - Minimum number of arguments accepted. * - Maximum number of arguments accepted. * - String to be displayed for usage (arguments only). * *-------------------------------------------------------------- * static Blt_OpSpec busyOps[] = { {"cget", 2, (Blt_Operation)CgetOp, 4, 4, "window option",}, {"configure", 2, (Blt_Operation)ConfigureOp, 3, 0, "window ?options?...",}, {"forget", 1, (Blt_Operation)ForgetOp, 2, 0, "?window?...",}, {"hold", 3, (Blt_Operation)HoldOp, 3, 0, "window ?options?... ?window options?...",}, {"isbusy", 1, (Blt_Operation)IsBusyOp, 2, 3, "?pattern?",}, {"release", 1, (Blt_Operation)ReleaseOp, 2, 0, "?window?...",}, {"status", 1, (Blt_Operation)StatusOp, 3, 3, "window",}, {"windows", 1, (Blt_Operation)WindowsOp, 2, 3, "?pattern?",}, }; static int numBusyOps = sizeof(busyOps) / sizeof(Blt_OpSpec);*/ /* *---------------------------------------------------------------------- * * BusyCmd -- * * This procedure is invoked to process the "busy" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ static int BusyCmd(clientData, interp, argc, argv) ClientData clientData; /* Main window of the interpreter */ Tcl_Interp *interp; /* Interpreter associated with command */ int argc; CONST84 char **argv; { int result; if (!initialized) { Tcl_InitHashTable(&busyTable, TCL_ONE_WORD_KEYS); initialized = 1; } if ((argc > 1) && (argv[1][0] == '.')) { return (HoldOp(clientData, interp, argc, argv)); } if (!strcmp(argv[1], "cget") && argc == 4) { result = CgetOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "configure") && argc >= 3) { result = ConfigureOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "forget") && argc >= 2) { result = ForgetOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "hold") && argc >= 3) { result = HoldOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "isbusy") && argc >= 2 && argc <= 3) { result = IsBusyOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "release") && argc >= 2) { result = ReleaseOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "status") && argc == 3) { result = StatusOp(clientData, interp, argc, argv); } else if (!strcmp(argv[1], "windows") && argc >= 2 && argc <= 3) { result = WindowsOp(clientData, interp, argc, argv); } else { return TCL_ERROR; } return result; } int Blt_busy_Init(Tcl_Interp *interp) { Tcl_CreateCommand(interp, "blt_busy", BusyCmd, NULL, NULL); return Tcl_PkgProvide(interp, "blt_busy", BUSYLIB_VERSION); }