home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltBusy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-06  |  25.2 KB  |  824 lines

  1. /*
  2.  * bltBusy.c --
  3.  *
  4.  *    This module implements busy windows for the Tk toolkit.
  5.  *
  6.  * Copyright 1993-1994 by AT&T Bell Laboratories.
  7.  * Permission to use, copy, modify, and distribute this software
  8.  * and its documentation for any purpose and without fee is hereby
  9.  * granted, provided that the above copyright notice appear in all
  10.  * copies and that both that the copyright notice and warranty
  11.  * disclaimer appear in supporting documentation, and that the
  12.  * names of AT&T Bell Laboratories any of their entities not be used
  13.  * in advertising or publicity pertaining to distribution of the
  14.  * software without specific, written prior permission.
  15.  *
  16.  * AT&T disclaims all warranties with regard to this software, including
  17.  * all implied warranties of merchantability and fitness.  In no event
  18.  * shall AT&T be liable for any special, indirect or consequential
  19.  * damages or any damages whatsoever resulting from loss of use, data
  20.  * or profits, whether in an action of contract, negligence or other
  21.  * tortuous action, arising out of or in connection with the use or
  22.  * performance of this software.
  23.  *
  24.  * Busy command created by George Howlett.
  25.  */
  26.  
  27. #include "blt.h"
  28. #include <X11/Xutil.h>
  29. #include <X11/Xatom.h>
  30.  
  31. #ifndef BUSY_VERSION
  32. #define BUSY_VERSION "1.2"
  33. #endif
  34.  
  35. typedef struct {
  36.     Tk_Window host;        /* Host window of the busy window. It is used
  37.                  * to manage the size and position of the busy
  38.                  * window. */
  39.     unsigned int width, height;    /* Last known size of the host window. Also
  40.                  * specifies the size of the busy window. */
  41.     int hostX, hostY;        /* Last known position of the host window */
  42.     int x, y;            /* Position of the busy window in its parent */
  43.     Tk_Window mainWin;        /* Used to key searches in the window hierarchy
  44.                  * See the "hosts" command. */
  45.     int mapped;            /* If non-zero, busy window is mapped */
  46.     Window window;        /* Busy window: InputOnly class window */
  47.     Display *display;        /* Display of parent, host, and busy windows */
  48.  
  49.     Cursor cursor;        /* Cursor for the busy window */
  50.     Tk_Window parent;        /* Parent window of the busy window. It may be
  51.                      * the host window or a mutual ancestor of the
  52.                  * host window */
  53.  
  54. } BusyWin;
  55.  
  56. #define TRUE        1
  57. #define FALSE        0
  58. #define DEF_BUSY_CURSOR "watch"
  59.  
  60. static int ParseParent _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  61.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  62. static char *PrintParent _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  63.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  64.  
  65. static Tk_CustomOption ParentOption =
  66. {
  67.     ParseParent, PrintParent, (ClientData)0
  68. };
  69.  
  70. static Tk_ConfigSpec configSpecs[] =
  71. {
  72.     {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor",
  73.     DEF_BUSY_CURSOR, Tk_Offset(BusyWin, cursor), TK_CONFIG_NULL_OK},
  74.     {TK_CONFIG_CUSTOM, "-in", (char *)NULL, (char *)NULL, (char *)NULL,
  75.     Tk_Offset(BusyWin, parent), TK_CONFIG_NULL_OK, &ParentOption},
  76.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  77. };
  78.  
  79. static int initialized = 0;    /* Flag to initialize the hash table */
  80. static Tcl_HashTable busyWinTable;    /* Hash table of busy window structures keyed
  81.                    * by the address of the host Tk window */
  82.  
  83. /* Forward declarations */
  84. static void DestroyBusyWindow _ANSI_ARGS_((ClientData clientData));
  85.  
  86. /*
  87.  *----------------------------------------------------------------------
  88.  *
  89.  * ParseParent --
  90.  *
  91.  *    Convert the pathname of a Tk window and check to see if its a
  92.  *    valid parent for the busy window.
  93.  *
  94.  *     A valid parent window is either
  95.  *
  96.  *            1) the host window itself, or
  97.  *         2) a mutual ancestor of the host window.
  98.  *
  99.  *
  100.  *----------------------------------------------------------------------
  101.  */
  102. /*ARGSUSED*/
  103. static int
  104. ParseParent(clientData, interp, tkwin, value, widgRec, offset)
  105.     ClientData clientData;    /* not used */
  106.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  107.     Tk_Window tkwin;        /* Used to search for window */
  108.     char *value;        /* Pathname of new parent window */
  109.     char *widgRec;        /* BusyWin structure record */
  110.     int offset;            /* not used */
  111. {
  112.     BusyWin *busyPtr = (BusyWin *)(widgRec);
  113.     Tk_Window parent;
  114.     int x, y;
  115.  
  116.     x = y = 0;
  117.     if (value == NULL) {
  118.     parent = busyPtr->host;
  119.     } else {
  120.     parent = Tk_NameToWindow(interp, value, tkwin);
  121.     if (parent == NULL) {
  122.         return TCL_ERROR;
  123.     }
  124.     if (parent != busyPtr->host) {
  125.         Tk_Window ancestor;
  126.  
  127.         for (ancestor = busyPtr->host; ancestor != parent;
  128.         ancestor = Tk_Parent(ancestor)) {
  129.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  130.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  131.         if (Tk_IsTopLevel(ancestor)) {
  132.             break;
  133.         }
  134.         }
  135.         if (ancestor != parent) {
  136.         Tcl_AppendResult(interp, "\"", Tk_PathName(parent),
  137.             "\" in not a parent of \"", Tk_PathName(busyPtr->host),
  138.             "\"", (char *)NULL);
  139.         return TCL_ERROR;
  140.         }
  141.     }
  142.     }
  143.     if (parent != busyPtr->parent) {
  144.     if (busyPtr->window != None) {
  145.         XReparentWindow(busyPtr->display, busyPtr->window,
  146.         Tk_WindowId(parent), x, y);
  147.         XMapRaised(busyPtr->display, busyPtr->window);
  148.     }
  149.     busyPtr->parent = parent;
  150.     busyPtr->x = x;
  151.     busyPtr->y = y;
  152.     }
  153.     return TCL_OK;
  154. }
  155.  
  156. /*
  157.  *----------------------------------------------------------------------
  158.  *
  159.  * PrintParent --
  160.  *
  161.  *    Returns the path name of the parent window for busy window
  162.  *
  163.  * Results:
  164.  *    The path name of the parent of the busy window.
  165.  *
  166.  *----------------------------------------------------------------------
  167.  */
  168. /*ARGSUSED*/
  169. static char *
  170. PrintParent(clientData, tkwin, widgRec, offset, freeProcPtr)
  171.     ClientData clientData;    /* not used */
  172.     Tk_Window tkwin;        /* not used */
  173.     char *widgRec;        /* BusyWin structure record */
  174.     int offset;            /* not used */
  175.     Tcl_FreeProc **freeProcPtr;    /* not used */
  176. {
  177.     BusyWin *busyPtr = (BusyWin *)(widgRec);
  178.  
  179.     return (Tk_PathName(busyPtr->parent));
  180. }
  181.  
  182. /*
  183.  * ------------------------------------------------------------------
  184.  *
  185.  * HostWindowEventProc --
  186.  *
  187.  *    This procedure is invoked by the Tk dispatcher for the following
  188.  *    events on the host window.  If the host and parent windows are
  189.  *    the same, only the first event is important.
  190.  *
  191.  *       1) ConfigureNotify  - The host window has been resized or
  192.  *                 moved.  Move and resize the busy window
  193.  *                 to be the same size and position of the
  194.  *                 host window.
  195.  *
  196.  *       2) DestroyNotify    - The host window was destroyed. Destroy
  197.  *                 the busy window and the free resources
  198.  *                 used.
  199.  *
  200.  *       3) MapNotify           - The host window was (re)mapped. Map the
  201.  *                 busy window again.
  202.  *
  203.  *       4) UnmapNotify      - The host window was unmapped. Unmap the
  204.  *                 busy window.
  205.  *
  206.  * Results:
  207.  *    None.
  208.  *
  209.  * Side effects:
  210.  *    When the host window gets deleted, internal structures get
  211.  *     cleaned up.  When it gets resized, the busy window is resized
  212.  *    accordingly. If it's mapped, the busy window is mapped. And
  213.  *    when it's unmapped, the busy window is unmapped.
  214.  *
  215.  * -------------------------------------------------------------------
  216.  */
  217. static void
  218. HostWindowEventProc(clientData, eventPtr)
  219.     ClientData clientData;    /* Busy window record */
  220.     register XEvent *eventPtr;    /* Event which triggered call to routine */
  221. {
  222.     register BusyWin *busyPtr = (BusyWin *)clientData;
  223.  
  224.     if (eventPtr->type == DestroyNotify) {
  225.     /*
  226.      * Arrange for the busy structure to be removed at a proper time.
  227.      */
  228.     Tk_EventuallyFree((ClientData)busyPtr, DestroyBusyWindow);
  229.     } else if (eventPtr->type == ConfigureNotify) {
  230.     if ((busyPtr->width != Tk_Width(busyPtr->host)) ||
  231.         (busyPtr->height != Tk_Height(busyPtr->host)) ||
  232.         (busyPtr->hostX != Tk_X(busyPtr->host)) ||
  233.         (busyPtr->hostY != Tk_Y(busyPtr->host))) {
  234.         int x, y;
  235.  
  236.         busyPtr->width = Tk_Width(busyPtr->host);
  237.         busyPtr->height = Tk_Height(busyPtr->host);
  238.         busyPtr->hostX = Tk_X(busyPtr->host);
  239.         busyPtr->hostY = Tk_Y(busyPtr->host);
  240.  
  241.         x = y = 0;
  242.         if (busyPtr->parent != busyPtr->host) {
  243.         Tk_Window ancestor;
  244.  
  245.         for (ancestor = busyPtr->host; ancestor != busyPtr->parent;
  246.             ancestor = Tk_Parent(ancestor)) {
  247.             x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  248.             y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  249.         }
  250.         }
  251.         if (busyPtr->window != None) {
  252.         XMoveResizeWindow(busyPtr->display, busyPtr->window, x, y,
  253.             busyPtr->width, busyPtr->height);
  254.         }
  255.         busyPtr->x = x;
  256.         busyPtr->y = y;
  257.     }
  258.     } else if (eventPtr->type == MapNotify) {
  259.     if ((busyPtr->parent != busyPtr->host) && (!busyPtr->mapped)) {
  260.         XMapRaised(busyPtr->display, busyPtr->window);
  261.         busyPtr->mapped = TRUE;
  262.     }
  263.     } else if (eventPtr->type == UnmapNotify) {
  264.     if ((busyPtr->parent != busyPtr->host) && (busyPtr->mapped)) {
  265.         XUnmapWindow(busyPtr->display, busyPtr->window);
  266.         busyPtr->mapped = FALSE;
  267.     }
  268.     }
  269. }
  270.  
  271. /*
  272.  * ------------------------------------------------------------------
  273.  *
  274.  * CreateBusyWindow --
  275.  *
  276.  *    Creates a child InputOnly window which obscures the parent
  277.  *      window thereby effectively blocking device events.  The size
  278.  *      and position of the busy window is exactly that of the host
  279.  *      window.
  280.  *
  281.  * Results:
  282.  *    Returns a pointer to the new busy window structure.
  283.  *
  284.  * Side effects:
  285.  *    When the busy window is eventually mapped, it will screen
  286.  *    device events (in the area of the host window) from reaching
  287.  *      its parent window and its children.  User feed back can be
  288.  *      achieved by changing the cursor.
  289.  *
  290.  * -------------------------------------------------------------------
  291.  */
  292. static BusyWin *
  293. CreateBusyWindow(interp, tkwin, searchWin)
  294.     Tcl_Interp *interp;        /* Interpreter to report error to */
  295.     Tk_Window tkwin;        /* Window hosting the busy window */
  296.     Tk_Window searchWin;    /* Window to use in searches of the parent
  297.                  * window by pathname */
  298. {
  299.     Tcl_HashEntry *entryPtr;
  300.     BusyWin *busyPtr;
  301.     XSetWindowAttributes attributes;
  302.     int dummy;
  303.  
  304.     busyPtr = (BusyWin *)malloc(sizeof(BusyWin));
  305.     if (busyPtr == NULL) {
  306.     interp->result = "can't allocate busy window";
  307.     return NULL;
  308.     }
  309.     busyPtr->host = busyPtr->parent = tkwin;
  310.     busyPtr->mainWin = searchWin;
  311.     busyPtr->cursor = None;
  312.     busyPtr->window = None;
  313.     busyPtr->display = Tk_Display(tkwin);
  314.     busyPtr->x = busyPtr->y = 0;
  315.  
  316.     /*
  317.      * Ignore the important events while the busy window is mapped.
  318.      */
  319.     attributes.do_not_propagate_mask = (KeyPressMask | KeyReleaseMask |
  320.     ButtonPressMask | ButtonReleaseMask | PointerMotionMask);
  321.     attributes.event_mask = (KeyPressMask | KeyReleaseMask |
  322.     ButtonPressMask | ButtonReleaseMask | PointerMotionMask);
  323.  
  324.     Tk_MakeWindowExist(tkwin);
  325.     /*
  326.      * Create an InputOnly window the size of the host window.
  327.      */
  328.     busyPtr->width = Tk_Width(tkwin);
  329.     busyPtr->height = Tk_Height(tkwin);
  330.     busyPtr->window = XCreateWindow(busyPtr->display, Tk_WindowId(tkwin),
  331.     busyPtr->x, busyPtr->y, busyPtr->width, busyPtr->height,
  332.     (unsigned int)0, CopyFromParent, InputOnly, CopyFromParent,
  333.     CWDontPropagate, &attributes);
  334.     if (busyPtr->cursor != None) {
  335.     XDefineCursor(busyPtr->display, busyPtr->window, busyPtr->cursor);
  336.     }
  337.     XMapRaised(busyPtr->display, busyPtr->window);
  338.     busyPtr->mapped = TRUE;
  339.     /*
  340.      * Track events in the host window to see if it is resized or destroyed.
  341.      */
  342.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, HostWindowEventProc,
  343.     (ClientData)busyPtr);
  344.     entryPtr = Tcl_CreateHashEntry(&busyWinTable, (char *)tkwin, &dummy);
  345.     Tcl_SetHashValue(entryPtr, (char *)busyPtr);
  346.     return (busyPtr);
  347. }
  348.  
  349. /*
  350.  * ------------------------------------------------------------------
  351.  *
  352.  * DestoryBusyWindow --
  353.  *
  354.  *    This procedure is called from the Tk event dispatcher. It
  355.  *     releases X resources and memory used by the busy window and
  356.  *    updates the internal hash table.
  357.  *
  358.  * Results:
  359.  *    None.
  360.  *
  361.  * Side effects:
  362.  *    Memory and resources are released and the Tk event handler
  363.  *    is removed.
  364.  *
  365.  * -------------------------------------------------------------------
  366.  */
  367. static void
  368. DestroyBusyWindow(clientData)
  369.     ClientData clientData;    /* Busy window structure record */
  370. {
  371.     BusyWin *busyPtr = (BusyWin *)clientData;
  372.     Tcl_HashEntry *entryPtr;
  373.  
  374.     if (busyPtr->cursor != None) {
  375.     Tk_FreeCursor(busyPtr->display, busyPtr->cursor);
  376.     }
  377.     Tk_DeleteEventHandler(busyPtr->host, StructureNotifyMask,
  378.     HostWindowEventProc, (ClientData)busyPtr);
  379.     entryPtr = Tcl_FindHashEntry(&busyWinTable, (char *)busyPtr->host);
  380.     Tcl_DeleteHashEntry(entryPtr);
  381.     free((char *)busyPtr);
  382. }
  383.  
  384. /*
  385.  * ------------------------------------------------------------------
  386.  *
  387.  * GetBusyWindow --
  388.  *
  389.  *    Returns the busy window structure associated with the host
  390.  *      window, keyed by its path name.  The clientData argument is
  391.  *      the main window of the interpreter, used to search for the
  392.  *      host window in its own window hierarchy.
  393.  *
  394.  * Results:
  395.  *    If path name represents a host window with a busy window, a
  396.  *    pointer to the busy window structure is returned. Otherwise,
  397.  *    NULL is returned and an error message is left in
  398.  *    interp->result.
  399.  *
  400.  * -------------------------------------------------------------------
  401.  */
  402. static BusyWin *
  403. GetBusyWindow(clientData, interp, pathName)
  404.     ClientData clientData;    /* Window used to reference search  */
  405.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  406.     char *pathName;        /* Path name of parent window */
  407. {
  408.     Tcl_HashEntry *entryPtr;
  409.     Tk_Window searchWin = (Tk_Window)clientData;
  410.     Tk_Window tkwin;
  411.  
  412.     tkwin = Tk_NameToWindow(interp, pathName, searchWin);
  413.     if (tkwin == NULL) {
  414.     return NULL;
  415.     }
  416.     entryPtr = Tcl_FindHashEntry(&busyWinTable, (char *)tkwin);
  417.     if (entryPtr == NULL) {
  418.     Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"",
  419.         (char *)NULL);
  420.     return NULL;
  421.     }
  422.     return ((BusyWin *)Tcl_GetHashValue(entryPtr));
  423. }
  424.  
  425. /*
  426.  * ------------------------------------------------------------------
  427.  *
  428.  * StatusBusyWindow --
  429.  *
  430.  *    Returns the status of the busy window; whether it's blocking
  431.  *    events or not.
  432.  *
  433.  * Results:
  434.  *    Returns a normal TCL result. If path name represents a busy
  435.  *    window, the status is returned via interp->result and TCL_OK
  436.  *    is returned. Otherwise, TCL_ERROR is returned and an error
  437.  *    message is left in interp->result.
  438.  *
  439.  * -------------------------------------------------------------------
  440.  */
  441. static int
  442. StatusBusyWindow(clientData, interp, pathName)
  443.     ClientData clientData;    /* Main window of interpreter */
  444.     Tcl_Interp *interp;        /* Interpreter to report error to */
  445.     char *pathName;        /* Path name of host window */
  446. {
  447.     BusyWin *busyPtr;
  448.  
  449.     busyPtr = GetBusyWindow(clientData, interp, pathName);
  450.     if (busyPtr == NULL) {
  451.     return TCL_ERROR;
  452.     }
  453.     Tk_Preserve((ClientData)busyPtr);
  454.     interp->result = (busyPtr->mapped) ? "1" : "0";
  455.     Tk_Release((ClientData)busyPtr);
  456.     return TCL_OK;
  457. }
  458.  
  459. /*
  460.  * ------------------------------------------------------------------
  461.  *
  462.  * ForgetBusyWindow --
  463.  *
  464.  *    Destroys the busy window associated with the host window and
  465.  *    arranges for internal resources to the released when they're
  466.  *    not being used anymore.
  467.  *
  468.  * Results:
  469.  *    Returns a normal TCL result. If path name represents a busy
  470.  *    window, it is destroyed and TCL_OK is returned. Otherwise,
  471.  *    TCL_ERROR is returned and an error message is left in
  472.  *    interp->result.
  473.  *
  474.  * Side effects:
  475.  *    The busy window is removed.  Other related memory and resources
  476.  *    are eventually released by the Tk dispatcher.
  477.  *
  478.  * -------------------------------------------------------------------
  479.  */
  480. static int
  481. ForgetBusyWindow(clientData, interp, pathName)
  482.     ClientData clientData;    /* Main window of the interpreter */
  483.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  484.     char *pathName;        /* Path name of host window */
  485. {
  486.     BusyWin *busyPtr;
  487.  
  488.     busyPtr = GetBusyWindow(clientData, interp, pathName);
  489.     if (busyPtr == NULL) {
  490.     return TCL_ERROR;
  491.     }
  492.     Tk_Preserve((ClientData)busyPtr);
  493.     if (busyPtr->window != None) {
  494.     XDestroyWindow(busyPtr->display, busyPtr->window);
  495.     busyPtr->window = None;
  496.     }
  497.     Tk_EventuallyFree((ClientData)busyPtr, DestroyBusyWindow);
  498.     Tk_Release((ClientData)busyPtr);
  499.     return TCL_OK;
  500. }
  501.  
  502. /*
  503.  * ------------------------------------------------------------------
  504.  *
  505.  * ReleaseBusyWindow --
  506.  *
  507.  *    Unmaps the busy window, thereby permitting device events
  508.  *    to be received by the parent window and its children.
  509.  *
  510.  * Results:
  511.  *    Returns a normal TCL result. If path name represents a busy
  512.  *    window, it is unmapped and TCL_OK is returned. Otherwise,
  513.  *    TCL_ERROR is returned and an error message is left in
  514.  *    interp->result.
  515.  *
  516.  * Side effects:
  517.  *    The busy window is unmapped, allowing the parent window and
  518.  *    its children to receive events again.
  519.  *
  520.  * -------------------------------------------------------------------
  521.  */
  522. static int
  523. ReleaseBusyWindow(clientData, interp, pathName)
  524.     ClientData clientData;    /* Main window of the interpreter */
  525.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  526.     char *pathName;        /* Path name of host window */
  527. {
  528.     BusyWin *busyPtr;
  529.  
  530.     busyPtr = GetBusyWindow(clientData, interp, pathName);
  531.     if (busyPtr == NULL) {
  532.     return TCL_ERROR;
  533.     }
  534.     Tk_Preserve((ClientData)busyPtr);
  535.     if ((busyPtr->mapped) && (busyPtr->window != None)) {
  536.     busyPtr->mapped = FALSE;
  537.     XUnmapWindow(busyPtr->display, busyPtr->window);
  538.     }
  539.     Tk_Release((ClientData)busyPtr);
  540.     return TCL_OK;
  541. }
  542.  
  543. /*
  544.  * ------------------------------------------------------------------
  545.  *
  546.  * HoldBusyWindow --
  547.  *
  548.  *    Creates (if necessary) and maps a busy window, thereby
  549.  *    preventing device events from being be received by the parent
  550.  *      window and its children.
  551.  *
  552.  * Results:
  553.  *    Returns a normal TCL result. If path name represents a busy
  554.  *    window, it is unmapped and TCL_OK is returned. Otherwise,
  555.  *    TCL_ERROR is returned and an error message is left in
  556.  *    interp->result.
  557.  *
  558.  * Side effects:
  559.  *    The busy window is created and mapped, blocking events from the
  560.  *    parent window and its children.
  561.  *
  562.  * -------------------------------------------------------------------
  563.  */
  564. static int
  565. HoldBusyWindow(clientData, interp, argc, argv)
  566.     ClientData clientData;    /* Main window of the interpreter */
  567.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  568.     int argc;
  569.     char **argv;        /* Window name and option pairs */
  570. {
  571.     Tk_Window searchWin = (Tk_Window)clientData;
  572.     Tk_Window tkwin;
  573.     Tcl_HashEntry *entryPtr;
  574.     BusyWin *busyPtr;
  575.     int result;
  576.  
  577.     tkwin = Tk_NameToWindow(interp, argv[0], searchWin);
  578.     if (tkwin == NULL) {
  579.     return TCL_ERROR;
  580.     }
  581.     entryPtr = Tcl_FindHashEntry(&busyWinTable, (char *)tkwin);
  582.     if (entryPtr == NULL) {
  583.     busyPtr = (BusyWin *)CreateBusyWindow(interp, tkwin, searchWin);
  584.     } else {
  585.     busyPtr = (BusyWin *)Tcl_GetHashValue(entryPtr);
  586.     }
  587.     if (busyPtr == NULL) {
  588.     return TCL_ERROR;
  589.     }
  590.     Tk_Preserve((ClientData)busyPtr);
  591.     result = Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 1, argv + 1,
  592.     (char *)busyPtr, 0);
  593.     if ((result == TCL_OK) && (busyPtr->window != None)) {
  594.     /*
  595.      * Define the new cursor and map the busy window
  596.      */
  597.     if (busyPtr->cursor != None) {
  598.         XDefineCursor(busyPtr->display, busyPtr->window, busyPtr->cursor);
  599.     }
  600.     XMapRaised(busyPtr->display, busyPtr->window);
  601.     busyPtr->mapped = TRUE;
  602.     }
  603.     Tk_Release((ClientData)busyPtr);
  604.     return result;
  605. }
  606.  
  607. /*
  608.  *----------------------------------------------------------------------
  609.  *
  610.  * ConfigureBusyWindow --
  611.  *
  612.  *    This procedure is called to process an argv/argc list
  613.  *    in order to configure (or reconfigure) a busy window.
  614.  *
  615.  * Results:
  616.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  617.  *    returned, then interp->result contains an error message.
  618.  *
  619.  * Side effects:
  620.  *    Configuration information get set for busyPtr;  old resources
  621.  *      get freed, if there were any.  The busy window may be reparented
  622.  *      to a new parent window.
  623.  *
  624.  *----------------------------------------------------------------------
  625.  */
  626. static int
  627. ConfigureBusyWindow(clientData, interp, argc, argv)
  628.     ClientData clientData;    /* Main window of the interpreter */
  629.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  630.     int argc;
  631.     char **argv;        /* Host window path name and options */
  632. {
  633.     BusyWin *busyPtr;
  634.     int result;
  635.  
  636.     busyPtr = GetBusyWindow(clientData, interp, argv[1]);
  637.     if (busyPtr == NULL) {
  638.     return TCL_ERROR;
  639.     }
  640.     Tk_Preserve((ClientData)busyPtr);
  641.     if (argc == 2) {
  642.     result = Tk_ConfigureInfo(interp, busyPtr->host, configSpecs,
  643.         (char *)busyPtr, (char *)NULL, 0);
  644.     } else if (argc == 3) {
  645.     result = Tk_ConfigureInfo(interp, busyPtr->host, configSpecs,
  646.         (char *)busyPtr, argv[2], 0);
  647.     } else {
  648.     Cursor oldCursor;
  649.  
  650.     oldCursor = busyPtr->cursor;
  651.     result = Tk_ConfigureWidget(interp, busyPtr->host, configSpecs,
  652.         argc - 2, argv + 2, (char *)busyPtr, TK_CONFIG_ARGV_ONLY);
  653.     if ((result == TCL_OK) && (busyPtr->window != None)) {
  654.         /* Set cursor on busy window */
  655.         if (busyPtr->cursor != None) {
  656.         XDefineCursor(busyPtr->display, busyPtr->window,
  657.             busyPtr->cursor);
  658.         } else if (oldCursor != None) {
  659.         XUndefineCursor(busyPtr->display, busyPtr->window);
  660.         }
  661.     }
  662.     }
  663.     Tk_Release((ClientData)busyPtr);
  664.     return result;
  665. }
  666.  
  667. /*
  668.  *----------------------------------------------------------------------
  669.  *
  670.  * BusyCmd --
  671.  *
  672.  *    This procedure is invoked to process the "busy" Tcl command.
  673.  *    See the user documentation for details on what it does.
  674.  *
  675.  * Results:
  676.  *    A standard Tcl result.
  677.  *
  678.  * Side effects:
  679.  *    See the user documentation.
  680.  *
  681.  *----------------------------------------------------------------------
  682.  */
  683. static int
  684. BusyCmd(clientData, interp, argc, argv)
  685.     ClientData clientData;    /* Main window of the interpreter */
  686.     Tcl_Interp *interp;        /* Interpreter associated with command */
  687.     int argc;
  688.     char **argv;
  689. {
  690.     char c;
  691.     int length;
  692.  
  693.     if (!initialized) {
  694.     Tcl_InitHashTable(&busyWinTable, TCL_ONE_WORD_KEYS);
  695.     initialized = 1;
  696.     }
  697.     if (argc < 2) {
  698.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  699.         " option window\"", (char *)NULL);
  700.     return TCL_ERROR;
  701.     }
  702.     c = argv[1][0];
  703.     length = strlen(argv[1]);
  704.     if ((c == '.') ||
  705.     ((c == 'h') && (strncmp(argv[1], "hold", length) == 0))) {
  706.     register int i, count;
  707.     char *savePtr;
  708.  
  709.     if (c == 'h') {
  710.         if (argc < 3) {
  711.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  712.             " hold window ?options...?\"", (char *)NULL);
  713.         return TCL_ERROR;
  714.         }
  715.         argc--, argv++;
  716.     }
  717.     for (i = 1; i < argc; i++) {
  718.         /*
  719.          * Find the end of the option-value pairs for this window.
  720.          */
  721.         for (count = i + 1; count < argc; count += 2) {
  722.         if (argv[count][0] != '-') {
  723.             break;
  724.         }
  725.         }
  726.         savePtr = argv[count];
  727.         argv[count] = NULL;
  728.         if (HoldBusyWindow(clientData, interp,
  729.             argc - i, argv + i) != TCL_OK) {
  730.         return TCL_ERROR;
  731.         }
  732.         argv[count] = savePtr;
  733.         i = count;
  734.     }
  735.     return TCL_OK;
  736.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  737.     return (ConfigureBusyWindow(clientData, interp, argc - 1, argv + 1));
  738.     } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) {
  739.     register int i;
  740.  
  741.     if (argc < 3) {
  742.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  743.         " release window ?window ...?\"", (char *)NULL);
  744.         return TCL_ERROR;
  745.     }
  746.     for (i = 2; i < argc; i++) {
  747.         if (ReleaseBusyWindow(clientData, interp, argv[i]) != TCL_OK) {
  748.         return TCL_ERROR;
  749.         }
  750.     }
  751.     return TCL_OK;
  752.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  753.     register int i;
  754.  
  755.     if (argc < 3) {
  756.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  757.         " forget window ?window ...?\"", (char *)NULL);
  758.         return TCL_ERROR;
  759.     }
  760.     for (i = 2; i < argc; i++) {
  761.         if (ForgetBusyWindow(clientData, interp, argv[i]) != TCL_OK) {
  762.         return TCL_ERROR;
  763.         }
  764.     }
  765.     return TCL_OK;
  766.     } else if ((c == 'h') && (strncmp(argv[1], "hosts", length) == 0)) {
  767.     Tk_Window searchWin = (Tk_Window)clientData;
  768.     Tcl_HashEntry *entryPtr;
  769.     Tcl_HashSearch cursor;
  770.     BusyWin *busyPtr;
  771.  
  772.     if (argc > 3) {
  773.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  774.         " hosts ?pattern?\"", (char *)NULL);
  775.         return TCL_ERROR;
  776.     }
  777.     for (entryPtr = Tcl_FirstHashEntry(&busyWinTable, &cursor);
  778.         entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  779.         busyPtr = (BusyWin *)Tcl_GetHashValue(entryPtr);
  780.         if (busyPtr->mainWin == searchWin) {
  781.         if ((argc != 3) ||
  782.             (Tcl_StringMatch(Tk_PathName(busyPtr->host), argv[2]))) {
  783.             Tcl_AppendElement(interp, Tk_PathName(busyPtr->host));
  784.         }
  785.         }
  786.     }
  787.     return TCL_OK;
  788.     } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0)) {
  789.     if (argc < 3) {
  790.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  791.         " status window\"", (char *)NULL);
  792.         return TCL_ERROR;
  793.     }
  794.     return (StatusBusyWindow(clientData, interp, argv[2]));
  795.     } else {
  796.     Tcl_AppendResult(interp, "bad option \"", argv[1], "\" should be ",
  797.         "configure, forget, hold, hosts, or release", (char *)NULL);
  798.     return TCL_ERROR;
  799.     }
  800. }
  801.  
  802. int
  803. Blt_BusyInit(interp)
  804.     Tcl_Interp *interp;
  805. {
  806.     Tk_Window tkwin;
  807.  
  808.     if (Blt_FindCmd(interp, "blt_busy", (ClientData *)NULL) == TCL_OK) {
  809.     Tcl_AppendResult(interp, "\"blt_busy\" command already exists",
  810.         (char *)NULL);
  811.     return TCL_ERROR;
  812.     }
  813.     tkwin = Tk_MainWindow(interp);
  814.     if (tkwin == NULL) {
  815.     Tcl_AppendResult(interp, "\"blt_busy\" requires Tk", (char *)NULL);
  816.     return TCL_ERROR;
  817.     }
  818.     Tcl_SetVar2(interp, "blt_versions", "blt_busy", BUSY_VERSION,
  819.     TCL_GLOBAL_ONLY);
  820.     Tcl_CreateCommand(interp, "blt_busy", BusyCmd, (ClientData)tkwin,
  821.     (Tcl_CmdDeleteProc *)NULL);
  822.     return TCL_OK;
  823. }
  824.