source: trunk/kitgen/8.x/blt/generic/bltBusy.c@ 199

Last change on this file since 199 was 175, checked in by demin, 12 years ago

initial commit

File size: 33.9 KB
Line 
1/*
2 * bltBusy.c --
3 *
4 * This module implements busy windows for the BLT toolkit.
5 *
6 * Copyright 1993-1998 Lucent Technologies, Inc.
7 *
8 * Permission to use, copy, modify, and distribute this software and
9 * its documentation for any purpose and without fee is hereby
10 * granted, provided that the above copyright notice appear in all
11 * copies and that both that the copyright notice and warranty
12 * disclaimer appear in supporting documentation, and that the names
13 * of Lucent Technologies any of their entities not be used in
14 * advertising or publicity pertaining to distribution of the software
15 * without specific, written prior permission.
16 *
17 * Lucent Technologies disclaims all warranties with regard to this
18 * software, including all implied warranties of merchantability and
19 * fitness. In no event shall Lucent Technologies be liable for any
20 * special, indirect or consequential damages or any damages
21 * whatsoever resulting from loss of use, data or profits, whether in
22 * an action of contract, negligence or other tortuous action, arising
23 * out of or in connection with the use or performance of this
24 * software.
25 *
26 * The "busy" command was created by George Howlett.
27 */
28
29#include "bltInt.h"
30
31#ifndef NO_BUSY
32#include "bltHash.h"
33
34#define BUSYDEBUG 0
35
36#ifndef TK_REPARENTED
37#define TK_REPARENTED 0
38#endif
39
40#define BUSY_THREAD_KEY "BLT Busy Data"
41
42typedef struct {
43 Display *display; /* Display of busy window */
44 Tcl_Interp *interp; /* Interpreter where "busy" command was
45 * created. It's used to key the
46 * searches in the window hierarchy. See the
47 * "windows" command. */
48
49 Tk_Window tkBusy; /* Busy window: Transparent window used
50 * to block delivery of events to windows
51 * underneath it. */
52
53 Tk_Window tkParent; /* Parent window of the busy
54 * window. It may be the reference
55 * window (if the reference is a
56 * toplevel) or a mutual ancestor of
57 * the reference window */
58
59 Tk_Window tkRef; /* Reference window of the busy window.
60 * It's is used to manage the size and
61 * position of the busy window. */
62
63
64 int x, y; /* Position of the reference window */
65
66 int width, height; /* Size of the reference window. Retained to
67 * know if the reference window has been
68 * reconfigured to a new size. */
69
70 int isBusy; /* Indicates whether the transparent
71 * window should be displayed. This
72 * can be different from what
73 * Tk_IsMapped says because the a
74 * sibling reference window may be
75 * unmapped, forcing the busy window
76 * to be also hidden. */
77
78 int menuBar; /* Menu bar flag. */
79 Tk_Cursor cursor; /* Cursor for the busy window. */
80
81 Blt_HashEntry *hashPtr; /* Used the delete the busy window entry
82 * out of the global hash table. */
83 Blt_HashTable *tablePtr;
84} Busy;
85
86#ifdef WIN32
87#define DEF_BUSY_CURSOR "wait"
88#else
89#define DEF_BUSY_CURSOR "watch"
90#endif
91
92static Tk_ConfigSpec configSpecs[] =
93{
94 {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor",
95 DEF_BUSY_CURSOR, Tk_Offset(Busy, cursor), TK_CONFIG_NULL_OK},
96 {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
97};
98
99typedef struct {
100 Blt_HashTable busyTable; /* Hash table of busy window
101 * structures keyed by the address of
102 * the reference Tk window */
103} BusyInterpData;
104
105static void BusyGeometryProc _ANSI_ARGS_((ClientData clientData,
106 Tk_Window tkwin));
107static void BusyCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin));
108
109static Tk_GeomMgr busyMgrInfo =
110{
111 "busy", /* Name of geometry manager used by winfo */
112 BusyGeometryProc, /* Procedure to for new geometry requests */
113 BusyCustodyProc, /* Procedure when window is taken away */
114};
115
116/* Forward declarations */
117static void DestroyBusy _ANSI_ARGS_((DestroyData dataPtr));
118static void BusyEventProc _ANSI_ARGS_((ClientData clientData,
119 XEvent *eventPtr));
120
121static Tk_EventProc RefWinEventProc;
122static Tcl_CmdProc BusyCmd;
123static Tcl_InterpDeleteProc BusyInterpDeleteProc;
124
125static void
126ShowBusyWindow(busyPtr)
127 Busy *busyPtr;
128{
129 if (busyPtr->tkBusy != NULL) {
130 Tk_MapWindow(busyPtr->tkBusy);
131 /*
132 * Always raise the busy window just in case new sibling
133 * windows have been created in the meantime. Can't use
134 * Tk_RestackWindow because it doesn't work under Win32.
135 */
136 XRaiseWindow(Tk_Display(busyPtr->tkBusy),
137 Tk_WindowId(busyPtr->tkBusy));
138 }
139#ifdef WIN32
140 {
141 POINT point;
142 /*
143 * Under Win32, cursors aren't associated with windows. Tk
144 * fakes this by watching Motion events on its windows. So Tk
145 * will automatically change the cursor when the pointer
146 * enters the Busy window. But Windows doesn't immediately
147 * change the cursor; it waits for the cursor position to
148 * change or a system call. We need to change the cursor
149 * before the application starts processing, so set the cursor
150 * position redundantly back to the current position.
151 */
152 GetCursorPos(&point);
153 SetCursorPos(point.x, point.y);
154 }
155#endif /* WIN32 */
156}
157
158static void
159HideBusyWindow(busyPtr)
160 Busy *busyPtr;
161{
162 if (busyPtr->tkBusy != NULL) {
163 Tk_UnmapWindow(busyPtr->tkBusy);
164 }
165#ifdef WIN32
166 {
167 POINT point;
168 /*
169 * Under Win32, cursors aren't associated with windows. Tk
170 * fakes this by watching Motion events on its windows. So Tk
171 * will automatically change the cursor when the pointer
172 * enters the Busy window. But Windows doesn't immediately
173 * change the cursor: it waits for the cursor position to
174 * change or a system call. We need to change the cursor
175 * before the application starts processing, so set the cursor
176 * position redundantly back to the current position.
177 */
178 GetCursorPos(&point);
179 SetCursorPos(point.x, point.y);
180 }
181#endif /* WIN32 */
182}
183
184/*
185 *----------------------------------------------------------------------
186 *
187 * BusyEventProc --
188 *
189 * This procedure is invoked by the Tk dispatcher for events on
190 * the busy window itself. We're only concerned with destroy
191 * events.
192 *
193 * It might be necessary (someday) to watch resize events. Right
194 * now, I don't think there's any point in it.
195 *
196 * Results:
197 * None.
198 *
199 * Side effects:
200 * When a busy window is destroyed, all internal structures
201 * associated with it released at the next idle point.
202 *
203 *----------------------------------------------------------------------
204 */
205static void
206BusyEventProc(clientData, eventPtr)
207 ClientData clientData; /* Busy window record */
208 XEvent *eventPtr; /* Event which triggered call to routine */
209{
210 Busy *busyPtr = clientData;
211
212 if (eventPtr->type == DestroyNotify) {
213 busyPtr->tkBusy = NULL;
214 Tcl_EventuallyFree(busyPtr, DestroyBusy);
215 }
216}
217
218
219/*
220 * ----------------------------------------------------------------------------
221 *
222 * BusyCustodyProc --
223 *
224 * This procedure is invoked when the busy window has been stolen
225 * by another geometry manager. The information and memory
226 * associated with the busy window is released. I don't know why
227 * anyone would try to pack a busy window, but this should keep
228 * everything sane, if it is.
229 *
230 * Results:
231 * None.
232 *
233 * Side effects:
234 * The Busy structure is freed at the next idle point.
235 *
236 * ----------------------------------------------------------------------------
237 */
238/* ARGSUSED */
239static void
240BusyCustodyProc(clientData, tkwin)
241 ClientData clientData; /* Information about the busy window. */
242 Tk_Window tkwin; /* Not used. */
243{
244 Busy *busyPtr = clientData;
245
246 Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask, BusyEventProc,
247 busyPtr);
248 HideBusyWindow(busyPtr);
249 busyPtr->tkBusy = NULL;
250 Tcl_EventuallyFree(busyPtr, DestroyBusy);
251}
252
253/*
254 * ----------------------------------------------------------------------------
255 *
256 * BusyGeometryProc --
257 *
258 * This procedure is invoked by Tk_GeometryRequest for busy
259 * windows. Busy windows never request geometry, so it's
260 * unlikely that this routine will ever be called. The routine
261 * exists simply as a place holder for the GeomProc in the
262 * Geometry Manager structure.
263 *
264 * Results:
265 * None.
266 *
267 * ----------------------------------------------------------------------------
268 */
269/* ARGSUSED */
270static void
271BusyGeometryProc(clientData, tkwin)
272 ClientData clientData; /* Information about window that got new
273 * preferred geometry. */
274 Tk_Window tkwin; /* Other Tk-related information about the
275 * window. */
276{
277 /* Should never get here */
278}
279
280/*
281 * ------------------------------------------------------------------
282 *
283 * RefWinEventProc --
284 *
285 * This procedure is invoked by the Tk dispatcher for the
286 * following events on the reference window. If the reference and
287 * parent windows are the same, only the first event is
288 * important.
289 *
290 * 1) ConfigureNotify - The reference window has been resized or
291 * moved. Move and resize the busy window
292 * to be the same size and position of the
293 * reference window.
294 *
295 * 2) DestroyNotify - The reference window was destroyed. Destroy
296 * the busy window and the free resources
297 * used.
298 *
299 * 3) MapNotify - The reference window was (re)shown. Map the
300 * busy window again.
301 *
302 * 4) UnmapNotify - The reference window was hidden. Unmap the
303 * busy window.
304 *
305 * Results:
306 * None.
307 *
308 * Side effects:
309 * When the reference window gets deleted, internal structures get
310 * cleaned up. When it gets resized, the busy window is resized
311 * accordingly. If it's displayed, the busy window is displayed. And
312 * when it's hidden, the busy window is unmapped.
313 *
314 * -------------------------------------------------------------------
315 */
316static void
317RefWinEventProc(clientData, eventPtr)
318 ClientData clientData; /* Busy window record */
319 register XEvent *eventPtr; /* Event which triggered call to routine */
320{
321 register Busy *busyPtr = clientData;
322
323 switch (eventPtr->type) {
324 case ReparentNotify:
325 case DestroyNotify:
326
327 /*
328 * Arrange for the busy structure to be removed at a proper time.
329 */
330
331 Tcl_EventuallyFree(busyPtr, DestroyBusy);
332 break;
333
334 case ConfigureNotify:
335 if ((busyPtr->width != Tk_Width(busyPtr->tkRef)) ||
336 (busyPtr->height != Tk_Height(busyPtr->tkRef)) ||
337 (busyPtr->x != Tk_X(busyPtr->tkRef)) ||
338 (busyPtr->y != Tk_Y(busyPtr->tkRef))) {
339 int x, y;
340
341 busyPtr->width = Tk_Width(busyPtr->tkRef);
342 busyPtr->height = Tk_Height(busyPtr->tkRef);
343 busyPtr->x = Tk_X(busyPtr->tkRef);
344 busyPtr->y = Tk_Y(busyPtr->tkRef);
345
346 x = y = 0;
347
348 if (busyPtr->tkParent != busyPtr->tkRef) {
349 Tk_Window tkwin;
350
351 for (tkwin = busyPtr->tkRef; (tkwin != NULL) &&
352 (!Tk_IsTopLevel(tkwin)); tkwin = Tk_Parent(tkwin)) {
353 if (tkwin == busyPtr->tkParent) {
354 break;
355 }
356 x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
357 y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
358 }
359 }
360#if BUSYDEBUG
361 PurifyPrintf("menubar2: width=%d, height=%d\n",
362 busyPtr->width, busyPtr->height);
363#endif
364 if (busyPtr->tkBusy != NULL) {
365#if BUSYDEBUG
366 fprintf(stderr, "busy window %s is at %d,%d %dx%d\n",
367 Tk_PathName(busyPtr->tkBusy),
368 x, y, busyPtr->width, busyPtr->height);
369#endif
370 Tk_MoveResizeWindow(busyPtr->tkBusy, x, y, busyPtr->width,
371 busyPtr->height);
372 if (busyPtr->isBusy) {
373 ShowBusyWindow(busyPtr);
374 }
375 }
376 }
377 break;
378
379 case MapNotify:
380 if ((busyPtr->tkParent != busyPtr->tkRef) && (busyPtr->isBusy)) {
381 ShowBusyWindow(busyPtr);
382 }
383 break;
384
385 case UnmapNotify:
386 if (busyPtr->tkParent != busyPtr->tkRef) {
387 HideBusyWindow(busyPtr);
388 }
389 break;
390 }
391}
392
393
394/*
395 * ------------------------------------------------------------------
396 *
397 * ConfigureBusy --
398 *
399 * This procedure is called from the Tk event dispatcher. It
400 * releases X resources and memory used by the busy window and
401 * updates the internal hash table.
402 *
403 * Results:
404 * None.
405 *
406 * Side effects:
407 * Memory and resources are released and the Tk event handler
408 * is removed.
409 *
410 * -------------------------------------------------------------------
411 */
412static int
413ConfigureBusy(interp, busyPtr, argc, argv)
414 Tcl_Interp *interp;
415 Busy *busyPtr;
416 int argc;
417 char **argv;
418{
419 Tk_Cursor oldCursor;
420
421 oldCursor = busyPtr->cursor;
422 if (Tk_ConfigureWidget(interp, busyPtr->tkRef, configSpecs, argc, argv,
423 (char *)busyPtr, 0) != TCL_OK) {
424 return TCL_ERROR;
425 }
426 if (busyPtr->cursor != oldCursor) {
427 if (busyPtr->cursor == None) {
428 Tk_UndefineCursor(busyPtr->tkBusy);
429 } else {
430 Tk_DefineCursor(busyPtr->tkBusy, busyPtr->cursor);
431 }
432 }
433 return TCL_OK;
434}
435
436/*
437 * ------------------------------------------------------------------
438 *
439 * CreateBusy --
440 *
441 * Creates a child transparent window that obscures its parent
442 * window thereby effectively blocking device events. The size
443 * and position of the busy window is exactly that of the reference
444 * window.
445 *
446 * We want to create sibling to the window to be blocked. If the
447 * busy window is a child of the window to be blocked, Enter/Leave
448 * events can sneak through. Futhermore under WIN32, messages of
449 * transparent windows are sent directly to the parent. The only
450 * exception to this are toplevels, since we can't make a sibling.
451 * Fortunately, toplevel windows rarely receive events that need
452 * blocking.
453 *
454 * Results:
455 * Returns a pointer to the new busy window structure.
456 *
457 * Side effects:
458 * When the busy window is eventually displayed, it will screen
459 * device events (in the area of the reference window) from reaching
460 * its parent window and its children. User feed back can be
461 * achieved by changing the cursor.
462 *
463 * -------------------------------------------------------------------
464 */
465static Busy *
466CreateBusy(interp, tkRef)
467 Tcl_Interp *interp; /* Interpreter to report error to */
468 Tk_Window tkRef; /* Window hosting the busy window */
469{
470 Busy *busyPtr;
471 int length;
472 char *fmt, *name;
473 Tk_Window tkBusy;
474 Window parent;
475 Tk_Window tkChild, tkParent;
476 Tk_FakeWin *winPtr;
477 int x, y;
478
479 busyPtr = Blt_Calloc(1, sizeof(Busy));
480 assert(busyPtr);
481 x = y = 0;
482 length = strlen(Tk_Name(tkRef));
483 name = Blt_Malloc(length + 6);
484 if (Tk_IsTopLevel(tkRef)) {
485 fmt = "_Busy"; /* Child */
486 tkParent = tkRef;
487 } else {
488 Tk_Window tkwin;
489
490 fmt = "%s_Busy"; /* Sibling */
491 tkParent = Tk_Parent(tkRef);
492 for (tkwin = tkRef; (tkwin != NULL) && (!Tk_IsTopLevel(tkwin));
493 tkwin = Tk_Parent(tkwin)) {
494 if (tkwin == tkParent) {
495 break;
496 }
497 x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
498 y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
499 }
500 }
501 for (tkChild = Blt_FirstChild(tkParent); tkChild != NULL;
502 tkChild = Blt_NextChild(tkChild)) {
503 Tk_MakeWindowExist(tkChild);
504 }
505 sprintf(name, fmt, Tk_Name(tkRef));
506 tkBusy = Tk_CreateWindow(interp, tkParent, name, (char *)NULL);
507 Blt_Free(name);
508
509 if (tkBusy == NULL) {
510 return NULL;
511 }
512 Tk_MakeWindowExist(tkRef);
513 busyPtr->display = Tk_Display(tkRef);
514 busyPtr->interp = interp;
515 busyPtr->tkRef = tkRef;
516 busyPtr->tkParent = tkParent;
517 busyPtr->tkBusy = tkBusy;
518 busyPtr->width = Tk_Width(tkRef);
519 busyPtr->height = Tk_Height(tkRef);
520 busyPtr->x = Tk_X(tkRef);
521 busyPtr->y = Tk_Y(tkRef);
522 busyPtr->cursor = None;
523 busyPtr->isBusy = FALSE;
524 Tk_SetClass(tkBusy, "Busy");
525#if (TK_MAJOR_VERSION > 4)
526 Blt_SetWindowInstanceData(tkBusy, busyPtr);
527#endif
528 winPtr = (Tk_FakeWin *) tkRef;
529 if (winPtr->flags & TK_REPARENTED) {
530 /*
531 * This works around a bug in the implementation of menubars
532 * for non-MacIntosh window systems (Win32 and X11). Tk
533 * doesn't reset the pointers to the parent window when the
534 * menu is reparented (winPtr->parentPtr points to the
535 * wrong window). We get around this by determining the parent
536 * via the native API calls.
537 */
538#ifdef WIN32
539 {
540 HWND hWnd;
541 RECT rect;
542
543 hWnd = GetParent(Tk_GetHWND(Tk_WindowId(tkRef)));
544 parent = (Window) hWnd;
545 if (GetWindowRect(hWnd, &rect)) {
546 busyPtr->width = rect.right - rect.left;
547 busyPtr->height = rect.bottom - rect.top;
548#if BUSYDEBUG
549 PurifyPrintf("menubar: width=%d, height=%d\n",
550 busyPtr->width, busyPtr->height);
551#endif
552 }
553 }
554#else
555 parent = Blt_GetParent(Tk_Display(tkRef), Tk_WindowId(tkRef));
556#endif
557 } else {
558 parent = Tk_WindowId(tkParent);
559#ifdef WIN32
560 parent = (Window) Tk_GetHWND(parent);
561#endif
562 }
563 Blt_MakeTransparentWindowExist(tkBusy, parent, TRUE);
564
565#if BUSYDEBUG
566 PurifyPrintf("menubar1: width=%d, height=%d\n", busyPtr->width,
567 busyPtr->height);
568 fprintf(stderr, "busy window %s is at %d,%d %dx%d\n", Tk_PathName(tkBusy),
569 x, y, busyPtr->width, busyPtr->height);
570#endif
571 Tk_MoveResizeWindow(tkBusy, x, y, busyPtr->width, busyPtr->height);
572
573 /* Only worry if the busy window is destroyed. */
574 Tk_CreateEventHandler(tkBusy, StructureNotifyMask, BusyEventProc, busyPtr);
575 /*
576 * Indicate that the busy window's geometry is being managed.
577 * This will also notify us if the busy window is ever packed.
578 */
579 Tk_ManageGeometry(tkBusy, &busyMgrInfo, busyPtr);
580 if (busyPtr->cursor != None) {
581 Tk_DefineCursor(tkBusy, busyPtr->cursor);
582 }
583 /* Track the reference window to see if it is resized or destroyed. */
584 Tk_CreateEventHandler(tkRef, StructureNotifyMask, RefWinEventProc, busyPtr);
585 return busyPtr;
586}
587
588/*
589 * ------------------------------------------------------------------
590 *
591 * DestroyBusy --
592 *
593 * This procedure is called from the Tk event dispatcher. It
594 * releases X resources and memory used by the busy window and
595 * updates the internal hash table.
596 *
597 * Results:
598 * None.
599 *
600 * Side effects:
601 * Memory and resources are released and the Tk event handler
602 * is removed.
603 *
604 * -------------------------------------------------------------------
605 */
606static void
607DestroyBusy(data)
608 DestroyData data; /* Busy window structure record */
609{
610 Busy *busyPtr = (Busy *)data;
611
612 Tk_FreeOptions(configSpecs, (char *)busyPtr, busyPtr->display, 0);
613 if (busyPtr->hashPtr != NULL) {
614 Blt_DeleteHashEntry(busyPtr->tablePtr, busyPtr->hashPtr);
615 }
616 Tk_DeleteEventHandler(busyPtr->tkRef, StructureNotifyMask,
617 RefWinEventProc, busyPtr);
618 if (busyPtr->tkBusy != NULL) {
619 Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask,
620 BusyEventProc, busyPtr);
621 Tk_ManageGeometry(busyPtr->tkBusy, NULL, busyPtr);
622 Tk_DestroyWindow(busyPtr->tkBusy);
623 }
624 Blt_Free(busyPtr);
625}
626
627/*
628 * ------------------------------------------------------------------
629 *
630 * GetBusy --
631 *
632 * Returns the busy window structure associated with the reference
633 * window, keyed by its path name. The clientData argument is
634 * the main window of the interpreter, used to search for the
635 * reference window in its own window hierarchy.
636 *
637 * Results:
638 * If path name represents a reference window with a busy window, a
639 * pointer to the busy window structure is returned. Otherwise,
640 * NULL is returned and an error message is left in
641 * interp->result.
642 *
643 * -------------------------------------------------------------------
644 */
645static int
646GetBusy(dataPtr, interp, pathName, busyPtrPtr)
647 BusyInterpData *dataPtr; /* Interpreter-specific data. */
648 Tcl_Interp *interp; /* Interpreter to report errors to */
649 char *pathName; /* Path name of parent window */
650 Busy **busyPtrPtr; /* Will contain address of busy window if
651 * found. */
652{
653 Blt_HashEntry *hPtr;
654 Tk_Window tkwin;
655
656 tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
657 if (tkwin == NULL) {
658 return TCL_ERROR;
659 }
660 hPtr = Blt_FindHashEntry(&dataPtr->busyTable, (char *)tkwin);
661 if (hPtr == NULL) {
662 Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"",
663 (char *)NULL);
664 return TCL_ERROR;
665 }
666 *busyPtrPtr = ((Busy *)Blt_GetHashValue(hPtr));
667 return TCL_OK;
668}
669
670/*
671 * ------------------------------------------------------------------
672 *
673 * HoldBusy --
674 *
675 * Creates (if necessary) and maps a busy window, thereby
676 * preventing device events from being be received by the parent
677 * window and its children.
678 *
679 * Results:
680 * Returns a standard TCL result. If path name represents a busy
681 * window, it is unmapped and TCL_OK is returned. Otherwise,
682 * TCL_ERROR is returned and an error message is left in
683 * interp->result.
684 *
685 * Side effects:
686 * The busy window is created and displayed, blocking events from
687 * the parent window and its children.
688 *
689 * -------------------------------------------------------------------
690 */
691static int
692HoldBusy(dataPtr, interp, argc, argv)
693 BusyInterpData *dataPtr; /* Interpreter-specific data. */
694 Tcl_Interp *interp; /* Interpreter to report errors to */
695 int argc;
696 char **argv; /* Window name and option pairs */
697{
698 Tk_Window tkwin;
699 Blt_HashEntry *hPtr;
700 Busy *busyPtr;
701 int isNew;
702 int result;
703
704 tkwin = Tk_NameToWindow(interp, argv[0], Tk_MainWindow(interp));
705 if (tkwin == NULL) {
706 return TCL_ERROR;
707 }
708 hPtr = Blt_CreateHashEntry(&dataPtr->busyTable, (char *)tkwin, &isNew);
709 if (isNew) {
710 busyPtr = (Busy *)CreateBusy(interp, tkwin);
711 if (busyPtr == NULL) {
712 return TCL_ERROR;
713 }
714 Blt_SetHashValue(hPtr, (char *)busyPtr);
715 busyPtr->hashPtr = hPtr;
716 } else {
717 busyPtr = (Busy *)Blt_GetHashValue(hPtr);
718 }
719 busyPtr->tablePtr = &dataPtr->busyTable;
720 result = ConfigureBusy(interp, busyPtr, argc - 1, argv + 1);
721
722 /*
723 * Don't map the busy window unless the reference window is also
724 * currently displayed.
725 */
726 if (Tk_IsMapped(busyPtr->tkRef)) {
727 ShowBusyWindow(busyPtr);
728 } else {
729 HideBusyWindow(busyPtr);
730 }
731 busyPtr->isBusy = TRUE;
732 return result;
733}
734
735
736/*
737 * ------------------------------------------------------------------
738 *
739 * StatusOp --
740 *
741 * Returns the status of the busy window; whether it's blocking
742 * events or not.
743 *
744 * Results:
745 * Returns a standard TCL result. If path name represents a busy
746 * window, the status is returned via interp->result and TCL_OK
747 * is returned. Otherwise, TCL_ERROR is returned and an error
748 * message is left in interp->result.
749 *
750 * -------------------------------------------------------------------
751 */
752/*ARGSUSED*/
753static int
754StatusOp(clientData, interp, argc, argv)
755 ClientData clientData; /* Interpreter-specific data. */
756 Tcl_Interp *interp; /* Interpreter to report error to */
757 int argc; /* Not used. */
758 char **argv;
759{
760 BusyInterpData *dataPtr = clientData;
761 Busy *busyPtr;
762
763 if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
764 return TCL_ERROR;
765 }
766 Tcl_Preserve(busyPtr);
767 Blt_SetBooleanResult(interp, busyPtr->isBusy);
768 Tcl_Release(busyPtr);
769 return TCL_OK;
770}
771
772/*
773 * ------------------------------------------------------------------
774 *
775 * ForgetOp --
776 *
777 * Destroys the busy window associated with the reference window and
778 * arranges for internal resources to the released when they're
779 * not being used anymore.
780 *
781 * Results:
782 * Returns a standard TCL result. If path name represents a busy
783 * window, it is destroyed and TCL_OK is returned. Otherwise,
784 * TCL_ERROR is returned and an error message is left in
785 * interp->result.
786 *
787 * Side effects:
788 * The busy window is removed. Other related memory and resources
789 * are eventually released by the Tk dispatcher.
790 *
791 * -------------------------------------------------------------------
792 */
793static int
794ForgetOp(clientData, interp, argc, argv)
795 ClientData clientData; /* Interpreter-specific data. */
796 Tcl_Interp *interp; /* Interpreter to report errors to */
797 int argc;
798 char **argv;
799{
800 BusyInterpData *dataPtr = clientData;
801 Busy *busyPtr;
802 register int i;
803
804 for (i = 2; i < argc; i++) {
805 if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) {
806 return TCL_ERROR;
807 }
808 /* Unmap the window even though it will be soon destroyed */
809 HideBusyWindow(busyPtr);
810 Tcl_EventuallyFree(busyPtr, DestroyBusy);
811 }
812 return TCL_OK;
813}
814
815/*
816 * ------------------------------------------------------------------
817 *
818 * ReleaseOp --
819 *
820 * Unmaps the busy window, thereby permitting device events
821 * to be received by the parent window and its children.
822 *
823 * Results:
824 * Returns a standard TCL result. If path name represents a busy
825 * window, it is unmapped and TCL_OK is returned. Otherwise,
826 * TCL_ERROR is returned and an error message is left in
827 * interp->result.
828 *
829 * Side effects:
830 * The busy window is hidden, allowing the parent window and
831 * its children to receive events again.
832 *
833 * -------------------------------------------------------------------
834 */
835static int
836ReleaseOp(clientData, interp, argc, argv)
837 ClientData clientData; /* Interpreter-specific data. */
838 Tcl_Interp *interp; /* Interpreter to report errors to */
839 int argc;
840 char **argv;
841{
842 BusyInterpData *dataPtr = clientData;
843 Busy *busyPtr;
844 int i;
845
846 for (i = 2; i < argc; i++) {
847 if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) {
848 return TCL_ERROR;
849 }
850 HideBusyWindow(busyPtr);
851 busyPtr->isBusy = FALSE;
852 }
853 return TCL_OK;
854}
855
856/*
857 * ------------------------------------------------------------------
858 *
859 * NamesOp --
860 *
861 * Reports the names of all widgets with busy windows attached to
862 * them, matching a given pattern. If no pattern is given, all
863 * busy widgets are listed.
864 *
865 * Results:
866 * Returns a TCL list of the names of the widget with busy windows
867 * attached to them, regardless if the widget is currently busy
868 * or not.
869 *
870 * -------------------------------------------------------------------
871 */
872static int
873NamesOp(clientData, interp, argc, argv)
874 ClientData clientData; /* Interpreter-specific data. */
875 Tcl_Interp *interp; /* Interpreter to report errors to */
876 int argc;
877 char **argv;
878{
879 BusyInterpData *dataPtr = clientData;
880 Blt_HashEntry *hPtr;
881 Blt_HashSearch cursor;
882 Busy *busyPtr;
883
884 for (hPtr = Blt_FirstHashEntry(&dataPtr->busyTable, &cursor);
885 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
886 busyPtr = (Busy *)Blt_GetHashValue(hPtr);
887 if ((argc == 2) ||
888 (Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), argv[2]))) {
889 Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkRef));
890 }
891 }
892 return TCL_OK;
893}
894
895/*
896 * ------------------------------------------------------------------
897 *
898 * BusyOp --
899 *
900 * Reports the names of all widgets with busy windows attached to
901 * them, matching a given pattern. If no pattern is given, all
902 * busy widgets are listed.
903 *
904 * Results:
905 * Returns a TCL list of the names of the widget with busy windows
906 * attached to them, regardless if the widget is currently busy
907 * or not.
908 *
909 * -------------------------------------------------------------------
910 */
911static int
912BusyOp(clientData, interp, argc, argv)
913 ClientData clientData; /* Interpreter-specific data. */
914 Tcl_Interp *interp; /* Interpreter to report errors to */
915 int argc;
916 char **argv;
917{
918 BusyInterpData *dataPtr = clientData;
919 Blt_HashEntry *hPtr;
920 Blt_HashSearch cursor;
921 Busy *busyPtr;
922
923 for (hPtr = Blt_FirstHashEntry(&dataPtr->busyTable, &cursor);
924 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
925 busyPtr = (Busy *)Blt_GetHashValue(hPtr);
926 if (!busyPtr->isBusy) {
927 continue;
928 }
929 if ((argc == 2) ||
930 (Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), argv[2]))) {
931 Tcl_AppendElement(interp, Tk_PathName(busyPtr->tkRef));
932 }
933 }
934 return TCL_OK;
935}
936
937/*
938 * ------------------------------------------------------------------
939 *
940 * HoldOp --
941 *
942 * Creates (if necessary) and maps a busy window, thereby
943 * preventing device events from being be received by the parent
944 * window and its children. The argument vector may contain
945 * option-value pairs of configuration options to be set.
946 *
947 * Results:
948 * Returns a standard TCL result.
949 *
950 * Side effects:
951 * The busy window is created and displayed, blocking events from the
952 * parent window and its children.
953 *
954 * -------------------------------------------------------------------
955 */
956static int
957HoldOp(clientData, interp, argc, argv)
958 ClientData clientData; /* Interpreter-specific data. */
959 Tcl_Interp *interp; /* Interpreter to report errors to */
960 int argc;
961 char **argv; /* Window name and option pairs */
962{
963 BusyInterpData *dataPtr = clientData;
964 register int i, count;
965
966 if ((argv[1][0] == 'h') && (strcmp(argv[1], "hold") == 0)) {
967 argc--, argv++; /* Command used "hold" keyword */
968 }
969 for (i = 1; i < argc; i++) {
970 /*
971 * Find the end of the option-value pairs for this window.
972 */
973 for (count = i + 1; count < argc; count += 2) {
974 if (argv[count][0] != '-') {
975 break;
976 }
977 }
978 if (count > argc) {
979 count = argc;
980 }
981 if (HoldBusy(dataPtr, interp, count - i, argv + i) != TCL_OK) {
982 return TCL_ERROR;
983 }
984 i = count;
985 }
986 return TCL_OK;
987}
988
989/* ARGSUSED*/
990static int
991CgetOp(clientData, interp, argc, argv)
992 ClientData clientData; /* Interpreter-specific data. */
993 Tcl_Interp *interp; /* Interpreter to report errors to */
994 int argc;
995 char **argv; /* Widget pathname and option switch */
996{
997 BusyInterpData *dataPtr = clientData;
998 Busy *busyPtr;
999 int result;
1000
1001 if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
1002 return TCL_ERROR;
1003 }
1004 Tcl_Preserve(busyPtr);
1005 result = Tk_ConfigureValue(interp, busyPtr->tkRef, configSpecs,
1006 (char *)busyPtr, argv[3], 0);
1007 Tcl_Release(busyPtr);
1008 return result;
1009}
1010
1011/*
1012 *----------------------------------------------------------------------
1013 *
1014 * ConfigureOp --
1015 *
1016 * This procedure is called to process an argv/argc list in order
1017 * to configure (or reconfigure) a busy window.
1018 *
1019 * Results:
1020 * The return value is a standard Tcl result. If TCL_ERROR is
1021 * returned, then interp->result contains an error message.
1022 *
1023 * Side effects:
1024 * Configuration information get set for busyPtr; old resources
1025 * get freed, if there were any. The busy window destroyed and
1026 * recreated in a new parent window.
1027 *
1028 *----------------------------------------------------------------------
1029 */
1030static int
1031ConfigureOp(clientData, interp, argc, argv)
1032 ClientData clientData; /* Interpreter-specific data. */
1033 Tcl_Interp *interp; /* Interpreter to report errors to */
1034 int argc;
1035 char **argv; /* Reference window path name and options */
1036{
1037 BusyInterpData *dataPtr = clientData;
1038 Busy *busyPtr;
1039 int result;
1040
1041 if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) {
1042 return TCL_ERROR;
1043 }
1044 if (argc == 3) {
1045 result = Tk_ConfigureInfo(interp, busyPtr->tkRef, configSpecs,
1046 (char *)busyPtr, (char *)NULL, 0);
1047 } else if (argc == 4) {
1048 result = Tk_ConfigureInfo(interp, busyPtr->tkRef, configSpecs,
1049 (char *)busyPtr, argv[3], 0);
1050 } else {
1051 Tcl_Preserve(busyPtr);
1052 result = ConfigureBusy(interp, busyPtr, argc - 3, argv + 3);
1053 Tcl_Release(busyPtr);
1054 }
1055 return result;
1056}
1057
1058/*
1059 * -----------------------------------------------------------------------
1060 *
1061 * BusyInterpDeleteProc --
1062 *
1063 * This is called when the interpreter hosting the "busy" command
1064 * is destroyed.
1065 *
1066 * Results:
1067 * None.
1068 *
1069 * Side effects:
1070 * Destroys all the hash table managing the busy windows.
1071 *
1072 * ------------------------------------------------------------------------
1073 */
1074/* ARGSUSED */
1075static void
1076BusyInterpDeleteProc(clientData, interp)
1077 ClientData clientData; /* Interpreter-specific data. */
1078 Tcl_Interp *interp;
1079{
1080 BusyInterpData *dataPtr = clientData;
1081 Blt_HashEntry *hPtr;
1082 Blt_HashSearch cursor;
1083 Busy *busyPtr;
1084
1085 for (hPtr = Blt_FirstHashEntry(&dataPtr->busyTable, &cursor);
1086 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1087 busyPtr = (Busy *)Blt_GetHashValue(hPtr);
1088 busyPtr->hashPtr = NULL;
1089 DestroyBusy((DestroyData)busyPtr);
1090 }
1091 Blt_DeleteHashTable(&dataPtr->busyTable);
1092 Tcl_DeleteAssocData(interp, BUSY_THREAD_KEY);
1093 Blt_Free(dataPtr);
1094}
1095
1096/*
1097 *--------------------------------------------------------------
1098 *
1099 * Busy Sub-command specification:
1100 *
1101 * - Name of the sub-command.
1102 * - Minimum number of characters needed to unambiguously
1103 * recognize the sub-command.
1104 * - Pointer to the function to be called for the sub-command.
1105 * - Minimum number of arguments accepted.
1106 * - Maximum number of arguments accepted.
1107 * - String to be displayed for usage (arguments only).
1108 *
1109 *--------------------------------------------------------------
1110 */
1111static Blt_OpSpec busyOps[] =
1112{
1113 {"cget", 2, (Blt_Op)CgetOp, 4, 4, "window option",},
1114 {"configure", 2, (Blt_Op)ConfigureOp, 3, 0, "window ?options?...",},
1115 {"forget", 1, (Blt_Op)ForgetOp, 2, 0, "?window?...",},
1116 {"hold", 3, (Blt_Op)HoldOp, 3, 0,
1117 "window ?options?... ?window options?...",},
1118 {"isbusy", 1, (Blt_Op)BusyOp, 2, 3, "?pattern?",},
1119 {"names", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
1120 {"release", 1, (Blt_Op)ReleaseOp, 2, 0, "?window?...",},
1121 {"status", 1, (Blt_Op)StatusOp, 3, 3, "window",},
1122 {"windows", 1, (Blt_Op)NamesOp, 2, 3, "?pattern?",},
1123};
1124static int nBusyOps = sizeof(busyOps) / sizeof(Blt_OpSpec);
1125
1126/*
1127 *----------------------------------------------------------------------
1128 *
1129 * BusyCmd --
1130 *
1131 * This procedure is invoked to process the "busy" Tcl command.
1132 * See the user documentation for details on what it does.
1133 *
1134 * Results:
1135 * A standard Tcl result.
1136 *
1137 * Side effects:
1138 * See the user documentation.
1139 *
1140 *----------------------------------------------------------------------
1141 */
1142static int
1143BusyCmd(clientData, interp, argc, argv)
1144 ClientData clientData; /* Interpreter-specific data. */
1145 Tcl_Interp *interp; /* Interpreter associated with command */
1146 int argc;
1147 char **argv;
1148{
1149 Blt_Op proc;
1150 int result;
1151
1152 if ((argc > 1) && (argv[1][0] == '.')) {
1153 return HoldOp(clientData, interp, argc, argv);
1154 }
1155 proc = Blt_GetOp(interp, nBusyOps, busyOps, BLT_OP_ARG1, argc, argv, 0);
1156 if (proc == NULL) {
1157 return TCL_ERROR;
1158 }
1159 result = (*proc) (clientData, interp, argc, argv);
1160 return result;
1161}
1162
1163static BusyInterpData *
1164GetBusyInterpData(interp)
1165 Tcl_Interp *interp;
1166{
1167 BusyInterpData *dataPtr;
1168 Tcl_InterpDeleteProc *proc;
1169
1170 dataPtr = (BusyInterpData *)
1171 Tcl_GetAssocData(interp, BUSY_THREAD_KEY, &proc);
1172 if (dataPtr == NULL) {
1173 dataPtr = Blt_Malloc(sizeof(BusyInterpData));
1174 assert(dataPtr);
1175 Tcl_SetAssocData(interp, BUSY_THREAD_KEY, BusyInterpDeleteProc,
1176 dataPtr);
1177 Blt_InitHashTable(&dataPtr->busyTable, BLT_ONE_WORD_KEYS);
1178 }
1179 return dataPtr;
1180}
1181
1182int
1183Blt_BusyInit(interp)
1184 Tcl_Interp *interp;
1185{
1186 static Blt_CmdSpec cmdSpec = {"busy", BusyCmd, };
1187 BusyInterpData *dataPtr;
1188
1189 dataPtr = GetBusyInterpData(interp);
1190 cmdSpec.clientData = dataPtr;
1191 if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
1192 return TCL_ERROR;
1193 }
1194 return TCL_OK;
1195}
1196#endif /* NO_BUSY */
Note: See TracBrowser for help on using the repository browser.