Next: , Previous: , Up: Part I Doing Interaction   [Contents][Index]


4.4 Dealing With Multiple Windows

It is not atypical that an application program may need to take interaction from more than one form at the same time, Forms Library provides a mechanism with which precise control can be exercised.

By default, fl_do_forms() takes interaction from all forms that are shown. In certain situations, you might not want to have interaction with all of them. For example, when the user presses a quit button in a form you might want to ask a confirmation using another form. You don’t want to hide the main form because of that but you also don’t want the user to be able to press buttons, etc. in this form. The user first has to give the confirmation. So you want to temporarily deactivate the main form. This can be done using the call

void fl_deactivate_form(FL_FORM *form);

To reactivate the form later again use

void fl_activate_form(FL_FORM *form);

It is a good idea to give the user a visual clue that a form is deactivated. This is not automatically done mainly for performance reasons. Experience shows that graying out some important objects on the form is in general adequate. Graying out an object can be accomplished by using fl_set_object_lcolor() (see objinactive.c. What objects to gray out is obviously application dependent.

The following two functions can be used to register two callbacks that are called whenever the activation status of a form is changed:

typedef void (*FL_FORM_ATACTIVATE)(FL_FORM *, void *);
FL_FORM_ATACTIVATE fl_set_form_atactivate(FL_FORM *form,
                                   FL_FORM_ATACTIVATE callback,
                                   void *data);

typedef void (*FL_FORM_ATDEACTIVATE)(FL_FORM *, void *);
FL_FORM_ATDEACTIVATE fl_set_form_atdeactivate(FL_FORM *form,
                                   FL_FORM_ATDEACTIVATE callback,
                                   void *data);

It is also possible to deactivate all current forms and reactivate them again. To this end use the functions:

void fl_deactivate_all_forms(void);
void fl_activate_all_forms(void);

Note that deactivation works in an additive way, i.e., when deactivating a form say 3 times it also has to be activated 3 times to become active again.

One problem remains. Mouse actions etc. are presented to a program in the form of events in an event queue. The library routines fl_do_forms() and fl_check_forms() read this queue and handle the events. When the application program itself also opens windows, these windows will rather likely receive events as well. Unfortunately, there is only one event queue. When both the application program and the library routines would read events from this one queue problems would occur and events missed. Hence, the application program should not read the event queue itself.. To solve this problem, the library maintains (or appears to maintain) a separate event queue for the user. This queue behaves in exactly the same way as the normal event queue. To access it, the application program must use replacements for the usual Xlib routines. Instead of using XNextEvent(), the program will use fl_XNextEvent(), with the same parameters except the Display * argument. The following is a list of all replacement routines:

int fl_XNextEvent(XEvent *xev);
int fl_XPeekEvent(XEvent *xev);
int fl_XEventsQueued(int mode);
int fl_XPutbackEvent(XEvent *xev);

Note that these routines normally return 1, but after a call of fl_finish() they return 1 instead.

Other events routines may be directly used if proper care is taken to make sure that only events for the application windows not handled by the library are removed. These routines include XWindowEvent(), XCheckWindowEvent() etc.

To help find out when an event has occurred, whenever fl_do_forms() and fl_check_forms() encounter an event that is not meant for handling by the library but by the application program itself they return a special object FL_EVENT. Upon receiving this special event, the application program can and must remove the pending event from the queue using fl_XNextEvent().

So the basis of a program with its own windows would look as follows:

/* define the forms */
/* display the forms */
/* open your own window(s) */

while (! ready) {
    obj = fl_do_forms();    /* or fl_check_forms() */
    if (obj == FL_EVENT) {
        fl_XNextEvent(&xevent);
        switch (xevent.type) {
            /* handle the event */
        }
    } else if (obj != NULL)
        /* handle the change in obj */
        /* update other things */
    }
}

In some situations you may not want to receive these "user" events. For example, you might want to write a function that pops up a form to change some settings. This routine might not want to be concerned with any redrawing of the main window, etc., but you also not want to discard any events. In this case you can use the routines fl_do_only_forms() and fl_check_only_forms() that will never return FL_EVENT. The events don’t disappear but will be returned at later calls to the normal routines fl_do_forms() etc.

It can’t be over-emphasized that it is an error to ignore FL_EVENT or use fl_XNextEvent() without seeing FL_EVENT.

Sometimes an application program might need to find out more information about the event that triggered a callback, e.g., to implement mouse button number sensitive functionalities. To this end, the following routines may be called

long fl_mouse_button(void);

This function, if needed, should be called from within a callback. The function returns one of the constants FL_LEFT_MOUSE, FL_MIDDLE_MOUSE, FL_RIGHT_MOUSE, FL_SCROLLUP_MOUSE or FL_SCROLLDOWN_MOUSE, indicating which mouse button was pushed or released. If the callback is triggered by a shortcut, the function returns the keysym (ascii value if ASCII) of the key plus FL_SHORTCUT. For example, if a button has a shortcut <Ctrl>C (ASCII value is 3), the button number returned upon activation of the shortcut would be FL_SHORTCUT + 3. FL_SHORTCUT can be used to determine if the callback is triggered by a shortcut or not

if (fl_mouse_button() >= FL_SHORTCUT)
    /* handle shortcut */
else
    switch (fl_mouse_button()) {
        case FL_LEFTMOUSE:
        ....
    }

More information can be obtained by using the following routine that returns the last XEvent

const XEvent *fl_last_event(void);

Note that if this routine is used outside of a callback function, the value returned may not be the real "last event" if the program was idling and, in this case, it returns a synthetic MotionNotify event.

Some of the utilities used internally by the Forms Library can be used by the application programs, such as window geometry queries etc. Following is a partial list of the available routines:

void fl_get_winorigin(Window win, FL_Coord *x, FL_Coord *y);
void fl_get_winsize(Window win, FL_Coord *w, FL_Coord *h);
void fl_get_wingeometry(Window win, FL_Coord *x, FL_Coord *y,
                        FL_Coord *w, FL_Coord *h);

All positions are relative to the root window.

There are also routines that can be used to obtain the current mouse position relative to the root window:

Window fl_get_mouse(FL_Coord *x, FL_Coord *y,
                    unsigned int *keymask);

where keymask is the same as used in XQueryPointer(3X11). The function returns the window ID the mouse is in.

To obtain the mouse position relative to an arbitrary window, the following routine may be used

Window fl_get_win_mouse(Window win, FL_Coord *x, FL_Coord *y,
                        unsigned int *keymask);

To print the name of an XEvent, the following routine can be used:

XEvent *fl_print_xevent_name(const char *where, const XEvent *xev);

The function takes an XEvent, prints out its name and some other info, e.g., expose, count=n. Parameter where can be used to indicate where this function is called:

fl_print_xevent_name("In tricky.c", &xevent);

Next: , Previous: , Up: Part I Doing Interaction   [Contents][Index]