|
||||
21: A Wimp Shell ProgramSection 10 of this guide introduced a simple Wimp shell program: a collection of useful functions and procedures that could easily be adapted to form the basis of a new multi-tasking application with minimal effort. That first version of the shell program was based on what we had learnt to date in creating our !Test application, and itself formed the basis of our more sophisticated Shapes application. Now that the Shapes application is complete, we can take some of the new functions and procedures that it contains and put them back into our shell in order to make it a more useful basis for future applications. Of course, as you become experienced in writing your own software, you will almost certainly wish to supplement the new shell with futher routines of your own. If you need to refresh your memory about the shell program, reread Section 10; what follows here will supplement the simple shell program by extending the contents of its !RunImage file, but the other related files will not change. Creating the Shell ProgramIf your !RunImage file is similar to the final version listed in the Appendix, you've already done most of the work as all you need to do is delete some of it, make a few changes and add a few procedures and functions (which are optional) on the end. First, save a copy of the final !RunImage from the Shapes application in the application directory for your new shell, so that you don't destroy your final copy of the Shapes program by accident!You will need to go through your !RunImage file, comparing it with the listing at the end of this section, making appropriate deletions, additions and changes. You will find that there are differences in some of the function and procedure definitions, so check carefully. There are a few new (optional) ones at the end for you to enter, but any functions and procedures that are not listed as part of the shell should be deleted, as they were used by the Shapes application and are no longer needed. You may wish to use your editor's Find/Replace facility to replace all instances of 'Shapes' with 'Shell', though note that the instance in PROCcreateicon (line 200) has a lower-case 's' as it is the name of a sprite. Each function or procedure begins with a REM statement to remind you of what it does - you can, of course, leave them out if you wish. PROCpoll contains a CASE ... OF structure to handle the reason codes you are almost certain to meet on return from Wimp_Poll, including reason code 1, a call to PROCredraw. If all your windows contain only icons, you can delete this line, together with PROCredraw itself. We've removed the mask from R0 when calling Wimp_Poll, as you will need to create one to suit your own application. This was covered at the end of Section 17. Unless your program needs to respond to reason code zero (the null reason code), you should be sure to mask it out by putting a number in R0 with bit zero set (such as 1). Error Handler ModificationsThe error handler has been expanded to check for three error numbers. The number 1<<30 produces an error box with both 'OK' and 'Cancel' icons, (1<<30)+1 produces a box with just the 'OK' icon and (1<<30)+2 one with just the 'Cancel' icon. This last one enables you to produce a fatal error without having to show the line number in the error message.PROCreceive now handles only the 'Close Down' message. You can add your own lines if you need to handle messages for loading and saving files. PROCmouseclick has been reduced so that it caters only for Menu- and Select-clicks on the icon bar, and for clicks in the main window, which are handled by PROCwindow_click. PROCget_origin works out the coordinates of a window's work area origin and returns them, thanks to the RETURN keywords before xorig% and yorig%. PROCload_templates is only used to load and create the Info box and main window, but the lines that do this can be copied, with modifications, to load other windows. Line 1330 puts the version string into the appropriate icon's indirected data. This line assumes that the 'version number' icon is icon number 4. You should check this in your template editor and either renumber the icons or alter the line if necessary. You should also check that the length of the icon's indirected data buffer is sufficient for your likely version number. The menu creation routine, FNmake_menu, is basically as we wrote it earlier. We've included a couple of checks to ensure that the total size of our menus does not exceed the amount of memory we set aside for the purpose - we don't want to start overwriting other data! PROCredraw has been included as it's a fairly standard routine for coping with requests from the Wimp to redraw a window. The procedure that it calls, though, PROCdraw, consists entirely of REM statements, because it has to be specially written for each individual application. Note that the window handle, in b%, is passed to PROCdraw; this is in case you have several windows that need redrawing, so that PROCdraw can distinguish between them. More Useful PROCs and FunctionsPROCterm simply puts a Return character - ASCII code 13 - on the end of any string starting at a%. This is necessary if the string has been produced by RISC OS, because it may end with a zero. If you want Basic to use the string, it has to end with a Return.PROCdrag is included in case you want to drag an icon, for example at the start of a save operation. This procedure starts a drag of type 5, which means a box of rotating dashes. The initial position of the box is taken from the bounding box of the icon where you held down the mouse button. FNicon_state checks whether or not a particular icon is selected, and returns TRUE if it is. This function makes dialogue boxes particularly easy to use. You don't have to worry about icons being selected or deselected, or 'radio' type icons cancelling each other through their exclusive selection group - the Wimp takes care of all that. You simply have to check the state of the icons when they have some relevance to what your program does. PROCforce_redraw takes the screen coordinates of the visible part of a window and forces the Wimp to redraw that rectangle. This is a simple way of making any changes to a window instantly visible. FNwind_open simply returns TRUE if a window is open. PROCpush simulates clicking the mouse over a particular icon. You could use this, for example, to make the default action button of a dialogue box appear to slab in when you press Return, just before the box vanishes from the screen. Note, though, that this routine is of most interest for programs that run on older hardware, because on fast machines the window is likely to close so quickly that you won't have time to see the slabbing-in effect. As explained in Section 20, considering the fact that this procedure uses a discouraged technique to produce its effect (albeit the only way of achieving it), you may prefer not to make use of PROCpush in your own programs. Additional RoutinesWe've included several short routines that were not in our example programs, but which can be useful when handling icons and menus.FNtick checks whether or not a menu item is ticked, and returns TRUE if it is; FNtoggle_tick changes the ticked or unticked state of an item; PROCgrey disables a menu item by turning it grey and PROCungrey reverses the process. These procedures work by altering the appropriate bits in the menu item flags. FNstring_addr returns the address of an indirected icon's text string; FNicon_string uses this to return the actual string and PROCset_icon_string puts a string into the icon's text buffer. Note that these routines do not check to ensure that the icon does have indirected text - it's up to you to ensure that it does when you design your window. Note also that PROCset_icon_string doesn't check the size of the icon's buffer before putting a string into it. Again, it's up to you to ensure that enough space is available (or alter the routines to make them more foolproof). FNindirect puts a string into the indirected data workspace and returns its address; very useful when creating icons with indirected data. No doubt you can think of many other useful routines to include in your shell. When you write your own application, you would probably do best to keep all these routines intact at first, until you have finally finished and debugged your program. Then you can delete any that you haven't used, and reduce the sizes of the data blocks at b%, ws% and menspc% to whatever your program needs. It is now up to you and your inventiveness to see what you can get out of Wimp programming. You will find that there is something very satisfying about producing an application that has the same 'feel' as expensive, commercially produced software. And have fun! 10 REM >!RunImage 20 REM (C) Martyn Fox 30 REM Wimp shell program 40 version$="0.01 (date)" 50 ON ERROR PROCclose:REPORT:PRINT" at line ";ERL:END 60 SYS "Wimp_Initialise",200,&4B534154,"Shell" TO ,task% 70 PROCinit 80 PROCcreateicon 90 ON ERROR IF FNerror THEN PROCclose:END 100 REPEAT 110 PROCpoll 120 UNTIL quit% 130 PROCclose 140 END 150 : 160 DEFPROCcreateicon 170 REM creates the application's icon and puts it on the icon bar 180 REM b%+0 = -1 for right side and -2 for left side 190 !b%=-1:b%!4=0:b%!8=0:b%!12=68:b%!16=68:b%!20=&3002 200 $(b%+24)="!shell":SYS"Wimp_CreateIcon",,b% TO i% 210 ENDPROC 220 : 230 DEFPROCclose 240 REM tells the Wimp to quit the application 250 SYS "Wimp_CloseDown",task%,&4B534154 260 ENDPROC 270 : 280 DEFPROCpoll 290 REM main program Wimp polling loop 300 SYS "Wimp_Poll",,b% TO r% 310 CASE r% OF 320 WHEN 1:PROCredraw 330 WHEN 2:SYS "Wimp_OpenWindow",,b% 340 WHEN 3:SYS "Wimp_CloseWindow",,b% 350 WHEN 6:PROCmouseclick 360 WHEN 8:PROCkeypress 370 WHEN 9:PROCmenuclick 380 WHEN 17,18:PROCreceive 390 ENDCASE 400 ENDPROC 410 : 420 DEFPROCmouseclick 430 REM handles mouse clicks in response to Wimp_Poll reason code 6 440 REM b%!0=mousex,b%!4=mousey:b%!8=buttons b%!12=window handle (-2 for icon bar):b%!16=icon handle 450 CASE b%!12 OF 460 WHEN -2: 470 CASE b%!8 OF 480 WHEN 2:PROCshowmenu(mainmenu%,!b%-64,96+2*44): REM replace '2' with number of main menu items 490 WHEN 4:!b%=main%:SYS "Wimp_GetWindowState",,b%: SYS "Wimp_OpenWindow",,b% 500 ENDCASE 510 WHEN main%:PROCwindow_click 520 ENDCASE 530 ENDPROC 540 : 550 DEFPROCget_origin(handle%,RETURN xorig%,RETURN yorig%) 560 REM returns coordinates of window work area origin 570 LOCAL c% 580 c%=FNstack(36) 590 !c%=handle% 600 SYS "Wimp_GetWindowState",,c% 610 xorig%=c%!4-c%!20:yorig%=c%!16-c%!24 620 PROCunstack(c%) 630 ENDPROC 640 : 650 DEFFNstack(size%) 660 REM allocates temporary memory from stack block 670 REM stack must be cleared after use with PROCunstack 680 IF stackptr%+size%>stackend% ERROR 1,"No room in stack" 690 stackptr%+=size% 700 =stackptr%-size% 710 : 720 DEFPROCunstack(old_ptr%) 730 REM removes temporary memory from stack 740 stackptr%=old_ptr% 750 IF stackptr%<stack% stackptr%=stack% 760 ENDPROC 770 : 780 DEFFNmake_menu 790 REM creates menu block from DATA statements 800 LOCAL start%,title$,item$,ul%,tail$,writable%,buffer%,buflen% 810 IF menspc%+28>menend% ERROR (1<<30)+2,"Not enough menu space" 820 start%=menspc% 830 READ title$ 840 $(start%)=title$ 850 start%?12=7:REM title foreground colour 860 start%?13=2:REM title background colour 870 start%?14=7:REM work area foreground colour 880 start%?15=0:REM work area background colour 890 start%!20=44:REM height of menu items 900 start%!24=0:REM gap between items 910 width%=LEN(title$)-3 920 menspc%+=28 930 REPEAT 940 READ item$ 950 IF item$<>"*" THEN 960 IF menspc%+24>menend% ERROR (1<<30)+2,"Not enough menu space" 970 !menspc%=0 980 writable%=FALSE 990 ul%=INSTR(item$,"_") 1000 IF ul% THEN 1010 tail$=RIGHT$(item$,LEN(item$)-ul%) 1020 IF INSTR(tail$,"T") !menspc%=!menspc% OR 1:REM tick 1030 IF INSTR(tail$,"D") !menspc%=!menspc% OR 2:REM dotted line 1040 IF INSTR(tail$,"W") !menspc%=!menspc% OR 4:writable%=TRUE: READ buffer%:READ buflen%:REM writable icon 1050 IF INSTR(tail$,"M") !menspc%=!menspc% OR 8:REM generate message 1060 item$=LEFT$(item$,ul%-1) 1070 ENDIF 1080 IF LENitem$>width% width%=LENitem$ 1090 menspc%!4=-1:REM submenu ptr 1100 IF writable% THEN 1110 menspc%!8=&0700F121:menspc%!12=buffer%: menspc%!16=-1:menspc%!20=buflen%:$buffer%=item$ 1120 ELSE 1130 IF LENitem$<12 THEN 1140 menspc%!8=&07000021:$(menspc%+12)=item$ 1150 ELSE 1160 menspc%!8=&07000121:menspc%!12=ws%:menspc%!16=-1:menspc%!20=LENitem$+1 1170 $ws%=item$:ws%+=LENitem$+1 1180 ENDIF 1190 ENDIF 1200 menspc%+=24 1210 ENDIF 1220 UNTIL item$="*" 1230 start%!16=width%*16+32 1240 !(menspc%-24)=!(menspc%-24) OR &80 1250 mptr%=menspc% 1260 =start% 1270 : 1280 DEFPROCload_templates 1290 REM opens window template file, loads and creates window 1300 SYS "Wimp_OpenTemplate",,"<Shell$Dir>.Templates" 1310 REM ****** load and create Info box ****** 1320 SYS "Wimp_LoadTemplate",,stack%,ws%,wsend%,-1,"progInfo",0 TO ,,ws% 1330 $stack%!(88+32*0+20)=version$ 1340 SYS "Wimp_CreateWindow",,stack% TO info% 1350 REM ****** load and create main window ****** 1360 SYS "Wimp_LoadTemplate",,stack%,ws%,wsend%,-1,"Main",0 TO ,,ws% 1370 SYS "Wimp_CreateWindow",,stack% TO main% 1380 REM ****** end of window creation ****** 1390 SYS "Wimp_CloseTemplate" 1400 ENDPROC 1410 : 1420 DEFPROCattach(menu%,item%,sub%) 1430 REM attach submenu or dialogue box to main menu 1440 !(menu%+28+item%*24+4)=sub% 1450 ENDPROC 1460 : 1470 DEFPROCinit 1480 REM initialisation before polling loop starts 1490 DIM b% 255,ws% 1023,menspc% 1023,stack% 1023 1500 wsend%=ws%+1024:menend%=menspc%+1024:stackend%=stack%+1024:stackptr%=stack% 1510 quit%=FALSE 1520 PROCload_templates 1530 PROCmenus 1540 ENDPROC 1550 : 1560 DEFPROCreceive 1570 REM handles messages received from the Wimp with reason codes 17 or 18 1580 CASE b%!16 OF 1590 WHEN 0:quit%=TRUE 1600 ENDCASE 1610 ENDPROC 1620 : 1630 DEFPROCkeypress 1640 REM processes keypresses in response to Wimp_Poll reason code 8 1650 LOCAL key% 1660 key%=b%!24 1670 CASE key% OF 1680 OTHERWISE 1690 SYS "Wimp_ProcessKey",key% 1700 ENDCASE 1710 ENDPROC 1720 : 1730 DEFPROCwindow_click 1740 REM handles mouse clicks on window 1750 REM b%!0=mousex,b%!4=mousey:b%!8=buttons: b%!12=window handle (-2 for icon bar):b%!16=icon handle 1760 VDU 7 1770 ENDPROC 1780 : 1790 DEFPROCmenus 1800 REM create menus and attach submenus and dialogue boxes 1810 PROCmain_menu 1820 PROCattach(mainmenu%,0,info%) 1830 ENDPROC 1840 : 1850 DEFPROCshowmenu(menu%,x%,y%) 1860 REM opens menu at given coordinates 1870 topmenu%=menu%:topx%=x%:topy%=y% 1880 SYS "Wimp_CreateMenu",,menu%,x%,y% 1890 ENDPROC 1900 : 1910 DEFPROCmenuclick 1920 REM handles mouse clicks on menu in response to Wimp_Poll reason code 9 1930 LOCAL c%,adj% 1940 c%=FNstack(20) 1950 SYS "Wimp_GetPointerInfo",,c% 1960 adj%=(c%!8 AND 1) 1970 SYS "Wimp_DecodeMenu",,topmenu%,b%,c% 1980 CASE $c% OF 1990 WHEN "Quit":quit%=TRUE 2000 ENDCASE 2010 IF adj% PROCshowmenu(topmenu%,topx%,topy%) 2020 PROCunstack(c%) 2030 ENDPROC 2040 : 2050 DEFPROCmain_menu 2060 REM creates main menu, calling FNmake_menu 2070 RESTORE +1 2080 DATA Shell,Info,Quit,* 2090 mainmenu%=FNmake_menu 2100 ENDPROC 2110 : 2120 DEFPROCredraw(b%) 2130 REM redraws window contents 2140 LOCAL xorig%,yorig%,more% 2150 PROCget_origin(!b%,xorig%,yorig%) 2160 SYS "Wimp_RedrawWindow",,b% TO more% 2170 WHILE more% 2180 PROCdraw(b%,xorig%,yorig%) 2190 SYS "Wimp_GetRectangle",,b% TO more% 2200 ENDWHILE 2210 ENDPROC 2220 : 2230 DEFPROCdraw(b%,xorig%,yorig%) 2240 REM called when all or part of window needs redrawing 2250 REM xorig% and yorig% are coordinates of work area origin (top left-hand corner of window work area) 2260 REM b% points to block: 2270 REM b%!0 : window handle 2280 REM b%!4 : visible area minimum x coordinate 2290 REM b%!8 : visible area minimum y coordinate 2300 REM b%!12 : visible area maximum x coordinate 2310 REM b%!16 : visible area maximum y coordinate 2320 REM b%!20 : scroll x offset relative to work area origin 2330 REM b%!24 : scroll y offset relative to work area origin 2340 REM b%!28 : current graphics window minimum x coordinate 2350 REM b%!32 : current graphics window minimum y coordinate 2360 REM b%!36 : current graphics window maximum x coordinate 2370 REM b%!40 : current graphics window maximum y coordinate 2380 : 2390 REM Fill in as necessary! 2400 ENDPROC 2410 : 2420 DEFFNicon_state(window%,icon%) 2430 REM chacks if icon is selected and returns TRUE or FALSE 2440 LOCAL c% 2450 c%=FNstack(56) 2460 !c%=window%:c%!4=icon% 2470 SYS "Wimp_GetIconState",,c% 2480 PROCunstack(c%) 2490 =((c%!24) AND (1<<21))<>0 2500 : 2510 DEFPROCforce_redraw(window%) 2520 REM forces redraw of the entire screen rectangle covering a window's contents 2530 LOCAL c% 2540 c%=FNstack(36) 2550 !c%=window% 2560 SYS "Wimp_GetWindowState",,c% 2570 SYS "Wimp_ForceRedraw",-1,c%!4,c%!8,c%!12,c%!16 2580 PROCunstack(c%) 2590 ENDPROC 2600 : 2610 DEFFNerror 2620 REM main error handling routine 2630 REM returns TRUE if Cancel box clicked 2640 !b%=ERR 2650 CASE !b% OF 2660 WHEN 1<<30:err_str$="":box%=3 2670 WHEN (1<<30)+1:err_str$="":box%=1 2680 WHEN (1<<30)+2:err_str$="":box%=2 2690 OTHERWISE:err_str$=" at line "+STR$ERL:box%=2 2700 ENDCASE 2710 $(b%+4)=REPORT$+err_str$+CHR$0 2720 SYS "Wimp_ReportError",b%,box%,"Shell" TO ,response% 2730 =(response%=2) 2740 : 2750 DEFPROCterm(a%) 2760 REM replaces terminator on end of string starting at a% with Return character 2770 LOCAL n% 2780 WHILE a%?n%>31 2790 n%+=1 2800 ENDWHILE 2810 a%?n%=13 2820 ENDPROC 2830 : 2840 DEFPROCdrag(window%,icon%) 2850 REM creates drag box to fit icon and starts drag operation 2860 LOCAL c% 2870 c%=FNstack(56) 2880 PROCget_origin(window%,xorig%,yorig%) 2890 !c%=window%:c%!4=icon% 2900 SYS "Wimp_GetIconState",,c% 2910 xmin%=xorig%+c%!8:ymin%=yorig%+c%!12:xmax%=xorig%+c%!16:ymax%=yorig%+c%!20 2920 c%!4=5:REM drag type 2930 c%!8=xmin%:REM coordinates of drag box 2940 c%!12=ymin% 2950 c%!16=xmax% 2960 c%!20=ymax% 2970 c%!24=0:REM screen min x 2980 c%!28=0:REM screen min y 2990 c%!32=1280:REM screen max x 3000 c%!36=1024:REM screen max y 3010 SYS "Wimp_DragBox",,c% 3020 PROCunstack(c%) 3030 ENDPROC 3040 : 3050 DEFFNwind_open(h%) 3060 REM returns TRUE if a window is open 3070 LOCAL c% 3080 c%=FNstack(36) 3090 !c%=h% 3100 SYS "Wimp_GetWindowState",,c% 3110 PROCunstack(c%) 3120 =(c%!32 AND 1<<16)<>0 3130 : 3140 DEFPROCpush(w%,i%) 3150 REM simulates clicking of a push-button icon 3160 LOCAL c% 3170 PROCget_origin(w%,xorig%,yorig%) 3180 c%=FNstack(56) 3190 !c%=w%:c%!4=i%:SYS "Wimp_GetIconState",,c% 3200 x%=xorig%+c%!8:y%=yorig%+c%!12 3210 SYS "OS_ReadMonotonicTime" TO t% 3220 SYS "OS_Byte",138,9,(x%+20) MOD 256 3230 SYS "OS_Byte",138,9,(x%+20) DIV 256 3240 SYS "OS_Byte",138,9,(y%+20) MOD 256 3250 SYS "OS_Byte",138,9,(y%+20) DIV 256 3260 SYS "OS_Byte",138,9,4 3270 SYS "OS_Byte",138,9,t% MOD 256 3280 SYS "OS_Byte",138,9,(t% DIV &100) MOD 256 3290 SYS "OS_Byte",138,9,(t% DIV &10000) MOD 256 3300 SYS "OS_Byte",138,9,(t% DIV &1000000) MOD 256 3310 PROCunstack(c%) 3320 ENDPROC 3330 : 3340 DEFFNtick(menu%,item%) 3350 REM returns TRUE if menu item ticked 3360 =(menu%!(28+item%*24) AND 1)<>0 3370 : 3380 DEFPROCtoggle_tick(menu%,item%) 3390 REM reverses state of tick on menu item 3400 menu%!(28+item%*24)=menu%!(28+item%*24) EOR 1 3410 ENDPROC 3420 : 3430 DEFPROCgrey(menu%,item%) 3440 REM turns menu item grey to disable it 3450 menu%!(28+item%*24+8)=(menu%!(28+item%*24+8)) OR (1<<22) 3460 ENDPROC 3470 : 3480 DEFPROCungrey(menu%,item%) 3490 REM enables menu item and removes greyness 3500 menu%!(28+item%*24+8)=(menu%!(28+item%*24+8)) AND (NOT (1<<22)) 3510 ENDPROC 3520 : 3530 DEFFNstring_addr(window%,icon%) 3540 REM returns address of icon's indirected text string 3550 LOCAL c% 3560 c%=FNstack(56) 3570 !c%=window%:c%!4=icon% 3580 SYS "Wimp_GetIconState",,c% 3590 PROCunstack(c%) 3600 =c%!28 3610 : 3620 DEFFNicon_string(window%,icon%) 3630 REM returns icon's indirected text string 3640 PROCterm(FNstring_addr(window%,icon%)) 3650 =$FNstring_addr(window%,icon%) 3660 : 3670 DEFPROCset_icon_string(window%,icon%,a$) 3680 REM sets icon's indirected text string 3690 $FNstring_addr(window%,icon%)=a$ 3700 ENDPROC 3710 : 3720 DEFFNindirect(a$) 3730 REM puts a$ into indirected icon workspace and returns its address 3740 LOCAL start% 3750 start%=ws% 3760 IF (start%+LENa$+1)>wsend% ERROR (1<<30)+2,"Out of workspace" 3770 $start%=a$ 3780 ws%=start%+LENa$+1 3790 =start% 3800 : 3810 DEFPROCupdate_titlebar(window%) 3820 REM redraws title bar of a window; 3830 REM useful when adding or removing " *" to indicate unsaved data 3840 LOCAL c%,tbbottom% 3850 c%=FNstack(36) 3860 !c%=window%:SYS "Wimp_GetWindowState",,c% 3870 tbbottom%=c%!16 3880 SYS "Wimp_GetWindowOutline",,c% 3890 SYS "Wimp_ForceRedraw",-1,c%!4,tbbottom%,c%!12,c%!16 3900 PROCunstack(c%) 3910 ENDPROC 3920 : |
||||
|
||||
|