Simple Event Loop and Main Windows

One of the advantages of the Framework is that developers can pick some or all of the facilities it provides. Let's start with a simple main event loop program. The text of the program is shown with line numbers which are for explanatory purposes only. First, we have the program that contains the main event loop.

[1] procedure MainEventLoop () \
[2] local (vlStatus, \  % Indicates status.
[3]        vlWindowName, \ % name of a window
[4]          vlAction)   % Action to execute.
[6] MainWindowProg (vlStatus, "ENTERWINDOW")
[8] let vlAction = ""

The program is called MainEventLoop (although any name would do). The most important line so far is line [6]. This line calls the program that manages the main application window. Using the Framework, all window management programs have a similar interface. The first parameter, vlStatus, is used by the program to return a status to the caller. By convention, a value of "OK" or blank means everything is fine. The second parameter, "ENTERWINDOW", is the name of the action that we want the window management program to execute. Since the window is opened on this first call, by convention, we pass in an action called "ENTERWINDOW" that signifies that the window has got the focus. The main application window program is discussed below. For now, suffice to say that this call causes the main application window to be opened and displayed. It also "registers" the window and its program with the Framework so it knows that this window is now active. Now we enter the actual event loop.

[10] while $IsWinOpen ("wMainWindow") = $true

The loop is terminated when we discover that the main application window (which is called wMainWindow in our example) is no longer open. We see later that this occurs under several circumstances (e.g. when the user clicks on the Close menu item in the main application window's system menu).

[12]        form input
[14]        let vlAction = Event.EventTag \
[15]            vlWindowName = Event.WindowTag

FORM INPUT gets the next user interface event and we capture the event tag (the event "name" if you like) and the tag of the window in which the event occurred. In our example, there is only one window (the main application window) but, in general, there can be many windows open and available at any time.

[17]        pDispatchAction (vlStatus,vlAction,"",0, \
[18]            vlWindowName)

This our first use of the Framework, a call to a program called pDispatchAction. All Framework objects are defined in the directory, $DeployServices, and are shown in this section in bold. pDispatchAction checks amongst the registered windows for the indicated window and invokes the program associated with that window. In this case, there is only one window so this causes the action in vlAction to be passed to MainWindowProg. In the more general case where many windows are open, pDispatchAction executes the program that is associated with the window in which the even occurred, passing the name of the action. As usual, the return status is passed back in vlStatus.

[19] endwhile
[21] endprocedure

The loop ends and we exit.

Now, let's look at the main application window program, MainWindowProg, which was started on line [6] above.

[1] %%-------------- plOpen -----------------------
[2] %%---------------------------------------------
[3] localprocedure plOpen (inout vlStatus)
[5] on error
[6]        let vlStatus = "ERROR"
[7]        return
[8] endon
[10] %% Open the Application window.
[11] window open wMainWindow at Center
[12] window set accelerator CtrlF4 AltF4 F1 ShiftF1
[13] menu open mMainMenu
[14] menu display
[15] form open fMainForm
[16] form display
[17] window activate wMainWindow
[19] let vlStatus = "OK"
[20] endprocedure
[22] %%-------------- plInitialise ----------------
[23] %%---------------------------------------------
[24] localprocedure plInitialise ( inout vlStatus )
[26] on error
[27]        plClose (vlStatus)
[28]        let vlStatus = "ERROR"
[29]        return
[30] endon
[32] let vlStatus = ""
[34] %% Initialise the user interface.
[35] plOpen (vlStatus)
[36] if (vlStatus = "ERROR")
[37]        set exception error
[38] endif
[40] %% Register the process.
[41] pRegisterProcess (vlStatus, \
[42]            "Main", \
[43]            "wMainWindow", \
[44]            "", \
[45]            "Application Window.", \
[46]            "MainWindowProg", \
[47]            cApplicationClass, \
[48]            $null, \
[49]            MainState.stProcessId)
[50] if (vlStatus = "ERROR")
[51]         set exception error
[52] endif
[54] endprocedure

The initial local procedure, plOpen, is called from plInitialise, which, in turn, is called when the main application window program determines that it is just getting started. Within plOpen, the main application window is opened, and then a menu and form are opened. The window is activated to display the window and we set up some accelerator keys.

plInitialise, itself, calls plOpen and then "registers" the main application window and this program with the Framework. This is done using the pRegisterProcess call. The important input parameters to pRegisterProcess here are the window name (line [43]), the main window program name (i.e. the name of this program) which is passed on line [46], and the process class which is passed on line [47]. The last of these parameters (cApplicationClass) tells the Framework what sort of process this is. cApplicationClass indicates a process that is the main window of an application. The final parameter to pRegisterProcess is an output parameter in which the Framework returns an assigned process id. This id is stored in the MainState structure in the stProcessId field. MainState is the name of a form that is actually used to retain information about the main application window between calls to MainWindowProg. MainState is not used for display purposes. In this example, MainState has only one field, stProcessId.

Note: The Framework operates on the assumption that window names and window tags are identical (the default case when a window is painted).

[56] %%---------------- plClose --------------------
[57] %%---------------------------------------------
[58] localprocedure plClose (inout vlStatus)
[60] if ($IsWinOpen ("wMainWindow") = $true)
[61]        window close wMainWindow
[62] endif
[63] endprocedure

plClose is the inverse of plOpen. It is called when the user asks that the application exit from the main application window. The next local procedure, plExecAction, contains a CASE construct that handles the various actions that this program deals with.

[65] %%---------------- plExecAction ---------------
[66] %%---------------------------------------------
[67] localprocedure plExecAction (inout vlStatus, \
[68]                 in vlAction )
[70] let vlStatus = "OK" \
[71]            vlAction = $toupper(vlAction)
[73] case
[74] when vlAction = "ENTERWINDOW"
[75]        %% window was just opened or got the focus
[77] when vlAction = "HELP"
[78]        help ThisWindow.WindowName
[79] when vlAction = "HELPITEM"
[80]        help ThisForm.FieldTag

The name of the action is converted to upper case to make subsequent comparisons case insensitive. The first action is ENTERWINDOW. As we discussed above, this action name is, by convention, associated with the window being opened or getting the focus. In our case, there is nothing to do. The next two actions (HELP and HELPITEM) illustrate how application specific menu or form events could be handled. These actions might be invoked by the user by clicking on certain menu items on the main menu. The tags associated with these menu items would be HELP or HELPITEM. HELP causes the help function to be invoked with a topic that matches the current window name (i.e. wMainWindow). HELPITEM is passed the current field's tag, presumably bringing up help on the current field.

[81] when vlAction = "CLOSED" and \
[82]          Event.EventType = "Window"
[83]        plClose (vlStatus)
[84]        pUnRegisterProcess (vlStatus, \
[85]         MainState.stProcessId, "")

When we get a CLOSED action, this means the user wants to exit from the main application window. First we call plClose which closes the window and cleans up. Then we call pUnRegisterProcess, another program in the Framework. This call tells the Framework that this "process" (i.e. window) is being closed. The Framework removes the program from the list of currently open and active windows. The call to pUnRegisterProcess requires the current program's process id. This process id was assigned by pRegisterProcess (line [41]) and saved in MainState.stProcessId and is passed to pUnRegisterProcess on line [84] where it is set to $NULL to indicate that the main application window is no longer open.

[87] when Event.EventType = "Accelerator" and \
[88]           Event.EventName in (AltF4,CtrlF4)
[89]        plClose (vlStatus)
[90]        pUnRegisterProcess (vlStatus, \
[91]           MainState.stProcessId, "")

This code is virtually identical to the code that is executed when the user clicks on Close. This case covers the situation where the user pressed Alt-F4 or Ctrl-F4. (These keys were set up as accelerators in plOpen). By convention in windowed applications (especially Microsoft Windows), Alt-F4 is the standard accelerator for a user to request that an application be terminated. Ctrl-F4 is the standard accelerator for "close this window". Since we only have one window, both accelerators mean the user wants to close the main application window.

[92] otherwise
[93] endcase
[95] endprocedure
[97] %%--------------- MainWindowProg --------------
[98] %%---------------------------------------------
[99] procedure MainWindowProg (inout vlStatus, \
[100]                in vlAction )
[102] on error
[103]        let vlStatus = "ERROR"
[104]        return
[105] endon
[106] %% Establish a running state.
[107] let vlStatus=""
[108] if MainState.stProcessId = $null or \
[109]        $IsWinOpen ("wMainWindow") = $false
[110]        plInitialise (vlStatus)
[111]        if (vlStatus = "ERROR")
[112]           set exception error
[113]        endif
[114] endif
[115] window set current wMainWindow
[117] %% Execute the Action.
[118] plExecAction (vlStatus, vlAction)
[120] endprocedure

This is the main procedure in MainWindowProg. It starts by checking that the main application window is open and that we have a valid process id for this program. If either check fails, we call plInitialise to open the main application window and register this program with the Framework. Then, on line [115], we make the main application window the current Zim window. This ensures that any FORM or WINDOW commands executed within this program are applied to the main application window. Finally, we call plExecAction to execute the required action and then we exit. This organization (check if window open and initialise if it isn't, set the current window, execute the action, return) is the standard for programs written to make use of the Framework.