/* Written by Dan Heller and Paula Ferguson.
* Copyright 1994, O'Reilly & Associates, Inc.
* Permission to use, copy, and modify this program without
* restriction is hereby granted, as long as this copyright
* notice appears in each copy of the program source code.
* This program is freely distributable without licensing fees and
* is provided without guarantee or warrantee expressed or implied.
* This program is -not- in the public domain.
*/
/* busy.c -- demonstrate how to use a WorkingDialog and to process
* only important events. e.g., those that may interrupt the
* task or to repaint widgets for exposure. Set up a simple shell
* and a widget that, when pressed, immediately goes into its own
* loop. Set a timeout cursor on the shell and pop up a WorkingDialog.
* Then enter loop and sleep for one second ten times, checking between
* each interval to see if the user clicked the Stop button or if
* any widgets need to be refreshed. Ignore all other events.
*
* main() and get_busy() are stubs that would be replaced by a real
* application; all other functions can be used as is.
*/
#include <Xm/MessageB.h>
#include <Xm/PushB.h>
#include <X11/cursorfont.h>
Widget shell;
void TimeoutCursors();
Boolean CheckForInterrupt();
main(argc, argv)
int argc;
char *argv[];
{
XtAppContext app;
Widget button;
XmString label;
void get_busy();
XtSetLanguageProc (NULL, NULL, NULL);
shell = XtVaAppInitialize (&app, "Demos",
NULL, 0, &argc, argv, NULL, NULL);
label = XmStringCreateLocalized ("Press Here To Start A Long Task");
button = XtVaCreateManagedWidget ("button",
xmPushButtonWidgetClass, shell,
XmNlabelString, label,
NULL);
XmStringFree (label);
XtAddCallback (button, XmNactivateCallback, get_busy, NULL);
XtRealizeWidget (shell);
XtAppMainLoop (app);
}
void
get_busy(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
int n;
TimeoutCursors (True, True);
for (n = 0; n < 10; n++) {
sleep (1);
if (CheckForInterrupt ()) {
puts ("Interrupt!");
break;
}
}
if (n == 10)
puts ("Done");
TimeoutCursors (False, False);
}
/* The interesting part of the program -- extract and use at will */
static Boolean stopped; /* True when user wants to stop task */
static Widget dialog; /* WorkingDialog displayed */
/* TimeoutCursors() -- turns on the watch cursor over the application
* to provide feedback for the user that she's going to be waiting
* a while before she can interact with the application again.
*/
void
TimeoutCursors(on, interruptable)
Boolean on, interruptable;
{
static int locked;
static Cursor cursor;
extern Widget shell;
XSetWindowAttributes attrs;
Display *dpy = XtDisplay (shell);
XEvent event;
Arg args[5];
int n;
XmString str;
extern void stop_();
/* "locked" keeps track if we've already called the function.
* This allows recursion and is necessary for most situations.
*/
if (on)
locked++;
else
locked--;
if (locked > 1 || locked == 1 && on == 0)
return; /* already locked and we're not unlocking */
stopped = False;
if (!cursor)
cursor = XCreateFontCursor (dpy, XC_watch);
/* if on is true, then turn on watch cursor, otherwise, return
* the shell's cursor to normal.
*/
attrs.cursor = on ? cursor : None;
/* change the main application shell's cursor to be the timeout
* cursor or to reset it to normal. If other shells exist in
* this application, they will have to be listed here in order
* for them to have timeout cursors too.
*/
XChangeWindowAttributes (dpy, XtWindow (shell), CWCursor, &attrs);
XFlush (dpy);
if (on) {
/* we're timing out, put up a WorkingDialog. If the process
* is interruptable, allow a "Stop" button. Otherwise, remove
* all actions so the user can't stop the processing.
*/
n = 0;
str = XmStringCreateLocalized ("Busy -- Please Wait.");
XtSetArg (args[n], XmNmessageString, str); n++;
dialog = XmCreateWorkingDialog (shell, "busy", args, n);
XmStringFree (str);
XtUnmanageChild (XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON));
XtUnmanageChild (XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON));
if (interruptable) {
str = XmStringCreateLocalized ("Stop");
XtVaSetValues (dialog, XmNcancelLabelString, str, NULL);
XmStringFree (str);
XtAddCallback (dialog, XmNcancelCallback, stop_, NULL);
}
else
XtUnmanageChild (XmMessageBoxGetChild
(dialog, XmDIALOG_CANCEL_BUTTON));
XtManageChild (dialog);
}
else {
/* get rid of all button and keyboard events that occured
* during the time out. The user shouldn't have done anything
* during this time, so flush for button and keypress events.
* KeyRelease events are not discarded because accelerators
* require the corresponding release event before normal input
* can continue.
*/
while (XCheckMaskEvent (dpy,
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
| PointerMotionMask | KeyPressMask, &event)) {
/* do nothing */;
}
XtDestroyWidget (dialog);
}
}
/* stop() -- user pressed the "Stop" button in dialog. */
void
stop_(dialog, client_data, call_data)
Widget dialog;
XtPointer client_data;
XtPointer call_data;
{
stopped = True;
}
/* CheckForInterrupt() -- check events in event queue and process
* the interesting ones.
*/
Boolean
CheckForInterrupt()
{
extern Widget shell;
Display *dpy = XtDisplay (shell);
Window win = XtWindow (dialog);
XEvent event;
/* Make sure all our requests get to the server */
XFlush (dpy);
/* Let Motif process all pending exposure events for us. */
XmUpdateDisplay (shell);
/* Check the event loop for events in the dialog ("Stop"?) */
while (XCheckMaskEvent (dpy,
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
PointerMotionMask | KeyPressMask, &event)) {
/* got an "interesting" event. */
if (event.xany.window == win)
XtDispatchEvent (&event); /* it's in our dialog.. */
else /* uninteresting event--throw it away and sound bell */
XBell (dpy, 50);
}
return stopped;
}