Mid-Michigan Computer Consultants - Bay City, Michigan

CONTENTS       (old style)
Mid-Michigan Computer Consultants
509 Center
Bay City, Michigan

Sales (989) 892-9242
Support (989) 686-8860

Plb-0267.cfm v1.0


ANSI Standard PL/B Language and Visual PL/B

Picture Menus

This web resource is written for our own use. But we feel strongly that the PL/B language should be shared with the software community. So feel free to use this at will! BUT PLEASE... if you find errors or omissions or have a better way to do something. TELL US! Dialog helps us all. Send e-mail to:


Menus are typically done with buttons and text. A visually more pleasing style is to use graphics and interesting fonts much like a web page does. Consider the image at right. That's the menu for our "Freddy Mobile" system. There are a series of these picture style menus in the system.

That's a scaled down version to fit this web page. Normally the image is 640 x 480. It's shown again, full size at the bottom of this page.


Here's how we make that work.

The first objective is to get the screen up. We create a PLFORM for that. The only event we process is CLOSE for the window so that we can detect the "X" at the top of the screen.

(Actually we also have a couple of small check boxes on the production screen. Those appear at the bottom of the screen and the Z-ORDER insures that they appear on TOP of the picture. They're processed as standard objects and events separate from the picture processing discussed here.)

We need the picture file itself at run time. Originally we distributed these pictures as separate GIF files. Now we make them RESOURCES of the PLFORM. That embeds the picture in the compiled PLC and we don't have to remember to distribute it. Our standard is to make the menu picture resource number 101.

Here's the important code from the program. The actual production program has a lot of other stuff, but we don't need that for this discussion.

When the program loads we start the action by loading the form. The PLFORM is not critically sized in the forms designer. In fact, it's rather small. We just need a form on which to work. After doing the FORMLOAD in the program we explicitely resize the Window object to make the size conform to our picture.
#PT            FORM   5
#PB            FORM   5
#PL            FORM   5
#PR            FORM   5
#PW            FORM   5
#PH            FORM   5
         SETPROP   F1_Window, WIDTH=640      ;Insure window size is right
         SETPROP   F1_Window,HEIGHT=480

With the screen loaded, we're ready for the picture. First we'll gather the screen size coordinates. You might think "we already know these because we just set them!". We get them here because if we change the size later we just have to do it once, above, and here we get whatever number we used.
         GETPROP   F1_Window, WIDTH=#PW      ;Get the screen size
         GETPROP   F1_Window,HEIGHT=#PH

Now we'll calculate the coordinates for the picture that we're going to create. NOTE: Picture's are created using TOP:BOTTOM:LEFT:RIGHT. Most objects use TOP, LEFT, WIDTH, HEIGHT.

We know that we want the top and left to be ZERO to orient the picture to the top left corner. The bottom and right are relative to that. We'll do a CALC below just to illustrate this relationship. Because #PT and #PL are zero, the calc is the same thing as moving #PH to #PB and #PW to #PR. BUT if, someday, we move the picture's top and left, the calc will be self correcting!
         MOVE      ZERO,   #PT          ;Set up the TOP, LEFT, BOTTOM, RIGHT
         MOVE      ZERO,   #PL          ;coordinates for the picture.
         CALC      #PB   = #PT + #PH    ;bottom relative to top and screen height
         CALC      #PR   = #PL + #PW    ;right relative to left and screen width

Now we're ready to create the picture object. Notice that we're using a RESOURCE for that. We put a comment in the code to remind us what the source picture file is.

Note that we trap any OBJECT errors. Using a RESOURCE pretty much guarantees that there will be no problem, but originally we distributed the pictures separately. . . and sometimes forgot them. Checking for the error was critical then, now it's just a good practice.
......   Resource 101 is added to the form.
......   It's FREDDY_SCREEN2.BMP and is in the source folder.
         MOVE      NO,  OBJECTNIF
         CREATE    F1_Window;PICTURE001=#PT:#PB:#PL:#PR:
                      101         ;resource in the PLFORM
         IF (OBJECTNIF = NO)
             ALERT STOP,"Picture not found!",ALERT_RESULT,"LOGO MISSING"
Notice the ACTIVATE above. That's where we link the routine (LOGO_EVENT) that will handle clicks on the picture. The results of the event will be store in EVENT_RESULT. (EVENT_RESULT is defined as FORM 11 in our standard COMMONWK.PLS include unit.)

I'll talk about the LOGO_EVENT routine later. Right now the program falls into the WAITEVENT loop.

The following is MMCC's standard method for handling events.

Each event we want to check has minimal code in the PLFORM (or in this case, in the program). That code just moves a value to the ACTION string variable. For example, a button might have code for the CLICK event and that code would be something like
(ACTION is also defined in our standard COMMONWK.PLS include unit.)

For this "picture menu" program, all events associated with the PICT are handled by LOGO_EVENT. It will figure out where the click occured and will return a value in ACTION. (Be patient... LOGO_EVENT will be explained below.)

Here's the WAITEVENT loop:
           UNPACK  NUL,ACTION               ;clear for peace of mind.
           WAITEVENT                        ;wait for something to happen
           MATCH   SPACES,ACTION            ;this happen if the click is not
           CONTINUE IF EOS                  ;on a defined part of the picture
           IF      (ACTION = "LOGOFF" |:
                    ACTION = "WINDOWCLOSE")
           ELSEIF  (ACTION = "SYNC")
               MOVE    "FM-85050",CURRPROG
           ELSEIF  (ACTION = "MLS")
               MOVE    "FM-45000",CURRPROG
           ELSEIF  (ACTION = "REPORTS") 
               MOVE    "FM-47000",CURRPROG
           ELSE IF (ACTION = "SERVICES")
               MOVE   "FM-88000",CURRPROG
               MOVE   ACTION,MENULINK
(Note: TITLE_LINE, used above, is another work string defined in our standard COMMONWK.PLS include unit. We use this as temporary work for any type of message, prompt, or working string.)

For the purposes of this article, and this example, we don't need to discuss what happens when we break the the WAITEVENT loop and fall through. But that would leave you hanging, so I'll include that part here.

Basically, all we do is chain out. CURRPROG would have been set in the loop above based on the click event. PRIORPG and NEXTPROG just track where control came from and where it should go after the next program runs. These items are stored in our standard COMMON include unit that's part of every program.
         MOVE      "FM-P0000",PRIORPG
         MOVE      "FM-P0000",NEXTPROG
         CHAIN     CURRPROG

NOW I'll talk about the LOGO_EVENT routime.

This routine is tied to the picture by the ACTIVATE of the PICT. The ACTIVATE also specified that the "event result" value should be stored in the EVENT_RESULT FORM variable (something else defined in our COMMONWK include unit.)

The event result for a click on a picture is a 9 digit number. Those 9 digits are defined as:
BYTE1        DIM     1       ;button that was used
H_POSITION   FORM    4       ;horizontal pixel position of the mouse pointer
V_POSITION   FORM    4       ;vertical pixel position of the mouse pointer

We'll convert the EVENT_RESULT number to a 9 byte form variable NWK09. We'll then unpack that into the three components defining the mouse event.

The picture is split into left and right halves. The left half is from pixel 1 to 290. There are no actions on the right side so if the click is anywhere to the right of horizontal 290 we just return with a null ACTION.

After we eliminate right side clicks, we know that we're on the left side. Now we look at the vertical component of the click to see if we're over one of the "buttons".

Note that the pixel positions were determined experimentally. The program had temporary code to just display the H:V positions on the main window (the one you get with WINSHOW). Once we knew the ranges we took that code out.

Here's the effective code:
         MOVE      EVENT_RESULT,NWK09       ;insure a 9 digit number variable
         UNPACK    NWK09,LOGO_BUTTON:       ;split out the 9 digits via UNPACK
                         LOGO_H:            ;horizontal position
                         LOGO_V             ;vertical position
         UNPACK    NUL,ACTION               ;in case the click if out in space.
         IF (LOGO_H > 290)                  ;when RIGHT side of screen
             RETURN                         ;take NO ACTION.
         ELSEIF (LOGO_V >  20 &:            ;Top button
                 LOGO_V <  80)                  
                 MOVE  "SYNC", ACTION
         ELSEIF (LOGO_V >  95 &:            ;Second button
                 LOGO_V < 135)                  
                 MOVE  "MLS",  ACTION
         ELSEIF (LOGO_V > 170 &:            ;Third button
                 LOGO_V < 220)  
                 MOVE  "PUBLISHING",ACTION

         ELSEIF (LOGO_V > 255 &:            ;Fourth button
                 LOGO_V < 300) 
                 MOVE  "SERVICES",ACTION
         ELSEIF (LOGO_V > 320 &:            ;Fourth button
                 LOGO_V < 390)    
                 MOVE  "LOGOFF", ACTION
         RETURN                             ;Return null ACTION on anything else

There's only one other component of this example that is not defined. When the PICT was created, we trapped OBJECT_ERROR IF OBJECT. That simple little routine is:
         MOVE  "X",OBJECTNIF



Write to MMCC Technical Support at:               Send
e-mail to MMCC.
MMCC - Technical Support
600 W. Midland
Bay City, MI 48708
(989) 686-8860
© 1997 - 2017 MMCC - All Rights Reserved