ArmBob v.1.02 Tutorial 4                               GCW 06/06/94

Wimp programming
----------------
The example !Exec is provided, as a minimal framework to start
with. It uses no classes. When double-clicked !Exec should put
its icon on the iconbar. Its iconbar menu has two items - Info and
Quit. If an object is dragged onto its iconbar icon, the file
!Exec.exec will be obeyed, with the object's pathname as argument.
A directory !Exec.Lib is provided for tools to be used by !Exec.
An example provided in !Exec.Lib is Templread, a template disassembler.
It expects a template file as argument, and when run opens an editable
window on Bob source code defining the template's windows. A window
called, say xyz, is defined by the data in a buffer called xyz_buf.

!Exec is an application whose !RunImage file has filetype Bob.
Shift-double-click it to see its contents. It loads four library files.

The first line

                @b = @(newstring(500)); 

creates a fixed buffer, 500 bytes long, at the address @b. Note
the convention of using @ as the first letter to remind ourselves that
@b is an address.

Then

 handler = newvector(20);         
 handler[Mouse_Click] = do_Mouse_Click;
 handler[Menu_Selection] = do_Menu_Selection;
 handler[User_Message] = handler[User_Message_Recorded] = do_Message;

creates a vector of handlers to respond to the window manager. We only
need to respond to four reason codes:

          Mouse_Click
          Menu_Selection
          User_Message
          User_Message_Recorded

The handler's components for these reason codes are set to functions
appearing later in the file Main. The rest are nil by default.
A generic handler function takes three arguments - the first, the address
of a message buffer - the second is a user-parameter, that can be used
for passing in data, and the third a pointer to a mask word to tell the
window manager which events our task is not interested in receiving. 
In this example, the user-parameter has the value nil. The handler 
function must return FALSE if it is to cause the program to terminate, 
and TRUE otherwise.

The statement

 in (mesg_list= newstring(8)) put
     { Message_DataLoad; 0; } 

creates a two-word buffer called mesg-list, containing the messages
we want !Exec to receive. The 'in .. put { .. ; ... ;}' structure
is convenient for filling buffers with words and strings.

We start !Exec as a wimp task with

 wimp_init(310,"Exec",mesg_list);

The Info box item of the menu is defined in a template file. The lines

 (wind = newvector(1))[0] = "info";
 info = templates("<Exec$Dir>.Templates",wind)[0];

load the template and get the window-handle 'info' of this box. The
templates function, defined in Bob:h.wimp.templates takes two arguments,
the first, a string, the pathname of the template file, the second
a vector of window names. It returns a vector of corresponding window
handles. The vector 'wind' is defined as a 1-component vector.

The lines

 icon = make_icon();
 menu = make_menu();

should be self-explanatory. The function main finishes with the
statement that does all the work

 (maskptr = newvector(1))[0] = &1933;
 event_process(maskptr,@b,handler,nil); 

The function event_process is defined in Bob:h.ev_process.

event_process(maskptr,buffer,action,user)
{
 local r, respond, go_on;
 r = newvector(8);
 go_on = TRUE;
 while(go_on)
 {
  r[0] = maskptr[0];
  r[1] = buffer;
  swi("Wimp_Poll",r);
  go_on =
     (typeof(respond = action[r[0]])
               == BYTECODE)?respond(buffer,user,maskptr):TRUE;
 }
 wimp_closedown();
}

This function polls the wimp, and, if the component of the vector
'action' (which MUST have at least 20 components) given by the
returned reason-code is a function, that function is called. If the
function returns FALSE, execution falls through to wimp_closedown
and the function main terminates.

The event_process function provides a way of modularizing wimp
programs. It is the possibility of having vectors of functions which
enables ArmBob to gain in expressiveness over Basic.

Now let us look at the handler functions. The most interesting is

do_Message(a,user,maskptr)
{
 switch(word(a+16))
    {
     case Message_Quit:
       return FALSE;  // i.e. do not continue
       break;
     case Message_DataLoad:  
        if (word(a+20)==-2 && word(a+24)==icon)
           act(a,user_process);
        return TRUE;
        break;
     default:
        return TRUE;
        break;
    }
}

The first argument a is the address of the message buffer. The word function
fetches the integer stored at its argument. If a DataLoad message is
received because something has been dragged to the iconbar icon,
then act(a,user_process) is called. The function act gets the pathname
and filetype of the object dragged, acknowledges the DataLoad message,
and passes the pathname and filetype to its second argument.

act(a,f)
{
 local filename, filetype;
 filename = $(a+44);
 filetype = word(a+40);
 putword(a+16,Message_DataLoadAck);
 putword(a+12,word(a+8));
 r[0] = User_Message_Acknowledge;
 r[1] = a;
 r[2] = 0;
 swi("Wimp_SendMessage",r);
 f(filename,filetype);
}

The $ function returns the string (terminated by an ASCII code less
than 32) stored at an address. The putword function may be used for poking 
4-byte words into positions in buffers.

Note how the function f, the action to be taken, is passed as a parameter.
