/* * This file contains a test driver program for the FuncButton class. It * illustrates function buttons with general-purpose call-back functions. It * also illustrates function buttons used to change a state value within a * dialog box. * * In addition to illustrating the use of callback-style function buttons, this * file attempts to illustrate a general "functional approach" to the use of * the InterViews toolkit. This functional approach contrasts to the normal * InterViews "state-based approach." * * To illustrate the contrast, consider an interface "Exit" button, which when * pressed by the user causes an interface and its application program to * terminate. In the functional approach, the exit button callback function is * passed its world, the callback explicitly deletes the world before exiting, * and then it calls the UNIX exit function directly. * * In the InterViews state-based approach, pressing an exit button does not * explicitly exit via a callback, but rather cause a state change that is * recognized elsewhere in the program, which state change eventually leads to * an exit. * * The worst part about the state-based approach to exiting, and the * state-based approach in general, is that the state change itself is not * directly visible in the functions that cause it. A typical case is the * state change that is effected by an InterViews push button. This change * happens within a virutual function named "Press", which is at least three * levels down in the InterViews calling hierarchy. Furthermore, after Press * makes the state change, it takes several further layers of call before the * change is noticed by an event loop, exit from which will lead to program * exit. * * Concretely, suppose an Exit push button is pressed. The way that pressing * the Exit button will effect exiting from a program is that the button press * will cause a state change, this state change will be noticed by the * event-handling loop in the button's parent scene, the state change will * cause the scene's loop to terminate, whereupon the program will exit. * * This state-changing behavior is quite obscure in terms of the levels of * function call that ensue. For example, in the case of the Exit push button, * here's what happens: * * (1) When the the mouse event that caused the push is recognized, the * button's Handle function is called; * (2) Handle in turn calls the button's Press function; * (3) Press then calls SetValue on the button's state; * (4) SetValue calls Modify; * (5) Modify calls Notify * (6) Notify calls i.Update, for all interactors i that share the button's * state; * (7) Meanwhile, in the event handling loop for the scene that contains the * Exit button, a call to GetValue on the shared state is maid * (8) GetValue returns the value of the state changed by SetValue earlier, * and if this value is the conventional exitloop value (typically non- * zero), the event loop exits. * (9) In the typical case of an Exit button, the event loop that was * exited was the top-level loop, which leads to program termination. * * In contrast, here are the steps performed in the functional approach to * exiting: * * (1) When the the mouse event that caused the push is recognized, the * button's callback function is called; * (2) The callback calls UNIX exit. * */ #include #include #include #include #include #include #include #include #include #include "FuncButton.h" #include "driver.h" /* * General-purpose callback function defs. */ void DoFb1(void* v, Event *e) { printf("In fb1 pressed\n"); } void DoFb2(void* v, Event *e) { printf("In fb2 pressed\n"); } void Exit(void *parm, Event *e) { World *world = (World *) parm; if (Ask("Are you sure you want to exit?", world)) { delete world; exit(1); } } /* * The Ask function asks a user for a yes/no answer in a pop-up dialog box. * The dialog box contains an "Are you sure?" message, and buttons labeled "OK" * and "Cancel". Ask returns true if OK is pressed, false if Cancel is * pressed. In either case, the pop-up dialog box is dismissed. * * This dialog box illustrates a more functional approach to user dialog than * the normal InterViews approach. In the functional approach, the state * changing behavior becomes more visible, rather than having it buried deep * within a hierarchy of function calls. */ int Ask(char *question, World *w) { /* * Build a state object that is shared by the dialog box and the two * buttons within it. The function buttons don't "share" the button state * in the same indirect way that InterViews push buttons do. Rather, the * state object is passed to the function button callbacks as an explicit * parameter. Then the callbacks explicitly set the state object. In this * way, the state-setting behavior of the buttons is more directly visible. */ ButtonState *state = new ButtonState(1); /* * An integer result is returned by the dialog Accept function. Accept * contains an event-handling loop that exits when the dialog state value * is set to a non-zero value. This setting is performed by the OK and * Cancel callbacks. */ int result; /* * Figure out the coordinates of the center of the world. */ Coord x = w->Width() / 2; Coord y = w->Height() / 2; /* * Build the dialog box with the message and buttons. The contents of the * box are built in layers: a tray contains the message and buttons, with * formatting glue between them. The tray is put in a shadow frame. The * shadow frame is put within the dialog box object. Finally, the dialog * box is put in the world. */ ShadowFrame *sf; Dialog *d = new Dialog(state, sf = new ShadowFrame()); Tray *t = new Tray( new VBox( new VGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)), new HBox( new HGlue(round(0.1*inch), round(0.05*inch), round(0.05*inch)), new Message(question), new HGlue(round(0.1*inch), round(0.05*inch), round(0.05*inch)) ), new VGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)), new HBox( new HGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)), new FuncButton(OK, "OK", (void *) state), new HGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)), new FuncButton(Cancel, "Cancel", (void *) state), new HGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)) ), new VGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)))); /* * Insert the tray in the shadow frame. */ sf->Insert(t); /* * Insert the whole dialog box in the world. */ w->InsertTransient(d, d, x, y, Center); result = d->Accept(); w->Remove(d); return result; /* * Following two lines are redundant, since the GetValue is done within * the Accept: * state->GetValue(result); * return result; */ } /* * The OK button in the Ask dialog. It communicates that it's been pressed by * setting the diaglog's state to 1. Note that the state must go non-zero in * order for the earlier call to Accept to complete. Futhermore, Accept * returns true if the dialog state is 1, false if dialog state is anything * other than 1. Therefore, when OK returns 1, Accept will subsequently return * true. Whereas, when Cancel returns 2, Accept will subsequently return * false. Got it?? */ void OK(void *parm, Event *e) { ButtonState *state = (ButtonState *) parm; state->SetValue(1); } /* * The Cancel button in the Ask dialog. It communicates that it's been pressed * by setting the dialog state to 2. Note that the state must go non-zero in * order for the earlier call to Accept to complete. Futhermore, Accept * returns true if the dialog state is 1, false if dialog state is anything * other than 1. Therefore, when OK returns 1, Accept will subsequently return * true. Whereas, when Cancel returns 2, Accept will subsequently return * false. Got it yet?? */ void Cancel(void *parm, Event *e) { ButtonState *state = (ButtonState *) parm; state->SetValue(2); } main() { World* world = new World(); world->InsertApplication( new Tray( new HBox( new HGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)), new VBox( new VGlue(round(0.3*inch), round(0.2*inch), round(0.5*inch)), new FuncButton(DoFb1, "FB1", 0), new VGlue(round(0.3*inch), round(0.2*inch), round(1.0*inch)), new FuncButton(DoFb2, "FB2", 0), new VGlue(round(0.3*inch), round(0.2*inch), round(1.0*inch)), new FuncButton(Exit, "Exit", (void *) world), new VGlue(round(0.3*inch), round(0.2*inch), round(1.0*inch)) ), new HGlue(round(0.25*inch), round(0.2*inch), round(1.0*inch)) ) ) ); world->Run(); delete world; return 0; }