Files
cpython-source-deps/generic/tkBusy.c
2021-03-30 00:54:10 +01:00

930 lines
25 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 on some
* platforms this may or may not have an effect.
*/
static const Tk_OptionSpec busyOptionSpecs[] = {
{TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
DEF_BUSY_CURSOR, -1, Tk_Offset(Busy, cursor),
TK_OPTION_NULL_OK, 0, 0},
{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,
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(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:
*/