  

          [42m      An ARexx and Graphics Interface      [40m
          [42m                                           [40m
          [42m         A reply to Peter Bagnato          [40m
    
    
    Peter Bagnato asks "John Collett, are you listening?", requests
    "an article about how to use the rexxarp.lib.", and even mentions
    GUI - a word which unfortunately reminds me of IBM and cold porridge.
    
    But great!  A direct request!  Since feedback of whatever kind is what
    contributors to MegaDisk always hope to receive, I've just got to
    write something for Peter, even though he said something not very
    complimentary about "Greek rocket scientists that don't know English."

    But hang on!  There's something here that doesn't quite add up.  I've
    already written articles about using 'rexxarplib.library'.  Did you
    read those articles, Peter?  (Not to mention, though I'm just going to,
    my [3mpièce de résistance[0m called [4mSixty Plus[0m - docs and demos of nearly 
    all 'rexxarplib' functions.) I can only conclude either that you have
    not seen them (all back issues are available from MegaDisc), or that
    you did read them, but that they were too hard, unclear, or instantly
    forgettable.  Oh dear!

    I suppose I've just got to try again, so here comes another version of
    running a gooey - sorry, GUI - from an ARexx program.  I assume you
    means things like windows, gadgets, and drawings.  I'll try to explain
    each step as I go, to avoid unproductive complications, and not to
    repeat too closely anything I've done before.

    So I'll open a window, to which I'll attach a small menu. In the window
    I'll put a few gadgets, and get them to perform a selection of typical
    tasks.  I'll also include a very simple drawing tool.  Collectively
    these features will give you a fair indication of what can be done and,
    I hope, show you how to do it - of course not necessarily the best and
    certainly not the only way.  It should be enough to get you going.
    If you need more, write to me.  Write to me anyway.  Here we go :

    /* Everything from here to 'End' is commented in such a way that it
    could be run as a program, but the accompanying ARexx program is a 
    much more compact version. */

    /* The first line of the program has to be a comment line */
    
    /*  Your libs: directory must contain rexxsyslib.library,
    rexxsupport.library, and rexxarplib.library, and the last two must
    already be active or must be made so.  Here I assume that if one of
    them needs to be added to the active list, then they probably both do. 
    */

    if ~show('l', "rexxarplib.library") then do
     check = addlib('rexxsupport.library',0,-30,0) 
     check = addlib('rexxarplib.library',0,-30,0)   
     end
    
    /* Now we call the CreateHost() function, without which we can't even
    open a window.  I'll name the controlport HOST, and the notifyport
    PORT.  Since I do not bother with a 'publicscreen', my window will
    open on the WorkBench screen.  */

    address AREXX '"call CreateHost(HOST, PORT)"'    
    
    /* Check that the port really is open before you go on.  
    You should have 'WaitForPort' in the same directory as 'RX'. */
    if ~show('Ports','HOST') then address command 'WaitForPort HOST' 

    /* Specify which system gadgets are to appear in the nem window...*/
    flags = 'WINDOWCLOSE+WINDOWDRAG+WINDOWDEPTH+WINDOWSIZING'
    
    /* .. and what sort of messages the window is to expect. Only those we
    actually intend to use need be included, so although we shall still be
    able, for example, to resize the window, the class of message which
    that action normally sends will not be recognised.  If we needed to
    receive and act upon such a message, then 'NEWSIZE' would have to be
    added to the idcmp set. */
    idcmp = 'CLOSEWINDOW+MENUPICK+GADGETDOWN+GADGETUP+MOUSEBUTTONS'
    
    /* Set position and dimensions for the window. These settings assume
    a screen of 255 lines.  Reduce the 'height' setting if your screen is
    the type which has only 200 lines.  */
    left = 20 ; top = 10 ; width = 600 ; height = 200
    
    /* Open the window and the notifyport ; activate the window. */
    call OpenWindow(HOST,left,top,width,height,idcmp,flags)
    call openport(PORT) ; call ActivateWindow(HOST)
    
    /* Later on I shall set up a loop in which the port will be repeatedly
    checked for messages until the message is 'CLOSEWINDOW', when the
    program will end.  
    
    · If the message is MENUPICK, the program will do whatever I 
      decide to tell it to do, depending on the menu item chosen. 

    · If the message is GADGETUP or GADGETDOWN, it will likewise do
      whatever I determine, according to the number of the gadget.  

    · I also included MOUSEBUTTONS as an idcmp message, in the belief
      that it's always handy to know what is being done with the mouse.
      Once I had included it, I thought I'd better use it, and so, just
      for fun, I included a sketching rectangle.

    No other class of messages will be expected or recognised, but even
    with just this small range of features, there is already more than I
    can discuss thoroughly in one article.

    Before starting the loop, I'll set up the menu and create some gadgets.
    The menu will contain just two simple items; the first will display the
    date and time in a requester, and the second offers a Quit route which
    is an alternative to the Close gadget. 

    I am using only three of the six possible arguments to the AddItem
    function.  In the third one we specify what sort of messages we want to
    be sent when the item is selected.  Just what do we need?   We need to
    know that a menu item has been selected, and which item.  Two-character
    sequences starting with '%' are used as codes to set the messages: '%l'
    sends the name of the action, and when the message includes 'MENUPICK'
    we will know that a menu selection has been made; '%i' sends the item
    number, counting from zero.  Later on we shall give instructions as to
    what the program is to do when it receives either a 'MENUPICK 0' or a
    'MENUPICK 1' message. */

      call AddMenu(HOST,'Sample menu   ')
      call AddItem(HOST,'Date and time ','%l %i')
      call AddItem(HOST,'Quit          ','%l %i')

    /* Now we'll set up some gadgets.  We'll try :
    
    · One string gadget - you can enter text into it, and make the program
      respond however you wish according to the text you enter.

    · Some boolean gadgets - they just know when you've clicked on them,
      either a down-click or an up-click, and you can make the program
      respond as required.

    Adding the string gadget :
    
    We'll make it longer than just the text we wish to appear. I've added
    10 extra characters, and assume font width to be 8.  It is because we
    set its length that the program knows that we intend it to be a string
    gadget.  In the message to be sent back we will need to know that a
    gadget has been pressed ('%l' will be 'GADGETDOWN' or 'GADGETUP'),
    which gadget is involved ('%d' will be the gadget number) and, because
    this is a string gadget, the text it contains when Return is pressed
    ('%g').   */

     x = 100 ; y = 40 ; gadno = 1 
     text = 'You can edit this text' ; hsize = (length(text) + 10) * 8
     call AddGadget(HOST,x,y,gadno,text,'%l %d %g',hsize)
    
    /* Variation for WB2 only : insert ',RIDGEBORDER' (no quotes) after
    'hsize', and see the effect.
    
    Loops are used here to set up a few boolean gadgets, in which 'hsize'
    and '%g' are irrelevant.  Their size is set by the text they contain,
    and '\' can be included to force a line break. The messages we will
    have sent back will contain 'GADGETDOWN' or 'GADGETUP' and the gadget
    number.*/

    /* Two boolean gadgets containing text */
    x.2 = 100 ; y.2 = 70 ; x.3 = 280 ; y.3 = 70 ; text. = '  '
    text.2 = 'Open a CLI window' ; text.3 = 'Place\Text' 
    do g = 2 to 3
     call AddGadget(HOST,x.g,y.g,g,text.g,'%l %d') ;  end
    
    /* Four boolean gadgets containing no text.  Because we have already
    set 'text.' to '  ', they will all be about 24 pixels wide and look
    approximately square, except for number 7 which we will make really
    small. */    

    text.7 = ''
    do g = 4 to 7
     call AddGadget(HOST,(g-2) * 50,100,g,text.g,'%l %d') ;  end

     /* Once these four gadgets are added, we tinker with them :   
    
     · Make no. 7 look smaller by hiding its lower part (using RectFill),
       though when you click it you will still be able to see its full
       height at the 'GADGETDOWN' stage.
     · Give it a new lower edge (Draw).
     · Fill nos. 6 and 7 with colour 3 (Flood). 
    
    You may have problems with Flood() if you have an early version of
    'rexxarplib.library'.  If so, use RectFill instead.
    Some of these adjustments may be possible by using SetReqColor(),
    but that function has given me a few problems, and I avoid it.
    */

     call SetAPen(HOST,0) ; call RectFill(HOST,248,102,256,110) 
     call SetAPen(HOST,1) 
     call Move(HOST,250,102) ; call Draw(HOST,255,102)
     call SetAPen(HOST,3)
     call Flood(HOST,1,202,100) ; call Flood(HOST,1,252,100)

    
    /*  As we added menu items and gadgets, we were able to specify what
    messages they were to return.  What about plain mouse clicks?  In this
    case we need to use ModifyHost() to set the messages.  It says :
     
      "If the idcmp message received is of class 'MOUSEBUTTONS', then
       modify that message so that it contains '%b', '%x', and '%y'".
    
    When replacements are made, '%b' will tell us the button state
    ('SELECTDOWN' or 'SELECTUP'), and '%x' and '%y' will give the pointer
    position at each button movement. */

    call ModifyHost(HOST,'MOUSEBUTTONS','%b %x %y')
    
    /* Since we're reading pointer positions, we might as well provide the
    pointer with a little job.  Here's a framed and flooded rectangle in 
    which we'll be able to draw lines. */

    call SetAPen(HOST,1) ; x = 50 ; y = 120 ; call Move(HOST,x,y)
    call Draw(HOST,x+400,y) ; call Draw(HOST,x+400,y+50)
    call Draw(HOST,x,y+50) ; call Draw(HOST,x,y)
    call SetAPen(HOST,2) ; call Flood(HOST,1,x+10,y+10)
    
    
    /* Now we can start.  We open a loop in which we wait for the user to
    do something.  We examine messages received, and act accordingly.  To
    help you see what's going on, I'll include a DisplayMessage function,
    so that a running commentary in the title bar will keep you up to
    date and, I hope, help you to understand what's going on.    */

      do forever  
       call waitpkt(PORT) ; p = getpkt(PORT)     /* Wait for a message.  */
       if p ~== NULL() then do                   /* If it is not empty,  */
         msg = getarg(p) ;  t = reply(p, 0)      /* read and acknowledge.*/
         call DisplayMessage(msg) 
    
    /* This is decision time.  We said earlier that Menu Item 0 would
    display a message, and Item 1 offer an alternative exit to the Close
    Gadget.  Mouse dragging within the rectangle will draw lines.
    What about mouse clicks outside the rectangle, above, below, to the
    left of, or to the right of the rectangle?  Any of these could be
    traced and acted upon.  And then we've got all the gadgets to play
    with.  I've allocated trivial tasks to each of them, but if you enjoy
    this sort of playing (and why else do we do it?) then you can try out
    your own ideas.

    · Gadget 1 creates a new boolean gadget just below itself, containing
      the text you entered in Gadget 1.  If you click on the new gadget it
      will be deleted.
    · Gadget 2 opens a CLI window.
    · Gadget 3 uses the Move and Text functions.
    · Gadget 4 decorates itself.
    · Gadget 5 lets you see any of 16 AreaEnd fill patterns. There are 
      also 125 dithering patterns available, but I'll leave it to you
      to include that option.
    · Gadget 6 activates the File Selector, but does nothing with any
      choice you may make.
    · Gadget 7 does the same for the Font Selector, which in any case only
      works under WB2. If your Fonts: directory is on a floppy, it'll take
      a couple of seconds to load. There is more work to be done, including
      using SetFont(), if you want your font choice to have any effect.

    Whatever message is received, it will be parsed to isolate its first
    component and thereby identify the idcmp class involved. The
    'remainder' will be ignored for now; the message will be parsed again
    when we know which class we are handling, how many arguments it
    contains, and what they are.  After the initial parsing, 'select' will
    be used to direct the program to whichever task is to be carried out.
    */

         parse var msg class remainder . 
         select
           when class = 'CLOSEWINDOW' then do       
             call CloseWindow(HOST) ; exit ; end
           when class = 'MENUPICK' then call Menu()
           when class = 'GADGETUP' then call Gad()
           when class = 'SELECTDOWN' then call Mouse()
           otherwise
         end            /* of 'select'     */
       end              /* of 'then do'    */
      end               /* of 'do forever' */
    
    exit

    DisplayMessage:
     text = arg(1)
        /* Extend text to overwrite previous */
     text = text || copies(' ',76 - length(text))   
        /* Set colours for message display. Probably not necessary
        to do this every time, but we'll play it safe. */
     call SetAPen(HOST,1) ; call SetBPen(HOST,3)   
     call Move(HOST,32,8) ; call Text(HOST,text)
     return
        
    Menu:
     if remainder = 0 then do
       ptxt = 'The Request() function is\being used to display this :\\'||,
              '  Date : ' || Date() || '\  Time : ' || Time('c') || '\'
       call Request(120,50,ptxt,,'Okay') ; end
     else do ; call CloseWindow(HOST) ; exit ; end
    return
    
    Gad:
    parse var msg class gadno tx
    select
    
     /* Add a new gadget containing your text.
        Set the length to be erased if Gadget 8 is clicked */
      when gadno = 1 then do
        call AddGadget(HOST,100,52,8,tx,'%l %d')
        eraselen = (length(tx)+2 ) * 8 ; end
    
     /* A new window */
      when gadno = 2 then address command newcli  
    
     /* Text placement */
      when gadno = 3 then do
        call SetBPen(HOST,0) ; tx.1 = 'These lines are placed'
        tx.2 = 'here by the Move' ; tx.3 = 'and Text functions.'
        do i = 1 to 3
          call Move(HOST,380,44+i*10) ; call Text(HOST,tx.i) ; end
        end
    
     /* A filled circle outside the gadget, and a square inside it. */
      when gadno = 4 then do 
        call DrawEllipse(HOST,110,105,20,10)  /* DrawCircle has problems */
        call SetAPen(HOST,3) ; call Flood(HOST,1,94,105) 
        call SetAPen(HOST,2) ; x = 102 ; y = 101 ; call Move(HOST,x,y)
        call Draw(HOST,x+15,y) ; call Draw(HOST,x+15,y+7) 
        call Draw(HOST,x,y+7) ; call Draw(HOST,x,y) ; call SetAPen(HOST,3) 
        end
      
     /* To see any of 16 fill patterns */      
      when gadno = 5 then do
        call SetBPen(HOST,2)
        tx = 'Choose a pattern\number 0 - 15.' ; x = 370 ; y = 20
        do forever
          patt = Request(30,20,tx,'',,'No more')
          if patt = '' then leave
          call AreaDraw(HOST,x,y) ; call AreaDraw(HOST,x+190,y)
          call AreaDraw(HOST,x+190,y+80) ; call AreaDraw(HOST,x,y+80)
          call AreaEnd(HOST,256 + patt)
          end
        end
      
     /* Requesters */     
      when gadno = 6 then req = GetFile(120,40)
      when gadno = 7 then do
        opts = 'BACKPEN DRAWMODE FRONTPEN STYLE' 
       req = getFont(120,20,'topaz.font',8,,,opts) ; end

     /* Remove the gadget added by Gadget 1.  RemoveGadget just makes it
        inoperative. RectFill is needed to erase the image. */
      when gadno = 8 then do
         call SetAPen(HOST,0) ; call RectFill(HOST,98,50,98+eraselen,64)
         call SetAPen(HOST,1) ; call RemoveGadget(HOST,gadno) ; end
      otherwise
      end
    return
    
    Mouse:
     parse var msg class x1 y1 .
     if x1 < 50 | x1 > 450 | y1 < 120 | y1 > 170 then return
    /* x1 and y1 are mouse positions when the mouse is clicked down. 
       You must start a line [4mwithin[0m the rectangle to get this far, but the 
       line can then be extended to anywhere on the screen. */
     call Move(HOST,x1,y1)
    /* Wait for the mouse to be released. Deal with the message in a 
    similar way to the one in the main loop. */    
     call waitpkt(PORT) ; p = getpkt(PORT)
     if p ~== NULL() then do                                  
       msg = getarg(p) ;  t = reply(p, 0) ;  parse var msg class x2 y2
       /* Draw a line from the 'down' to the 'up' position. 
    
    The title bar message is NOT updated to show you the x and y release
    points, since the printing of the message would undermine the control
    which we need to retain for the line drawing process itself.  It could
    be worked around, but is an unnecessary complication.  */

       call Draw(HOST,x2,y2)
       end 
     return
    
                            /* End */
    
    That's it.  
    Explore, change, add, remove, do what you will with it.
    Above all, enjoy.  
    

    [33;3mHamilton, New Zealand  July, 1993[0m
    
                     -------------------------
    