This example assumes a window already created, and gadtools gadgets attached. One could alter the state machine so that it worked differently, advancing states as more stuff is initialized.
The actual state machine is rather simple:
The code is nearly as simple:
/*
* Example of state machine driven GUI program...
*/
typedef PROGEVENT (*FUNCPTR)(struct IntuiMessage *msg);
typedef enum e_prog_states {
NO_DOC, EMPTY_DOC, SAVED_DOC, UNSAVED_DOC,
QUERY_SAVE, QUERY_DELETE, ASK_FILENAME, QUITTING
} PROGSTATE;
typedef enum e_prog_events {
NEW_BUTTON, CLOSE_BUTTON, OPEN_BUTTON, SAVE_BUTTON,
KEY_EVENT, MOUSE_EVENT, OK_BUTTON, CANCEL_BUTTON,
QUIT_EVENT, NO_EVENT
} PROGEVENT;
typedef struct {
PROGSTATE old_state;
PROGEVENT event;
PROGSTATE new_state;
FUNCPTR action;
} AUTOMATA_RECORD;
/*
* Note that we only define those combinations of
* state and event which are valid (or which require
* special error processing)...
*/
AUTOMATA_RECORD state_machine[] = {
{ NO_DOC, NEW_BUTTON, EMPTY_DOC, create_document },
{ NO_DOC, OPEN_BUTTON, SAVED_DOC, open_document },
{ EMPTY_DOC, CLOSE_BUTTON, NO_DOC, free_document },
{ EMPTY_DOC, KEY_EVENT, UNSAVED_DOC, doc_process_event },
{ EMPTY_DOC, MOUSE_EVENT, UNSAVED_DOC, doc_process_event },
{ EMPTY_DOC, OPEN_BUTTON, SAVED_DOC, free_and_open_doc },
{ SAVED_DOC, CLOSE_BUTTON, NO_DOC, free_document },
{ SAVED_DOC, KEY_EVENT, UNSAVED_DOC, doc_process_event },
{ SAVED_DOC, MOUSE_EVENT, UNSAVED_DOC, doc_process_event },
{ UNSAVED_DOC, KEY_EVENT, UNSAVED_DOC, doc_process_event },
{ UNSAVED_DOC, MOUSE_EVENT, UNSAVED_DOC, doc_process_event },
{ UNSAVED_DOC, CLOSE_BUTTON, QUERY_SAVE, save_requester },
{ UNSAVED_DOC, SAVE_BUTTON, SAVED_DOC, save_document },
{ QUERY_SAVE, OK_BUTTON, ASK_FILENAME, filename_requester },
{ QUERY_SAVE, CANCEL_BUTTON, QUERY_DELETE, delete_requester },
{ ASK_FILENAME, OK_BUTTON, NO_DOC, save_and_free_doc },
{ ASK_FILENAME, CANCEL_BUTTON, UNSAVED_DOC, NULL },
{ QUERY_DELETE, OK_BUTTON, NO_DOC, free_document },
{ QUERY_DELETE, CANCEL_BUTTON, UNSAVED_DOC, NULL }
};
int num_records = sizeof(state_machine) / sizeof(state_machine[0]);
PROGSTATE current_state = NO_DOC;
/*
* Obviously, this is NOT complete code, but it should be
* enough to give you an idea of what could be done.
*/
int event_loop(struct Window *win)
{
struct IntuiMessage *msg, my_msg;
PROGEVENT event;
int i;
while (current_state != QUITTING) {
WaitPort(win->UserPort);
while ((msg = GT_GetIMsg(win->UserPort)) != NULL) {
memcpy(&my_msg,msg,sizeof(struct IntuiMessage));
GT_ReplyIMsg(msg);
if (my_msg.Class == GADGET_UP) {
struct Gadget *gad = (struct Gadget *)my_msg.IAddress;
event = gad->GadgetID;
} else if ((my_msg.Class == MOUSE_UP) || (my_msg.Class == MOUSE_DOWN))
event = MOUSE_EVENT;
else if ((my_msg.Class == KEY_UP) || (my_msg.Class == KEY_DOWN))
event = KEY_EVENT;
else if (my_msg.Class == CLOSEWINDOW)
event = QUIT_EVENT;
else
event = NO_EVENT;
/*
* This while-loop gives action functions a chance to
* generate their own events (such as responses to
* modal dialog boxes...
*/
while (event != NO_EVENT) {
for (i = 0; i < num_records; i++) {
if ((state_machine[i].old_state == current_state) &&
(state_machine[i].event == event)) {
if (state_machine[i].action)
event = (*(state_machine[i].action))(&my_msg);
current_state = state_machine[i].new_state;
break;
}
}
}
}
}
}