Converting+programs+from+RISC+OS+to+Windows

//by Richard Russell, October 2009//

The conversion of **WIMP-based** BBC BASIC programs (i.e. programs having a Graphical User Interface) from **RISC OS** to **Windows** is a very broad subject, and it would be virtually impossible to provide a comprehensive guide. Therefore this article takes the form of a Case Study, dealing specifically with the conversion of one program: **BasCalc**. BasCalc is a relatively simple application, but it is hoped that the techniques used in its conversion to Windows may be applicable to other programs.

Sections of the program that require alteration will be listed in their 'before' and 'after' forms, with an explanation of the changes. Line numbers will be retained, partly because the original version had them and partly for the convenience of being able to refer to the program by that means. However they are not required in the final (Windows) version and may be removed using the Renumber utility if desired.

Unfortunately this Wiki doesn't provide a means to annotate the **RISC OS** and **Windows** code differently (for example using different colours). Therefore you may find it more satisfactory to compare the two versions using a utility like [|WinDiff], which highlights the differences. To enable you to do that the two programs are available for download as plain-text files as follows: [|BASCalc_RISCOS.bas] and [|BASCalc_Windows.bas]

Firstly here are some screenshots of the RISC OS and Windows versions for comparison:



Now for the conversion. The first few lines require no alteration:

code format="bb4w" 10 REM >BASCalc Source code 20 REM (C)1999 PAUL VIGAY 30 REM Inspired by !Calcula, by Iain Logan and thanks are due to him for the idea. 40 REM Please feel free to examine my rudimentary wimp library and experiment to  50 REM create your own applications. A limited number of comments have been inserted for you! 60 version$="1.05 (12th Mar 2006)" : ver$=LEFT$(version$,4) code The first line to require a change is this one:

code format="bb4w" 70 DIM pb1% 256 : task%=&4B534154 : path$="." 70 DIM pb1% 256 : task%=&4B534154 : path$=@dir$ code The RISC OS path specification isn't appropriate for Windows. Under Windows it could be set to an absolute or relative path, but for convenience here we simply set it to **@dir$**, which in BB4W is a pre-defined 'system' variable containing the directory from which the program was loaded. Neither **pb1%** nor **task%** are required in the Windows version (in fact, **pb1%** isn't used even in the RISC OS version!) and can be retained or deleted as preferred.

code format="bb4w" 80 SYS "Wimp_Initialise",200,task%,"BASCalc" TO ,task_handle% 80 SYS "SetWindowText", @hwnd%, "BASCalc" code Windows has no direct equivalent to **Wimp_Initialise** so here we replace the call with one which sets the window title.

code format="bb4w" 90 PROCinit 100 ON ERROR PROCerror 110 REPEAT 120  SYS "Wimp_Poll",1,pb% TO reason%

90 ON ERROR PROCerror 100 PROCinit 110 REPEAT 120  reason% = FNwimp_poll(pb%) code Because Windows is a //pre-emptive// OS rather than a //co-operative// OS it has no direct equivalent to Wimp_Poll. However, to maintain the structure of the program, and to reduce the extent of the alterations as far as possible, the call is replaced by the function **FNwimp_poll** which we shall see later. Lines 90 and 100 have been swapped so that any errors occurring in **PROCinit** will be properly reported.

code format="bb4w" 130  CASE reason% OF  140     WHEN 2:PROCopen(pb%!0) 150    WHEN 3:PROCclose(pb%!0) 160    WHEN 6:PROCmouseclick 170    WHEN 8:PROCkeypressed 180    WHEN 9:PROCmenu 190    WHEN 17,18:CASE pb%!16 OF  200         WHEN 0:end%=TRUE 210        WHEN 2:REM This application dragged something elsewhere 220          f$=FNgetstring(pb%+44):VDU7:END:REMf$=LEFT$(f$,LEN(f$)-1):l$=FNleafname(f$) 230          IF pb%!12<>saveref% ERROR 1,FNmsg("err6") 240          CASE step% OF  250             WHEN 4:PROCcheckupgrade(f$,l$) 260          ENDCASE 270          oldpb%!12=oldpb%!8:oldpb%!16=3:SYS "Wimp_SendMessage",17,oldpb%,oldpb%!4 280      ENDCASE 290  ENDCASE

130  CASE reason% OF  160     WHEN 6:PROCmouseclick 170    WHEN 8:PROCkeypressed 180    WHEN 9:PROCmenu 190    WHEN 17: end%=TRUE 290  ENDCASE code There are no essential changes here, but for neatness lines 140-150 and 190-280 have been deleted. These perform operations needed to support RISC OS which are not required under Windows. It would do no harm, other than leaving 'dead' code, to retain them since the associated 'reason codes' won't be generated by **FNwimp_poll**.

code format="bb4w" 300 UNTIL end% 310 PROCmsgend:@%=at%:SYS "Wimp_CloseDown",task_handle%,task% 320 END

300 UNTIL end% 310 QUIT code Here we can simply replace the calls to **Wimp_CloseDown** and **PROCmsgend** with QUIT. In a more complex program it may well be necessary to perform some 'cleanup' operations before exit, even in the Windows version.

code format="bb4w" 330  340 DEF PROCmouseclick 350 LOCAL b%,w%,i%,pos%,reason%,nextmenu% 360 pos%=pb%!0 : ypos%=pb%!4 : b%=pb%!8 : w%=pb%!12 : i%=pb%!16 370 lastwindow%=w% : lasticon%=i%

330  340 DEF PROCmouseclick 350 LOCAL i% 360 i%=pb%!16 code For the mouse-click processing the information required by the Windows version is much simpler than by the RISC OS version, basically it's a single value (here **i%**) to indicate what **control** (**icon** in RISC OS-speak) was clicked. Again, there's not actually any need to make changes but for clarity the unnecessary variables have been deleted.

code format="bb4w" 380 CASE b% OF 390   WHEN 1,4:CASE w% OF  400       WHEN -2:IF open% THEN 410          PROCclose(window%(2)) 420        ELSE 430          PROCfront(window%(2)):SYS "Wimp_SetCaretPosition",window%(2),2,0,0,-1,LEN($arg%) 440        ENDIF 450      WHEN window%(2):CASE i% OF  460           WHEN 4:PROCui(window%(2),2,arg%,""):PROCui(window%(2),3,res%,"") 470            SYS "Wimp_SetCaretPosition",window%(2),2,0,0,-1,LEN($arg%) 480          WHEN 11:PROCui(window%(2),8,m1%,""):mem(1)=0 490          WHEN 12:PROCui(window%(2),9,m2%,""):mem(2)=0 500          WHEN 13:PROCui(window%(2),10,m3%,""):mem(3)=0 510          WHEN 20:PROCui(window%(2),17,m4%,""):mem(4)=0 520          WHEN 21:PROCui(window%(2),18,m5%,""):mem(5)=0 530          WHEN 22:PROCui(window%(2),19,m6%,""):mem(6)=0 540          WHEN 23:PROCui(window%(2),8,m1%,""):mem(1)=0:PROCui(window%(2),9,m2%,""):mem(2)=0 550            PROCui(window%(2),10,m3%,""):mem(3)=0:PROCui(window%(2),17,m4%,""):mem(4)=0 560            PROCui(window%(2),18,m5%,""):mem(5)=0:PROCui(window%(2),19,m6%,""):mem(6)=0 570            PROCui(window%(2),2,arg%,""):PROCui(window%(2),3,res%,"") 580            SYS "Wimp_SetCaretPosition",window%(2),2,0,0,-1,LEN($arg%) 590          WHEN 24:PROCinsertcode($res%) 600        ENDCASE 610    ENDCASE 620  WHEN 2:CASE w% OF  630       WHEN -2:SYS "Wimp_CreateMenu",,menu1%,pos%-60,228 640      WHEN window%(2):SYS "Wimp_CreateMenu",,menu1%,pos%-60,ypos% 650    ENDCASE 660 ENDCASE 670 ENDPROC

450 CASE i% OF 460   WHEN 4:PROCui(window%(2),2,arg%,""):PROCui(window%(2),3,res%,"") 480  WHEN 11:PROCui(window%(2),8,m1%,""):mem(1)=0 490  WHEN 12:PROCui(window%(2),9,m2%,""):mem(2)=0 500  WHEN 13:PROCui(window%(2),10,m3%,""):mem(3)=0 510  WHEN 20:PROCui(window%(2),17,m4%,""):mem(4)=0 520  WHEN 21:PROCui(window%(2),18,m5%,""):mem(5)=0 530  WHEN 22:PROCui(window%(2),19,m6%,""):mem(6)=0 540  WHEN 23:PROCui(window%(2),8,m1%,""):mem(1)=0:PROCui(window%(2),9,m2%,""):mem(2)=0 550    PROCui(window%(2),10,m3%,""):mem(3)=0:PROCui(window%(2),17,m4%,""):mem(4)=0 560    PROCui(window%(2),18,m5%,""):mem(5)=0:PROCui(window%(2),19,m6%,""):mem(6)=0 570    PROCui(window%(2),2,arg%,""):PROCui(window%(2),3,res%,"") 590  WHEN 24:PROCinsertcode($res%) 600 ENDCASE 670 ENDPROC code The only necessary change to this code is the substitution of the calls to **Wimp_SetCaretPosition** (lines 470 and 580) with a suitable Windows equivalent. In fact, since these calls are non-essential, for convenience (and laziness!) they have here simply been deleted.

However, in Windows, the outer **CASE b% OF** and **CASE w% OF** constructs aren't required, so for neatness and clarity lines 380-440 and 610-660 have been removed and in line 450 the **WHEN window%(2)** has been deleted. Again, it would do no harm to retain this 'dead' code (so long as the original lines 350 and 360 are kept) because **FNwimp_poll** does not return values which would cause it to be activated.

code format="bb4w" 680  690 DEF PROCkeypressed 700 LOCAL key%,window%,icon%,k%,len%,process% 710 key%=pb%!24 : window%=pb%!0 : icon%=pb%!4 : process%=FALSE

680  690 DEF PROCkeypressed 700 LOCAL icon% 710 icon%=pb%!4 code Here we have a very similar situation to **PROCmouseclick**; once again the information required by the Windows version is simpler than the RISC OS version: just the control (icon) into which input was entered (**icon%**). In Windows, handling of keyboard input to an 'edit box' does not require the attention of the main program. Checking that the key pressed was Return (Enter), which in the RISC OS version was done here, has been transferred into **FNwimp_poll** in the Windows version.

code format="bb4w" 720 CASE window% OF 730   WHEN window%(2):CASE key% OF  740       WHEN 13:CASE icon% OF  750           WHEN 2:PROCcalc 760          WHEN 8:mem(1)=VAL($m1%) 770          WHEN 9:mem(2)=VAL($m2%) 780          WHEN 10:mem(3)=VAL($m3%) 790          WHEN 17:mem(4)=VAL($m4%) 800          WHEN 18:mem(5)=VAL($m5%) 810          WHEN 19:mem(6)=VAL($m6%) 820        ENDCASE 830        SYS "Wimp_SetCaretPosition",window%(2),2,0,0,-1,LEN($arg%) 840      OTHERWISE process%=TRUE 850    ENDCASE 860  OTHERWISE process%=TRUE 870 ENDCASE 880 IF process% SYS "Wimp_ProcessKey",key% 890 ENDPROC

740 CASE icon% OF 750   WHEN 2:PROCcalc 760  WHEN 8:mem(1)=VAL($m1%) 770  WHEN 9:mem(2)=VAL($m2%) 780  WHEN 10:mem(3)=VAL($m3%) 790  WHEN 17:mem(4)=VAL($m4%) 800  WHEN 18:mem(5)=VAL($m5%) 810  WHEN 19:mem(6)=VAL($m6%) 820 ENDCASE 890 ENDPROC code Once again the only essential change is the substitution or removal of **Wimp_SetCaretPosition** (line 830) but here the superfluous **CASE window% OF** and **CASE key% OF** constructs (lines 720-730 and 830-880) and the **WHEN 13** (in line 740) have been deleted. The **Wimp_ProcessKey** has no equivalent in Windows (it is related to the co-operative nature of RISC OS).

code format="bb4w" 900  910 DEF PROCmenu 920 LOCAL menu%,b%,w%,ptr%,i% 930 w%=lastwindow%:i%=lasticon% 940 CASE w% OF 950   WHEN -2,window%(2):menu%=menu1% 960 ENDCASE 970 SYS "Wimp_DecodeMenu",,menu%,pb%,mbuffer%:SYS "Wimp_GetPointerInfo",,pb% 980 IF LEFT$($mbuffer%,9)="Decimals." decimals%=VAL(MID$($mbuffer%,10)):$mbuffer%="Decimals" 990 b%=pb%!8 1000 IF b% THEN 1010  CASE $mbuffer% OF 1020     WHEN "Quit":end%=TRUE 1030    WHEN "Decimals":@%=&01020000+(decimals%<<8):PROCsavechoices 1040  ENDCASE 1050 ENDIF 1060 PROCmenucreate 1070 IF b% AND 1 SYS "Wimp_CreateMenu",,menu%,pb%!0-60,228 1080 ENDPROC

900  910 DEF PROCmenu 920 LOCAL select%, pt{} 930 DIM pt{x%,y%} : SYS "GetCursorPos", pt{} 940 select% = FNtrackpopupmenu(menu1%, &100, pt.x%, pt.y%) 950 CASE select% OF 960   WHEN 100: SYS "ShellExecute", @hwnd%, 0, @dir$+"BASCalc.txt", 0, "", 1 970  WHEN 101: 980    showfull% = NOT showfull% 990    PROCsizewindow 1000    PROCsavechoices 1010  WHEN 109: end%=TRUE 1020  WHEN 200,201,202,203,204,205,206,207,208,209,210: 1030    SYS "CheckMenuItem", menu2%, 200+decimals%, 0 1040    SYS "CheckMenuItem", menu2%, select%, 8 1050    decimals% = select%-200 : @%=&01020000+(decimals%<<8) 1060    PROCsavechoices 1070 ENDCASE 1080 ENDPROC code This is the first major change. Since the means of displaying and processing a //context// menu is quite different, **PROCmenu** has been substantially re-written. In Windows the context menu is displayed by clicking the //right// mouse button.

code format="bb4w" 1090 1100 DEF PROCinit 1110 DIM pb% 256,spr% 12,msgtext% 256,msgtrans% &500,inv% 28,outv% 28 1120 DIM ws% 1500 :REM workspace for icons in windows 1130 DIM wb% 1500 :REM window block (multi-purpose) 1140 DIM mb% 500:REM menu data block 1150 DIM md% 100:REM menu block containing in-directed data 1160 DIM mbuffer% 256,mem(6) 1170 nw%=2 :REM number of windows supported 1180 DIM window%(nw%):end%=FALSE:FOR N%=1 TO 6:mem(N%)=0:NEXT N%: leavealone=FALSE:open%=FALSE:at%=@%:decimals%=8 1190 PROCmsgload(path$+"Messages"):PROCloadwindows:PROCloadchoices:PROCmenucreate 1200 @%=&01020000+(decimals%<<8) 1210 iconbar%=FNiconbar("!BASCalc"):PROCgetmodeinfo 1220 ENDPROC

1090 1100 DEF PROCinit 1110 DIM pb% 256,msgtext% 256 1160 DIM mbuffer% 256,mem(6) 1170 nw%=2 1180 DIM window%(nw%):end%=FALSE:at%=@%:decimals%=8:showfull%=FALSE 1190 PROCloadwindows:PROCloadchoices:PROCmenucreate:PROCsizewindow 1200 @%=&01020000+(decimals%<<8) 1210 SYS "CheckMenuItem", menu2%, 200+decimals%, 8 1220 ENDPROC code Here the changes consist mostly of (optional) deletions. The allocation of buffers in lines 1120 to 1150 is not required (along with most of line 1110); **PROCmsgload** can be dispensed with (as we will see later, the most convenient way of emulating the RISC OS **MessageTrans** facility does not require initialisation) and Windows looks after its iconbar (task bar) without user involvement.

A couple of additions have also been made: **showfull%=FALSE** (line 1180) and **PROCsizewindow** (line 1190) initialise the size of the window, and **SYS "CheckMenuItem"** (line 1210) initialises the 'tick' against the default number of decimal places in the relevant sub-menu (**menu2%**).

code format="bb4w" 1230 1240 DEF FNiconbar(A$) 1250 LOCAL sp$,ih% 1260 pb%!0=-1:pb%!4=0:pb%!8=0:pb%!12=68:pb%!16=68:pb%!20=&2102 1270 $spr%=A$:pb%!24=spr%:pb%!28=1:pb%!32=LEN(A$):SYS "Wimp_CreateIcon",,pb% TO ih% 1280 =ih% 1290 1300 DEF PROCgetmodeinfo 1310 inv%!0=4:inv%!4=5:inv%!8=6:inv%!12=7:inv%!16=11:inv%!20=12: inv%!24=-1:SYS "OS_ReadVduVariables",inv%,outv% 1320 dx%=1<<(outv%!0):dy%=1<<(outv%!4):scrx%=(outv%!16+1)*dx%:scry%=(outv%!20+1)*dy%-2 1330 ENDPROC 1340 1350 DEF PROCopen(handle%) 1360 IF handle%<>-1 THEN pb%!0=handle%:SYS "Wimp_OpenWindow",,pb% 1370 CASE handle% OF 1380  WHEN window%(2):open%=TRUE 1390 ENDCASE 1400 ENDPROC 1410 1420 DEF PROCfront(handle%) 1430 LOCAL mx%,my% 1440 SYS "OS_Mouse" TO mx%,my% 1450 pb%!0=-1:pb%!4=iconbar%:SYS "Wimp_GetIconState",,pb%:mx%=pb%!8+46: IF iconbar%=0 mx%=scrx%-250 1460 IF handle%<>-1 PROCgetw(handle%):pb%!28=-1 1470 CASE handle% OF 1480  WHEN window%(2):pb%!4=mx%-396:pb%!12=mx%+396:REM 792x300 (main window) 1490 ENDCASE 1500 PROCopen(handle%) 1510 ENDPROC 1520 1530 DEF PROCclose(handle%) 1540 !pb%=handle%:SYS "Wimp_CloseWindow",,pb% 1550 CASE handle% OF 1560  WHEN window%(2):open%=FALSE 1570 ENDCASE 1580 ENDPROC 1590 1600 DEF PROCgetw(handle%) 1610 !pb%=handle%:SYS "Wimp_GetWindowState",,pb% 1620 vminx%=pb%!4:vminy%=pb%!8:vmaxx%=pb%!12:vmaxy%=pb%!16 1630 scrollx%=pb%!20:scrolly%=pb%!24:bx%=vminx%-scrollx%:by%=vmaxy%-scrolly% 1640 bhandle%=pb%!28:flags%=pb%!32 1650 ENDPROC code This code is specific to RISC OS and can be **deleted in its entirety**. The program will still work correctly if it is left, but doing so will waste a substantial amount of memory and potentially cause confusion to somebody reading the code.

code format="bb4w" 1660 1670 DEF PROCui(window%,icon%,ptr%,text$) 1680 pb%!0=window%:pb%!8=0:pb%!12=0:$ptr%=text$:pb%!4=icon%:SYS "Wimp_SetIconState",0,pb% 1690 ENDPROC

1660 1670 DEF PROCui(window%,icon%,ptr%,text$) 1680 SYS "SetDlgItemText", window%, icon%, text$ 1690 ENDPROC code Here the original code has been substituted with a Windows near-equivalent.

code format="bb4w" 1700 1710 REM Setup messagetrans 1720 DEF PROCmsgload(n$) 1730 SYS "MessageTrans_FileInfo",,n$ 1740 SYS "OS_Module",6,,,17+LEN(n$) TO ,,msgdesc% 1750 $(msgdesc%+16)=n$:SYS "MessageTrans_OpenFile",msgdesc%,msgdesc%+16 1760 ENDPROC 1770 1780 REM Finish with messagetrans 1790 DEF PROCmsgend 1800 SYS "XMessageTrans_CloseFile",msgdesc%:SYS "XOS_Module",7,,msgdesc% 1810 ENDPROC code This code is not required and may be **deleted**.

code format="bb4w" 1820 1830 REM decode tag$ into relevant text from Messages file 1840 DEF FNmsg(tag$) 1850 LOCAL F%,L% 1860 SYS "XMessageTrans_Lookup",msgdesc%,tag$,msgtext%,255,"","" TO ,,,L%;F% 1870 IF F% AND 1 L%=0 1880 msgtext%?L%=13 1890 =$msgtext%

1820 1830 REM decode tag$ into relevant text from Messages file 1840 DEF FNmsg(tag$) 1860 SYS "GetPrivateProfileString","messages",tag$,"",msgtext%,256,path$+"BASCalc.ini" 1890 = $$msgtext% code A convenient way of emulating the RISC OS **MessageTrans** facility is to use a file in the Windows '.INI' format. Here we are using such a file called **BASCalc.ini** in which the mesages are contained in a section called **messages** (the file is listed at the end of this article). Note the use of **$$** rather than **$** in line 1890.

code format="bb4w" 1900 1910 DEF FNgetstring(a%) 1920 LOCAL a$ 1930 WHILE ?a%<>0:a$+=CHR$(?a%):a%+=1:ENDWHILE 1940 =a$ 1950 1960 DEF PROCstring0(a%,a$) $a%=a$:a%?LENa$=0:ENDPROC code These routines may be **deleted**. BBC BASIC for Windows supports NUL-terminated strings natively using the **$$** syntax.

code format="bb4w" 1970 1980 DEF PROCerror 1990 LOCAL message$,action$,E%,err%,a% 2000 REMON ERROR OFF 2010 message$=REPORT$:err%=ERR:action$=FNmsg("e1"):a%=3:message$=message$+". "+action$:VDU7 2020 E%=FNerror(a%,err%,message$) 2030 IF E%=2 OR end%=TRUE end%=TRUE:@%=at%:PROCmsgend: SYS "Wimp_CloseDown",task_handle%,task%:END 2040 ENDPROC

1970 1980 DEF PROCerror 1990 LOCAL message$,action$,E%,err%,a% 2000 REMON ERROR OFF 2010 message$=REPORT$:err%=ERR:action$=FNmsg("e1"):a%=3:message$=message$+". "+action$:VDU7 2020 E%=FNerror(a%,err%,message$) 2030 IF E%=2 OR end%=TRUE end%=TRUE:@%=at%:QUIT 2040 ENDPROC code The only alterations required here are to delete the **PROCmsgend:SYS "Wimp_CloseDown"** and change **END** to **QUIT**. In a more complex program it may be necessary to call a 'cleanup' routine before exit.

code format="bb4w" 2050 2060 DEF FNerror(E%,N%,M$) 2070 SYS "Hourglass_Smash":SYS "Wimp_DragBox",,-1 2080 !pb%=N%:$(pb%+4)=M$+CHR$(0):SYS "Wimp_ReportError",pb%,E%,"!BASCalc" TO ,E% 2090 =E%

2050 2060 DEF FNerror(E%,N%,M$) 2070 SYS "MessageBox", @hwnd%, M$, "Error "+STR$(N%), 49 TO E% 2090 =E% code Here **SYS "Wimp_ReportError"** has been substituted with **SYS "MessageBox"**. Fortunately the codes returned for OK (1) and CANCEL (2) are the same as in RISC OS!

code format="bb4w" 2100 2110 DEF PROCmenucreate 2120 LOCAL loop%,pass%,T$,C1%,C2%,C3%,C4%,W%,H%,V%,NI%,items% 2130 menu1%=0:menu2%=0 2140 FOR pass%=0 TO 2 STEP 2 2150  P%=mb%:indir%=md% 2160  menu1%=P% 2170  RESTORE 2540 2180  [ OPT pass% 2190  EQUS "BASCalc":EQUB 0:EQUD 0:EQUB 7:EQUB 2:EQUB 3:EQUB 0:EQUD 120:EQUD 44:EQUD 0 2200  ] 2210   FOR items%=1 TO 3 2220    READ t$ 2230     nm%=-1:f4%=indir%:f6%=LEN(t$):$f4%=t$:indir%=indir%+f6%+1:f%=0 2240    CASE t$ OF 2250       WHEN "Info":f%=0:nm%=window%(1) 2260      WHEN "Decimals":nm%=menu2% 2270      WHEN "Quit":f%=&80 2280    ENDCASE 2290    [ OPT pass% 2300    EQUD f%:EQUD nm%:EQUB 49:EQUB 1:EQUB 0:EQUB 7:EQUD f4%:EQUD -1:EQUD f6% 2310    ] 2320   NEXT items% 2330  menu2%=P% 2340  RESTORE 2580 2350  [ OPT pass% 2360  EQUS "Decimals":EQUD 0:EQUB 7:EQUB 2:EQUB 3:EQUB 0:EQUD 120:EQUD 44:EQUD 0 2370  ] 2380   FOR items%=0 TO 10 2390    READ t$ 2400     nm%=-1:f4%=indir%:f6%=LEN(t$):$f4%=t$:indir%=indir%+f6%+1:f%=0 2410    IF items%=decimals% f%+=1 2420    CASE t$ OF 2430       WHEN "10":f%=&80 2440    ENDCASE 2450    [ OPT pass% 2460    EQUD f%:EQUD nm%:EQUB 49:EQUB 1:EQUB 0:EQUB 7:EQUD f4%:EQUD -1:EQUD f6% 2470    ] 2480   NEXT items% 2490 NEXT pass% 2500 IF P%-mb%>450 ERROR 1,FNmsg("e2")+" ("+STR$(P%-mb%)+")" 2510 IF indir%-md%>50 ERROR 1,FNmsg("e3")+" ("+STR$(indir%-md%)+")" 2520 ENDPROC 2530 2540 REM data for menu 1 2550 DATA Info,Decimals,Quit 2560 2570 REM data for menu 2 2580 DATA 0,1,2,3,4,5,6,7,8,9,10 2590

2100 2110 DEF PROCmenucreate 2120 SYS "CreatePopupMenu" TO menu2% 2130 SYS "AppendMenu", menu2%, 0, 200, "0" 2140 SYS "AppendMenu", menu2%, 0, 201, "1" 2150 SYS "AppendMenu", menu2%, 0, 202, "2" 2160 SYS "AppendMenu", menu2%, 0, 203, "3" 2170 SYS "AppendMenu", menu2%, 0, 204, "4" 2180 SYS "AppendMenu", menu2%, 0, 205, "5" 2190 SYS "AppendMenu", menu2%, 0, 206, "6" 2200 SYS "AppendMenu", menu2%, 0, 207, "7" 2210 SYS "AppendMenu", menu2%, 0, 208, "8" 2220 SYS "AppendMenu", menu2%, 0, 209, "9" 2230 SYS "AppendMenu", menu2%, 0, 210, "10" 2240 2250 SYS "CreatePopupMenu" TO menu1% 2260 SYS "AppendMenu", menu1%, 0, 100, "&Info" 2270 SYS "AppendMenu", menu1%, 16, menu2%, "&Decimals" 2280 SYS "AppendMenu", menu1%, 0, 101, "&Show full" 2290 SYS "AppendMenu", menu1%, 0, 109, "&Quit" 2300 ENDPROC code This is another case where the RISC OS and Windows code is quite different. Also, an extra menu item (**Show full**) has been added to the Windows version as a more convenient way of switching between the two window sizes.

code format="bb4w" 2590 2600 DEF PROCloadwindows 2610 LOCAL W%,window$,N% 2620 SYS "Wimp_OpenTemplate",,path$+"Templates":wm%=ws%+4500 2630 RESTORE 2760 2640 FOR W%=1 TO nw% 2650  READ window$:SYS "Wimp_LoadTemplate",,wb%,ws%,wm%,-1,window$,0 TO ,,ws% 2660  CASE window$ OF 2670     WHEN "info":$(wb%!(88+128+20))=version$ 2680    WHEN "main":arg%=wb%!(88+(2*32)+20):res%=wb%!(88+(3*32)+20) 2690      m1%=wb%!(88+(8*32)+20):m2%=wb%!(88+(9*32)+20):m3%=wb%!(88+(10*32)+20) 2700      m4%=wb%!(88+(17*32)+20):m5%=wb%!(88+(18*32)+20):m6%=wb%!(88+(19*32)+20) 2710  ENDCASE 2720  SYS "Wimp_CreateWindow",,wb% TO window%(W%) 2730 NEXT W% 2740 SYS "Wimp_CloseTemplate" 2750 ENDPROC 2760 DATA info,main

2590 2600 DEF PROCloadwindows 2605 INSTALL @lib$+"WINLIB2A" 2610 window% = FN_newdialog("BASCalc",0,0,264,95,8,1000) 2615 window%!16=&508800C4 : REM remove title bar and make child window 2620 PROC_static(window%,"Argument:",0,0,6,60,16,2) 2625 PROC_static(window%,"Result:",1,0,24,60,16,2) 2630 PROC_editbox(window%,"",2,65,4,152,14,&81) 2635 PROC_editbox(window%,"",3,65,22,152,14,1) 2640 PROC_pushbutton(window%,"Clear",4,220,4,40,15,0) 2645 PROC_static(window%,"M1",5,0,44,19,16,2) 2650 PROC_static(window%,"M2",6,0,62,19,16,2) 2655 PROC_static(window%,"M3",7,0,80,19,16,2) 2660 PROC_editbox(window%,"",8,22,41,67,14,1) 2665 PROC_editbox(window%,"",9,22,59,67,14,1) 2670 PROC_editbox(window%,"",10,22,77,67,14,1) 2675 PROC_pushbutton(window%,"Clear",11,93,41,34,15,0) 2680 PROC_pushbutton(window%,"Clear",12,93,59,34,15,0) 2685 PROC_pushbutton(window%,"Clear",13,93,77,34,15,0) 2690 PROC_static(window%,"M4",14,132,44,19,16,2) 2695 PROC_static(window%,"M5",15,132,62,19,16,2) 2700 PROC_static(window%,"M6",16,132,80,19,16,2) 2705 PROC_editbox(window%,"",17,155,41,67,14,1) 2710 PROC_editbox(window%,"",18,155,59,67,14,1) 2715 PROC_editbox(window%,"",19,155,77,67,14,1) 2720 PROC_pushbutton(window%,"Clear",20,226,41,34,15,0) 2725 PROC_pushbutton(window%,"Clear",21,226,59,34,15,0) 2730 PROC_pushbutton(window%,"Clear",22,226,77,34,15,0) 2735 PROC_pushbutton(window%,"AC",23,234,22,26,15,0) 2740 PROC_pushbutton(window%,"»",24,220,22,12,15,0) 2745 PROC_showdialog(window%) 2750 DIM arg% 255,res% 255,m1% 255,m2% 255,m3% 255,m4% 255,m5% 255,m6% 255 2755 window%(2) = !window% 2760 ENDPROC code Once again the Windows version is quite different. Although it's possible in Windows to create a dialogue window using a template (which would be more similar to the RISC OS version) it's conventional in BB4W to incorporate the various controls using inline code as shown.

code format="bb4w" 2780 2790 DEF PROCcalc .... 2990 DEF FNparse(a$) .... 3350 DEF FNconvtoupper(a$) .... 3410 DEF FNstripspaces(a$) .... 3510 DEF FNextractno(a$,x%,RETURN N%) .... 3590 DEF FNchr(a$) .... 3660 DEF FNvat(a$,t%) .... 3780 DEF FNgcol(c%) .... 3870 DEF FNconvtobin(a$) .... 3970 DEF FNcomma(a$) .... 4230 =a$ code Lines 2780 to 4230 inclusive are the 'meat' of the BASCalc application and **require no changes**.

code format="bb4w" 4240 4250 DEF PROCinsertcode(a$) 4260 LOCAL N% 4270 IF a$<>"" FOR N%=1 TO LEN(a$):SYS "OS_Byte",153,0,ASC(MID$(a$,N%,1)):NEXT N% 4280 ENDPROC

4240 4250 DEF PROCinsertcode(a$) 4260 LOCAL N% 4270 IF a$<>"" FOR N%=1 TO LEN(a$):SYS "PostMessage",Focus%,258,ASC(MID$(a$,N%,1)),0:NEXT N% 4280 ENDPROC code The **SYS "OS_Byte"** has been substituted with a Windows equivalent (258 is WM_CHAR).

code format="bb4w" 4290 4300 DEF PROCloadchoices 4310 LOCAL t%,x%,v$ 4320 SYS "OS_File",5,path$+"choices" TO t% 4330 IF t%=1 THEN 4340  x%=OPENIN(path$+"choices") 4350  IF x%>0 AND x%<256 THEN 4360    INPUT#x%,v$,decimals% 4370    CLOSE#x% 4380  ENDIF 4390 ELSE 4400  PROCsavechoices 4410 ENDIF 4420 ENDPROC

4290 4300 DEF PROCloadchoices 4310 SYS "GetPrivateProfileInt","choices","decimals",8,path$+"BASCalc.ini" TO decimals% 4320 SYS "GetPrivateProfileInt","choices","showfull",0,path$+"BASCalc.ini" TO showfull% 4330 ENDPROC code The Windows code is again quite different. In addition to loading the user's preference as regards the **decimals** setting, his most recent choice for the window size is also loaded.

code format="bb4w" 4430 4440 DEF PROCsavechoices 4450 LOCAL x%,d% 4460 d%=0 4470 x%=OPENOUT(path$+"choices") 4480 IF x%>0 AND x%<256 THEN 4490  PRINT#x%,ver$,decimals% 4500  CLOSE#x% 4510 ENDIF 4520 ENDPROC

4430 4440 DEF PROCsavechoices 4450 LOCAL @% 4460 SYS "WritePrivateProfileString","choices","decimals",STR$decimals%,path$+"BASCalc.ini" 4470 SYS "WritePrivateProfileString","choices","showfull",STR$showfull%,path$+"BASCalc.ini" 4480 ENDPROC code Here the user's most recent window size setting is saved in addition to the number of decimal places.

Finally here are the **FNwimp_poll**, **FNtrackpopupmenu** and **PROCsizewindow** routines which are specific to the Windows version:

code format="bb4w" DEF FNwimp_poll(pb%) LOCAL click%, reason%, fg%, L%     PRIVATE icon% Queue$ = "" : !^wParam$ = ^@wparam% : ?(^wParam$+4) = 4 ON SYS Queue$ += wParam$ : RETURN ON CLOSE Queue$ += CHR$(0)+CHR$(0)+CHR$(3)+CHR$(0) : RETURN REPEAT WAIT 1 IF Queue$<>"" THEN click% = !!^Queue$ Queue$ = MID$(Queue$,5) ENDIF UNTIL click% OR INKEY(-12) SYS "GetDlgItem",window%(2),icon% TO Focus% IF click% = 24 SYS "PostMessage",window%(2),40,Focus%,1 IF click% = 0 SYS "GetForegroundWindow" TO fg% : IF fg% = @hwnd% click% = &20000 IF click% = 1 click% = icon% OR &10000 ELSE icon% = click% AND &FFFF : REM return key CASE click% >> 16 OF       WHEN 0: reason% = 6 : pb%!8 = 1 : pb%!12 = window%(2) : pb%!16 = icon% : REM mouse WHEN 1,512: reason% = 8 : pb%!0 = window%(2) : pb%!4 = icon% : pb%!24 = 13 : REM key WHEN 2: reason% = 9 : REM menu WHEN 3: reason% = 17 : pb%!16 = 0 : REM close ENDCASE SYS "GetDlgItemText",window%(2),2,arg%,256 TO L% : arg%?L%=13 SYS "GetDlgItemText",window%(2),3,res%,256 TO L% : res%?L%=13 SYS "GetDlgItemText",window%(2),8, m1%,256 TO L% : m1%?L%=13 SYS "GetDlgItemText",window%(2),9, m2%,256 TO L% : m2%?L%=13 SYS "GetDlgItemText",window%(2),10,m3%,256 TO L% : m3%?L%=13 SYS "GetDlgItemText",window%(2),17,m4%,256 TO L% : m4%?L%=13 SYS "GetDlgItemText",window%(2),18,m5%,256 TO L% : m5%?L%=13 SYS "GetDlgItemText",window%(2),19,m6%,256 TO L% : m6%?L%=13 = reason% DEF FNtrackpopupmenu(hmenu%,flags%,x%,y%) LOCAL M%, O%, P%, T%     DIM P% LOCAL 54 SYS "GetWindowLong", @hwnd%, -4 TO O%     [OPT 2 .T%     push 0 push @hwnd% push 0 push y%     push x%      push flags% push hmenu% call "TrackPopupMenu" ret 16 .M% cmp dword [esp+8],&500 : jz T% : jmp O%     ] SYS "SetWindowLong", @hwnd%, -4, M%     SYS "SendMessage", @hwnd%, &500, 0, 0 TO T%      SYS "SetWindowLong", @hwnd%, -4, O%      SYS "SendMessage", @hwnd%, 0, 0, 0 = T%     DEF PROCsizewindow LOCAL rc{}, style% DIM rc{l%,t%,r%,b%} rc.l% = 0 : rc.t% = 0 : rc.r% = 267 IF showfull% rc.b% = 97 ELSE rc.b% = 43 SYS "CheckMenuItem", menu1%, 101, showfull% AND 8 SYS "MapDialogRect", window%(2), rc{} SYS "GetWindowLong", @hwnd%, -16 TO style% SYS "AdjustWindowRect", rc{}, style%, 0 SYS "SetWindowPos", @hwnd%, 0, 0, 0, rc.r%-rc.l%, rc.b%-rc.t%, 6 ENDPROC code Here is what **BASCalc.ini** looks like; it may be compared with the equivalent RISC OS message file:

code format="ini" [messages]
 * 1) Messages file for !BASCalc
 * 2) ©1999 Paul Vigay

vat=17.5
 * 1) Current VAT rate

money=Yes
 * 1) If money is set to Yes then VAT calculations will be rounded to the nearest penny,
 * 2) otherwise left as a decimal value

e1=Click OK to continue or CANCEL to Quit application e2=Menu data too long for mb% block e3=Menu data too long for md% block
 * 1) Application messages - DO NOT CHANGE!

berr18=Division by zero berr20=Number too big berr21=Negative root berr22=Log range! berr23=Accuracy lost! berr24=Exponent range! berr26=Unknown variable berr27=Missing bracket berr28=Bad binary/hex

chr0/chr=null chr1=send chr to prnt chr2=Printer ON chr3=Printer OFF chr7=bell chr8=cursor back chr9=tab chr10=LF chr11=cursor up chr12=cls chr13=CR chr30=home chr32=space chr127=delete

[choices] decimals=8 showfull=-1 code