930 lines
25 KiB
C
930 lines
25 KiB
C
/*
|
||
* 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,
|
||
¬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;
|
||
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:
|
||
*/
|