Bay City, Michigan
Sales (989) 892-9242             Support (989) 686-8860
Visual PL/B Language
PL/B PICTURE MENUS
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............................. FORM_PICMENU PLFORM FM-P0000 PICTURE001 PICT . #PT FORM 5 #PB FORM 5 #PL FORM 5 #PR FORM 5 #PW FORM 5 #PH FORM 5 . FORMLOAD FORM_PICMENU . 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 TRAP OBJECT_ERROR IF OBJECT CREATE F1_Window;PICTURE001=#PT:#PB:#PL:#PR: 101 ;resource in the PLFORM TRAPCLR OBJECT IF (OBJECTNIF = NO) ACTIVATE PICTURE001,LOGO_EVENT,EVENT_RESULT ELSE ALERT STOP,"Picture not found!",ALERT_RESULT,"LOGO MISSING" STOP ENDIFNotice 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
MOVE "EXIT-BUTTON" TO ACTION.
(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: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") STOP . ELSEIF (ACTION = "SYNC") MOVE "FM-85050",CURRPROG BREAK . ELSEIF (ACTION = "MLS") MOVE "FM-45000",CURRPROG BREAK . ELSEIF (ACTION = "REPORTS") MOVE "FM-47000",CURRPROG BREAK . ELSE IF (ACTION = "SERVICES") MOVE "FM-88000",CURRPROG MOVE ACTION,MENULINK BREAK ENDIF . PACK TITLE_LINE,"UNKNOWN:",ACTION ALERT CAUTION,TITLE_LINE,ALERT_RESULT,"FM-P0000" REPEAT(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:logo_event 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 ENDIF . 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:object_error MOVE "X",OBJECTNIF RETURN
FULL SIZE IMAGE
Send e-mail to MMCC.