SIBO 'C' Software Development Kit 


OBJECT ORIENTED PROGRAMMING GUIDE 


Version 2.30 


March 1, 1999 


(C) Copyright Psion PLC 1994-95 


All rights reserved. This manual and the programs referred to herein are copyrighted works of Psion PLC, 
London, England. Reproduction in whole or in part, including utilization in machines capable of 
reproduction or retrieval, without express written permission of Psion PLC, is prohibited. Reverse 
engineering is also prohibited. 


The information in this document is subject to change without notice. 


Psion and the Psion logo are registered trademarks, and Psion, Psion MC, Psion HC, Psion Series 3, Psion 
Series 3a and Psion Workabout are trademarks of Psion PLC. 


TopSpeed is a registered trademark of Clarion Software Corporation. Intel 8086 and 80286 are registered 
trademarks of Intel Corporation. IBM, IBM XT and IBM AT are registered trademarks of International 
Business Machines Corp. Microsoft and MS-DOS are registered trademarks of Microsoft Corporation. 
Apple and Macintosh are registered trademarks of Apple Computer Inc. VAX and VMS are registered 
trademarks of Digital Equipment Corporation. Brief is a registered trademark of Underware Inc. Psion 
PLC acknowledges that some other names referred to are registered trademarks. 


6102 0016 03 


CONTENTS 


1 Introduction.............scccsccssscrsssrseesscersseeseeceseessseeseessessseesseesseesseesseesseessesscessceescesseeesessseseseesoeees 1-1 
Basic-concepts a2) Sn iedstiis tien letshegtad eta iedetedindeentectshdiad om ietsientan ied teedomeitetes 1-3 
Classesi si tesvidiesivansaisvedte sities eaten ea er oleae beset aanneaeeSy 1-3 
OBject Creat On x. 24.05. t0es Ah ail eistect at aitee thnk aie ditaetas eit at a aie 1-3 
Comiponent-objects acc. .senicacisier di atinivaiinlenias aim cmevganeieie 1-3 
OBject destruct On’ ye crs cor sep adeceeeeeces ze veloee ce oeees hte adapaveg bees bt scenanh feet ede potegenepeentites 1-4 
Cates ories .acfeiecayacltegscetegigaetbeuiy esd ahs ces edipdasd canneehbadipduel caoydehbgtbdust cgueassbagepdee eae 1-4 
Category handles and category NUMDETS .............eeeeeeseeesseeeeneeceseeeesaeeesseereneeeeee 1-5 
Message: passin yi. o.vin sri diskite, sites high i niai rain teie aii aee lees 1-5 
Notation and CONVENTIONS ...........eseeeeeeecsscessseecsseeceseeeesseecsscecssceceeecesaeeesseecsaeecsseeeeneeeenaes 1-6 
Category numbers: s..2.7.5cc.c.gyasbedyieibessyaeeben pies decsvaeibedipiasledbyheibege yous beheaesbaspaeh ben aeey 1-6 
CASS MAMeS 5.02.62 oxb veut ovav sue aah caiuteses taut seh sallow saint dou vauet ot sGtna Distant Werem editors statements 1-6 
Class numbers i3.s.ie2 iatisevairates avg ial eacavai nei asisaiel Gummdaeteioas 1-6 
Method attics. ses: exec teeeedecstegelore deus eat tegatana ledet hte dale erecat ete dadesem pedal ty dotegenvrestetes 1-6 
Method function Names ...........eecesseceeseecssceceseecsseeeesseecsaeecsseeceseeeesaeeesaeesseessneeeesaes 1-6 
Messages and message NUMDETS ............ceseccesseeesseecsseecsscecesaeeesaeecsaeecseeceseeeesaeessaeers 1-6 
Object handles 23. .:3.th:eaviseids ain ev hi eae hi ei avi aed 1-6 
Method function prototypes ............cccessccceessscceeeseeeeeceeneeeeeseeeeceesaeeeseenaeeeseeneeeeesneeeess 1-7 
Class :dia stam sis0c3 381 cee Bivieleepteek Given begin epee bese Giyaidesiaet eeyaitesientens 1-7 
PrOSrammMmins OPtONS 1562: ccs cdetecey sis ocbetest see caad oes Met ah aie taal ae Glad od at ae aN cleat aeons 1-8 
Using existing object Libraries... eee eescecsscecesceeesseecsseecsseeceseeeesaeecsaeerseessseeeesaes 1-8 
Defining application-specific Classes .........cseesecesseecesseecsneecseeceseeeesseecsaeecsaeessseeeesaes 1-8 
Creating and using a DYL.... eee eesecsnceceseeceseeeesaeecsseecsseeceseeeesaeecsaeersaeessneeeesaes 1-8 
NS 19 EL WiIMA 5.0: Sasi shasta shied sees ate hes one Mat ook Ua ak ote atta tata cau seated ante caveats atte y 1-8 
The basic HWIM application component Objects ...........eseeeseeceseeeeseeeeeecsneecsneeeesaeeesaeers 1-9 
The application Manager ..........eeseesecceseeceseeeseecsseecssceceseecesaeeesaeecsaeecseesseeesseeeesaes 1-9 
RESOUICES wu. seitesiei atheist AS Sheesh pease RGU iba ang balgetareeehey 1-10 
The window server ObjeCt 2: sb esos cas teed seh shtd eke tuetsche shad oats tees cake stleddsbint ccbested sh denteets 1-10 
The command manager ...........cescceseccesseecsscecsseecsseeeesaeecsseecsseeceseeeesaeecsaeesseessseeeesaes 1-10 
THe Clem t: Wind Ow se sad: ccecscad. hates Ques cette dakar s Mes date sey ates Suge eatbees datewersvatedepslttivestoaes 1-10 
THE On GNC ioc sieccescesd caeedaes ceaededbedseiavvedandesbedandevvesardesbederdeseilerdevbiderdesvigetdesbudendevsddeabens 1-11 
Menus Dat cscs et ext a ited aha eh ti a es ee al Lit nt at at eat na Sat 1-11 
Dials ces cessciccsecdaacedscees ceatedas coandesuevacddaccsaadaevevadadeucvaa deauevaadéveisaacess aadedevdeaacenvetaea tes 1-11 
aN 0) 0) 8Cer:18 (0) 0) (6) Os (oh) ee 1-12 
The required files’ sii...cisetccssacktccsedckh ceevicanccovdchncdeveceoeeuveceh eueydcboedundevbedendepuesandesesavengdavdvataes 1-12 
Cates ory Tle asics ket ece ties feb ea Niataect Su Alta e Sict ie Roh Let Soe Rt ot ie oe A ist Bak 1-12 
Source file8s; oi. fastesvsite ei neaveei fat dai eavdeieiiel dei os vdgioioss dos wanes Muara aint 1-14 
Method: funrcti otis: ises cere, staceveracethtng stun cseteceteat potter edb tet bvny scanned beet htes atone eebeetedes 1-14 
Malt. :isc.sseiiynitesiietiinkiebratainebeniialanehaipial eeaaieiel dite ae 1-15 
Application start-Up ............cesecssecssceceseeceseceesenecsscecsscecseecnsseesasecanecsaeeceseeceaseesaneesaners 1-16 
Resource externals files: :.0::.chi en eehi oeeiionr i nvidia aie vdinii aida ave 1-16 
Application Resource file 0.0... ceeeeecessseecsseecsscecesseeesaeecsseecsseeceaeeesaeecsaeessaeessseeeesaes 1-16 
System resource file ...3..sc.tce.ssevs ccspeeh besapdeskcdepdaineghs Qesbeeipaaibesepdebiegupeesvesd seit ceiyosbegetees 1-17 
Miscellaneous: files si. s.ttsccievied iui tect aeesttond Mie eect batons tat enchants Hatem lant ant ewes 1-17 
We Oishii cs eves eet hs een as setae Pa en ate ete aen ues eae ate a 1-17 
PCG sHES StS istrefi cetl a select terest iate eM etal tps ti aatet rou dede basal dl odes Mls tatltds on, 1-17 


OBJECT ORIENTED PROGRAMMING GUIDE 


2 Building an Object Oriented Application .................ccssssscsssssscssscseessscsecsssccessssssessssseseesseees 2-1 

An example application.............::ccccesccceesseceeeseneeeceseneeeceeeeceeeeaeeecsenneeeeeeaeeeeeenaeeeeeenneeeenees 2-2 

The-example Source sccccteeecsacer ecg egestas vated adeeb wee eased ned ebb devien bebe oobeeviae ets 2-2 

Building the example application 0.0.00... ceeceeseesseecenceceseeeesseessseecsacesseeseseeeesaeessaeers 2-5 

3 Building a Dynamic Library...............ccsscccsssccssssssscsssecsssecsssssssssesssssenssscsssscscsssscsssssssseneseonese 3-1 

AM example DY Visi sssccievestces shea hegoedevs coh iuen Soebeaus bc otanea He ovansacashechadzuyecuusaub aces Sasseoevoasesus aeteey 3-1 

The example sourcevishs cst eties ste davis iva Aundiiibits aii datiie davdias a disses 3-1 

Building the example DY Lis .cissseis ct cecescesscivese ctvus cots ebeeseh Stuve fesgekescen stnbe subsebvoaes sveedvlse 3-2 

Usins the example: DYL s.4c:sicsssicisdasassgsazesadahosinas aoasndashesesanassondauseaeaeaianeandaneastest 3-3 

A DYL that supplies the ROOT Class 0.0... ee ceeeseecsseeceseeeeseeecsaeecsaeesseesssaeeesaeecsaeesseeeees 3-4 

Building DYLs into an application .......... eee eeeeeeseeecesneecseecsseecssceeesaeecsaeesseessseeeesaeessaeers 3-5 

DYIb ddd-fle lists: sc. s2trie etal ee Paha Peak enh aten een hel 3-5 

Accessing a builtin: DY Lig. si siete isuesids sahepeoneaeteaticteaeeheauetenbactupeetaaisteaiaaisgeatesyecenteas 3-5 

4 An HWIM Example - Hello World.................ccsssccssssssccsscscecssccsescsscccessscsessssccsesssscsesssscseessoees 4-1 

THE Cate SOry Ales, el weesees ent odes becbece eset cane eopetedasah dus Sestete date toate beet dedatenenep abs tedoreterte eds 4-3 

The resourcé:externals fle: c12.2..5cc.5s:00 case ees beeiyces ig desdecovenbiQigdustesayoesbezerasibesupbesecneysuanessete 4-3 

PRE TESOULCE Me drs sas Seesece cen hed cect ane eels te ces ties ses Cee oabvtgict ont ayn tests eet ote auch oiuboss ae esteates 4-4 

The-source:cod@sc:e.t34 hi sarentetien tavaniatdisordn atau terginatasicovein ni asenineas 4-4 

Buildings thesappl ata ons. siec cesses cag soe eauue tes oced eas est otit-cesaues sued viet onyocay pant sat sautedeppectiett ey, ss 4-6 

5 Commands and Command MenuG............cscccrssercsrsserssersserssersserssesscessserssssessssssesessseeeseees 5-1 

The Command Manager .2-s. 56.5 ics cess2edes002 Fes cove ta ves ces Suse redsteSesoes dean suvadeSecovs des pevitdedorovsiia Phe 5-1 

Addins commarid OptlOns?ss.:..sc.sesssavksesieshdesapeenesieteuhseeaeeana sutenddaanpsasaaiboesigaaseeanasuicagses 5-2 

Series: 3a shifted accelerators .......c.sccsccseceehseeudvesesebersiosessontonsaerssodvodsensagnerssecussostesenens 5-5 

Series 3a command Option QrOUPING...........: ee eseceeseecesseecseeceseeeeseeeesaeecsaeerseeesseeeesaes 5-6 

Sharing method function Code ........ cee eeeeeesseessneessscecsseecsseecesaeeesaeecsaeecseeceneeeesaeeesaeeseaeers 5-6 

Changing the text of am OptiOn ........ eee eeeeeeseeceseeeeseeeesseessseecsseeceeecesaeeesaeecsaeesseeeeteeeesaes 5-7 

Disabling. menu: Opt On. o..s6.5éss-cchsccugued Sestceui cies doeks aviewes se eeqedbacteesh ccvsstent sot eoubonstorenedcbeosbed 5-9 
Changing the number of options in 2 MCNU ......... ee eeeeeeeeeeeseseeceseeeesaeecsseecsaeecsseeeesaeeesaeers 5-10 
Displaying a status WINdOW...........ceeseesseessneeesseecesceeesseecsseecseecsseecesaeeesaeecsaeesseeeeneeeenaes 5-11 
Application-specific initialisation ..........eeceeeeeesneecseeeseecseecssceceeeeeseeesaeecsaeecseessneeesaeens 5-12 
Replacin ga ment Dates. sisish.ctseust ciate euskal g 5-12 
Accelerators for replacement menu bal ............ceeeeeesseecsseeeseeeceseeeeseeecsaeesseessneeeesaes 5-13 
SUBDMEN US sai bs coe liesd og sevesces Staas cov Pees ech saute Covbaube Cob souks Covboibetebseres Pevbchbededscnus east abedetstaeedy 5-14 
Shutdown Messages. nics sieciusdavseestsaeceuslatiedetiataceasladncstisaaentamodstietaadetateestiansendatedel 5-14 

GO: WINGOWS 6. cuvasssccesasssscesondsovecndscoeveddsosesoassusavosdensasossensavesdundedessunsesesdnndesesenodesesdeasesosnsavesecseadeseenea 6-1 

Wiridow usage rm WIM cissecccic tes tevgeuet bev sinh sep eeuteeystotentppeutesesstes ests anteds pote reupeesteneiens 6-2 

The draw/redraw mechanism .............:cceescccesecsseceeseeeesseecsseecsscecsseecesaeeesaeecsaeessneeseteeeesaes 6-2 

Drawin ssa: WindOw 2. costes rata bat elie ast eked Red nai ait tl een estates 6-3 

Lodger: windows s):2cnsiuihnehiineiinnehsl ab ei ards aie hil ei avi eee 6-4 

RESIZINS A: WITLCOW 0. oo eso sup sat edeesee) ents edetedepsget ant ete levestceboasdednteaipbeetegdensntpdent-cevededestprastig 6-4 

Window emphasis. s:y:..2c,25.500yshbesietest cesphei oldie duel aeyhehbeshbdeel euphbnite hed die aehibenieane. 6-5 

T DIALOGS wcvisccveusdsccvsacdssuvededsouvessisensadsdeagDhossebedesesenstsoedenciededensisestessdesedvnssesdvaaseseivenseosieasdesessansoesses 7-1 

Wsitig dialog BOXES :i:iscisicteitatiesathotaetalissateathonietaoesiathonistaocetdotendateectdationtaticasttas 7-2 

Default dialog behaviour ............ccccccceesccecesssceceeeeseeeecesneeeeseaeeeceseneeecseeeeesseaeeeseeaees 7-2 

Dialogs and resource: files: sc Jerieth din Mahi laet Atenas bases ts 7-3 

Teaunichimg: ai dial Og: cesz eed os sive cavtan csta eh yeh 2th sects Seen teeta oss tacos feeiteesseta deen ea deeseeds Seondeass 7-4 


CONTENTS 


Simple: dial ows ‘creche seessb aschistesztees cay stbetee steko cudidinetova voxs cus adivs ieapeve eobsdeveteniees Savseebess 7-5 
Dynamically initialised dialogs... ceeeeeseeesseeesseesssceceseeeesaeeesaeecsaeecsacesseeseneeeesaes 7-5 
Further dynamic imitialisation ............cccecccccsssccceeeencecceseneeeeeeeeecesnneeecssneeeeesseeessenees 7-7 
Retrieving dialog results: s.:¢. s/.ccscci des deidspeehe se uae desicetesodeaphaashsvdasohep haath setesv heeds AA 7-9 
Dialogs with and without "WAIT"... cee eeeeseeceseeeeseecseecseeceseeeesaeecsaeesseeesseeeesaes 7-9 
Controlling the width of a dialog ....... ee eeseseseecsseeeeseecseecsseecsseeeesseecsaeesseessneeessaes 7-9 
SUBAIAaLG SS. .5 555 shat Hess eh LSed oh ee Gh Sad abs Sesh Shag eect oTiebink ceeded a died eos culodes 7-11 
8 Dialog Controls ...............sssccssssssccsscscecssscscessscsccessscccessssssesssscccesssscssesssscssssssscsesssscssessssesessssocsoes 8-1 
TOXt WINdOWS 0: c.cic.ccessduadccseashd cevgdsatecevisetccsgdcatecovdshsccovdcen cdevicencdoudcancdevadhe sda vdcenedenecnaceuucess 8-2 
Und tialiSati Of s.3 222 cee vhics ceeckies See cteessehhiah eee hehehe eect etaae Leche Se ehacas este ae aae te anes 8-3 
Seth Ge sssssslhei ereheisty Ga tesieiie Saree astai oni sesegi ist Mei oavaieesl Go dan eaeeren 8-4 
SCHIST Oe ale esac as oceg oak Pretty sosegeat east tenotes sah Petath te, odatne esto as edetach Pela rt natal None aat asi 8-4 
ChoiGe LSts 30... scccessceiedesdesigdestesaadestagseduatageedvalccoplsatesondseledevdces cds dene cdovdeabcdevcanendevsagendevices 8-5 
Ti tialiSati Off vic cee ese eect ce tees ide cabs ea ntec deme dace van chbebeata ese ses biveum deedlecscdeeted arent te 8-5 
Seth Osinb seis od eerste eee ad coi. eee ee 8-6 
WS CHISTING2 3o5 sles catch dah ca teae eahe de vata esta testa hoveses ate bastadevedes sing buet awit etetedvehigtats detetoves iantanse 8-6 
Push buttons and action lists ...........ccccscccceesscceeseseceeeeeseeeeeeeeeeeeeseeeeeseaeeeceeeneeeeseneeeeeseneeeess 8-7 
Ini tialiSati Gs: 0 oe. tics cet eie ee eee tester alte tite ae led teen Geeta dae ed ee als 8-8 
Setting and sensi G voiiss5) deveisisies eaveniei aa avaniai aa rareatasereiiot eater 8-9 
Edit BOXES 2 i oii 0en ctiscass  Biveleatbicocelec. fede cecedeldedesevtve dated. dedauesseceditededestdcastaleds tolsebuceee do’ 8-9 
Initialisati onss.cccc.tcgeeiessciades tediedestecaedestedevasat sdavdial cdevdsetecavdee ledepiaas cabs dead egevdeabeasvecabees 8-9 
DENG. i5 vs Rot Seth Od ks kd malt ea, edict SS oh, A Cael Sod aio, oat Lod Ahan cad Se 8-10 
SENSI Ses} sei tvhel er eiee ai ae ee ee eis 8-10 
LONG numeric Cito «2.0... eeecceeeececeesenceeceeenececeeaneeceseneeecesneeeeneaeeeeeseaeeeeseeeeesetaeeeeeenees 8-10 
Tnitialisati onie.ssceccescacecesveciecesscdieveane davdetcdeveeadadavdvas (devasetccavdcatedevicenedevdcae cdovacsdedsveceeees 8-11 
DEMING sis SNCs 6 Rita tin Galatta eat a tis Ge lt a el tls erin at ad ol 8-11 
SONSING seo ios oni eaitav girs i ciineevdgass tase se veel stat ates cesvdei seston mavdnerauamaveseyaiaeie 8-11 
Integer Mumeric Cd tor ys... csscie.socee saa state vegacet tes lop eet ccv gesntieaty eout seeodetestgvanteespetebantpsanteven th 8-11 
Tnitialisati on i..ecccscgeuiess ccsadesdesieienncesadestedevdsabadonduededevdaescdevdae Ueduvdcab cevdcab cdevdcabceevecdees 8-12 
DENG So et At ee sttd el ceed Met ee SORA ae a eA et au a A ea cal SC ated Sa 8-12 
SENSI Ge53,0 Bei eieesied. hier ei viei oui sae es vigd nite ea vagiese ro eave aire? 8-12 
WORD numeric CIOL «2.2... eeeeecceeeeceeeesecceeeeeneeeceeeneeecesnececesaeeceeseaeeeceenaeeeeseeeeessneeeeeess 8-12 
Tnitialisati Ons: s.ccc.: catsicsuesiedeascdeaveaus doedestcdevesabadovdsas (dsvashtecovdcabedevachbedevdcas cdevachoedevecdbees 8-13 
CPIM Ges Vi cacti os alta Abate itt Bt a Alia lid ee a Bll A, lid eM t an Aly 8-13 
SENSING 3.343): aiiravgiitet Gumaydgisteet Gai seevigi sist dei caval iest ans maraeat dames au? 8-13 
Rance TumerntcieditOn sss. deci fovea state eves enhe vetored vadethte gatos Meus cot htegadateve avetbee daleateeeeatyaes 8-13 
Initialisati oni. .acc.ccgeeiesscdedestedeeiestaseedssbedsedsatsduedval cdevdsetecsvdceledepieabcduy dead cdevdcabeesveaevens 8-14 
DCL Gl i vise Ae eet oe sh ee Sh ch et elt, eld Saks, tn taal Sad tit, ome act fal ol ad oe 8-14 
SENSING: s3.o} eee nevi oe ihe ai pene a eon a a eee 8-14 
Floating POmmt CQItOL: 2) sc cckesestecedstacesle bests cepadadsip beets dedaceoahy best devededeatg beste deportes wate benteven eds 8-15 
Initialisati Oni .:.sccc..sscecessececeasccieveand sovdesncdeeesanadavivas cdevisenccavdcaneduvseancdevicancdevedsocesvecaeees 8-15 
CEM Sei sk se het ee lta tee ee alte Dts oe lat at Sd et Rt i fe, ih aed ahi 8-15 
SENSING sid. ce fei eaveassieteiineevgiast a tesso nee edeistanates cesvdeisvasiasoi Pavdnersrtianaveeiey auntie 8-16 
Date/tim@ editor oc eeleledenaescevesacteccenevngsaetetedetsnevsesinsore poansacegeiet oes dolste dogs invecessansevsgsaasevedent 8-16 
Tmitialisati Onis. s.cccccccteiess cccadestedeedenncdeadestadeedsad cdonduadedevdaescdvvdae leduvicab cevydeadcdevdcabcesvechedee 8-16 
DCU Ges Sa vk A etsee she es cache Mee cee Stal ali al Seal ut es ct ah ee cau SNC ated See 8-17 
SENSING ey scHiecieiv hei eiy eles ai ee sae eee veo ene ieee 8-18 
Hatitude/Loneitude dior: 2.5 cc.cedssicesieivetecedasadeiie beste dedecteshy bake dedecea ests texte deveceseetpataverers 8-18 
Tnitialisati Ons: sccccecaiesuecieseascaeateabecaedesidevesadadovisas cdevisbtecopicarcdevienbedovicanedevachbedveceeees 8-18 
SEEING sh ses si ache tee cae Peltha ch AO th oR 8 cecal Mt a et a Rll ON, it Ni a ll 8-19 
SENSING. sécdsshei ees, Havieantet eu mesvdeiseme i egso seven stat tsi casvdeisias totes wander umes aaatee 8-19 
Pile Name Edits. Pevedlodedeuseideveda tle etegeddecuedddetuee ed deead ede gelaesdddeiatecddelaue igbiete Blunts deans evedest 8-19 
Trnitialisati on ss. sccc.tcgcsiestgaiedes vedeeiestedandestadavieabadovduadsdspdaetcduvise ledepdeabeeevdcadesevdcabedevecaeees 8-20 
DEIN Gs Ath A et Se sth a oe Batok Det eth d lad Sekt Sn tael Sod tok, A al hh att tek 8-20 
SENSIN Oss ).v)i eevee. vind) serene vai pane al a eevee ee Rie 8-20 
File name: CHOICE MISE. oicccscseiesecsatadececedassevssdedecucetessentedadeducecesbantsdevedacedessintetedadsdedevsensatensd’ 8-21 
Initialisati on st. ccccescscecesseciecesscteveaut sovdeancdevesanadavdvas cesvisetecavdcaresuvicnnccevicancdevechocdsvecseces 8-21 
DCN Get ahs Arte GA Ate Seale hat te RON eA Ge SM RR RO, a A hi oat a ley 8-22 
SCMSIN Sse. ssdeceihes pea va leet eae eevdaieets edie deevdelea i dor eve lpinanevaiye baie veesey ine 8-22 


iii 


OBJECT ORIENTED PROGRAMMING GUIDE 


DACHVE. ODJOCHS:s.c.casssscevondsovecnnssesevesdsasesoesensevossensasdesensadosdansedesseneedessandesesenstesesdeasesosnsavesocsoasessenea 9-1 
Active objects and asynchronous requests ..........:::sscccescccesseeescesseeceseecssaeessaeeesseessseeeesaes 9-1 
Active object: priorities::.:2:..:8.iecrc dint ih esi baein redid pb aie haiyinee ds 9-2 
Application respOmsivVeness............::ccsscccsseccesseesseecseeceeecsscecesaeeesaeecsaeecseessneesssaeeesaes 9-2 
Background processing...........scccsscccssscessseecsscecsscecsseeeesaeecsaeecseecssaeeesseeesaeesseeesseeeesaes 9-2 
GPOPS so esccted Seccce i ootea sobs eek oes beeteded otesistesaccts decctenstesbeenedadetesstesdentedusesenedes beecodedasetodeeees 9-3 
A. SIMPLE TM OL si. 3.05 avecieieescdledes te caeieaucdagucabidandead cdsvesanecovdal cdsyasetedapicas cdevscneccevacad edevecsoae ee 9-3 
10 Error Handling and Error Recove rJy...............sccsscccssscsssscsssecssscsssecsssecssssssssssesesesssscssssosees 10-1 
Errors during initialisation .............cccccesecccceessceeesescecesneeeceeneeeecseeeeeeseaeeeeneeeeseeeeeesseeeeess 10-1 
Getieralerror: TECOVELY Fs .0.. ces cick sckeoes Sees gabe cet iss Savi guekh chug des Havhouche sede deasenbeuahseuseousdeca ceed egebenn 10-2 
The roll-back principle .0...........ccccsccceesecceeeesneceeeeeeeeecescecesseaeeeeesnaeeesecsseeeessnaeeeseeaees 10-2 
Roll-back for component Objects ............::cceeessceeeeeneeeceeseeeeeeeeeeceenneeececseeeeesesaeeeeteanees 10-3 
Other resources in an object's PrOPerty...........cceeeecceeseseceeeeeceeceeneeeeeseeeeessneeeesseeeeees 10-4 
Using the: CLEANUP Wistes cess cccisecectea ices tetdeetnecnes chcceebeekac bhevslcceibeseesseaniosententocuensntes 10-5 
Interactions with system COde ...........::cccceesccceessnceeeeseeeeeeeeeeeeeseeeecesneeeeeseaeeeeeseaeeeseenneeeeneas 10-5 
11 File-based Applications.................csccscccsssssscssccsccsscssccssscssessscsseesssssesssscscessscessesssscessssscssesens 11-1 
Start-up IMitlalisati OM? 2.2305. ceet sel et sce cel coke eons ceed ede ioee cas ode divieceestntoat WM eceentte dete 11-1 
Opening and creating files. i120: 3.0s05. cesses essicda covescancceseda covedcneceevdeda cevvccaasessiagaseseiandeestieseed 11-2 
Switch files Messages .0.........c:cccceeeseceeseceeeesseeeeeeeneeeceesaeeeeeseaeeecseeeceeeeeeeeesneeeeesneeeess 11-3 
Saving PIES vecccvecdccssseadceseddoeesvisvecs das devavievveseedes cede rievedsedeaevderdcvigaadeveudladevscdeadevtectebes ads 11-3 
Application termination............:.ccsccceeeesceceeseceeeeeeeeeeeaeeeceseneeeceseeecsaeeeceenaeeeeeeneeeeeneas 11-3 
Shutdown Messages .:ses.eceiseccbicsessrcedescciceseasescedesettccseaaaa coda sete cebvecnucsseacauensvaeea cebeeenvens 11-3 
12. Edit: Wind OWS i sssccseccssectecsssecteccssoctesssccsansssocsaassoecoansssensanssoeveadcsoautasdsousdesesonsissesonsseseseacscecsunase’ 12-1 
Introduction to EDWIN ............cccccccceseseeeecccceceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeees® 12-1 
Dialogs and edit windows contrasted ............c::cccssesccceeeseeeeeeeneeeceseeeeeceseeeeeesaeeeeseaees 12-1 
The NOTES example program .......... eee eeeesccceesnececesseeecessaeeecessneeecesseeesessaeeesesaaees 12-2 
The HelloWorld program for edit WiNdOWS............:::cccsssccceenseeeeeneeeeeeeeeeeeenaeeesneeeeeeees 12-2 
The EHELLO category file’. isis s.c; ensohebidaedies Wadi sidap ies Aasteadedinn Aacheaiderdice Anis 12-3 
Initialisation code in EHELLO ...0....... ccc cceceeceeeeeeesseseeeessessneceeeeeeeeeeeeeeeeeeeeeeeeeceeeeeeeeees 12-4 
Other:code:in: FHBLL Oo sc5s.cenkess Seance ieeduidewasosncaiiecedacaboasasieteekasteooatenieeeeneseonetdsaetote 12-5 
Simple: useOf BIO WIUIN foci oi25 eh spke cis sateen se POiSeSh sh cubed Soke bu Sooetc Shane ba dbasfoegh Sih ba eaatoessathy 12-5 
Initialising an instance Of EDWIN...........:.eseeseesseeceseecesceceseeeneeeesaeecsaeecsaeeeseeeneeenes 12-5 
The landlord of the edit Window ..........cccccccccccccccccccececececeeeeeeeeeeeeeeeeeeeeeeeceeeeeeeeeeeeeess 12-6 
The IN_EDWIN and IN_EDWIN_X data structs.................cccccseesssesseesesessesseseseeeseees 12-6 
The Ig_set_id_pos method ...........eeccceeeeccceeesneceeeenceeeeeeeeecesnneeecsseeceeseaeeeessnneeeeseeeeess 12-7 
Other edit window initialisation flags ......... ee eee eeeseeeeeeeneesseecseeceseeceeeeeseeeesaeessaeers 12-7 
A note on the CONTENTS field in the IN-EDWIN struct ..................::::::sseesseeeeeeeee 12-8 
Values of special characters in the text ..........eeseescesescecsseceseeessaeceaeecsaeesseeesneeeeaeees 12-9 
A note on the MAXLEN field in the IN_EDWIN struct...................ccccssseeeesseeeeeeeeeees 12-9 
The wn_sense method .................cccccseeeeeseesssesseseeeeesseseesstsessesssessesesessestessssstssseseeeeeees 12-9 
The wn. Set Methods 2.523 ce.sssetsocseed Qesseeteoesetd cexeaetecebend Buyeacteoubedd eueesetacteeged uote 12-10 
The: wiikey: methods)ssisccivsiseissstesioccesdisissntesieccautassoestasiecasitaadeeatesiacasbiasvesdagiceusdaaioess 12-10 
The wn_emphasise method ...............ccesecsccessseceeeeeneeeeeenceeeeeeeneeecesnneeecesneeecessaeeeeeeaees 12-11 
The wn_ draw method ..........cccccccccccesseeecccccccceesecccccsssceusseecccsssseuesseecccsssseuuccesssseeeneess 12-11 
Additional EDWIN methods. ..................cccceeeeesseeeceeeeeccscsssssssssssseeceeeeeeeceeeeeeeseeeeeeeeees 12-11 
The ew_imsert method ..............cccccsssssssssccccecesssvessvccsenesccceccesesssvssscccceeensssvscesscesensnees 12-11 
The ew_find method... cece cccceseeeccccccccceesseccccssseuccceccssseeeseeccesssseeueeecsesssseeeneess 12-11 
The-ewareplace:- methods jis sess sg5s avisees Asa sees ousebies fesweeesovsaeees sasesdasovntiees ua ieeasse teers seans 12-12 
The:éw replace <clip:m eth Od sss, ct seccsdevncivedel ctenenephcies fck cdeesrebtacbe dul cdeeeteidcevedtedeeeentss 12-12 
The-ews paste clip method tess ctis2.sisiisestasiacduadavpestacianeendawscestartaseendaute tea areandaeeetatt 12-13 
The ew_evaltiate method .......ccccccccccccccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeess 12-13 
The ew_set Method .................ccccceseeeessessssssssssessseseessessesseessteetsesseesetseseessseeseseseeeeeseees 12-13 
Thesew= Sensemethod es ccccis2hs$28c2ececets cbubectathcosesstueleetetunsselacbeddateduessete lew bdvatuesseesagesss 12-14 


CONTENTS 


The concept of document offset .............:cesccceeseseceeeeseeceeeeeeceeeeneeeeseneeeeenseeeceeseeeeeees 12-14 
Allowed values of document offset..........ccccesseseseecsseeceseecesseeesseecsaeecseesssaeeesatessaeers 12-14 
The EDWIN.CHANGE property ............c::ceeessseceessceecesseceeseseeeseesaeeecessaeeesesseeeseenas 12-15 
Read-only edit boxes and the ew_readonly method ......... eee eeeeeesneeeeeeeneeeeneeeeneeeeee 12-15 
The ew: leave method .;cvicdcscrcscecchnosvesgesenvsachsatsnetehoriiadesasieetesessvacebacwbade ses wecusecevecee 12-16 
Controlling the layout and formatting .0...... ee eee eeeeseeeeeneeceseeceseeeeeeeesaeessaeecsaeesseeseeeees 12-16 
An introduction to SCRLAY ............cesecccesscesesceceseeeesesecsscecssceceeeeesaseesanersaeeseneeeesars 12-17 
SCRLAY. structure definitions 3 -.5.3scc st is.dptedesssetinaseets iota deetesavedasedeipieseersede Sedeeedase 12-17 
Example: changing visibility of special Characters ..........c:eeseceseecsseeeeseceeeeeeseeeesneers 12-19 
Default values of SCRLAY_STYLE in edit Windows..............ccccccccccccccccececeeeeeeeeeeeees 12-19 
Changing from the default layout style 0.0... eee eeeeeceeceeneeeseeceseecsseeseseesseeseteeeesaes 12-20 
An introduction to: SGRIMGs:. 3.35. e.siusstiis cas iess ices Mat asiertedice st hnasede eitnandes 12-20 
SCRIMG structure definitions 0.0... ee eeeceeseecssneeesneecsseecseeceseeeesaeessaeesseeseseeeesaeeees 12-21 
Example: changing the width of the text CULSOD .............ccescscceeeeeeeeeeneeeeteeeeeeeneeeeeeees 12-22 
Changing the font used by an editor ........ eee eeeeeeeeeesneeeseecsceceneeeesaeessaeersaeesssaeensaes 12-22 
The ew_sense_size and ew_set_size Methods..............cccccceceseeeeeessecceeeeceeeeeeeeeeeeeeeeeees 12-23 
Changing the paragraph margins ...........ceseeescccsseceesceecesseecseeceseeeesaeecsaeerseessseeessaes 12-23 
Notifying SCRIMG of a change in style... eeeeseeeceecssneeesneeseaeecssceseseesseeessaeeesaes 12-23 
Initialising the SCRIMG_WIN data structure ......... eee eeeeeeeseeeeeeeeeneenseerseeesaeeeees 12-24 
Direct interaction with document Objects ...........ceeeeesseeesseceneecsseeesseecesaeessaeecaeessaeesseeenee 12-25 
Setting text directly into the document Object......... ee eeceesseesseeesneeesneeceeesseeeeseeeesaes 12-25 
Dual variables at the EDWIN and SCRIMG levels ..........ccceeeeeseeeseeseneeeeneeeeseeeneeeeee 12-26 
Adjusting the Cursor POSitiONn ..........eecceeeseesseeceseecsseeeesseecsseecsseecsseeeesaeessaeessaeeesseeeesaes 12-26 
Logical cursor movement and physical cursor MOVEMENL............ceeeeeeseeeeteeeeteeeeeeeeee 12-26 
Notifying SCRIMG of a change in document content .......... ce eeeeeeseeeeeeereeeeneeeeneeeees 12-27 
Notifying SCRIMG of a Jocal change in document content..........eeeeseeeeseeeeereeeeneees 12-28 
When there is a change of content and a change in cursor POSItiON.........eeeeeeeeeeeeees 12-29 
The SCRLAY_DOC data Structure ........000ccccccccccccccccecceeeeeeeeeeeeeeeeeceeeeeeeeeeeeeeeeeeeeeeeees 12-30 
The five soft method numbers in SCRLAY_DOC..........ccccccccccccccccccccccccccceeeeeeeeeeeeeees 12-30 
The SENSECHARS call-back ...........::csccsesscsesseveceeseoteseosensscensuobensotevsssensvonesonveceenecs 12-30 
Structure of SCRLAY font width tables... eee eeeeessecsseeseseeesseeeeseeeesaeecseesseeeees 12-32 
The FOPRARST callbacks. (ssscictin abscess deatiess Aicene Matias Aaoteas bation Ano eas Aerie Mattes 12-32 
The: ENQPAGE Call backs: sos s2c05 505 cess covschbeseh avis eas csbeced Sines pevietes ced stubs nettesbsied sabes tates 12-32 
The: SENSEPDATA ‘eall- back: v2.si:.cccesascsssadssarestactacdbidssttsstegeassondaseeniegtaseendaseasteaisy 12-32 
The SENSEPLABEL call-back.............s:c:cscsccseceedesrerensenonocessonersssenensenoneeesneneesserenes 12-33 
Some examples of edit-like WindOWS ...........::ccsssccsseccesseecsneeceseecsseeeesaeeesaeecsaeessteeeesaeessaeers 12-34 
General comments on creating edit-like WINdOWS ...........cecceeceesseeeeeeeseneeseeeeeeeeeeeeeee 12-34 
The:st-redraw methods: sccsscs sic cancaaieestag sat sateaatedataa sa ceate sanadeiea sae cate aaiceeteg an saidasageetea tet 12-35 
The si_emphasize method ........... cc eeeceseseecescecsseeeeseeeesseecsaeecseeceseesesaeecsaeesseeseneeeesaes 12-35 
Lhe stpani Method ss: is -ssiscvcoiess oserteth Aveda ss seep es castes tosntp bak Anfang sespdase cs ateasosnsrdsce postin sy 12-35 
DS: PHAM CAG 055 cases Sonvassecte snsissdandosssctesesasceodocdeateseesenveiossentndeeuserSacsuekesedsnsieSecsushedsesessedecssss sosesenseves 13-1 
PETE PEE VIC We 280 Seda ot ott coe varet oat Wt eevee sats Hit eoenaeet Debit emetecete obit en eecte ae? 13-1 
The basic model of WDR printing ........... ee eeeeeesecescecsneecsseeeesaeeesaeecsacecsaeeseeecesaeeesaeeesaeers 13-1 
Calculation: Of page breaks... oce.see pss, us otsee seg siet as pettee cep eseg MG tthe vestes ah eohe cepsdet ey eas 13-2 
Calculation: of line breaks::::.2.21s:: osetia ei ehisiaiaieiiniewienendiie. 13-2 
Prifiter Units & arian sees leet tes citi ot ti eae lend Ma teal rei os ton Pea anh nla id 13-3 
The difference between INDENT and RIGHT, and between DOWN and HEIGHT... 13-3 
Mareins:and page size... rcc.csgoder sunsessteregoces antiasht ive soceaneg stat ovthzedgens ales evutatens idee ety as 13-3 
The PRINTER class and storage of the ‘Print setup’ dialog settingS.........0.. cess 13-4 
Changing font or font style while printing ........ ee eee eeeeeseeesneeceeeesneeseseeeeeeeneeeesaes 13-4 
The text referenced in a print CleMeNt «0.0.0... ee eee eeeeeeeeseeseeesneecsaeecsseeceseeeteeessaeeesaes 13-5 
Limitations with the WDR_PRINT_KEEP flag ...00.........cceeescceeseeeeeeneeeeeeeeeeeeeenneeeens 13-5 
The need to specify font and style for cach Line... eee eesecseeceneeeeneeceneeeeteeeesaeenaes 13-5 
Useof WDR=PRIN TIDE, 0 ....65 csc iet iad on EN ae te ait ae Memes 13-5 
Using LPRINTER for standard printing PUrpoSes.............ceeeessseeceseceneeceseeeeseeeeseeeesaeeeaes 13-6 
The syntax of the LPR_SENSE_TEXT callback .00.......e cc eeeccceeesseceeeeeneeeeeeeeeeeeneeeeeeees 13-6 
LPRINTER and Word-Wrap .........:::ccccssccccesessceeesseeeecesneeeeeeeaeeecessaeeeseeneeeeeeeneeeeeeneeeees 
Working Out Widths vvibelece ec ciemctecl ech abik eck as aude cchvbuetces authechvatdesequdle usa seks 
Launching the print setup dialog SUite ............eeeeecceesencceeesnceeeeeeeeceenneeeeesaeeeeeetaeeeeaees 
Examples of use of LPRINTER....................055 


Framework of the example applications 


OBJECT ORIENTED PROGRAMMING GUIDE 


Vhe.*Print details’ dialog:.2.. cs sevveits savin fovstebsdenttnns cosadeosdonstebeseiaduvstevSinyesetsdevelenstehedast 13-9 
Startup code and WS_DYN_INIT code ..0........eccccceeesscceeeenceeeeeneeeeeeeseeeeeeeeeeeeeenneeeenees 13-10 
The LPRINTER initialisation code (first example) ............cc::cceesescceeeeeeeeeeseeeeeeeeeees 13-11 
The LPR_SENSE_TEXT method (first example) ............cc::cceseesceceeeeeeeeeeeeeeeeneeeeeeeee 13-12 
Second example: additional initialisation COde€............. cc eeceseeeeseeeeeneeeeneecsteeseeeeeeeeees 13-12 
The three states in printing a two-column display..........ceeeeseeeeseceseesseeeeseeeesaeeeeaeers 13-13 
More details about printing in columns with LPRINTER......... cee eeeeeeeseeeeneeeneeeeneers 13-14 
Advanced uses of LPRINTER - and beyond... eee eeeeeseecesneeeseeeseecseeeeseecesaeeeseeesaeers 13-15 
The LPR_READ method of LPRINTER ............esccesseseeesseetsseeceseeeeseeeesaeessaeessneeeesaee 13-15 
LPRINTER property introduced .0........e ce eeseessecsseeeeseseecscecseeceseeeesaeeesaeessneeeeseeeesaee 13-16 
The default word-wrapping algorithm 00.0.0... eeeesseseeeesseeeseecsaeecsseeceeesseesesaeeesaes 13-17 
Calculating widths of text with variable font... cee eeeeeseecsseeeeseesseeceeeesaeeseneeeees 13-17 
Where printer font width tables come frOM........ eee eeseeesseecneeeeseeeesaeeesaeecsaeeesseseeeeees 13-18 
LPRINTER initialisation - phase ONC............eeseeeseesseecsseecssceceseeeesaeeeaeeesaeessaeesseeees 13-18 
A brief description of the PAGES active object Class.........eeeeeseeeseecsseeceseeeeseeeeeaeers 13-19 
More about the interface to and from PAGES 1.0.0... eeeeeeseeesseeceseeeeeeeeseesseeseneeeesaes 13-20 
A brief description of the WDR ClaSS.0......eeeeeeseeeseeeeseeeesceeesaeeeeaeecsaeessaeessneessnaeeesaes 13-21 
Creating and destroying WDR Objects ..........eeseeeseseseeseseeceseeceseeeesaeessaeecsaeeesseeeesaes 13-23 
Using XPRINTER for print previeW...........:ccesscsesecsscecsseecesseeeseecsaceseseecsseeeesaeecsaeesseeeee 13-24 
The difference between XPRINTER and LPRINTER.........ceeeeseeesseecsseeeseeeeseeessaeers 13-24 
Extended example of print and print preview using XPRINTER........ ce eeeeeeeeeeeeee 13-25 
The category file: s.t:icc. .siaiinesisaisn, pastcestiseigestaiioras ten bigs dt stuamosenaslceeigasoands casters 13-25 
Comimand Mana Serge ctioi cess he sca Zande sh chek bievaeed cab hci biendoehoa shah gh oenboekogh coubs dosed osghewenhs 13-26 
Print:détails dialog iiss. scoiss2.ssisssossttessAsohiassaghssessaetesssenphisscusecaassepbiseeaptaseousasteaea 13-27 
Application initialisation ..........eeeeseccesscesceeesseecseecsscecsseeceeceseeeesaeecsaeecsaeesneeseeeenes 13-28 
XPRINTER subclass initialisation ..0...... cece eeeeseecesneecsneecsseeceseesseesesaeeesaeessaeeesaees 13-29 
The XPRINTER LPR_SENSE_TEXT callback .0... eee eeseeeseecsseecsseecneeeeseeeesaeeesaeers 13-30 
Comments on the differences between XPRINTER and LPRINTER ......... eee 13-31 
WDR: printings Miscellany is: isi2.05 sts avceces divs tvs fiestues.oussusasuoseuisenhesidedyoeress chereubedvesrevackeseusg 13-32 
WDR printing classes pictorial OVETVICW ...........scceeeesceeseeeeseecsscceeeceseeeesaeeesaeecsaeeeaeers 13-32 
The: PDR Classiiocccsssiacecischitoeie ced cx, Senet dasbase Suncast signibussisigocbosubetusndea grslocesdeerecsdeotboteb ite 13-32 
Print preview without XPRINTER............cseccssscecsscecseecsseeceseeceseeeesaeecsesesaeessaeessseeeees 13-33 
Saving and restoring print context from file... ee eeeeeeseeceeessneeeseeeceseeeesaeeesaeenaes 13-34 
14 Link Paste............ccsssscsssssssessssssesssssssesssesssessscsssesssesssesssesssesssesssesssesssesssesssesssssocsssesesessossoess 14-1 
The server side of link paste .............cccecessccceesncceeeenececeseneeeceseeeeessaeeecesnaeeeeseeeeeestaeeeenenees 14-1 
Creating a LINKSV subclass instance .............csccessessseeceseessseeceseeeesaeecsaeecsaeessseeeesaes 14-1 
Declaring link paste server Status .............cceesceeeeseceenceeceeeeceeeeseeeeseeaeeeseaeeeeeeneeeeeeeas 14-2 
Initialising the SYSTEM component Of W_am........ cee eeeeeseceeseeceeeecsneecseeeeeseneeeees 14-4 
The anatomy of a link paste transaction (server-side VieWPOIN1)...........:::ccessceeeeeeeeeeee 14-4 
Example: LINKS Code wsissvccecntstecevepecepedehethyedetensadenestedextenssbeeevts sts becesdeebacnpedscensbens 14-4 
General remarks about link Servers..........eeeeeeseessseeseeessceceseeessceeesaeessaeesaeessaeesseesees 14-5 
Some standard link paste data formats............eeeeeeeseeeseseecsecceseeceseecesaeessaeecsaeeseeesseeenee 14-6 
DF_LINK_TEXT and DF_LINK_PARAS contrasted .............cccccccccccccccccceceeeeeeeeeeeees 14-6 
Word wrap and link paste...........ceececcccesescecesneceeeseneeeceseneeeceeaceeeeseaeeeeesnaeeeeeeneeeeeeeaeas 14-7 
DF. LINK -TABTEXT siissintaicei ti elaine aininl nehitaiei eet 14-7 
The:hierarchy’ Of text types. ve... eccicdse.cavedensntteeeds ctceeouanacedecsscadscblutecscoustsceeuaseecseaends 14-7 
The client side of link paste... lee eeeecescecesneecsneecsscecsseecesaeeesaeecsaeesseecsseeeesaeessaeesseeese 14-8 
Determining whether there is suitable data available .......... ee eeseeeseeeeseeeeseeeeeneeeeee 14-8 
The anatomy of a link paste transaction (client-side VIEWPOINT) ......... ee eeeeseeeeeeeeees 14-8 
Simple example of use of LINKCL..........cesceeseccesseeesneessseecsseeceseeeesseessaeesseeesseeeesaes 14-9 
Special help with link pasting to and from edit WINdOWS ..........ceseeeeeeesneeseeeeeeeeeseeeeeneers 14-10 
The ew_bring_in method of EDWIN ...0..........cccssescceeseceeeeeneeeeeenaeeeeesneeeessseeeeeeseaees 14-10 
Simple example of calling EW_BRING_IN....... ce ceeeeesceesseeceseeeeseeecsaeesneesseeseeeeeee 14-11 
The EWLINKSY Class: oie. oisits se tieosets ten Maa hat aaa Bat see ae ems 14-11 
The three text formats revisited ........ cee eeeeeeseecsseceseecseecececesaeeesaeeceeeesaeesseesseeesee 14-12 
Nat Ve POLIS 5 eet cs Sot ced etsee edd ect teatdevat eDivseat exegeSatedeyaeek ack pds teats aeat ath pte lege eelettes eb eatbaeesed 14-13 
Bimal COMMECNES.$ svose.55:05 teste eecsy ded Biases ee sey Fi eda es a aves gaa yon ipa aavdesk epeekbesheee 14-13 


CONTENTS 


15 HWIM Resource Files.............ssscccsssssssssesssscsseesseessecsscesseesseesseesseesseessessceeseeeseessceeseesseesseesorss 15-1 
Thesapplication: resource: file 5 sc. seceescysaecsdesodepacea state duvecetedsastusedveocehecepsteg ectpeushetegneaepeast ss 15-1 
Resource file location: :..ccsececccsccieseigeedestidsoisabaconbvan sd solsebcdvedatecepishbedesdaneeesddanceesdceodes 15-1 
Loading an application reSOUrCe .......... eee eeseeessceeseeceeeeeseeeesaeeesaeecsaeecssecsseeeseeeenaeeesaes 15-2 
RESOUPCESUTUCTUIES os2..ssiveesaceeuedeavan eiacees ends sueneasvesuessuauei ebay eeu geoeae eiaeeeriteeTabetee 15-2 
THE system TESOULCE Hess, csc sees sced ete tedh; besten betes setts esbevesetes atte sestensbedepodegsuntevssedetodepntate ees 15-2 
Loading a SYSteM FCSOULCE......... ce eeeeceeseeceseeeesseecsseecsseecseecesaeeesaeecsacecseecssaeeesaeessaeers 15-3 
USING “SyStEM TESOUTCES® vo scsee see 50. count tsk ocee shed ceea beet sensatadedlstesteenestted that eh aithediiet set 15-3 
Referencing system resources from an application resource file ...........eeeeeeeeeeeeeees 15-3 
HEL py PESOULCES vo foc esst cis hetsenotatioesdatenarnpoaesa ce eine cuntedeaeteverseeusacteleNeriutoong sens euvendnne cegbeseeany 15-4 
Using Help resources 3.203 cc.ccsstesseiestevaedevtevaniessevandesbesendeseddardevesderdeavidandeousdancersadeadens 15-4 
16 Application Desig. ................scsssccsssscssssssssscsssscsssecssssssssssssssscsssecsssssscssssnsscsssscssssssosssssessones 16-1 
BaSiCsd eS t ius 2e tes seeshis bis icaniceatic apie id buteusustuSbosgsutnesscathovatea sens iestoauaatatensiontaaessanonsiaatss 16-1 
TMG USER ANCELTACE vs .5eeock cou ick dee eentec oh cuske Sentuee jovawek edeuired voce tant oostgetwereteemewelvereeeueienby 16-2 
The engine sic:5:\ vei Metin AsiinrsiialAiesiatiat Asn A wine hi lS ones aarit A chess 16-3 
The Record application... 4 ssc. e.tessebisns onl eavidel tens avlaniivusavsss tans ilsmussp bans iscessevt aed 16-3 
SPCCiACAHOM ::426.3520as.-sdavieasicetasyesdayseisiceteteaadastensiieieteadatea Maieieadesteasiertacedhesctealteck 16-3 
Topslevel view isi iindiacitinieadiac iene cal sind nod cidade dae 16-3 
Pla yiine ss s55. dco iisAscta teh asiest deste Wh sohesteverte Dh Asiossddandett Assteadee det Aaoies ohana atts 16-4 
RECOfdIN 8 x5 ons s2.vseis Avbeveseivsesis Aaceeisdivsnte daaeeis Avrsies Ang esis eres Aiasviadioseevs da et ov 16-4 
Rurinitis for the first times s).c2. setiosetesiees dietassosdsiiees dda tsacabeshicasestaneneaueonssiataoess 16-4 
MG rn Us sco secs Seka caueced cosh iii cuateoen cause venedeneoed gue vecubencedewasduvecubecevves exukecehecev@enenavecehencs 16-4 
DeS1 Stes soccseeesest teeter ss sich thsestaetisiea Siispdezesasid uosioshebbiain sblansseetsasiAsoeabemstissehateiay 16-5 
WHE CHEN t: WIN GOW’ seexs cers cesded scene cuyactvetenadcus cavacuveles ssvuscevscuvedesstukacedssvedessantecedadey 16-6 
Phe Cn BINS vsss2i.6s.cdeidsdeieslacysetasioentestarasedacapeatas aodendasireatectassend setcanten aeseadateostenios 16-7 
Dialogs tacit athystia tinea nal iaoi od wean nie dance neds 16-8 
‘The:applicationiman agers. ::is5..:scasieseAesteiecvapiess ad Asp tisvaphestnsedassesbnapdsettsetestl 16-9 
17 Series 3a Attached Applications ...............ccsccssssssssssesssscssecssecsseeseessessceesesescesscesscessessseesseees 17-1 
Starting the attached. process 2s. cents ena srentieestvenciieds a cecenmeetechee te cneeelscnceiecaicads 17-1 
Initialisation of the attached process.............ccssccceseseeeseceeeeeneeeceecaeeeseeeeeeseaeeeeeeeaeeeeeeneeeesas 17-2 
Termination of the attached Process ............cscccssesseceessceeeeseneeecsaeeeceeneeeecseeeeeeenneeeenaeeeenas 17-2 
18 The Series 3a Automatic Test System...............cscccsscssscssscssccsscseecsscsessssceessssssesssssssesssceseesens 18-1 
THE-ATS Mech ami Sri es sass 2s seschaces Hea taees days Bevadensteescid’s Fevedes Paes des steve leaadu ge dads uve devPanee ded taneds 18-1 

The: ATS Message types i c..scsscccetas.ecsesdaass sekaseks send saktaebssuetsusdoasscubagasasesseassebecstoasacastactegass 18-2 
Ati;example MacrO;recorder ss ic.55 sess Sachi eusscnetsgeiin Sulobels cod ia eavtarsoeed Seasysiebehecehebensuns ery 18-7 
Appendix A - Category Files.................scccsssssccssssssscsscssesssscscccssscscesssscsesssscccessssseesssscseessscesesssees A-1 
Category file Comtent s.cisicccsices ccisicadeciyiaes cobecadeetyiees cesshsivlevecdaadesvedaplentcdiadeauigevaeabadevdvalaes A-1 
Class: definition.cee..iee aie. bia ene hoe eee ee AACN eae A-2 
Sub-Category filesivi.csscscsesssciecelvacedeedeaeav eden aicesetabvoeeadesevds dapvendaecadsesaucaieetaucedsesanceiseanvees A-3 
Using sub-category files..........ceeeccceeesssececeeeeececeeneeeeeeeaeeecesnneeecesneeeeeenaeeeensneeeeeseeeeess A-4 
Category translation . iciccccvisctccesicadceeeccstceseccae ceuvecdoesusdiveedardesvedandesveder deen serceseiderdcnvadancevecce A-6 
The .ext external reference file ............ccccccccccssssssscccecceessessnsceceecesssessaeeeeecesssesseaeeees A-7 
The .c C language category source file............eeeeceeeseseeceeseeeeeceeneeeeeeeaeeecesneeeeeeeeeeess A-7 
The .g C language include file......... eee eceeccceeesceeeeeeneeeeeeeeeeeeeseeeeeesaeeecsenneeeeseeeeees A-8 
The .asm assembly language category source file..............ccceeecceeeeseeeeeceeeneeeeesteeeeeees A-9 

The .ing assembly language include file .......... eee eeeeeeseeceseceneeceeecseeceteeeesaeeseeessaes A-10 

The: lis: category listing file .s2:) irekieaed di aa dead oieeaaaned A-10 

The .c skeleton method function source file ..............ccceeescceeeeeeeeeeeeeeeeeeeneeeeeeeeeeeeenees A-10 


OBJECT ORIENTED PROGRAMMING GUIDE 


Appendix B - Method Function Source FileS..................ssccsssssssssscssescsscssesssscsesssscsessssessesssseseeees Dad 


Method function parameters .......... ee eeseeeseecsseeceseeeesseecsacesssceceeeeesseessaeesseeseneeeesaes B-1 
Calling conventions and function Order .0........ eee eeeseeesseeeeseeseseeceseecesaeecsaeessaeessaeeeees B-1 
Appendix C - Mechanisms ..............sccssccsssscssssccsssscsssecsssscsssssscsssssssscsssscssssescsssscsssscssssssssssossseons Om 

CLASSES se stessssteadsiecatestuateddssseestastes senda astsshasdeasahdsast cuss sec aud sas saasMegoadisasgeatdsMareeaast oat 
Class descriptor 
Object creation........... 

Cates Ores. vies bersteveres head Seevesus sats aah seevs sess 2evsnabstves rai Sebbsaeh savas Pave soseed tues peteeesed sie he ds 
Category handles and category NUMDETS .............eeeeeeeeeeeeeeeeeeeeceeeceseeeesaeeesaeers C-3 

Dynamicwdinkages.sciiinitiettioa ail wists ciel Si ties Sloe oetiok uisied Noli C-4 
Referencing by category handle .0...... eee eeeeeeseceseeeeseeeesaeeesaeecseseeeesaeessaeesseeese C-5 

MESSA BE: PaSSini Os 2sssecesciu$ sigs Havecubstius coh peed cobs Yowsscba sing cobs twee ba.shag eubath wvsebs thay eebtdwsedv haahy C-5 
Calling conventions for method fUNCtIONS ........ ee eee eeseeeeneeteneeeeeeeeeseeeesaeeeeaeers C-6 
Method: parameters. -.:..0.:h ieee dle e OR  e tae e ee C-6 


viii 


CHAPTER 1 


INTRODUCTION 


This manual provides practical information on the use of Psion's Object Oriented Programming (OOP) 
system for the Series 3 and Series 3a machines. It illustrates how to use Object Oriented techniques to 
write applications, using the HWIM (Hand-held WIMP) dynamic class library, which is built into the 
ROM of Series 3 and Series 3a machines. Such applications can be written to at least the level of 
functionality obtainable by using standard C programming and the Hwif library, as described in the 
Programming in Hwif manual. In many respects the use of Object Oriented techniques allows applications 
to be written to a standard that is well above what can be achieved with the Hwif library. 


The content of this manual assumes no particular prior knowledge of Object Oriented programming 
techniques. However, it would be useful to have read a textbook on the fundamental principles of OOP 
and to be familiar with basic concepts and the terminology in common use. There are many topics that are 
covered briefly in an early chapter and then described more fully at a later stage. The manual should 
therefore be read in its entirety for a full understanding, although many of the details may be skipped on 
first reading. 


This manual assumes that the reader is familiar with the basics of producing applications for the Series 3 
range of machines, as described in the Series 3/3a Programming Guide. It also assumes some familiarity 
with the use of windows and pull-down menus in the application's user interface as described, for 
example, in the Programming in Hwif manual. This form of user interface will also be familiar to those 
who have written OPL applications for the Series 3 or Series 3a. The basic OOP functions that are 
associated with objects and categories are described in the Object Oriented Programming chapter of the 
PLIB Reference manual. 


The built-in object libraries: 


Machines in the Series 3 range have the following object libraries built into the ROM: 


OLIB The OLIB library is described in the OLIB Reference manual. It contains classes that 
do not depend on the user interface or on the window server and thus may be used in 
exactly the same way on all machines. As well as being used by the other built-in 
object libraries, OLIB is used by an application to provide its application framework 
and to implement that portion of the application that is independent of the user 
interface. 


OLIB contains an application manager class that runs a non-preemptive multi-tasking 
system, driven by active objects that represent event sources. Active objects make it 
much easier to write structured (and hence robust) multi-threaded applications. The 
application manager and active object mechanisms also support a structured and 
efficient method of error handling with roll-backs to a pre-defined level. 


OLIB includes active object subclasses that implement timer events and inter-process 
events. Several class hierarchies and object aggregations in OLIB implement a range 
of dynamic data structures, including many kinds ofvariable length arrays, segmented 
data buffers and text storage. Other classes include resource file handling and file 
management. 


OBJECT ORIENTED PROGRAMMING GUIDE 


FORM 


HWIM 


XADD 


The FORM library is described in the FORM Reference manual. It contains classes 
that implement on-screen text formatting and a system for printing to any printer that 
is supported by a printer device driver. Essentially it implements much of the engine 
of the Series 3 word processor. FORM depends on the window server but is 
independent of the higher level HWIM. 


It is used widely by HWIM to implement, for example, edit boxes and is thus used 
indirectly by all applications. It is used directly by the word processor and by other 
applications for text processing and for printing. 


The HWIM library is described in the HWIM Reference manual. It contains classes 
that implement the Series 3 interface style, including menus and dialog boxes. 


HWIM subclasses OLIB's application framework to provide an application framework 
(with support for error handling) for programs that conform to Series 3 user interface 
conventions. 


There is a substantial window class hierarchy that is used both internally and 
externally to implement 'model world' views and interaction systems of many kinds. 
Some classes may be used directly and some are suitable as building blocks for 
custom views (either for further specialisation or for use in custom aggregations). 


A large part of HWIM implements the Series 3 dialog box system and its controls. As 
well as providing abstract classes for application-specific dialogs, very many of the 
commonly required concrete dialogs are provided - for example, for print set-up, page 
layout and for presenting help. There is extensive support for file selection. 


The XADD library is present in the Series 3a ROM and not in that of the Series 3. It 
is described in the XADD Reference manual. XADD contains classes that extend the 
user interface functionality provided by the HWIM and FORM libraries. 


It contains the classes used to implement the print preview system that is used by 
many of the built-in Series 3a applications. The print preview system is potentially 
available to any application that supports the FORM/HWIM printing mechanism. 


It also contains classes that provide the means to create and use a graphical calendar 
view of one or more months. 


Advantages of using the object libraries 


Applications written using OOP and the object libraries have a number of advantages over those written in 
OPL and those written in C, using the Hwif library. Some of the more significant advantages are: 


By default an HWIM application maintains its screen's appearance by using a dynamic redrawing 
technique. OPL and Hwif allow only the use of a backed-up bitmap, which consumes more 
memory and slows down drawing to the screen. 


A number of classes such as dialog boxes and dialog box controls already exist, with default 
behaviour. As such, they can be used directly, or subclassed as required for more specialised 
behaviour. Such classes permit quite sophisticated applications to be built fairly quickly. 


The contents of dialog boxes and menus can be changed dynamically, allowing a more 
sophisticated dialog with the user. Also, consistency and dependency checks can be performed 
before allowing the user to exit from a dialog. Both of these are difficult, if not impossible, under 
Hwif. 


Program activity may continue while a menu or a dialog is being displayed. 


All resources are placed in resource files. This encourages the creation of language independent 
applications. 


OOP permits software to be re-used. Standard classes can be developed for one application and 
can be re-used in another, thus avoiding the need to "re-invent" software. 


The object paradigm is well suited to the design process, particularly for interactive event-driven 
programs with graphical interfaces. Good design permits quicker modification and allows 
maintainance to be done more easily, more quickly, more cheaply and with greater reliability. 


1 INTRODUCTION 


Basic concepts 


The material in this section provides a general overview of many of the basic mechanisms of Psion's OOP 
system. Further details on many of these topics will be found in Appendix C. 


An object may be regarded as a combination of a number of items of data (referred to in this manual as the 
object's property) and a number of functions (the object's methods) that manipulate that data. An object 
usually represents some particular aspect of the problem under consideration. In general, the name of an 
object is noun-like and the names of its methods are verb-like. As an example, one of the most commonly 
used objects is a window, that is, an object that represents a rectangular area on a computer screen in 
which data may be displayed. In this case, the property includes the dimensions of the rectangle and one 
of the object's methods would be resize, to alter the window's dimensions. 


An object is characterised by its class definition, specifying the property and the methods that are 
supported. Each object is said to be an instance of its class. 


Classes 


A class specifies the data (property) and behaviour (methods) of a particular type of object and is defined 
by an entry in a category file. The category file entry lists the property items and the method functions, in 
a way that is described later in this chapter and, in more detail, in Appendix A. 


In general, a class is derived from another, more general class, known as its superclass. In such a case, 
one or more of the class methods (and items of property) may be supplied by the superclass. The class is 
said to be a subclass of its superclass. The subclassing process may be repeated to construct arbitrarily 
long subclass chains. In practice, however, such chains tend to be fairly short. On grounds of 
maintainability and comprehensibility it is inadvisable to construct long chains. 


Although each subclass inherits methods and property from its superclass, a subclass can add its own 
methods and replace (that is, redefine) inherited methods to provide more specialised behaviour. 


Object creation 


An instance of a class is created by calling p_new, £_new, f_newlibh, p_newlibh, £_newsend or 
f_newlibhsend. These functions return a handle for the instance, and this handle must always be used to 
identify a particular instance. The handle is, in fact, a pointer to an allocated heap cell that contains the 
property (if any) of that class. This property includes any property inherited from superclasses. 


All the functions (p_new etc) that create an object initialise the property with zeros. 


Property may only be accessed via the object's handle. Within the method function code, this handle is 
conventionally given the name se1¢f. Note that, in contrast to some other object oriented systems such as 
C++, any code to which an object's handle is available has access to all the property of that object, 
including the property of all its superclasses. For those who are familiar with C++, the idea of private and 
protected data and functions does not exist. In terms of access to property and methods, Psion's Object 
Oriented Programming system is similar to Object Pascal. 


Although not enforced by the programming system, the intention is that, by default, property should be 
considered as private to a particular class unless there are valid reasons for it to be externally accessed. 
Property of the classes supplied in the object libraries should always be considered to be private unless it is 
explicitly stated otherwise. 


Component objects 


An object's property may contain the handles of other objects, usually for the purpose of sending messages 
to them. Such a handle may have been passed to the object as the parameter to one of its methods and may 
be stored purely for convenience. A roughly equivalent result could have been obtained by storing the 
handle in a global variable. 


A more significant case, however, is where the object 'owns' a subsidiary object that is an integral part of 
the owning object. Such a subsidiary object will normally have been created by the owning object, which 
is usually the only object to have any knowledge of its existence, and must be destroyed when the owning 
object is itself destroyed. In such a case the subsidiary object is said to be a component of the owning 
object. A class definition in a category file can mark one or more items of property as being the handles of 
component objects. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Object destruction 


An object is normally destroyed by sending it a pestroy message (sending a message is described later). 


A root class (i.e. a class that has no superclass) normally contains a single method. This implements the 
default destroy method and is inherited by all other objects. 


The default destroy method is designed to destroy the object and all its components (and the components 
of components and so on). In addition to avoiding the need to duplicate component-destroying code, this 
feature is one of the cornerstones of efficient recovery from error conditions. The standard root class 
(imaginatively named root) is supplied by the OLIB class library and the default destroy method function, 
root_destroy, 1s provided by the PLIB library. 


Some object classes may create resources that are not objects (e.g. an I/O channel, which needs to be 
closed) or may wish to destroy objects in a specific order. Such classes generally replace the destroy 
method to clean up the resources introduced by the class. In addition to its class-specific action, the 
method must "supersend" the destroy message to the superclass in order to continue the destruction 
process. Every object is expected to execute root_destroy at some stage in its destroy method. 


Objects in existence at the termination of an application do not need to be explicitly destroyed. The EPOC 
operating system ensures that all resources used by a process are released when the process terminates. 


Categories 


A category is a group of one or more classes packaged into a load module which, when loaded, occupies a 
single code segment. Category code segments are shared - there is only one copy of a particular category 
in memory, however many processes are executing it. 


There are two main groups of categories: 


e Image categories are used to implement programs. The name of a code segment that contains an 
image category has the extension . $sc. 


e¢ Dynamic library categories (DYLs) contain classes that are referenced from image categories 
and other DYLs. The name of a code segment containing a DYL has the extension .dyl. 


A straightforward small- to medium-sized application, such as those described in this manual, typically 
consists of a single image category that references the built-in ROM DYLs. 


Programmers may, however, develop their own DYLs for one of the following reasons: 


e A larger application can choose to be organised into multiple categories to limit its working set 
by selectively loading transient subsystem categories into memory (analogous to overlays in 
single-tasking operating systems). 


e A large application may use DYLs simply to overcome the 64K code segment limit. 


e An application may wish to develop an open-ended set of "polymorphic" DYLs to implement, for 
example, a set of different printer drivers. 


e To develop a general-purpose DYL which supplements the system object libraries. 


e To provide the common functionality for a product that consists of a suite of application 
programs. 


Dynamic libraries have the following advantages over normal (static) libraries: 
e only one copy of the code is present in memory however many processes are using it 
e the DYL code does not detract from the 64K segment limit of the application that is using it 


e provided you don't change the interface to the DYL (or at least make it upward compatible), you 
don't need to relink the applications that use the DYL when you build a new DYL 


1 INTRODUCTION 


Category handles and category numbers 


A reference to a category may be made either by a category handle or by category number. The reference 
may be to: 


e the local category, that is, to the category containing the code that makes the reference 
¢ an external category, that is, any category other than the local category. 


A category handle uniquely identifies a category code segment, but is only known at run time, after the 
categories have been dynamically linked (for example, by means of a call to p_linklib). 


A category code segment may also be identified from within a category by a category number, which is 
known at compile time. A category number is not unique, in that two categories will, in general, use 
different category numbers to refer to the same external category. 


Because of this fact, a category number should not be passed as a parameter to an external method (for 
example, to create a component of variable class). When there is a requirement to pass a category as a 
parameter, the category handle rather than the category number should be used. After dynamic linking, 
the category handle may be obtained from the category number by calling p_get1ibh. 


A category number is mainly used to create an instance of an object class using p_new, f_new or 
f_newsend. These functions automatically convert the passed category number to the corresponding 
category handle. 


The most common use of a category handle is to create an instance of an external class using p_newlibh, 
£_newlibh OF f£_newlibhsend. 


Message passing 


In OOP terminology, sending a message to an object means calling a method function of the class or 
superclass of which that object is an instance, the method function being identified by its method number. 


The most common way of sending a message is to use p_send or, more efficiently, one of the p_sendn 

variants. All of these functions must be supplied with the handle of the object instance (as returned by, 
Say, p_new OF p_newlibh) and the method number as their first two parameters. Up to three additional 

parameters may be supplied. 


The p_sena function locates the appropriate method function by scanning up the superclass chain, starting 
with the class of which the object is an instance, selecting the first matching method function. 


If no suitable method is located in the superclass chain, the sending function panics with panic number 
48. The send will also panic (with panic number 55) if any class that is scanned does not have a valid 
structure. This catches, amongst other things, the sending of a message to an object that has already been 
destroyed. 


If successfully located, the method function is passed the object handle and the optional parameters (the 
method number passed to p_send is suppressed). 


Although p_send is the most commonly used message-sending function, the following functions may also 
be used: 


p_supersend this is used within a method function to send a message of the same method 
number to the same object, but to be handled by a superclass method. It 
works like p_send except that the search for a method starts at the 
immediate superclass of the class associated with the method containing the 
call to p_supersend. It is typically used within a subclass method that adds 
further processing (before, after or around the call to p_supersend) to the 
method being replaced. 


p_entersend this works like a p_sena that has been called with a p_enter - but more 
efficiently 
p_exactsend this can send a message to a method of a specific class in an object's class 


tree. The search for the method starts at the specified class. It is frequently 
used within a method function to send a message to the same object, but to 
be handled by a superclass method once removed - in effect a super- 
supersend. 


A method function is normally declared with the mzetHop_catt calling convention. Other calling 
conventions may be needed in some circumstances, as described in the Calling conventions for method 
functions section of Appendix C. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Notation and conventions 


Category numbers 


The local and external category numbers are represented in code by means of generated symbolic 
constants. The symbolic name for a category number is generated, in upper case, from the names of the 
local and externally referenced categories, separated by an underscore, and a cat_ prefix. 


The symbolic name indicates in an explicit manner the category from which the reference is being made 
and the category that is being accessed. For example, from a MYCAT category the (external) category 
number of the OLIB category is represented by cat_mycat_o.1B. The local category is, in this case, 
represented by caT_MyCAT_mycarT. 


Class names 


A reference to a class name in the text of this manual is given in upper case. The HWIM command 
manager class, for example, whose class definition starts with the line: 


CLASS comman root 
is referred to in the text as comman. 
Class numbers 


Each class within a category has an associated class number, used by code that creates an instance of the 
class (such as a call to p_new). A class number is represented by a generated symbolic constant. The 
symbolic constant name is generated, in upper case, by prefixing the class name with c_. Thus the comman 
class number is represented by c_comman. 


Method names 


A method name, as declared in a class definition, by convention is normally given a two or three letter 
prefix that indicates the class in which it is declared. The prefix is separated by an underscore from the 
remainder of the name. For example, com_init is a method of the comman class. The prefix will, in 
general, be different from the prefix used for methods of a superclass. 


The method name is used when building the symbolic constant for the associated method number. 


Method function names 


A method function must be given a name constructed from the class name, followed by an underscore and 
the method name. For example, the method function associated with the com_init method of the comman 
class must have the name comman_com_init. 


Messages and message numbers (method numbers) 


Each method is identified by a method number, represented by a generated symbolic constant. The symbol 
is generated, in upper case, by prefixing o_ to the method name. For example, the com_init method has a 
method number represented by the symbol o_com_1n1T. 


The message itself is referred to in the text simply by an uppercased method name. For example, the 
com_init method is invoked by the sending of a com_INIT message. 


Object handles 


An object handle identifies a particular object (instance of a particular class). It is, in fact, a pointer to the 
memory cell that results from a call to one of the object-creating functions (p_new, for example). The cell 
contains a C struct, whose typedef appears in the appropriate .g file generated from the category file by 
CTRAN. The type name of the struct is generated by prefixing pr_ to the class name. Thus the handle of 
an object of class w1n is a pointer to a PR_WIN struct. Within the method functions of a class the handle of 
an instance of that class is conventionally given the name self. 


The memory cell pointed to by the object handle contains the object's property, including the property 
defined for all its superclasses. Take, for example, the pwrmman class. This subclasses appman which, in 
turn, subclasses Root. The handle of an pwrmman object points to a PR_HWIMMaN struct that is composed as 
follows: 


self points to: | property of Root accessed by: self->root 


accessed by: self£->appman 


accessed by: se1£->hwimman 


If a class supplies no additional property then the corresponding element of the struct does not exist. 


1-6 


1 INTRODUCTION 


The object handle may optionally be declared as a pointer to any superclass. Thus the pointer in the above 
example may be declared as a pointer to either pR_Root (which gives access to only the Root component) 
or PR_APPMAN (with access to Root and appman property). It may also declared as a vorp * in which case, 
obviously, there is no access to any of the object's property. 


Method function prototypes 


The description of each method in the documentation contains a function prototype that specifies the 
nature of any return value and the parameters with which the method is called. By convention, the listed 
parameters always exclude the object handle that is passed as the first parameter to every method function. 
It also does not show the method function number, which is passed to the message-sending functions such 
as p_send, but is removed by the message-sending mechanism and is never passed to a method function. 


For example, a wn_emphasise method for the class w1n, described in the documentation by the prototype: 
VOID wn_emphasise(UINT flag); 

would be invoked by, for example: 

p_send3 (hand, O_WN_EMPHASISE, TRUE) ; 

where hand is the handle of an object of the win class and o_wIN_EMPHASISE is the method number. 

This corresponds to a method function declared in C source code as: 


METHOD VOID win_wn_emphasise(PR_WIN *self,UINT flag) 
{ 


} 
Class diagrams 


The class diagrams in this manual broadly follow the notation as used in Object Oriented Analysis and 
Design with Applications, second edition, by Grady Booch (The Benjamin/Cummings Publishing 
Company Inc, 1994). A class is represented as shown below, where the class name is written inside the 
symbol. An underlined name represents an imported class, that is, a class whose class definition is in an 
external category. 


foi eee 
fp "eS: 7 
- ed) 
ie 


Relationships between classes may be that one class subclasses another, or that a class uses another. The 
superclass/subclass relationship is represented by an arrow joining the two classes, as illustrated in the 
following diagram. 


ma a 


GRO aaa te 

7 supe lass y subi ass. 

= =) ie ad 
Le Ce 


A ‘using’ relationship is illustrated in the following diagram, where class A uses class B. The using 
relationship may represent aggregation, meaning that class B is a component of class A, or may simply 
indicate that class A sends messages to class B. 


a Coe es 
a an See, ee ee 
eae La 


In some cases, where the two classes may be considered co-equals, or where the two classes send messages 
to each other, the relationship may be shown as a simple association, without the circle that indicates the 
direction of the relationship. 


YS ea Has ea 
a a ae ee 
Mee OF a 


OBJECT ORIENTED PROGRAMMING GUIDE 


Programming options 


An application can make use of OOP techniques in one of a number of different ways. This section 
explains the main options that are available. 


Using existing object libraries 


At the most straightforward level, an otherwise non-object oriented C application can simply create and 
use one or more objects from existing object libraries. A typical example of this kind of usage is described 
in the Interface to the ISAM library section of the Introduction chapter of the ISAM Reference manual. 
The example code in that section illustrates how to create an instance of a class from an external category 
and send messages to it. Note that, since the ISAM DYL is not in the ROM, it has to be explicitly loaded 
before being linked. 


In principle, this technique can be used with any class from an existing object library. In practice, 
however, for the reasons given in the later discussion of the use of HWIM, it is restricted to classes that do 
not depend on the user interface. The ISAM example is typical in this respect, in that uses the console 
services to supply its user interface. 


The technique is particularly suitable for creating and using instances of classes such as the variable array 
(container) classes in OLIB. In such a case, where the class library being used is in the ROM, there is no 
need to load it before linking (with, say, a p_linklib(0) call). 


One of the main advantages of this technique is that it requires very little additional knowledge, other 
than the details of the particular class or classes that are being used. Since the bulk of the application's 
code will not use Object Oriented techniques, the gains are relatively modest. There are savings in 
application code size, since the application does not have to duplicate the object library code that it uses. 


Defining application-specific classes 


Rather than simply using an existing class, an application may define one or more application-specific 
classes. These classes may, by subclassing, add value to other existing library classes or may be totally 
new classes (although a 'new' class is, in fact, a subclass of the OLIB root class). Such an application may 
also make use of existing object libraries, as described above. 


Although it is possible to write a fully object oriented application of this type, a typical application will 
still be largely written in non-object oriented code. As for applications that simply use existing classes, the 
technique is better suited to classes that do not depend on the user interface. 


A simple example of an application of this type appears in the Building an Object Oriented Application 
chapter of this manual. The additional knowledge that is required to create and build an application of this 
type is entirely contained within that chapter. 


Creating and using a DYL 


Instead of using the classes of an existing object library, an application can use objects in a custom object 
library (or DYL). The way to create and use a custom DYL is described in the Building a Dynamic 
Library chapter of this manual. 


The classes in the DYL may be any combination of 'new’' classes or subclasses of existing library classes. 
This technique may be combined with the use both of custom libraries and of custom classes in the 
application itself, as described above. 


In addition to the advantages of the previous techniques, writing one or more parts of an application as 
separate DYLs allows for easier code sharing and reuse and allows large applications to be written (a 
single code segment may not exceed 64 kbytes). 


Using HWIM 


A developer who wishes to create an application with an object oriented user interface should make use of 
the HWIM class library. 


This case is qualitatively different from those described above, in that many of the classes in the HWIM 
library are designed to be used together, and are not particularly suited to being used in isolation. Some of 
the classes rely on the existence of instances of other classes and on certain specific initialisation having 
been performed. This is the reason why the techniques described above are not recommended for the 
HWIM user interface classes. 


1 INTRODUCTION 


An HWIM application always contains a basic framework of objects, briefly described in the following 
section, to provide those features that are common to all HWIM applications. These features include the 
provision of command menus and dialogs, the handling of multiple event sources and the direction of 
keyboard events to the appropriate object(s) within the application. Such an application requires a specific 
form of start-up code in its main() function to create the basic framework and perform the necessary 
initialisation. The exact form of this start-up code is described later in this chapter. 


Many of the HWIM application framework classes can be used directly, but an application will always 
define and use application-specific classes, including subclasses of the classes supplied by HWIM. The 
application may also, of course, use or subclass the classes from other object libraries - either the standard 
libraries that are in the ROM, or application-specific DYLs. 


A simple example of such an application is described in the An HWIM Example - Hello World chapter. 
The construction of more complex applications is essentially the topic of the rest of this manual. 


The basic HWIM application component objects 


There are five main static objects (by static we mean an object that exists for the lifetime of the 
application) provided by HWIM; the application manager, the application's resources, the window server 
object, the command manager and the client window. A sixth static component that is usually present in 
non-trivial applications is the engine. 


The menu bar and, optionally, one or more dialog boxes are transient objects, being created when they are 
required and destroyed on completion of their function. 


The application manager 


The application manager provides the framework for an application, including its main event-scheduling 
loop. It is the first object to be created during the start-up of an HWIM application and performs all 
standard start-up and initialisation. As part of this initialisation it creates a window server active object 
(and hence a command manager - see later) and opens the system resource file and the application's own 
resource file. In addition, the application manager supplies methods for manipulating other standard 
system components and adding further optional components. 


— 
— 


avolication 
~ manager 
— 
ae oo 
| a kes 
7 eso! ces gO 
server 
~~ ) ca 
Rue oe 


The application manager and the window server object together form the central core of the application. 
From the application programmer's point of view they may be considered as a single entity that provides 
the application's main event-handling loop and a range of system services. The separation of this 
functionality between two objects represents a division of labour; the window server object deals with the 
user interface and the application manager handles those aspects that are independent of the user 
interface. (Although it is beyond the scope of this manual, it is worth pointing out that an application with 
no user interface can be constructed around the application manager alone.) 


The HWIM library supplies the xwimman application manager class, which is a subclass of the OLIB 
appmaN Class (see the OLIB Reference manual). An instance of this class is normally created and 
initialised from the application's main (). It is rarely subclassed by an application, the main exception 
being that of a multi-lingual application, which will need to modify the mechanism that loads the 
application resource file. 


An application may subclass awrmman by replacing existing methods. In the interests of future 
compatibility, application-specific subclasses should not add methods or property. An application that 
adds methods or property to the application manager is not guaranteed to run on future versions of 
machines in the Series 3 range. 


The handle of the application manager is globally available via the magic static w_am. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Resources 


All standard HWIM applications are assumed to have access to two resource files - the system resource 
file (in the ROM) and an application resource file. These are opened automatically during initialisation of 
the application manager, which also provides methods to access individual resources. There is rarely any 
need for an application programmer to subclass the resource file class. 


The application resource file must contain the resources that provide the accelerator keypresses and the 
text of the application's menu bar and pull-down menus. In addition to these essential items, it may also 
contain resources for any application dialogs and other application-specific text. 


The objects representing the resource files (instances of the OLIB rscr1te class) are normally only 
accessed via application manager methods. 


The window server object 


The window server is an active object that acts as the source of those events (keypresses, redraws etc) that 
are sent to the application by the window server process. On receipt of such an event the window server 
object directs it to the appropriate object, normally either a window (the client window or a dialog box) or 
the objects involved in the command execution mechanism. 


An instance of a window server object is automatically created during the initialisation of the application 
manager. As part of its standard initialisation the window server object creates and initialises a command 
manager. 


“~~ 


te = 

C window’ 

~ server ) 

ee 
ft <.. ran = Hey, 
C command client / 
smanager, ~ window j 
eageee y wae 


The HWIM library supplies the wszrv window server object class which is a subclass of the OLIB active 
class. HWIM applications will almost invariably subclass wsERv , replacing its ws_dyn_init method, to 
provide application-specific initialisation. Part of this initialisation will be the creation and initialisation 
of a client window. 


An application is free to subclass wsERv by replacing existing methods. In the interests of future 
compatibility, application-specific subclasses should not add methods or property. An application that 
adds methods or property to the window server object class is not guaranteed to run on future versions of 
machines in the Series 3 range. 


The handle of the window server object is globally available via the magic static w_ws. 


The command manager 


The command manager supplies the functionality to execute the command options that may be selected 
from the application's pull-down menus. A command manager instance is automatically created during 
the initialisation of the window server active object. 


The HWIM library supplies the comman command manager class, which provides the basic skeleton for a 
command manager. Although a very simple application could make direct use of an instance of the 
comman class, HWIM applications will normally subclass comman, replacing one or more of the supplied 
methods and adding application-specific methods and property. 


The handle of the command manager is stored within the window server object's property and is available 


Vla w_ws->wserv.com. 


The client window 


All standard HWIM applications are assumed to have a main window, designated as the client window to 
provide the principal view of the application's data. This window will receive messages from the window 
server active object in response to window server events (such as keypresses). The client window must be 
explicitly created and initialised by application-specific code, normally from within the window server 
object's ws_dyn_init method. 


1 INTRODUCTION 


The functionality of the client window varies widely from application to application and much of an 
application's code will be associated, directly or indirectly, with the client window. For this reason, the 
HWIM library provides very general window classes that will normally be extensively subclassed in most 
applications. 


The handle of the client window is stored within the window server object's property and is available via 


w_ws->wserv.cli. 


The engine 


The engine is an application-specific class that is normally created at the same time as the client window. 
A typical engine will subclass root. Engines are further discussed in the Application Design chapter. 


— 
— 


C client = / 
~ window 


L — 


Ue ENT es 
gf engire / 
zd 
ad 
An application programmer may choose to make the handle of the engine globally available, for example, 
by storing it in one of the magic statics in the range Datapp1 to DatApp7. 


Menu bar 


An instance of the application's menu bar class is automatically created by the window server object 
whenever the menu bar must be displayed (for example, when the Menu key is pressed) and is destroyed 
when the menu bar disappears. 
fer 
window ' 
~ server ) 
Laer 


je 
men bar. 
s ) 
Lge 
The menu bar class contains the mechanism to convert a menu selection into the appropriate command 
manager message and is unlikely to be subclassed in any application. 


The menu bar is normally only manipulated by means of methods of the window server object. 
Dialogs 
A dialog is usually created from application-specific code in a command manager method that is called in 


response to the selection of a command menu option. (The command manager actually makes use of the 
window server object's ws_do_dial1 method, which is used to start all dialogs). 


— 


command 
kd manaden 


U = 


a 
— 
— 


2 dialo box 
= ae 
Ae 
Such a dialog may simply use the supplied picgox class or may be an application-specific subclass. The 
use of dialogs is discussed in some detail in the Dialogs and Dialog Controls chapters. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The handle of a current dialog will normally be stored in the magic static DatDialogPtr and many dialog 
utility functions (see the HWIM Utility Functions chapter of the HWIM Reference manual) make use of 
this fact. Otherwise, access to a dialog's handle is an application-specific matter. 


Application overview 


The following diagram illustrates the overall basic class structure of a typical HWIM aplication and shows 
the principal relationships between the classes mentioned above. 
Sei 


z anolication 


manager 

PSE ee LC ~ jo OR Gr a ee, 
¢ reso ces. C window ’ y men _ bar 
= ) ~ server se ) 
ee Mn 
/ Eee Dai ; er e Pies! ee 
/ tient = 7 

la comman lalo Ox / cllen 
~ manager a 7 ) a yagew j 

Na Ne a 


— 
— 


a enc ie / 
* ) 
oe? 
These relationships are discussed further throughout this manual and in the OLIB Reference and HWIM 
Reference manuals. There is further general discussion on the general structure of an HWIM application 
in the Application Design chapter of this manual. 


The required files 


To create an object oriented application the writer's task consists of constructing a number of files which 
will be input to the build process. A number of tasks must be performed which can be broadly defined as 
follows: 


e building the required classes by defining their property and methods; further, deciding whether any of 
the required classes can be subclassed from existing classes, thereby re-using existing software 


e providing the functionality for a number of method functions that will be called by system code 


e writing a short main() that creates and initialises an application manager, supplying it with one or 
more of a set of options 


e building the resource file(s) and optionally, the Icon file, the Add files list and the Shell data file. 


The following sections describe the structure and content of the files required to create an object oriented 
application in more detail and broadly correspond to the tasks outlined above. 


Category file 


The category file defines the application-specific classes - methods, property and associated defined 
constants and structs. As part of the process of building an application, the category file is translated with 
the aid of the CTRAN tool. The output from the translation process is a C source file (which is also 
compiled, ready for linking into the application) one or more generated include files, each with a .g 
extension and an external file with a .ext extension. Other files may optionally be generated. 


The external file contains information about this category which will be needed when translating any 
other category which makes an external reference to this one. 


1-12 


1 INTRODUCTION 


The content of a category file is best explained in conjunction with the following short example. A more 
complete explanation is contained in Appendix A. Here we shall concentrate on the basic content of a class 


definition. 


The file header contains the category name, external category references (the order of which determines 
the external category number sequence) and a number of included header files: 


A demonstration cat file 
IMAGE demo 


! External reference to OLIB library 
EXTERNAL olib 


INCLUDE p_std.h 


INCLUDE p_object.h 
INCLUDE varray.g required, in this case, for knowledge of VAFLAT 


This is followed by one or more class definitions, each of which follows the general model illustrated 
below: 


CLASS dummy root the class name and its superclass 
Dummy class definition, 
as an illustration only 

{ Methods follow... 


REPLACE destroy free buffer and supersend 

ADD dm_init create VAFLAT component and allocate buffer 
DEFER dm_sub defined by a subclass... 

CONSTANTS auxiliary symbolic constants 


{ 

! for the buffer 

DUMMY_BUF_SIZE 128 allocated buffer size 
! for the VAFLAT component 


DUMMY_GRAN 16 
} 
TYPES contains auxiliary structs 
{ 
typedef struct /* comments here are exceptional */ 
{ 
TEXT *buf; pointer to allocated buffer 
UWORD len; 
} DUMMY_BUF; 
} 
PROPERTY 1 


{ 
PR_VAFLAT *array; the component VAFLAT instance 


DUMMY_BUF buffer; 
} 
} 


The crass keyword introduces a class definition. It is followed by the name of the class and then the name 
of the parent superclass. The above example defines the class pummy which is a direct subclass of the Root 
class. The layout of a class definition is significant; apart from leading whitespace, which is ignored, it 
must follow the pattern that is illustrated above - and in the class definitions given elsewhere. 


The class definition of each subclass lists its additional methods and any additional property. It may also, 
as in the above example, include the definitions of auxiliary structures and constants used by that class. 
There are many further examples of class definitions throughout this and other manuals (in the OLIB 


Reference manual, for example). 


The class definition may include any number of method declarations,! introduced by the app, REPLACE or 
DEFER keywords. Each of these is followed by a method name. The method declarations may be followed 
by one of each of the constants, Types and property keywords. 


'Subject to a maximum of 255 methods, including those inherited from superclasses. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The method declaration keywords have the following meanings: 


ADD declare a method in addition to the methods provided by the superclass. The name 
must be unique in relation to all other methods in this category, or any externally 
referenced categories. Although not compulsory, the name conventionally starts with 
a short prefix related to the name of the class in which it is introduced. 


REPLACE declare a method whose functionality is to replace that of a method supplied by a 
superclass. The name must be that of an existing method in the superclass 
inheritance tree. 


DEFER declare an additional method as for app, except that the functionality of the method 
is not defined by the current class and is expected to be provided by a subclass (using 
REPLACE). A class containing one or more pEFERred methods is known as an 
abstract class and, in general, no instances of such a class will ever be created.? 


It is recommended that each method name be followed by a concise descriptive comment. 


The constants keyword introduces a list of symbolic constant definitions, each consisting of the symbol 
name (conventionally in upper case) followed by the numeric value. The value may be an expression 
involving symbolic constants defined earlier, either in the category file itself, or in any included file. The 
expression itself must not contain any whitespace. 


The types keyword introduces a list of C language typedef struct definitions, whose layout should follow 
that given in the example category file. (Many further examples may be found in the class definitions 
shown for each class in, say, the OLIB Reference manual.) 


The property keyword introduces a list of data element declarations to be included in the struct that 
defines the class property. The form of this struct is described in appendix A of this manual. 


This keyword may optionally be followed by a literal number (expressions may not be used) that specifies 
how many component items listed in the property are to be sent an automatic pestTRoy message when an 
instance of the class is destroyed. 


This assumes that, for a value ncomp, the first ncomp items in the additional property for the class are 
either nuLL or handles (pointers to instances) of component objects (as defined earlier in this chapter). 


In the above example, pummy's component var.at instance will be automatically destroyed when pummy 
recelves a DESTROY Message. 


Source files 


Method functions 


A method function must be supplied for each added or replaced method in each application-specific class. 
The method functions may be supported by auxiliary (or utility) functions - that is, normal C functions 
called from within the method functions. These functions may be in a separate file or in the same file as 
the method functions. 


The method functions themselves may be organised into C files in any suitable way. Normally, all the 
method functions for a particular class will be grouped into one file, but this is not a requirement. It may 
be convenient to group together all the methods of a related set of objects, and a simple application may 
have all its method functions in a single source file. 


Where a file contains a mixture of method functions and auxiliary functions, it is conventional to put all 
the auxiliary functions at the top of the file, followed by the method functions. One advantage of this 
scheme is that it minimises the number of compiler directives needed to establish the correct calling 
conventions for the different function types. This may be extended to include other function calling 
conventions so that, in general, a source file will have the following form: 


2There is no formal requirement for all p—ErzRred methods to be REPLACE and it is acceptable to create an 
instance of such a class provided that it is known that no pErErRred method will ever be called. Window 
subclasses, for example, do not need to REPLACE all pEFERred methods of the HWIM win superclass. 


1-14 


1 INTRODUCTION 


<includes> 

<'normal' functions> 

#pragma ENTER_CALL 

<functions called via p_enter> 

#pragma CDECL 

<method functions that are callable via p_enter> 
#pragma METHOD_CALL 


<method functions> 


Failing to declare the correct calling convention for a function will cause unpredictable run-time errors 
when the function is called. 


Main 
The main() of an object oriented application that uses the HWIM library takes the following form: 


#include <hwimman.g> 


GLDEF_C VOID main(VOID) 
{ 
IN_HWIMMAN app; 
IN_WSERV ws; 
VOID *handle; 


p_linklib(0); 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE|FLG_APPMAN_CLEAN; 
app.wserv_cat=p_getlibh (CAT_MYAPP_MYAPP) ; 
app.wserv_class=C_MYAPPWS; 

ws.com_cat=p_getlibh (CAT_MYAPP_HWIM) ; 

ws.com_class=C_COMMAN; 

handle=p_new (CAT_MYAPP_HWIM, C_HWIMMAN) ; 

p_send4 (handle, O_AM_INIT, &app, &ws) ; 

} 


The call to p_link1lib dynamically links the application category with its externally referenced DYL 
categories. These consist of those categories that are declared with ExTERNAL statements in the 
application's category file (together with any further ExTERNAL references that these categories contain). In 
general, all such externally referenced categories must be loaded (for example, by calling p_loadlib) 
before the call to p_1ink1ib is made. In the above example code, it is assumed that all such external 
references are to the DYLs (HWIM, OLIB etc.) in the ROM. Such DYLs are deemed to be loaded by 
default, so no calls to p_1oad1ib are required. If the application uses one or more application-specific 
categories that are not in the ROM it may be necessary, depending on how and when they are used, to 
load them at this point, before the call to p_1ink1ib. A failure to link the appropriate categories can cause 
a wide range of object-related run-time errors. 


The flags field of the 1n_Hwrmman struct specifies a range of options that are used during initialisation of 
the (qwrmman) application manager. The basic range of flags is described in the APPMAN Application 
Manager Class chapter of the OLIB Reference manual, and additional flags are described in the 
HWIMMAN Application Manager chapter of the HWIM Reference manual. The three flags used in the 
above code, specifying that the application uses the system resource file, an application resource file and a 
CLEANUP object, are mandatory for all HWIM applications. 


By default an application will be built to run on the Series 3 and will run in compatibility mode on the 
Series 3a. onRINg FLG_APPMAN_FULLSCREEN into the app. flags field specifies that the application should not 
run in compatibility mode on the Series 3a. 


The wserv_cat and wserv_class fields of the 1N_Hwrmman struct must be set to the category and class 
numbers of the application's window server object. As indicated in the example, all applications will use 
an application-specific subclass of the wsErv class that is supplied by HWIM. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Similarly, the com_cat and com_ciass fields of the 1n_wsERv struct must be set to the category and class 
numbers of the application's command manager that is created during the initialisation of the window 
server object. The example specifies the use of comman itself: normally an application will use an 
application-specific subclass of comman. 


The final action is to create and initialise the application's application manager (typically, as in this case, 
the Hwimman class supplied by the HWIM category). The application manager locates and opens the 
resource files, creates and initialises both the window server object and command manager and starts the 
application's main event-processing loop. All further interaction with application-specific code is via calls 
from system code to a range of method functions. The first such call is to the window server object's 
ws_dyn_init method, which is assumed to perform all necessary application-specific initialisation. 


Note that the application will never return from the sending of the am_rin1tT message to the application 
manager. 


Resource externals file 


The resource externals file, with a .re extension, is a source file that contains a list of those defined 
constants that are referenced by the application resource file. Once the developer has created a.re source 
file, the re.bat batch file is used to generate a .rg file. The .rg file can then be #included in the 
application resource file, thus avoiding the need to #include a large number of separate header files. 


The resource externals file is not strictly necessary, since the information it contains is present in one or 
more of the header files used by the application. In larger applications, the use of such a file can reduce 
the time taken to perform a resource compilation. It also reduces the risk of out of memory situations 
occurring by effectively passing only those definitions required for successful resource compilation. 


The resource externals file is also useful if the resource file is to be translated. The (usually small) .rg file 
can be supplied to the translator together with the resource file, rather than having to supply a (usually 
large) number of other header files. The translator can then easily make a test compilation of the 
translated resource file, with consequent savings in time and effort. 


A ..re source file simply contains a list of those defined constants that are needed in the compilation of the 
application's resource file (together with includes of the header files in which they are defined). Each item 
in the list is the name of a defined constant with an additional leading underscore. 


A commonly occuring item in a resource externals file is the defined constant for a command menu 
method number, since such a method number is needed to construct the resource for the pull-down menu 
option that selects the execution of that method. Suppose, for example, that an application's resource file 
refers to the o_com_ExiT method number, defined in comman.g. The resource externals file would contain 
the lines: 


#include <comman.g> 


_O_COM_EXIT 
After processing this file by means of re. bat, the generated .rg file will contain the line: 


#define O_COM_EXIT 7 


Application Resource file 


Application resource files contain the text strings used in the application's command menus, dialogs, text 
messages and so on. In object oriented programming, such strings must be kept separate from code. 


In general, resource files are useful for the following main reasons: 


e having data in a resource file rather than in the code reduces the amount of memory used by the 
running application 


e resource files make it easier to write language-independent applications 


In essence, any resource item within a resource file can be identified by a unique number. This number is 
usually assigned to a symbolic constant that is published in a header file generated by the resource 
compiler. Including this header file in a source file allows code to reference the resources. 


The structure of resource files is described in much greater detail in the Resource Files chapter in the 
Additional System Information document. 


Further information can also be found in the HWIM Resource Files chapter in this manual. 


1-16 


1 INTRODUCTION 


System resource file 


The system resource file is built into the ROM and is functionally similar to application resource files. 
However it contains common resources, including many system dialogs, the text for standard information, 
error messages and basic help. 


The general structure of the system resource file is similar to that of application resource files and is 
described in the Resource Files chapter in the Additional System Information. 


Again, further information can be found in the HWIM Resource Files chapter in this manual. 


Miscellaneous files 


Icon 


An application can be represented by an Icon. Although not mandatory, the system screen will refuse to 
install the application unless it contains one. In this situation, the application can still be run from 
RunImg but an empty icon boundary will be displayed. 


An Icon is normally placed in a .pic file and can be produced in a variety of ways: 


e using the Jconed demonstration application that can be built using the HWIF part of the SDK, or 
the Series 3a Iconeda application that is installed into the \sibosdk\s3atool directory. 


e using the window server tool wspcx.exe on the .pcx output of a PC program such as Windows 
PaintBrush 


The format of .pic files is given in the Bitmaps section in the Window Server Reference manual. 
For further information on Icons, see the Series 3/3a Programming Guide. 


Add files list 


An add files list is a text file with extension .af] which contains from one to four filenames. In essence, as 
part of the process of building an application, the files referred to in this list are combined with the 
application's .img file to produce a larger .img file. 


The list can refer to a .pic file for an Icon, a .rsc file for an application's resource file and so on. 


For further information on add files, see the chapter Building an Application in the General Programming 
manual. 


Shell data file 


The Shell data file is a file which can be included in the add files list (and therefore embedded into the 
application). It specifies information required by the System Screen application (also known as the Sheil). 


For example, it tells the Shell the expected extensions of any files to be edited and the default directory of 
these files. This information is specified at compile time. 


For further information on the Shell data file, see the Communicating with the System Screen chapter in 
the Series3 Programming Guide. 


CHAPTER 2 


BUILDING AN OBJECT ORIENTED APPLICATION 


The process of building an application that contains object oriented code is very similar to that used for 
building non-object oriented multi-file programs. The basic mechanisms of compiling and linking the 
various modules are as described in the Building an Application chapter of the General Programming 
Manual. 


Perhaps the most obvious difference is that, in addition to the file(s) containing source code, an object 
oriented program also requires a category file, described in appendix A of this manual. 


The application must also supply a method function for each additional or replacement method declared 
in the category file. For clarity, it is generally preferable to use a separate file for the source code of the 
method functions of each subclass. This has the added advantage that it also tends to reduce the amount of 
data in the included files, particularly if the category file is separated into a number of sub-category files 
(see Appendix A - Category Files). It is, however, perfectly acceptable to combine the method functions of 
two or more subclasses in a single file, particularly if they are closely related, or if they share some 
common functionality. 


The process of generating a .img file is illustrated in the following diagram. 


Classes 
CAT 


class info 
G 


= 


Methods 
.C 


The category file is translated, producing an object file and one or more .g files. The .g files are included, 
as required, into the .c files containing the method functions. These files are compiled, to produce object 
files, in the same way as for any other .c file. The .img file is formed by linking these object files and the 
object file that results from category translation. From version 5.00 of CTRAN a file of type .cif is also 
produced from the .cat file; this is used for Windows development, such as producing custom controls 
for Oval. 


The processing of the category file is shown in more detail in the following diagram. The CTRAN 
category translator tool actually generates a .c file that contains, in source form, the data for the class 
descriptors of each class in the category. This file has to be compiled to produce the class descriptors in 
object form. Since class descriptors have to be in the code space of a process, the object file has to be 
further processed. This processing is performed by the OBJCONV tool, which modifies all data in the 
object file so that it will be loaded into the process code space when the application runs. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Classes 
CAT Compiler 


aan OBJCONV 


The entire process, from category file to converted object file, is performed by the supplied ct.bat batch 
file. You only need to be aware of the underlying mechanisms because of the .c file that is generated in 
this process. This file has the same name as the .cat file so that, for example, the category file mycat.cat 
will generate the file mycat.c. You should therefore avoid creating a separate C source file ( a method 
source file, for example) with the same name as the application's category file, otherwise it will be 
overwritten during the category translation process. 


An object oriented application (.app file) is constructed from a .img file by adding an icon, a shell data file 
and a resource file, in the same way as is described in the Series 3 Programming Guide. 


An example application 


This example application (the source of which, on installation, is copied into a \sibosdk\oopdemo 
directory) prints specified directory listings to the screen. It is based on the (non-object oriented) p_prndir 
example mentioned in the Building an Application chapter of the General Programming Manual. It uses 
object oriented techniques to store the file names in a variable array object. This adds value by allowing 
the names to be ordered so that the list can be displayed in alphabetical order, with all directory file names 
appearing first. 


To avoid obscuring the main principles of building an object oriented application, this example makes 
minimal use of the object libraries built into the Series 3 and Series 3a. It does not, for example, use any of 
the user interface mechanisms provided by the HWIM library and is therefore not a typical example of an 
object oriented application. Its user interface uses the console device as used in many of the straight C 
examples (that is, using neither the HWIM or the HWIF libraries) given in, say, the PLIB Reference 
manual. The example described in the chapter An HWIM Application - Hello World makes use of the user 
interface objects and thus forms a better model for a real application. 


The example source 


The source for this application consists of three files: 


prndir.cat the prrLIstT object class definition 
dirlist.c the prRLIst method functions 
dirmain.c main() and auxiliary functions 


The category file, prndir.cat, is as follows: 


IMAGE prndir 
EXTERNAL olib 
INCLUDE varray.g 
INCLUDE p_file.h 


CLASS dirlist vaxvar 
Directory list 
{ 
REPLACE va_test sort by various criteria 
TYPES 
{ 
typedef struct 
{ 
P_INFO info; 
TEXT name [P_FNAMESIZE]; 
} DIRLIST_ITEM; 


2 BUILDING AN OBJECT ORIENTED APPLICATION 


The method function file, dirlist.c, contains only one method function, the replacement for the va_test 
method: 


/* 
DIRLIST 
#7 


#include <plib.h> 
#include <prndir.g> 


#pragma METHOD_CALL 


METHOD INT dirlist_va_test (PR_DIRLIST *self,RC_VAXVAR *precl,RC_VAXVAR *prec2) 
/* 
Firstly perform a 2 way test on file or dir name records. 
Order dir names before file names. 
Otherwise order names alphabetically. 
Return 0 if equal, <0 if *precl is before *prec2, >0 if after. 
/, 
{ 
FAST DIRLIST_ITEM *pl,*p2; 
FAST INT ret; 


ret=0; 

pl=(DIRLIST_ITEM *)precl-—->buf; 

p2=(DIRLIST_ITEM *) prec2->buf; 

if ((pl->info.statusé&P_FADIR) * (p2->info.status&P_FADIR) ) 
ret=(pl—->info.status&P_FADIR) ?-1:+1; 

else /* both records either dir or file names */ 
ret=p_scmp (&p1->name[0], &p2->name[0]); 

if (self->varoot.key.desc) 
ret=(-ret); /* reverse result if required */ 

return (ret); 


} 


The file dirmain.c, listed below, uses the console device for obtaining keyboard input and displaying its 
output. Provided a valid directory name is typed in, the code builds, in an instance of the prru1st variable 
array class, a corresponding directory listing. The file names are stored and displayed in alphabetical 
order. Press Enter at the input prompt to exit the program. 


/* 
DIRMAIN 

ay 

#include <plib.h> 
#include <prndir.g> 


LOCAL_D VOID *dcb=NULL; 
LOCAL_D VOID *hand; 


LOCAL_C VOID error(TEXT *msg, INT errno) 


{ 
TEXT bb[E_MAX_ERROR_TEXT_SIZE]; 


p_close(dcb); 

dcb=NULL; 

p_errs (&bb[0],errno) ; 
p_printf("%Ss: %s",msg, &bb[0]); 
} 


LOCAL_C VOID panic(TEXT *msg, INT errno) 
{ 
error (msg,errno); 
p_leave (0); 
} 


OBJECT ORIENTED PROGRAMMING GUIDE 


LOCAL_C VOID PrintDirLine (TEXT *name, P_INFO *pinfo) 
{ 
P_DAYSEC ds; 
P_DATE dt; 
TEXT *p,b[40]; 


p=&b[0]; 
if (pinfo->status&P_FAVOLUME) 
p=p_scpy(p,"Vol,"); 
if (pinfo->status&P_FADIR) 
p=p_scpy(p,"Dir,"); 
if (pinfo->status&P_FAMOD) 
p=p_scpy (p, "Mod, "); 
if (! (pinfo->status&P_FAWRITE) ) 
p=p_scpy (p, "Read,"); 
if (pinfo->status&P_FASYSTEM) 
p=p_scpy (p,"Sys,"); 
if (pinfo->status&P_FAHIDDEN) 
p=p_scpy (p,"Hid,"); 
if (*(p-1)==',') 
*-—p=0; 
p_sttods (&pinfo->modst, &ds) ; 
p_dstodt (&ds, &dt) ; 
p_printf£("S- 12s S7lu %02u-%02u-%S02u %02u:%02u Ss", 
name, pinfo->size,dt.day+1l,dt.month+1,dt.year,dt.hour,dt.minute, &b[0]); 
} 


LOCAL_C VOID MakeDirList (TEXT *dir) 
{ 
INT ret; 
DIRLIST_ITEM d; 
RC_VAXVAR rec; 


p_send2 (hand, O_VA_RESET) ; 


if ((ret=p_open (&dcb, dir, P_FDIR) ) !=0) 
panic("Failed to open directory file",ret); 
while (! (ret=p_iow(dcb, P_FREAD, &d.name[0],&d.info) )) 


{ 
rec.buf=(UBYTE *) &d; 
rec.len=sizeof (d. info) +p_slen(&d.name[0]) +1; 
p_send4 (hand, O_VA_INSERTISQ, érec, &i) ; 
} 

p_close(dcb); 

dcb=NULL; 

if (ret !=E_FILE_EOF) 
panic("Failed to read directory",ret); 


2 BUILDING AN OBJECT ORIENTED APPLICATION 


GLDEF_C INT CDECL DoDirLists (VOID) 


{ 

UWORD i, count; 
DIRLIST_ITEM *pd; 

TEXT name [P_FNAMESIZE]; 


hand=f_newsend (CAT_PRNDIR_PRNDIR, C_DIRLIST,O_VA_INIT,16); 
while (p_getl(">", &name[0],P_FNAMESIZBE) ) 
{ 
MakeDirList (&éname[0]); 
if (! (count=p_send2 (hand, O_VA_COUNT) ) ) 
p_printf("No files found"); 
else 
{ 
for (i=0;i<count;i+t+) 
{ 
pd=(DIRLIST_ITEM *)p_send3 (hand, O_VA_PBUF,i) ; 
PrintDirLine (&pd->name[0], &pd->info) ; 
} 
} 
} 
p_send2 (hand, O_DESTROY) ; 
return (0); 


} 


GLDEF_C INT main(VOID) 


{ 
INT err; 


p_linklib(0); /* Link to OLIB */ 

if ((err=p_enter((VOID *)DoDirLists) ) !=0) 
error ("Called p_leave",err) ; 

return (0); 


} 


Note that a standard console application is not suitable for running on an EPOC emulator. See the code in 
the Using the example DYL section of the Building a Dynamic Library chapter for how to adapt console 
code so that it is suitable to run on an emulator. 


Building the example application 


In order to illustrate the various stages in building an object oriented application, the description of the 
building of the example application makes use of a number of separate batch files. An alternative would 
be to make more use of the TopSpeed development environment and this approach is used in the building 
of the Hello World example, described in the chapter An HWIM Application - Hello World. 


The first step in building the application is to translate the category file, prndir.cat, using the tool 
ctran.exe. This generates a number of files, including a prndir.g include file and a prndir.c C language 
source file. The generated .c file must be compiled and the resulting .obj file must be converted so that its 
class descriptor data is located in the code segment, using ecobj.exe. The entire process is conveniently 
performed by means of the supplied batch file, ct.bat (in \sibosdk\sys) whose content is: 


@echo off 

ctran %1 -e..\include -x..\include -g..\include -c -l -s -v 
if errorlevel 1 goto end 

call cc $1 

ecobj %1 

send 


The meaning of the various flags that can be passed to ctran.exe are explained in appendix A of this 
manual. 


Note that the above batch file is set up assuming that the source code is in a \sibosdk\oopdemo directory 
and that all include files are assumed to be found in a ..\include directory (which is also the destination for 
include files generated by ctran). You must ensure that the current TopSpeed redirection file, ts.red, is set 
up to correspond with the location of all include files. In particular, the redirection file must include a line 
such as: 


Reg = .; ..\ INCLUDE; 


OBJECT ORIENTED PROGRAMMING GUIDE 


There is no need to include the location of generated .ext files in ts.red, since these are only referenced 
by ctran. 


The remaining .c source files must be compiled as normal, for example, by using the same cc.bat file as is 
suitable for being called from ct.bat: 


@echo off 
call checkvid 
tsc %1l.c /fpunnamed /%jpivids% 


This assumes the presence of an unnamed.pr project file of a similar form to that used when compiling 
non-object oriented programs, for example: 


#system epoc img 
#set epocinit=iplib 
#model small jpi 
#compile %main 
#link %main 


Finally the application must be linked. A suitable /f-bat link batch file is: 


@echo off 
call checkvid 
tsc %l.pr /1 /%jpivids 


which is called as: 
lf prndir 
assuming the presence of a prndir.pr file containing: 


system epoc img 

set epocinit=iplib 
model small jpi 
pragma link (olib.1lib) 
pragma link (prndir) 
pragma link (dirlist) 
pragma link (dirmain) 


link prndir 


This generates a prndir.img file which may be copied to a SIBO machine and executed as any other .img 
file. Note that, during the link process, two warning messages are always displayed, warning of duplicate 
tables. These messages should be ignored. 


Note that an object oriented application must be linked with the PLIB library since none of the object 
oriented mechanisms are supported by CLIB. 


The entire build process may be summarised in, say, a bldpdir.bat batch file (not supplied) as follows: 


call ct prndir 
Call. ce-dirlist 
call cc dirmain 
1f prndir 


CHAPTER 3 


BUILDING A DYNAMIC LIBRARY 


A dynamic library (DYL) is built from a category file and one or more method function source files in a 
similar way to the building of an application. 


A DYL differs from an application in the following ways: 
e it has no specific entry point and thus does not require a main function 
e it may not contain any static data 
e its category file should start with a LIBRARY statement 
e itis linked with a different startup module 


A DYL must be built with the PLIB (rather than CLIB) library, although you may, as usual, use a mix of 
PLIB and CLIB function calls. You should not use true floating point arithmetic in a DYL, but you may 
use the PLIB functions that avoid the 8087 emulator, described in the Floating Point chapter of the PLIB 
Reference manual. 


An example DYL 


This dynamic library (the source of which, on installation, is copied into a \sibosdk\oopdemo directory) 
contains one class, providing a method to sort an array of integers, using the quicksort service provided by 
PLIB. 


The example source 


The source for this application consists of two files: 


sort.cat the sort object class definition 
isort.c the tsort method function 


The category file, sort.cat, is as follows: 
LIBRARY sort 
EXTERNAL olib 
INCLUDE olib.g 
CLASS isort root 


{ 


ADD sort Sort a given list of integers 


} 


OBJECT ORIENTED PROGRAMMING GUIDE 


The method function file, isort.c, contains only one method function: 
/ * 

SORT.C 

*/ 


include <sort.g> 


define IdataBuf ((INT *) DataBuf) 


LOCAL_C INT OrdFunc(INT i, INT j,VOID *DataBuf) 
/* 

Order function used in qsort 

*/- 

{ 

return (IdataBuf [i]-IdataBuf[j]); 

} 


LOCAL_C VOID ExchFunc(INT i, INT j,VOID *DataBuf) 


/* 
Exchange function used in qsort 
a 

{ 

INT k; 


k=IdataBuf [i]; 

IdataBuf [i]=IdataBuf[j]; 
IdataBuf [j]=k; 

} 


#pragma METHOD_CALL 


METHOD VOID isort_sort(PR_ROOT *self,INT *start,INT num) 
{ 


p_qsort (num, OrdFunc, ExchFunc, start) ; 


} 
Building the example DYL 


As in the previous chapter, in order to illustrate the various stages, a number of separate batch files are 
used. Again, an alternative would be to use the more integrated approach as is used in the building of the 
Hello World example, described in the chapter An HWIM Application - Hello World. 


The first step in building the DYL is to translate the category file, sort.cat, using the tool ctran.exe. This 
generates a number of files, including a sort.g include file and a sort.c C language source file. 


As in the case of building an application, the generated .c file must be compiled and the resulting .obj file 
must be converted so that its class descriptor data is located in the code segment. The entire process is 
again conveniently performed by means of the same ct.bat batch file as is used for application category 
files. See the Building an Object Oriented Application chapter for further details. 


The remaining .c source files (in this case, only isort.c) must be compiled as normal, using the same 
cc.bat file as is suitable for compiling application source files. 


Finally the DYL must be linked. As for building an application, the link is controlled by a project file, in 
this case sort.pr: 


#system epoc dyl 

#set epocinit=iplib 
#model small jpi 
#pragma link (olib.1lib) 
#pragma link (sort) 
#pragma link (isort) 
#link sort 


The significant difference between a project file for a DYL and that for an object oriented application is 
that the file type is declared as ay1, rather than img. Again, it is essential that the PLIB library be used. 


3 BUILDING A DYNAMIC LIBRARY 


You may use the same /f-bat link batch file as for linking applications, but you may wish to use the 
following [fc.bat variant: 


@echo off 

if exist %1.dyl del %1.dyl 
call checkvid 

tsc Sl.pr /1 /Sjpivids 


This ensures that any failure in the link process does not result in an older version of the DYL being left. 
It is called as: 


Life. sort 
The entire build process may be summarised in a dylbld.bat batch file, as follows: 


call ct sort 
call ce isort 
lfc sort 


Using the example DYL 


The following code, in runsort.c, illustrates a simple application that uses sort.dyl to sort the contents of 
an array of ten integers. 


/* 
RUNSORT.C 
yf, 


#include <plib.h> 
#include <sort.g> 


GLREF_D VOID *DatCommandPtr; 
GLDEF_D P_RECT _DefScreenRect; 
LOCAL_D INT array[] = {10,1,5,7,9,3,6,8,4,2}; 


#pragma save, ENTER_CALL 


LOCAL_C INT RunSort (HANDLE dyl) 


{ 
VOID *sort; 


sort=f_newlibh (dyl,C_ISORT) ; 
p_send4 (sort,O_SORT, &array[0],10); 
p_send2 (sort,O_DESTROY) ; 

return (0); 


} 


#pragma restore 


GLDEF_C INT main(VOID) 
{ 
INT err,i; 
HANDLE dyl; 
TEXT buf [P_FNAMESIZE]; 


p_fparse("sort.dyl",DatCommandPtr, &buf[0],NULL) ; 

err=p_loadlib (&buf[0],&dyl, TRUE) ; 

if (!'!err) 
{ 
_DefScreenRect.t1.x=0 
_DefScreenRect.tl.y=0; 
_DefScreenRect.br.x=40; /* 40 columns */ 
_DefScreenRect.br.y=8; /* 8 rows */ 


; /* set console window size */ 


err=p_enter2 (RunSort,dyl); 
p_unloadlib(dyl) ; 
for (i=0;i<10;i++) 
p_printf("%sd",array[i]); 
} 
return(err); 


} 


In this example the DYL is loaded and linked by the call to p_loadiib. Since the DYL name is parsed 
with DatCommanaPtr, sort.dyl will be expected to be found in the directory from which runsort is executed. 


3-3 


OBJECT ORIENTED PROGRAMMING GUIDE 


Reporting is via an automatically opened console window, whose size is set to be suitable for the Series 3 
screen. Note that the code to set the console window size, using _DefScreenRect, 1s only necessary when 
you want to override the default size, or when the application is to be run on an emulator. If you include 

this code in an application, ignore the spurious warning of duplication that is given by the linker. 


A DYL that supplies the ROOT class 


Since OLIB contains classes that provide many basic services, most DYLs will either reference or subclass 
one or more OLIB classes. They will therefore need to declare an external reference to OLIB, as in the 
previous example. 


In that example, however, the external reference is necessary only because the rsort class subclasses the 
Root class provided by the OLIB dynamic library. In such a case it may be more reasonable for a DYL to 
define its own Root class and be independent of OLIB. The following sample code provides the same 
integer sorting functionality as the previous example, but without requiring an external reference to OLIB. 


The category file, rsort.cat, 1s as follows: 


LIBRARY rsort 


INCLUDE p_std.h 
INCLUDE p_object.h 


CLASS root 


{ 
ADD destroy 


ADD sort Sort a given list of integers 
PROPERTY 

{ 

P_OBJECT pc; Class link 


} 
} 


The class definition of Root duplicates that of the OLIB root class with, in this case, the addition of the 
sort method. 


The method function source file, root.c is almost identical with that of isort.c, the only significant 
difference being that the method function is renamed to root_sort. 


/* 
ROOT.C 
*/ 


include <rsort.g> 


define IdataBuf ((INT *)DataBuf) 


LOCAL_C INT OrdFunc(INT i, INT j,VOID *DataBuf) 
/* 

Order function used in qsort 

*/ 

{ 

return (IdataBuf [i]-IdataBuf[j]); 

} 


LOCAL_C VOID ExchFunc(INT i, INT j,VOID *DataBuf) 
/* 
Exchange function used in qsort 
a: 
{ 
INT k; 


k=IdataBuf [i]; 

IdataBuf [i]=IdataBuf[j]; 
IdataBuf [j]=k; 

} 


3 BUILDING A DYNAMIC LIBRARY 


#pragma METHOD_CALL 


METHOD VOID root_sort (PR_ROOT *self, INT *start,INT num) 
{ 


p_qsort (num, OrdFunc, ExchFunc, start) ; 


} 


Note that there is no need to provide the root_destroy method function, since this is supplied by the 
PLIB library. 


Building the DYL follows exactly as in the previous example, using the link project file, rsort.pr: 


#system epoc dyl 

#set epocinit=iplib 
#model small jpi 
#pragma link (olib.1lib) 
#pragma link (rsort) 
#pragma link (root) 
#1link rsort 


Building DYLs into an application 


One or more DYLs may be combined into a .img or a .app file. The technique is similar to the add-file 
technology that can combine a resource file, an icon and a shell data file with a .img file. A significant 
difference is that whereas add-files are limited to a maximum of four add-files, there is no limit on the 
number of DYLs that may be added. 


The main advantage of building DYLs into an application is that there is then no danger of the various 
files becoming separated, or of an essential DYL being accidentally deleted. There can never be any 
confusion over the location of a DYL and if the application is present, its DYLs must also be present. 


DYL add-file lists 


A DYL add-file list is a text file with a .dfl extension, containing a list of the names of the DYLs that are 
to be combined with a .img file. For example, the Series 3a Spreadsheet has a .dfl file with the content: 


hfl.dyl 
hgf.dyl 
hgp.dyl 
hdb.dyl 
hta.dyl 
.dyl 
hpr.dyl 
hrg.dyl 
hvw.dyl 
hso.dyl 


HHONDHHHADWH YN DD 
=) 
ion 
BK 


to build ten DYLs into the Spreadsheet application. 


When any .pr project file is invoked that leads to the building of a .img file, a check is made for the 
existence of a .dfl file with the same name as the application. If this file exists, the DYLs it lists are 
automatically built into the .img file, in the order in which they are listed. 


Accessing a built-in DYL 


A DYL that is built into an application is accessed by use of the functions p_openiib and p_loadfilelib., 
as in the following example. The required DYL is specified in a call to p_loadfilelib by an index, 
counting from zero, where the numbering order is determined by the order in which the DYLs are listed 
in the .dfl file. 


The example code, dylsort.c, is a variant of runsort.c, described earlier. It has exactly the same action as 
the earlier example, but sort.dyl is built into the resulting .img file, rather than being a separate file. 


/* 
DYLSORT.C 
*/ 


#include <plib.h> 
#include <sort.g> 


OBJECT ORIENTED PROGRAMMING GUIDE 


GLREF_D VOID *DatCommandPtr; 
GLDEF_D P_RECT _DefScreenRect; 
LOCAL_D INT array[] = {10,1,5,7,9,3,6,8,4,2}; 


#pragma save, ENTER_CALL 


LOCAL_C INT RunSort (HANDLE dyl) 


{ 
VOID *sort; 


sort=f_newlibh (dyl,C_ISORT) ; 
p_send4 (sort,O_SORT, &array[0],10); 
p_send2 (sort, O_DESTROY) ; 

return (0); 


} 
#pragma restore 


GLDEF_C INT main(VOID) 
{ 
INT err,i; 
HANDLE dyl; 
VOID *dcb; 


p_openlib(&dcb,DatCommandPtr); /* open application .img file for DYL access */ 
err=p_loadfilelib(dcb,0,&dyl,TRUE); /* load the first (and only) DYL */ 
if (!'err) 

{ 

_DefScreenRect.t1l.x=0; 

_DefScreenRect.tl.y=0; 

_DefScreenRect.br.x=40; 

_DefScreenRect.br.y=8; 


peprintL ("Setting ...");7 
err=p_enter2 (RunSort,dyl); 
p_unloadlib(dyl) ; 
for (i=0;i<10;i+=2) 
p_printf£("s4d %4d",array[i],array[it1]); 
p_getch(); 
} 
return(err); 


} 


The only difference in the code between this example and runsort.c is in the first two lines of main(). In 
contrast with the earlier example, there is no need to parse the application's full file specification (pointed 
to by DatcommanapPtr) with the DYL file name, since the file containing the DYL is the application file 
itself. 


The DYL add-file list is dylsort.dfl, which contains the single line: 
sort.dyl 
The application can be built in a similar way to runsort, that is, by: 


cc dylsort 
1f dylsort 


Running edump.exe, by typing: 
edump dylsort 


produces the following output, showing the presence of the built-in sort.dyl. 


3 BUILDING A DYNAMIC LIBRARY 


EDump V4.30F (09/11/93) Copyright (C) Psion PLC 1989-92 
LOC: :E:\SIBOSDK\OOPDEMO\DYLSORT.IMG IMAGE file data 


Image version = 200F 

Code Segment = 0330 (bytes) 
Initial IP = 0000 

Stack = 1000 (bytes) 
Data = 0070 (bytes) 
Heap = 0800 (bytes) 
Data Segment = 1870 (bytes) 
Initialized data = 0050 (bytes) 
Code checksum = 765A 

Data checksum = 06B7 

Code Version = 100F 

Priority = 0080 

Header size = 0040 (bytes) 

Dyl count = 0001 

Dyl table offset = 000005E0 

Dyl 00 SORT.DYL offset = 000003C0 
Image file size = 000005F2 (bytes) 


CHAPTER 4 


AN HWIM EXAMPLE - HELLO WORLD 


This chapter describes a minimal HWIM application. The application displays a bordered window 
containing the text "Hello world" and has a menu bar that offers a single Exit option. It is written so that 
it will run on either the Series 3 or the Series 3a (in compatibility mode). 


Note that this application is not intended to exercise the full potential of OOP. Instead, it gives a "feel" for 
the construction of an OOP application and, while its broad structure will be described, a full 
understanding may not be apparent until later chapters in this manual have been read. The example code 
does, however, provide the basic framework of all HWIM applications and may be used as a starting point 
for the construction of more complex applications. 


The source of this application is supplied in the \sibosdk\oopdemo directory that can be installed from the 
C SDK disks. The main source files for the application are listed below, and are described in more detail 
in the following sections. 


Category file, hello.cat 


IMAGE hello 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE hwimman.g 


CLASS hellows wserv 
window server active object 
{ 
REPLACE ws_dyn_init 
} 


CLASS hellobw bwin 
a simple bordered window 


{ 
REPLACE wn_init 
REPLACE wn_draw 
} 


Resource file, hello.rss 


/* 
HELLO.RSS 


English resource file for Hello World application 
*/ 


#include <hwim.rh> 
#include <hello.rg> 


RESOURCE WSERV_INFO hello_accs 
{ 
menbar_id=hello_menbar; 
first_com=O0_COM_EXIT; 
accel={'x'}; /* Exit */ 


} 


OBJECT ORIENTED PROGRAMMING GUIDE 


RESOURCE MENU_BAR hello_menbar 
{ 
items= 
{ 
MENU_BAR_ITEM 
{ 
menu_id=special_menu; 
mb_item="Special"; 


} 


} 


RESOURCE MENU special_menu 
{ 
items = 
{ 
MENU_ITEM 
{ 
com_id=O_COM_EXIT; 
mn_item="Exit"; 
} 
di 
} 


Source code, o_hello.c 


/* 
O_HELLO.C 
my 


#include <hwimman.g> 
#include <hello.g> 


GLREF_D WSERV_SPEC *wserv_channel; 


GLDEF_C VOID main (VOID) 
{ 
IN_HWIMMAN app; 
IN_WSERV ws; 


p_linklib(0); 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE|FLG_APPMAN_CLEAN; 
app.wserv_cat=p_getlibh (CAT_HELLO_HELLO) ; 

ws.com_cat=p_getlibh (CAT_HELLO_HWIM) ; 

app.wserv_class=C_HELLOWS; 

ws.com_class=C_COMMAN; 

p_send4 (p_new (CAT_HELLO_HWIM, C_HWIMMAN) ,O_AM_INIT, &app, &ws) ; 


} 
#pragma METHOD_CALL 


METHOD VOID hellows_ws_dyn_init (PR_HELLOWS *self) 


{ 
self->wserv.cli=f_new (CAT_HELLO_HELLO, C_HELLOBW) ; 


p_send2 (self—->wserv.cli,O_WN_INIT)j; 
} 


METHOD VOID hellobw_wn_init (PR_HELLOBW *self) 


{ 
W_WINDATA wd; 


wd.extent.tl.x=0; 

wd.extent.tl.y=0; 

wd.extent .width=wserv_channel->conn.info.pixels.x; 
wd.extent .height=wserv_channel-—>conn.info.pixels.y; 
p_send5 (self, O_WN_CONNECT, NULL, W_WIN_EXTENT, &wd) ; 
p_send3 (self,O_WN_VISIBLE, WV_INITVIS) ; 

p_send3 (self, O_WN_EMPHASISE, TRUE) ; 


} 


METHOD VOID hellobw_wn_draw(PR_HELLOBW *self) 
{ 
p_supersend2 (self,O_WN_DRAW) ; 
gPrintText (50,50,"Hello World",11); 


} 


4 AN HWIM EXAMPLE - HELLO WORLD 


The category file 


The application's category file is as follows: 


IMAGE hello 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE hwimman.g 


CLASS hellows wserv 
window server active object 


{ 
REPLACE ws_dyn_init 
} 


CLASS hellobw bwin 

a simple bordered window 
{ 
REPLACE wn_init 
REPLACE wn_draw 
} 


The imac statement identifies the file as being one that will create a category for an application (img or 
.app) file. Note that the external references to the OLIB and HWIM DYLs are mandatory for all HWIM 
applications. 


The category file defines two application-specific classes, HELLows and HELLOBW. These are subclasses of 
the HWIM wserv window server active object and pwn bordered window classes respectively. These two 
subclasses add no property or new methods; they just replace methods that are defined in a superclass. 


The swin class is described in more detail in the Windows chapter. 


The resource externals file 


The resource file, described in the next section, contains a reference to the o_com_Ex1T method number. 
This symbol is defined in the hwimman.g include file (itself generated by the translation of the category 
containing the hwimman class). 


A resource externals file, with file name hello.re, should be written, containing the following lines: 
#include <hwimman.g> 
_O_COM_EXIT 
The file hello.rg is generated by the re. bat batch file by typing: 
re hello 
and contains the single line: 
#define O_COM_EXIT 7 


This process has extracted the definition of the symbol o_com_exi1t from the hwimman.g include file, so 
that the generated .rg file can be #included in the application resource file instead of the larger 
hwimman.g. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The resource file 


The resource file, hello.rss, is one of the simplest possible HWIM application resource files: 


/* 
HELLO.RSS 


English resource file for Hello World application 
ae A 


#include <hwim.rh> 
#include <hello.rg> 


RESOURCE WSERV_INFO hello_accs 
{ 
menbar_id=hello_menbar; 
first_com=O_COM_EXIT; 
accel={'x"'}; /* Exit */ 


} 


RESOURCE MENU_BAR hello_menbar 
{ 
items= 
{ 
MENU_BAR_ITEM 
{ 
menu_id=special_menu; 
mb_item="Special"; 
} 
‘i; 
} 


RESOURCE MENU special_menu 
{ 
items = 
{ 
MENU_ITEM 
{ 
com_id=O_COM_EXIT; 
mn_item="Exit"; 
} 
i 
} 


In addition to hello.rg, it includes the HWIM resource header file, hwim.rh, that defines the standard 
resource structures used by HWIM applications (for example, the wszERv_1nFo resource structure). This 
resource file contains three resources that must be present in all HWIM application resource files, being 
used by the HWIM command menu mechanism. 


The first resource must always be a wsERV_INFo resource structure. Its reference name (in this case, 
hello_accs) 1s normally not relevant. The menbar_id element must refer to a following mzenu_BAR 
resource structure that defines the content of the application's menu bar, and first_com Is set to the 
method number of a method of the application's command manager (usually, as in this case, o_coM_EXIT). 
The acce1 element defines one or more accelerator keypresses. Each accelerator may be used to invoke a 
command menu option by executing a command manager method function. In this example there is only 
one accelerator, Psion-X, which executes the command manager's exit method (with method number 
O_COM_EXIT). 


The menu_gar resource defines a menu bar containing a single menu, with menu name "Special", and an 
associated pull-down menu defined in the menu resource structure referenced by special_menu. This pull- 
down menu contains only the single menu option "Exit". 


The source code 


The code is sufficiently brief that there is no advantage in writing it as a number of separate C modules. 
All the code is in the file o_hello.c (so named to distinguish it from the hello.c that is generated during 
the translation of hello.cat). 


4-4 


4 AN HWIM EXAMPLE - HELLO WORLD 


/* 
O_HELLO.C 
af 


#include <hwimman.g> 
#include <hello.g> 


GLREF_D WSERV_SPEC *wserv_channel; 


GLDEF_C VOID main(VOID) 
{ 
IN_HWIMMAN app; 
IN_WSERV ws; 


p_linklib(0); 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE|FLG_APPMAN_CLEAN; 
app.wserv_cat=p_getlibh (CAT_HELLO_HELLO) ; 

ws.com_cat=p_getlibh (CAT_HELLO_HWIM) ; 

app.wserv_class=C_HELLOWS; 

ws.com_class=C_COMMAN; 

p_send4 (p_new (CAT_HELLO_HWIM, C_HWIMMAN) ,O_AM_INIT, &app, &ws) ; 

} 


#pragma METHOD_CALL 
/* APPLICATION-SPECIFIC WINDOW SERVER OBJECT */ 


METHOD VOID hellows_ws_dyn_init (PR_HELLOWS *self) 
{ 
self—->wserv.cli=f_new (CAT_HELLO_HELLO, C_HELLOBW) ; 
p_send2 (self-—>wserv.cli,O_WN_INIT) ; 

} 


/* BORDERED CLIENT WINDOW */ 


METHOD VOID hellobw_wn_init (PR_HELLOBW *self) 


{ 
W_WINDATA wd; 


wd.extent.tl.x=0; 

wd.extent.tl.y=0; 

wd.extent .width=wserv_channel->conn.info.pixels.x; 
wd.extent .height=wserv_channel-—>conn.info.pixels.y; 
p_send5 (self, O_WN_CONNECT, NULL, W_WIN_EXTENT, &wd) ; 
p_send3 (self,O_WN_VISIBLE, WV_INITVIS) ; 

p_send3 (self, O_WN_EMPHASISE, TRUE) ; 

} 


METHOD VOID hellobw_wn_draw(PR_HELLOBW *self) 
{ 
p_supersend2 (self,O_WN_DRAW) ; 
gPrintText (50,50,"Hello World",11); 
} 
Main 


The main() function is basically as specified in the Source files section of the Introduction chapter. Note 
that, since the window server active object is subclassed by the application, it is indicated to be in the local 
category (category number caT_HELLO_HELLO). In contrast, the application uses the basic command 
manager supplied by HWIM and thus in the HWIM external category (category number caT_HELLO_HWIM). 


The method functions are preceded by: 
#pragma METHOD_CALL 

to ensure the correct calling convention. 

Window server object 


The ws_dyn_init method supplied for the HzLLows class simply creates an instance of the HELLOBW 
bordered window subclass and sends it a wn_InrT message. The window is set to be the application's client 
window by writing its handle to the window server active object's wserv.cli property. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Client window 


Initialisation of the bordered client window sets the window's size to the screen dimensions, as read from 
the wsERV_sPEc struct pointed to by the magic static wserv_channe1. Most application-specific window 
classes will supply a wn_init method that, at some stage, sends a wN_connEcT message. Normally, as in 
the present case, the client window is a top-level window (it has no parent) as indicated by the nuLu 
parameter to the wN_conNECT message. 


The window is made visible and set to the emphasised state by sending wN_v1sIBLE and wN_EMPHASISE 
messages. These messages are processed by superclass method functions (described in the Windows 
chapter of this manual). 


The wn_draw method is not explicitly called by application code, but will be called directly from system 
code whenever the window must be drawn (such as when it first becomes visible). See also the The 
draw/redraw mechanism section of the Windows chapter. 


After supersending the wn_praw message (handled by the swin superclass, to draw the window's border) 
the text "Hello World" is drawn, using the window server function gPrintText. 


Building the application 


Unlike the simple examples in earlier chapters, the "Hello World" example provides a good basic model 
for most object oriented applications written for the Series 3 and Series 3a machines. The mechanism 
supplied for building this example, described below, is suitable for use with any such application. It uses 
the TopSpeed Make mode to invoke the project system, in order to minimise the amount of compilation 
and linking required to make sure that the .img file is up-to-date. 


The .pr file for the "Hello World" application is hello.pr and is listed below. 


system epoc img 
set epocinit=iplib 
model small jpi 


abort on 


set version=0x100F 
set priority=0x80 
set heapsize=0x80 


compile %main.cat 


if tremake #or (%main.rg #older Smain.re) #or (%Smain.rg #older %main.g) #then 
run "re %main" no_window no_abort 
endif 


if (Smain.rsg #older %main.rss) #or (%main.rsg #older %Smain.rg) #then 
run "rs %main" no_window no_abort 
endif 


if Smain.rzc #older %Smain.rsg #then 
run "rch $main" no_window no_abort 
endif 


compile o_hello.c 


if Smain.shd #older %main.ms #then 

#run "makeshd %main" no_window no_abort 
#file delete %smain.img 

endif 


if (Smain.img #older %main.afl) #or (%main.img #older %main.pic) #then 
#file delete %Smain.img 
endif 


pragma link(olib.1lib) 
pragma link (hwim.1lib) 


link %Smain 


The TopSpeed project system takes account of the dependencies in the application's C source files but, as 
can be seen in this example, the .pr file must explicitly take into account the dependencies of all other files 
that are needed to build the application. 


4 AN HWIM EXAMPLE - HELLO WORLD 


The hello.pr project file is suitable for use as a model for building any object oriented application using 
the HWIM library. In the vast majority of cases the main change will be to replace the line: 


#compile %o_hello.c 


with one or more #compile statements to compile the various C source files that contain the application's 
code. 


In this example the variables version, priority and heapsize are set (as hexadecimal numbers) to their 
default values - they will be given these values automatically if they are not set in the .pr file. Only in 
exceptional circumstances will you need to alter the application's priority, but you should set both the 
version number and the heap size as a matter of course. See the General System Services chapter of the 
PLIB Reference manual for a description of the format of a version number. The heap size is measured in 
paragraphs (16-byte units) and should normally be set to a value that is not less than the amount of the 
heap used when the application is started up (one way of determining this is to use the Spy application, 
described in the Series 3/3a Programming Guide). There is more information about the use of these three 
variables in the Building an Application chapter of the General Programming Manual. 


The version of tsprj.txt supplied with the SDK contains a compiler entry to handle the translation of 
category files. It is therefore important to copy this file into your \ts\sys directory and run tscfg.exe, as 
described in the Installation chapter of the General Programming Manual, even if you are upgrading from 
an earlier version of the SDK. This entry, which is listed below, takes into account the creation dates of 
the category file and the .g file that is generated from it when determining if the category file needs 
translating. 


#declare_compiler cat= 
'#split %%srce 

#set make=%%remake 

#if Stmake #or %sname.g #older S%name.cat #then 

#run "ctran %%name -c -s -v -l -e..\include\ -x..\include\ -g..\include\" no_window 
no_abort 

#rundll TSC S%name.c %%name.obj 

#run "ecobj S%name.obj" no_window no_abort 

#endif 

#pragma link(%%name.obj)' 


If you include application-specific header files in the application's category file you will need to add an 
explicit check in your .pr file to ensure that the application is rebuilt properly. If, for example, your 
category file contains the line: 


NCLUDE myheader.h 
you will need to insert the lines: 


if myheader.h #older %main.cat #then 
#file delete %main.g 
endif 


immediately before the line: 


compile %Smain.cat 


Apart from the #pragma link statements to include the standard libraries (OLIB and HWIM) the 
remainder of the .pr file takes care of the dependencies of the application on the remaining component 
files in a fairly straightforward way. 


If you develop an application that contains built-in DYLs you will need to add further checks on the 
creation dates of the .dfl DYL add-file list and the DYLs that it lists. A suitable check for the .dfl file 
would be: 


#if Smain.img #older %main.dfl #then 
#file delete %Smain.img 
#endif 


OBJECT ORIENTED PROGRAMMING GUIDE 


and a check for mydyl.dyl is: 


#if Smain.img #older mydyl.dyl #then 
#file delete %Smain.img 
#endif 


Any such lines should be inserted immediately before the first #pragma link statement. 
The "Hello World" application is built by typing: 

make hello 
This makes use of a make.bat batch file, whose contents is as follows: 


@echo off 

call checkvid 

if not exist %l.pr goto error 

tscx /m %1 /%jpivids 

tscx /m %1 /%jpivids 

goto end 

:error 

echo Project file %1.pr does not exist 
send 


Note that this batch file uses tscx, rather than tsc, so that full use may be made of the extended memory 
of your PC. If you do not have extended memory, you should replace each occurrence of tscx with tsc. 


The batch file uses two passes of the TopSpeed project system. This is necessary because the project 
system does not take into account dependencies that change dynamically as a result of the ‘compilation’ of 
the Psion-specific source files. This will not normally result in any unnecessary repetitions of compilation 
or linking. 


The result of typing make hello is the creation of hello.img. The final step in producing the "Hello 
World" application is (optionally) to rename hello.img as hello.app. 


Variants 


You may like to experiment with making the following variations to the supplied example code: 


e To make the application take advantage of the larger screen size on the Series 3a, or the flag 
FLG_APPMAN_FULLSCREEN into the flag values assigned to app. flags iN main(). 


e =©In the HELLOWS ws_dyn_init method, the lines: 


self—->wserv.cli=f_new (CAT_HELLO_HELLO, C_HELLOBW) ; 
p_send2 (self-—>wserv.cli,O_WN_INIT); 


could be replaced by the more compact: 


self—->wserv.cli=f_newsend (CAT_HELLO_HELLO, C_HELLOBW, O_WN_INIT) ; 


e In the HELLOBW wn_init method, replace 


p_send3 (self, O_WN_VISIBLE, WV_INITVIS) ; 
with the equivalent, but more compact: 
hiInitVis (self); 


e Modify the bordered window's wn_draw method to centre the text in the window, in a way that 
works for both the Series 3 and Series 3a, by reading the screen dimensions from 


wserv_channel->conn.info.pixels 


e §=Put the "Hello World" text in the resource file and load it from there (for guidance, see the 
examples in the Commands and Command Menus chapter). 


CHAPTER 5 


COMMANDS AND COMMAND MENUS 


The command manager 


The command manager is created and initialised by system code during the start-up process, immediately 
before the window server active object receives a WS_DYN_INIT message. If the creation of the command 
manager fails the application process will terminate immediately. If the command manager is created 
successfully, its handle is accessible via the handle of the window server active object, held in the magic 
static w_ws. This static should be declared as!: 


GLREF_D PR_WSERV *w_ws; 


and the command manager's handle is then w_ws->wserv.com. See also the utility function hwservCcomSend 
that is used by system code to send messages to the command manager. 


The HWIM cowman class definition (whose associated generated header file is comman.g) is as follows: 


CLASS comman root 
Superclass of all command managers 


ADD com_init=p_dummy User's own initialisation 

ADD com_statwin=p_dummy Toggle permanent status window 

ADD com_accl_check=p_true Called whenever an accelerator is matched 
ADD com_menu=p_dummy Called whenever a menu is pulled down 


ADD com_mode_change=p_dummy W_KEY_MODE received 
ADD com_file_change=p_dummy The core code for open or new 


ADD com_exit Message sent here on exit accelerator 
CONSTANTS 

{ 

O_COM_SYS_LAST O_COM_EXIT 


} 
} 


The supplied methods of the command manager are called by system code. As is apparent from the class 
definition, the supplied method functions do very little. Although there is no need to replace any of these 
methods, they are intended to be replaced, as necessary, in an application-specific subclass. The supplied 
com_exit method, for example, simply calls p_exit (0). While this is sufficient for a simple application, 
the method will usually be replaced - particularly if the application is file-based. The uses of most of these 
methods, and the circumstances under which the corresponding messages are sent by system code are 
discussed later in this chapter and are fully described in the in the Command Manager chapter of the 
HWIM Reference manual. The com_file_change and com_exit methods are also discussed in the File 
Based Applications chapter of this manual. 


'Depending on the application, it may be more appropriate to declare w_ws as a pointer to an instance of 
an application-specific subclass of wsERV. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Adding command options 


Any HWIM application that has a more complex set of commands than that of the "Hello World" example 
applicaton will need to supply more extensive menu bar and pull-down menu resources. It will also need 
to subclass the comman command manager class. 


The basic mechanism for extending the number of command options involves the following steps: 


e Extend the menu bar and pull-down menu resources in the application resource file to include a 
menu item for each command. 


e Subclass the command manager to add a new method for each additional command and supply a 
method function for each method.? The methods must appear consecutively in the command 
manager's class definition and normally must immediately follow the last (com_exit) method of 
the comman superclass. 


e Include an accelerator in the first resource (a wsERV_INFo resource) in the application resource 
file for each additional command. 


The list of accelerators must obey the following rules: 
e There must be a unique accelerator for every command option that appears in the menus. 


e The number of accelerators in the list must match with a contiguously declared set of command 
manager methods. 


e The accelerators must be listed in the same order as the method declarations in the command 
manager class. 


Note, however, that there need be no relation between the order of the accelerators and the order in which 
the commands appear in the command menus. The Exit option, for example, conventionally appears as 
the last item in the last menu, although it is normally first in the list of accelerators. 


The sending of the appropriate message to the command manager when a command menu option is 
selected, either by means of an accelerator keypress or via the pull-down menus, is handled by system 
code in the window server active object and requires no additional application-specific code. The method 
functions that correspond to the menu options are not expected to return a value and should be declared as 
voip functions. In general these functions do not take parameters, but may optionally make use of a single 
INT parameter, containing the function's method number, this being passed in system-generated messages 
(see the later Sharing method function code section). 


Example 


The following example extends the "Hello World" example to provide two additional command options in 
a separate pull-down menu. The additional commands switch the display to one of two alternative 
messages. The source code is supplied in the files hello2.rss, hello2.cat and o_hello2.c. 


The resource file is as follows: 


/* 

HELLO2.RSS 
English resource file 
af, 


#include <hwim.rh> 
#include <hello2.rg> 


RESOURCE WSERV_INFO hello_accs 
{ 
menbar_id=hello_menbar; 
first_com=O_COM_EXIT; 
accel={'x', /* Exit */ 
‘ht, /* Hello */ 
ou a /* Bye */ 


2Note that, if appropriate, several commands may share the code of a single method function. This 
alternative is described later. 


5-2 


5 COMMANDS AND COMMAND MENUS 


RESOURCE MENU_BAR hello_menbar 
{ 
items= 
{ 
MENU_BAR_ITEM 
{ 
menu_id=message_menu; 
mb_item="Message"; 
hy 
MENU_BAR_ITEM 
{ 
menu_id=special_menu; 
mb_item="Special"; 


} 


RESOURCE MENU message_menu 
{ 


items = 

{ 

MENU_ITEM 
{ 
com_id=O_HCM_HELLO; 
mn_item="Say hello"; 
hy 

MENU_ITEM 


{ 
com_id=O_HCM_BYE; 
mn_item="Say goodbye"; 


} 


} 


RESOURCE MENU special_menu 
{ 
items = 
{ 
MENU_ITEM 


{ 
com_id=O_COM_EXIT; 
mn_item="Exit"; 


} 


RESOURCE STRING hello_message {str="Hello World"; } 


RESOURCE STRING bye_message {str="Goodbye"; } 


The list of accelerators contains three items, and the menu bar has two pull-down menus; a 'Message' 
menu and a ‘Special’ menu. The ‘Message’ menu contains the two options 'Say hello’ and 'Say goodbye’, 
associated with the command manager methods nem_hello and hcm_bye respectively. The two text 
messages to be displayed are included in the resource file, rather than appearing as data in the source 


code. 


The category file defines a HELLO2 category (the tmacE statement 1s IMAGE hello2) that contains an 
additional class definition for the HELLOc™ class - a subclass of the HWIM comman class: 


CLASS hellocm comman 
command manager 


{ 
ADD hcm_hello 


ADD hcm_bye 
} 


Note that the order of declaration of the methods must agree with the order of the associated accelerators 


in the resource file. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The corresponding command manager method functions are: 


METHOD VOID hellocm_hcm_hello(PR_HELLOCM *self) 
{ 
p_send3 (w_ws—>wserv.cli,O_WN_SET, FALSE) ; 
} 


METHOD VOID hellocm_hcm_bye (PR_HELLOCM *self) 
{ 
p_send3 (w_ws->wserv.cli,O_WN_SET, TRUE) ; 
} 


Both of these methods send a wN_szeT message to the client window, whose handle is available as 
w_ws->wserv.cli (similar to accessing the command manager itself, as described earlier). Note that these 
actions correspond to a design decision that the client window should itself be responsible for recording 
which message to display. 


The client window class thus needs to replace the wn_set method and specify an item of property to record 
the message state, as follows: 


CLASS hellobw bwin 
bordered client window 
{ 
REPLACE wn_init 
REPLACE wn_draw 
REPLACE wn_set 
PROPERTY 
{ 
UWORD isbye; 
} 
} 


The client window wn_draw method uses the state of its isbye property to determine which of the two text 
resources to display: 


METHOD VOID hellobw_wn_draw(PR_HELLOBW *self) 
{ 
UINT resid; 
TEXT buf [40]; 


resid=self-—>hellobw.isbye ? BYE_MESSAGE:HELLO_MESSAGE; 

p_send4 (w_am,O_AM_LOAD_RES_BUF, resid, &buf[0]); /* load the resource */ 
p_supersend2 (self,O_WN_DRAW) ; /* draw the border */ 
gPrintText (50,50, &buf[0],p_slen(&buf[0])); 

} 


Note that the line: 


p_send4 (w_am, O_AM_LOAD_RES_BUF, resid, &buf[0]); 


could be replaced by the following use of the hLoadResBuf utility function: 
hLoadResBuf (resid, &ébuf[0]); 


The wn_set method sets or clears this property and causes the window to be drawn with the appropriate 
message: 


METHOD VOID hellobw_wn_set (PR_HELLOBW *self,UINT isbye) 


{ 
self—>hellobw.isbye=isbye; 
p_send2 (self, O_WN_DODRAW) ; 
} 


Note that the application does not explicitly write to the isbye property on initialisation of the window. It 
takes advantage of the fact that property is zero filled when an object is created. 


Essentially the same effect could be achieved by replacing the line: 
p_send2 (self, O_WN_DODRAW) ; 
with the window server call: 


wiInvalidateWin(self-—>win.id) ; 


5-4 


5 COMMANDS AND COMMAND MENUS 


This would result in the client window receiving, at some future time, a wN_REDRAw message. This 
technique might prove useful if two or more things could change that require the window to be redrawn. If 
all such changes invalidate the window, there is less likelihood that each change will cause the window to 
be redrawn. 


In this example the difference between the two alternatives is not noticeable. Invalidating the window 
causes inter-process messages to be sent between the application and the window server, whereas the 
sending of the wn_popRaw message does not and will, in general, result in the window being updated more 
responsively. Which of these two techniques to use depends to a large extent on the requirements of a 
particular application. 


The code of main() must also be modified slightly to take into account the use of an application-specific 
command manager: 


GLDEF_C VOID main(VOID) 
{ 
IN_HWIMMAN app; 
IN_WSERV ws; 


#ifndef EPOC 
GLREF_D P_DEVICE p_file; 
GLREF_D P_DEVICE p_serial; 
p_inst (&p_file, &p_serial,NULL_D); 

#endif 
p_linklib(0); 
app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_CLEAN|FLG_APPMAN_SRSCFILE; 
app.wserv_cat=p_getlibh (CAT_HELLO2_HELLO2) ; 
ws.com_cat=p_getlibh (CAT_HELLO2_HELLO2) ; 
app.wserv_class=C_HELLOWS; 
ws.com_class=C_HELLOCM; 
p_send4 (p_new (CAT_HELLO2_HWIM, C_HWIMMAN) ,O_AM_INIT, &app, &ws) ; 
} 


As mentioned before, replace the line: 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_CLEAN|FLG_APPMAN_SRSCFILE; 

iN main() with the line: 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_CLEAN|FLG_APPMAN_SRSCFILE|FLG_APPMAN_FULLSCREEN; 
to take advantage of the larger screen on the Series 3a. 

Series 3a shifted accelerators 

The Series 3a machine introduced the capability to use shifted accelerators. 


A shifted accelerator is declared by using an upper case character in the accelerator list of the wszeRv_INnFo 
resource. For example, if the wseRv_1nro resource in hello2.rss is replaced by: 


RESOURCE WSERV_INFO hello_accs 

{ 

menbar_id=hello_menbar; 

first_com=O0_COM_EXIT; 

accel={'X', /* Exit */ 
hit, /* Hello */ 
poke hy /* Bye */ 

} 


then the 'Exit' command will be selected by pressing Shift-Psion-X, and will be represented as such in the 
‘Special’ menu. 


An application may use any combination of shifted and unshifted accelerators and may use both the 
shifted and unshifted versions of any or all alphabetic keys. A number of the built-in applications use, for 
example, the unshifted Psion-X for a normal 'Exit' option and the shifted Shift-Psion-X for an 'Exit, lose 
changes' option. In consequence, a Series 3a application may have up to 26 additional options in its 
command menus, compared with a Series 3. 


Because of the additional key depression that is needed to use a shifted accelerator, unshifted accelerators 
should, where possible, be used in preference to shifted accelerators. By convention, however, a Series 3a 
application that has a 'Diamond' menu should use shifted accelerators for the options in this menu (and, 
also by convention, the accelerators should, if at all possible, match the first letters of the items shown in 
the Diamond list in the application's status window). 


OBJECT ORIENTED PROGRAMMING GUIDE 


Series 3a command option grouping 


On the Series 3a, the options in a pull-down menu can be divided into logically connected groups by 
means of one or more horizontal lines. These divisions have a purely cosmetic function, as a guide to the 
user, and have no impact on application code. 


A dividing line is specified in a menu resource struct by oring the flag BREAK_LINE_FOLLOws with the 
command ID of a Menu_1TEm resource struct. For example, if the message_menu MENU resource in 
hello2.rss is replaced by: 


RESOURCE MENU message_menu 
{ 


items = 

{ 

MENU_ITEM 
{ 
com_id=0_HCM_HELLO|BREAK_LINE_FOLLOWS; 
mn_item="Say hello"; 
Ly 

MENU_ITEM 


{ 
com_id=O_HCM_BYE; 
mn_item="Say goodbye"; 
} 
i 
} 


the pull-down menu will have a horizontal dividing line between the 'Say hello’ and 'Say goodbye’ options. 


There is no limit on the number of divisions that may appear in a menu. Excessive use should, however, 
be avoided since it can lead to a cluttered and confusing appearance to the menu. 


Sharing method function code 


All messages sent by system code to the command manager as the result of selecting a menu option or 
using an accelerator (that is, the com_exit method and any following methods added by a subclass) are 
sent by a call to the utility function nwservcomsend. The code for this utility function is effectively as 
follows: 


VOID hWservComSend(INT comid) ; 
{ 


p_send3 (w_ws->wserv.com, comid, comid) ; 


} 


Note that the parameter comia, which is the appropriate method number, is passed as an additional 
parameter and is thus available to the method function. This can be used to advantage when two or more 
methods have very similar method functions, as do the two command manager methods introduced in the 
previous example. The command manager class definition could have been written so that both methods 
share the same method function: 


CLASS hellocm comman 
command manager 
{ 
ADD hcm_hello 
ADD hcm_bye=hellocm_hcm_hello 
} 


The method function code could then be written as: 


METHOD VOID hellocm_hcm_hello(PR_HELLOCM *self, INT comid) 
{ 
if (comid==0_HCM_HELLO) 
p_send3 (w_ws->wserv.cli,O_WN_SET, FALSE) ; 
else 
p_send3 (w_ws—>wserv.cli,O_WN_SET, TRUE) ; 


5 COMMANDS AND COMMAND MENUS 


or, more simply: 


METHOD VOID hellocm_hcm_hello(PR_HELLOCM *self, INT comid) 


{ 
p_send3 (w_ws—>wserv.cli,O_WN_SET, comid==O_HCM_BYE) ; 


} 


Clearly, there is very little advantage in this particular case, since the two methods are so simple. 
Nevertheless, the technique can frequently be used to advantage in real applications. 


Changing the text of an option 


The options to set the message text in the previous example could be replaced by a single option that 
toggles between the two messages. To be effective, the text of the option should also change, depending on 
which message is currently visible. 


Each time a pull-down menu is displayed, its content is reloaded from the resource file. The command 
manager's com_menu method is called immediately before a pull-down menu is displayed, but after the 
menu's text has been loaded.A command manager subclass may therefore replace this method to change 
the text of one or more items. 


Continuing from the previous (hello2) example, we can use a single method instead of the two methods 
com_hello and com_bye. The class definition of HELLocm thus becomes: 


CLASS hellocm comman 
command manager 
{ 
REPLACE com_menu 
ADD hcm_hello 


} 


The resource file needs to be changed to remove an accelerator and the menu item for the 'Say goodbye' 
option. An additional string resource is required to specify the alternative text for the option. The relevant 
resources become: 


RESOURCE WSERV_INFO hello_accs 
{ 
menbar_id=hello_menbar; 
first_com=O_COM_EXIT; 
accel={'x', /* Exit */ 
dee /* Hello/Bye */ 
} 


RESOURCE MENU message_menu 
{ 
items = 
{ 
MENU_ITEM 
{ 
com_id=O_HCM_HELLO; 
mn_item="Say goodbye"; 
} 
i 
} 


RESOURCE STRING say_hello {str="Say hello"; } 
All other resources are unchanged. 


In all cases where menu text is replaced, the text that appears in the mzNu_ITEM resource determines the 
amount of memory allocated for the menu item data and so must be the longest text of all the possible 
alternatives. Thus, in this case, it is important that the text in the option corresponding to the message 
number 0_HCM_HELLO is "Say goodbye" and that the replacement resource is the shorter "Say hello". The 
reason is that the menu resource is loaded into memory as a sequence of items of fixed length, determined 
by the size of the items in the resource. It is therefore possible to overwrite an element with a shorter 
replacement, but an attempt to replace it with a longer element will overwrite part of the following item. 


OBJECT ORIENTED PROGRAMMING GUIDE 


A suitable com_menu method function is as follows: 
#include <varray.g> 


METHOD VOID hellocm_com_menu(PR_HELLOCM *self, INT menu_num, PR_VAROOT *array) 


{ 
UBYTE *p; 


if (menu_num==0) 
{ 
if (p_send2 (w_ws->wserv.cli,O_WN_SENSE) ) 


{ 
p=(UBYTE *)p_send3 (array, O_VA_PREC, 0); 
hLoadResBuf (SAY_HELLO, p+2); /* +2 skips byte count and method number */ 


} 


} 


The method is passed the menu number (counting from zero for the leftmost menu in the menu bar) of the 
pull-down menu that is about to appear and the handle of an OLIB variable array containing the data for 
each of the options in that menu. Each element of this array consists of a leading length byte that indicates 
the (fixed) length of a following mznu_item struct, defined in pulldown.g as: 


typedef struct 
{ 
UBYTE com_id; /* command manager method number */ 
TEXT mn_txt[1]; /* option text, as a zero terminated string */ 
} MENU_ITEM; 


The com_menu method tests if the relevant menu (in this case, the first menu, with menu number zero) is 
about to appear. If so, it further tests whether the text needs to be changed, indicated by the client 
window's isbye property being set (that is, the window is displaying "Goodbye" and so the menu option 
should be 'Say hello'). The additional client window wn_sense method is described below. 


Provided both conditions are met, the address of the appropriate array element (in this case the first - and 
only - element, whose index is zero) is found by sending the array object a va_pRec message. Finally, the 
replacement zero terminated text string is loaded from the resource file to overwrite the original text that 
starts at a two byte offset within the array element. 


An additional client window method, wn_sense, is needed so that the command manager can determine 
the client window's state. The client window class definition must become: 


CLASS hellobw bwin 
bordered client window 
{ 
REPLACE wn_init 
REPLACE wn_draw 
REPLACE wn_set 
REPLACE wn_sense 
PROPERTY 
{ 
UWORD isbye; 
} 
} 


and a suitable method function could be: 


METHOD UINT hellobw_wn_sense (PR_HELLOBW *self) 


{ 
return (self—>hellobw.isbye) ; 


} 


The com_menu method may be used to replace the text of any number of menu options in any number of 
menus. Any one call to the method will, of course, only replace the text of items in the one pull-down 
menu indicated by the menu_num parameter. 


If the application is translated into one or more other languages, it is important to ensure that the text in 
the menu resource is the longest in each translation. To avoid the possibility that the the string to be 
replaced may be different in different languages, it is safer to create stRING resources for every alternative 
and replace the text in all cases. 


5 COMMANDS AND COMMAND MENUS 


In the current example, the resource file would therefore include an additional say_Byz resource and, 
ideally, a note to the translator, as follows: 


RESOURCE MENU message_menu 
{ 
items = 
{ 
MENU_ITEM 
{ 
com_id=O_HCM_HELLO; 
mn_item="Say goodbye"; 
/* TRANSLATOR: use the longer of the two strings in SAY_HELLO & SAY_BYE */ 
} 
i 
} 


RESOURCE STRING say_hello {str="Say hello"; } 
RESOURCE STRING say_bye {str="Say goodbye"; } 
The com_menu method is also modified to replace the text in both cases: 


#include <varray.g> 


METHOD VOID hellocm_com_menu(PR_HELLOCM *self, INT menu_num, PR_VAROOT *array) 
{ 
UBYTE *p; 
INT rid; 


if (menu_num==0) 
{ 
p=(UBYTE *)p_send3 (array, O_VA_PREC, 0); 
rid = p_send2 (w_ws-—>wserv.cli,O_WN_SENSE) ? SAY_HELLO : SAY_BYE; 
hLoadResBuf (rid, p+2) ; 
} 


Disabling a menu option 


Many applications may, from time to time, enter a state in which one or more command options do not 
represent valid operations. A common example is for a 'Copy' option that copies a highlighted section of 
data to a clipboard. This option is clearly inappropriate if none of the data is highlighted. 


The recommended way of handling such a situation is to display an information message that explains 
why the option is not valid. In the case of copying, for example, the built-in applications display a 
"Nothing to copy" information message. 


One way of implementing this is to add a validity check at the start of each of the relevant command 
manager methods. This is adequate if only one command is affected, but if it applies to a number of 
options this can lead to much duplicated code. A more efficient solution may be to subclass the 
com_accl_check method. 


Whenever a menu option is selected, either from a pull-down menu or by an accelerator keypress, the 
command manager first receives a coM_ACCL_CHECK message, passing the method number of the message 
that corresponds to the selected command option. Only if the com_acc1_check method function returns a 
TRUE Value is the command manager sent (via hWservComSena) the message that executes the selected 
command option. 


Suppose an application has a com_copy method in its mycom command manager, used to copy a 
highlighted region. A suitable com_acc1_check method function would be of the form: 


OBJECT ORIENTED PROGRAMMING GUIDE 


METHOD INT mycom_com_accl_check (PR_MYCOM *self, INT comid) 
{ 
if (comid==O0_COM_COPY) 
( 
if (!RangeHighlighted() ) 
{ 
hInfoPrint (-NOTHING_TO_COPY) ; 
return (FALSE) ; 
} 
} 
return (TRUE) ; 
} 


where the RangeHighlighted function is assumed to return TRuE only if a range is highlighted (possibly 
detecting this case by sending some form of SENSE message to the appropriate object). Note that 
hInfoPrint is passed a negative resource ID, indicating that the resource with ID NoTHING_TO_coPy is 
located in the system resource file (see the Resource Files chapter). 


In the case of Copy it may be more efficient to perform the test within the com_copy method function, as 
follows: 


METHOD VOID mycom_com_copy (PR_MYCOM *self) 


{ 

if (!CopyRangeTo Clip()) 
hiInfoPrint (-NOTHING_TO_COPY) ; 

} 


where CopyRangeToClip is assumed to return TRUE only if a highlighted range has been copied to a 
clipboard. 


Which of these two methods to use will depend on the exact circumstances in a particular application. 


Changing the number of options in a menu 


In some circumstances one or more commands options may be disabled for the greater part of the time. 
An example of this is the Spell check option of the built-in word processor. This option is not available 
unless the Spell-checker application has been purchased and installed on the machine. It would be 
inappropriate to handle such an option in the way described in the previous section. A better way is to 
display the option in a command menu only if the option may validly be selected. This involves 
subclassing both the com_menu and com_accl_check methods. 


The content of a pull-down menu is loaded from the application's resource file into an allocated memory 
cell whose size is determined by the size of the resource. It is therefore not an easy task to add items 
dynamically, once the resource has been loaded. It is, however, quite simple to remove items. 


The recommended technique for varying the number of options in a menu is thus to include an entry for 
each such option in the appropriate menu resource, and provide it with an accelerator as normal. The item 
may then be removed, if necessary, from the pull-down menu by replacing the com_menu method. 
Selection of the option by its accelerator (which does not involve the pull-down menu) must also be 
disabled, if necessary, by a suitable replacement com_accl_check method. 


Suppose that an application's 'Optional' command option, with a Psion-O accelerator, is the third item in 
the fifth menu of an application and is executed by a com_opt ional method in a mycom subclass of the 
command manager. The wszeRv_INFo resource and the appropriate menu resource would contain the 
following: 


RESOURCE WSERV_INFO my_accs 


ig Py /* Optional */ 


5 COMMANDS AND COMMAND MENUS 


RESOURCE MENU fifth_menu 
{ 
items = 
{ 
MENU_ITEM 
{ 
com_id=O_COM_OPTIONAL; 
mn_item="Optional"; 
hy. 
i 
} 


The com_menu and com_acc1_check methods would need to be of the form: 


METHOD VOID mycom_com_menu(PR_MYCOM *self, INT menu_num, PR_VAROOT *array) 
{ 
if (menu_num==4) /* in fifth menu */ 
{ 
if (!OptionalCommandIsValid() ) 
p_send3 (array,O_VA_DELETE,2); /* delete third item */ 


} 


METHOD INT mycom_com_accl_check(PR_MYCOM *self, INT comid) 
{ 
if (comid==0_COM_OPTIONAL) 
( 
if (!OptionalCommandIsValid() ) 
return (FALSE) ; 


} 
return (TRUE) ; 
} 


Displaying a status window 


The command manager is sent a com_sTATWIN message when the application receives a Control-Menu 
keypress. The supplied com_statwin method does nothing, so the default action of an application is to 
ignore this keypress. An application that wishes to respond to the Control-Menu keypress by altering the 
state of its status window should subclass this method. 


A Series 3 application may record (generally in an element of property) the current state of visibility of its 
status window and toggle its visibility by appropriate calls to either wsEnable OF wsDisable. Before 
changing the status window it should adjust the size of its client window display to fill the space that will 
not be occupied by the status window. 


A Series 3a application has the option of switching between a large, small or no status window. The 
following code illustrates how the com_statwin method may be used to cycle around the three possible 
states. Note that this code assumes that the client window has a wn_change_width method that adjusts the 
width of the window, given the (signed) amount by which the width needs to be changed. Possible code 
for such a method is given in the Windows chapter of this manual. 


METHOD VOID wpcman_com_statwin(PR_WPCMAN *self) 
{ 
INT winType, delta; 
P_EXTENT oldExtent; 
P_EXTENT newExtent; 


winType=wInquireStatusWindow(-1, &£0ldExtent) ; 

if (winType==W_STATUS_WINDOW_OFF) 
winType=W_STATUS_WINDOW_BIG; 

else if (winType==W_STATUS_WINDOW_SMALL) 
winType=W_STATUS_WINDOW_OFF; 


else 


winType=W_STATUS_WINDOW_SMALL; 
wiInquireStatusWindow (winType, &newExtent) ; 
delta=(oldExtent.width-newExtent.width) ; 
p_send3 (w_ws—>wserv.cli,O_WN_CHANGE_WIDTH, delta) ; 
wStatusWindow (winType) ; 


} 


OBJECT ORIENTED PROGRAMMING GUIDE 


Application-specific initialisation 


The command manager's com_init method is intended to be replaced in applications that need some form 
of application-specific command manager initialisation. Many applications will not need to replace this 
method. Application-specific initialisation may be necessary, for example, to create and initialise 
component objects used in the execution of one or more command options. 


Note that the command manager is created and initialised (by being sent a com_INIT message) 
immediately before the window server object is sent a ws_DyN_INIT message. This is important because it 
means that at the time the com_init method function is executed, the client window does not yet exist 
(since an application creates and initialises its client window in the ws_dyn_init method). The com_init 
method may not therefore make any direct reference to the client window or any components that it may 
create. 


Replacing a menu bar 


An application may, in some circumstances, wish to make a permanent or temporary replacement of its 
menu bar. An example is an application that can switch between, say, a graphic and a text display and 
requires a different set of command menu options for each view. Alternatively, an application may operate 
under a number of aliases (see the Aliasing applications section of the Communicating with the System 
Screen chapter of the Series 3 Programming Guide) and require a different set of command menu options 
for each alias. 


The initial menu bar of an application is set up by system code, during the initialisation of the window 
server object, immediately before the creation and initialisation of the command manager. The data for the 
initial menu bar is read from the wsERV_INFo resource that must be the first item of the application's 
resource file. This struct specifies the resource ID of the mznu_par resource that contains the menu bar 
text, the accelerators for each option and the command manager method number of the method to be 
associated with the first of these accelerators. The menu bar resource, in turn, contains the resource ids of 
the menu resources that contain the pull-down menus associated with the menu bar items. 


For each alternative menu, the application resource file must contain a separate wsERV_INFO resource, its 
associated MENU_BAR resource and any additional menu resources (two or more menu bars may share a 
single menu resource that is common to them). These additional resources may appear at any position, and 
in any order, in the application's resource file. 


To change the menu bar, an application should send the window server active object a ws_SET_MENUBAR 
message, passing the resource ID of the new wszRv_inro resource. If, for example, an application has an 
alternate menu defined in its resource file as: 


RESOURCE WSERV_INFO alternate_menubar 
{ 


} 
it would change its menu bar by means of code of the form: 


VOID *pOldMenBar; 


pOldMenBar = p_send3 (w_ws, O_WS_SET_MENUBAR, ALTERNATE_MENUBAR) ; 


The method returns a pointer to an allocated memory cell that contains the data for the original menu bar. 
If this is not to be restored the application should free this memory by calling, for example: 


p_free (pOldMenBar) ; 


If, however, the application is intending to restore the original menu at some future time it should 
preserve this pointer. The original menu bar is restored by the message: 


p_send3 (w_ws, O_WS_RESET_MENUBAR, pOldMenBar) ; 


Neither of these two messages cause the new or the restored menu bar to be displayed. The appropriate 
menu will be made visible as normal when the Menu key is next pressed. 


A permanent replacement of a menu bar, say for an aliased application, may be made at any time during 
application-specific initialisation. Suitable locations for the code ar either the command manager's 
com_init method or the window server object's ws_dyn_init method. 


5 COMMANDS AND COMMAND MENUS 


Note that any alias information text that was passed to the application in its start-up command line is 
pointed to by a text pointer in the Hwrmman application manager's property, accessed through the w_am 
magic static by w_am->hwimman.aliasinfo. In order to gain such access, application code must declare 
w_am as a pointer to an instance of HwImMmaN: 


GLREF_D PR_HWIMMAN *w_am 


If the application subclasses Hw1mman the declaraton may, of course, be as a pointer to an instance of the 
application-specific subclass. 


Accelerators for replacement menu bars 


The accelerators for a replacement menu bar must obey the same set of rules that are obeyed by 
accelerators for the main menu bar: 


e There must be a unique accelerator for every command option that appears in the menus. 


e The number of accelerators in the list must match with a contiguously declared set of command 
manager methods. 


e The accelerators must be listed in the same order as the method declarations in the command 
manager class. 


If the two menu bars do not share any common command options, the situation is quite simple. Suppose, 
for example, that an application has a main menu bar that uses com_exit (with accelerator 'x') and 
application-specific command manager methods com_one and com_two (with accelerators 'a' and 'b' 
respectively). Suppose that a replacement menu bar uses command manager methods com_three, 
com_four and com_five (with accelerators 'c’, 'd' and 'e'). The application's command manager class 
definition could contain method declarations such as: 


REPLACE com_exit; 
ADD com_one; 

ADD com_two; 

ADD com_three; 
ADD com_four; 

ADD com_five; 


The wsERV_INFo resource for the main menu bar would then be of the form: 


RESOURCE WSERV_INFO main_accs 
{ 
menbar_id=main_menbar; 
first_com=O0_COM_EXIT; 


accel={'x', /* Exit */ 
he» /* com_one */ 
Voldy /* com_two */ 


} 
and that for the replacement menu bar would be: 


RESOURCE WSERV_INFO replace_accs 
{ 
menbar_id=replace_menbar; 
first_com=O0_COM_THREE; 


accel={'c', /* com_three */ 
tay /* com_four */ 
"e'}; /* com_five */ 


} 


The situation is only slightly more complicated if the two menu bars share common options. Suppose that 
the main menu bar is as before, but that the replacement menu bar uses com_exit, com_three, com_four 
and com_five. In this case the main menu bar resource would be exactly as before, but the replacement 
menu bar resource would change to: 


OBJECT ORIENTED PROGRAMMING GUIDE 


RESOURCE WSERV_INFO replace_accs 
{ 
menbar_id=replace_menbar; 
first_com=O_COM_EXIT; 


accel={'x', /* Exit */ 
Uy /* dummy, to skip com_one */ 
Pt /* dummy, to skip com_two */ 
Yon, /* com_three */ 
yo Rae /* com_four */ 
‘e'}; /* com_five */ 


} 


The trick is to note that the rules do not prevent the accelerator list from containing more items than the 
number of command options that appear in the menus. Since accelerator characters are not validated 
during compilation of the resource file, there is nothing to prevent the appearance of duplicated or illegal 
accelerator characters in the list, provided they will never be matched with commands at run-time. It is 
convenient to use the exclamation mark (!) as a marker for dummy entries since HWIM code will never 
recognise it as an accelerator. Any other character that is not a valid accelerator may be used. 


The principle can be extended to provide any number of replacement menu bars (the Series 3a Word 
application, for example, has three replacement menu bars, for use by program, text and memo editors). 
The same technique could also be used to match accelerators with a non-contiguous set of command 
manager methods for the main menu bar, but it is usually more convenient (and always possible) to make 
the main menu bar command manager methods a contiguous set that immediately follows com_exit. 


Submenus 


An application may make a temporary replacement of its menu bar to implement a submenu by sending 
the window server object a ws_Do_suBMENU message. An example of this, taken from the Series 3 
Spreadsheet application, is illustrated below. 


File Edit View Search Range | Special ) 


Print setup 


Jump to page 


The method will normally be called from the command manager method corresponding to a main menu 
option. As for the ws_set_menubar method, ws_do_submenu requires the resource ID of a WSERV_INFO 
resource. Note that, as illustrated above, commands in a submenu may have names and/or accelerators 
that duplicate those appearing in the main menu. One use of submenus is therefore to provide a means of 
exceeding an application's normal limit on the number of its commands. 


A typical call would be of the form: 


p_send3 (w_ws, O_WS_DO_SUBMENU, SUBMENU_ACCS) ; 


and causes the submenu to become visible, ready for selecting one of its commands. Note that, unlike 
ws_set_menubar, this method does not return a pointer. The current main menu is restored automatically 
when the submenu menu bar ceases to be visible. 


Shutdown messages 


The System Screen may send an application a Shutdown message at any time, unless the application has 
explicitly elected not to receive such messages. It may do this by adding 4000 to its type number in its 
shell data file (see Application type numbers in the Communicating With the System Screen chapter of the 
Series 3 Programming Guide). 


An HWIM application receives a Shutdown request from the System Screen in the form of a normal 
COM_EXIT message to its command manager. It should handle this in exactly the same way as when the 
user explicitly selects the application's Exit option (the application actually has no means of 
distinguishing between the two cases). The default com_exit method supplied by the comman class is quite 
adequate for an application that is not file-based. See the File-based Applications chapter for the required 
behaviour of com_exit when the application maintains an open file. 


An application may indicate that it is temporarily unable to accept a Shutdown message by setting the 
Dat Locked reserved static to be non-zero. This will generally only be relevant for a file-based application: 
a typical case is while an application is performing an extended operation that must run to completion, 
such as saving a file. The application must ensure that it clears Dat Locked again, as soon as possible. 


5-14 


CHAPTER 6 


WINDOWS 


An HWIM window object represents a rectangular region on the screen, in which data may be displayed. 
The concept of a window is discussed in the /ntroduction and Windows chapters of the Window Server 
Reference manual. In addition, drawing to a window requires a knowledge of the Graphics Output 
chapter of the Window Server Reference manual. The HWIM window classes add a relatively modest layer 
of functionality over the window functions described in the Window Server Reference manual and their 
use relies heavily on the concepts and techniques described in that manual. In consequence, this chapter 
contains explanations of only a few additional mechanisms. 


The window classes themselves, listed below, are fully described in the Windows chapter of the HWIM 
Reference manual. 


WIN The ultimate window superclass. 


BWIN A subclass of win that draws a standard border around the window. An application's client 
window is normally a subclass of Bw1n. 


LODGER A pseudo-window that subclasses win. Such a window is assumed to occupy a rectangular 
region within another window. The enclosing window (referred to as the landlord) will 
receive all messages (notably wn_kEy and wN_pRaw messages) but will normally delegate all 
processing for that rectangle to the lodger. Lodger windows are used extensively by system 
code - perhaps the most common usage is for the controls and prompts that form the 
components of a dialog box. 


The relationships between the window classes is illustrated in the following class diagram. 


— o~ 


[rs — 7 — 

3h win / / bwin / 

a ) = a 
Ne Me 

oe ee oe We 

y wswin ji Z lodger / 

= e .} os 2) 
kee Me 


In addition to the methods and property in the application's code and data segments, a window has an 
associated data structure in the window server's resources. One of the elements in this structure is the 
window's handle and thus the window server is able to direct redraw events to a particular window in an 
application. 


The structure is created by the call to wcreat ewindow that occurs (in the window's wn_connect method) 
when a newly created instance of win is initialised. The window server provides a set of functions that 
operate on such data structures, each data structure being uniquely identified by a window id returned by 
wCreateWindow. 


Since a uniquely identifiable data structure with a set of functions that operate on it is effectively an 
object, the window server resources associated with a window can be considered as a component object. 
The existence of the window server resources is indicated in the above class diagram by the 'using' 
relationship between win and a notional wswin (Window Server WINdow) class. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Window usage in HWIM 


In general, an HWIM application uses a number of different windows. Some windows may exist for the 
entire lifetime of the application, for example, an application's main display window. Others may exist for 
a short time, such as the windows used to display a menu bar, a pull-down menu or a dialog box. 


An HWIM application is expected to have one particular window, designated the client window, that 
(generally) exists for the lifetime of the application and provides the main view of the application's data. 
This window should be created as part of the application's initialisation, in the wseRv window server active 
object's ws_dyn_init method, with its handle being written to the wserv.cli property element. See, for 
example, the creation of a bordered client window in the "Hello World" example application. 


All HWIM windows subclass the w1n class, which is thus the ultimate superclass of all windows. The 
window classes supplied by HWIM are abstract classes and will normally need to be subclassed in order to 
create useful window objects. 


In contrast with the mechanisms supplied for the handling of command menus and dialogs, the HWIM 
library provides relatively few 'solutions' for the display of data in a window (but see the Edit Windows 
chapter). For example, an application can normally simply use the supplied dialog box class or, at most, 
replace one or two methods. It will, however, usually have to subclass the window classes to provide a 
variety of application-specific mechanisms. 


The draw/redraw mechanism 


This section (and, indeed, the whole of this chapter) is written under the assumption that a window has 
been created without a back-up bitmap. An HWIM application does not normally create backed-up 
windows and will therefore have to handle redraw events. The main advantage that this confers on an 
application is that drawing to such a window is faster and much less memory is used for each window. See 
the Windows and Redrawing sections of the Introduction chapter of the Window Server Reference manual 
for the background to drawing and redrawing the content of a window. 


All or part of the content of a window may have to be drawn for one of the following basic reasons: 
e the application's displayed data has changed 


e all or part of the window has been exposed, for example, by the disappearance of an overlying dialog, 
or by the application being brought to the foreground. 


In both cases the drawing is ultimately handled by the window's wn_draw method, but the mechanism by 
which this is invoked differs in the two cases. 


The first case generally results from some operation within the application itself, such as a keypress or the 
execution of a command. Normally the application will be aware that such a change has taken place and 
can explicitly initiate the drawing. Most applications can do so by sending the window a wn_popRaw 
message. This creates a temporary graphics context with default properties, sends a wN_pRAw message and 
then destroys the temporary graphics context. The graphics context may be modified, if necessary, in the 
window's wn_draw method. 


The second case may occur at any time, without the application necessarily being aware of any particular 
need to do any redrawing. The window server process will, however, send the aplication a wmM_REDRAW 
inter-process message, indicating that a particular area of a specific window needs to be redrawn. This is 
processed by the application's window server object which then sends a wN_REDRAW message to the 
specified window (omitting to pass on the update rectangle that is passed to the wn_redraw method). As 
for the wn_dodraw method, a temporary graphics context is created around a wn_pRaw message, which may 
be processed exactly as in the previous case. 


At the level of the wn_draw method, a window can not (and does not need to) distinguish between the two 
cases. In fact, as an alternative to sending the window a wN_popRaw message when its data has changed, 
the application could simply invalidate the window (by calling wInvalidateWin). System code will ensure 
that the window eventually receives a wN_REDRAW message. In many cases this may be simpler from the 
point of view of coding, but is generally less efficient. It may lead to poor responsiveness, particularly in 
applications that make rapid changes to their data. 


6 WINDOWS 


An HWIM application is free to replace any or all of the wn_redraw, wn_dodraw and wn_draw methods if 
necessary. The mechanism whereby both wn_redraw and wn_dodraw invoke wn_draw has been found to be 
convenient, but is not mandatory. Since the update rectangle passed to the wn_redraw method is not 
passed in the wn_pRaw message, the wn_draw method must draw the whole of the window. This is not as 
bad as it sounds, since the drawing is always clipped by the update rectangle, so pixels outside that 
rectangle will never be physically set or cleared. Normally, it is the setting or clearing of pixels that takes 
the most time in any drawing operation. 


If, in a particular application, the time taken to calculate what has to be drawn becomes dominant, it may 
be worth taking the update rectangle into consideration. In this case the wn_redraw method should be 
replaced, either to handle the drawing itself, or to pass on the update rectangle to a replacement wn_draw 
method. In such a case, if the wn_dodraw method is used, it may also need to be replaced to specify a 
rectangular drawing region. 


Drawing a window 


When implementing a window's wn_draw method, there are at least two strategies that may be followed, 
depending to some extent on the nature of the window's contents. 


The first case is where the contents densely fill the window, particularly if the content is frequently 
changing (and especially if only a small part changes at any one time). An archetypal application of this 
type is the built-in Word application. In such a case the prime consideration is to avoid clearing all or part 
of the window before drawing its content, so that the drawing is 'flicker-free’. 


The basic strategy in such a case is to: 


e create the window with its background attribute set to w_wIn_BACK_NonE and, if appropriate, 
W_WIN_BACK_GREY_NONE, 


e ensure that the win_draw method draws to every pixel in the window. 


If this strategy is adopted, it is acceptable (but possibly wasteful of processing power) to initiate drawing, 
following any change of the data, by calling wInvalidateRect OF wInvalidatewin. Any drawing of 
existing, unchanged, content will not be visible to the user since the existing content will not be erased 
before being overdrawn. 


This approach is incompatible with the use of a bordered window to display the data, since a bordered 
window needs to clear its background when drawing itself. If the content needs to be shown with a 
surrounding border, the solution is to use two windows, as illustrated below. 


The bordered window is the parent of the smaller superposed window, indicated in the diagram by a 
dotted line. The child window, an instance of a subclass of win, displays the content and should be 
initialised with a non-clearing background, as described earlier. The bordered window is an instance of (a 
subclass of) swin and will be initialised to clear its background. When the bordered window draws itself, 
drawing will be clipped to exclude the area covered by the child window and will therefore draw the 
border and clear the area between the border and the child window. 


Both windows will independently receive system-generated wN_REDRAW messages, so there is no need for 
either window to pass WN_REDRAW (or WN_DRAW) messages to the other. Some messages may need to be 
passed between the two windows. These may include resize messages if the windows are allowed to 
change their dimensions, wN_EMPHASIS and WN_KEY messages. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Although this strategy can be followed with all windows, it may be inconvenient in cases where a window 
is sparsely filled, especially if the content is restricted to specific rectangles and does not change 
frequently. A second strategy is feasible in such a case, exemplified by the Record application that is 
described in the Application Design chapter of this manual. 


In this case, even if the window is to display a border, a single window is used, initialised to clear its 
background. The advantage over the previous technique is that the wn_draw method does not have to draw 
to every pixel in the window but can, for example, draw specific items of text with, say, gPrintBoxText. 


The method relies on the fact that drawing caused by receipt of a wN_REDRAW message will be clipped to a 
specific rectangle and will therefore not redraw existing unchanged text. Unlike in the previous method, 
however, the application must not use wInvalidateWin to force a redraw following any change in the 
application's data. Invalidating the window would cause the whole window to be cleared before being 
drawn again and users would not be impressed. Instead, the application should draw directly to the 
rectangle, or rectangles, that are affected by the change. See the description of the Record application, and 
the application's source code that is supplied with this SDK, for further details. 


The main visible difference between the two techniques occurs when an overlying window, such as a 
dialog or pull-down menu, disappears. In the second case the rectangular area previously occupied by the 
window will be cleared before the contents are drawn, whreas in the first case the redrawn contents will 
directly replace the remains of the previous window. 


Lodger windows 


A lodger window is defined to be any window that subclasses LopGER. 


A lodger window does not have its own independent window server data structure, and therefore does not 
have an independent window server ID. Such a window is intended to occupy a rectangular region within 
another window, referred to as the lodger window's landlord, and shares the window ID of the landlord 
window. In other respects a lodger window has broadly similar behaviour to a normal window, supporting 
a similar set of drawing and key processing messages. A lodger window can be considered to take over the 
responsibility for drawing the content of a rectangular region of the landlord window. 


The fact that a lodger window shares the ID and window server data structure of its landlord has a number 
of significant consequences: 


e A lodger window will never receive a wN_REDRAw message. It is therefore the responsibility of the 
landlord to ensure that its lodgers are drawn when appropriate. The normal way of doing this is 
to replace the landlord's wn_draw method with code that sends wn_pRaw messages to all of its 
lodgers, in addition to drawing any areas of the landlord that are not covered by lodger windows. 


e Drawing in a lodger window is clipped by the enclosing landlord window boundary and is not 
confined to the area of the lodger window itself. In consequence, a lodger window has a duty 
never to draw outside its bounding rectangle. 


e Using lodger window components instead of normal windows is more efficient in terms of 
memory usage. This can be significant in the case of a complex compound window (a dialog box, 
for example, can easily need ten or more component sub-windows). 


e Acompound window with lodger components will scroll more smoothly - the scroll mechanism 
operates on only the single 'real' window and does not require drawing messages to be sent to any 
of the component lodger windows (except for those that have freshly exposed regions to draw). 


Resizing a window 


The following suggestion for a wn_resize method illustrates a simple way to change the position and size 
of a window without changing any other attribute: 


METHOD VOID mywin_wn_resize(PR_MYWIN *self, P_EXTENT *pext) 


{ 
W_WINDATA wd; 


wd.extent=*pext; 
wSetWindow (self->win.id,W_WIN_EXTENT, é&wd) ; 
} 


6 WINDOWS 


S3/S3a client windows generally do not change size once they have been created and made visible. The 
main exception is a change in width corresponding to a change in the state of any permanent status 
window. Thus a common requirement is to change the width, without changing the position or height, as 
in the following example for a possible wn_change_width method: 


METHOD VOID mywin_wn_change_width(PR_MYWIN *self, INT delta) 


{ 
W_WINDATA wd; 


wiInquireWin (self—>win.id, &wd) ; 

wd.extent .width+=delta; 

wSetWindow (self->win.id,W_WIN_EXTENT, &wd) ; 
} 


Window emphasis 


Emphasis is used to indicate which of several windows is the one with which the user is currently 
interacting. 


System code will send the application's client window a wN_EMPHASISE message to turn emphasis on or 
off; for example, when a dialog appears or disappears. Application code is not normally expected to send 
WN_EMPHASISE messages itself, except under the circumstances described below. 


A window will usually respond to a wN_EMPHASISE message by changing its appearance in some way. The 
normal technique is to set or clear, as appropriate, the pR_wIN_EMPHASISED flag in the window's 
win.flags property and then trigger a redraw, either by calling winvalidatewin or by sending itself a 
WN_DODRAW message. In either case the window's wn_draw method will subsequently be executed. This can 
then test the pR_wIN_EMPHaASTSED flag and draw the window as appropriate. Common means of showing 
that a window is not emphasised are: 


¢ a window with a shadowed border is drawn without its shadow 
e any highlighted region may be drawn without its highlight 
e a window that has a text cursor does not display its cursor 


The last of these three differs from the other two in that it must not be done in the window's wn_draw 
method. The reason is that there is only ever one cursor visible on the screen at any one time and the 
window server function weraseTextCursor erases the text cursor regardless of the window in which it 
appears. An unemphasised window will receive wn_praw messages (for example, from the wn_redraw 
method) and so a call to weraseTextCursor from within the wn_draw method could result in the removal 
of the text cursor from another currently emphasised window. 


An application can avoid the possibility of 'stealing' another window's text cursor by confining all calls to 
wlextCursor and weraseTextCursor to be from within a window's wn_emphasise method. When 
changing emphasis from one window to another, system code always sends a WN_EMPHASISE, FALSE 
message (which may call weraseTextCursor) to the window losing emphasis before sending a 
WN_EMPHASISE, TRUE message (which will, if necessary, call wrextcursor) to the window gaining 
emphasis. The currently emphasised window will thus be guaranteed to display its text cursor, if it has 
one. 


A window that contains one or more child windows may delegate all or part of the processing of a 
WN_EMPHASISE message to its child windows. An example of this is shown in the behaviour of a dialog 
box. On receipt of a wN_EMPHASISE message, a dialog box changes the appearance of its border and then 
sends a wN_EMPHASISE message to one of its items (the one with 'focus'). 


A dialog box also illustrates the case where application code may send wN_EMPHASISE messages other than 
in response to such a message sent by system code. When a user presses the up or down arrow keys, a 
dialog responds by changing the focus from one dialog item to another. As part of this process 
WN_EMPHASISE messages are sent to the two items concerned. A similar process occurs when switching 
between the Find window and the main display window in the Database application. 


In such a situation it is important to obey the rule that a wN_EMPHASISE, FALSE message must be sent to the 
window losing emphasis before sending a WN_EMPHASISE, TRUE Message to the window gaining emphasis. 


CHAPTER 7 


DIALOGS 


A dialog box displays the current values of one or more data items and, in general, allows the user to 
modify one or more of these values. 


This chapter describes the basic mechanisms provided in HWIM for the creation and operation of dialogs. 
Dialog boxes may be created and used at a number of levels; this chapter is intended to describe the more 
common uses that cover the great majority of cases. 


Application-specific dialogs use an instance of (a subclass of) pLcBox which is a subclass of the BwIN 
bordered window class, via the pLccHAIN abstract class, as indicated in the following class diagram. These 
classes are fully described in the Dialog Boxes chapter of the HWIM Reference manual. 


fo — SE haw 

re win / / bwin / 

> a S _) 
Lo ees 

per en e a 

f digchain / y  dlgbox / 

= a) SS 2) 
Nee Ne 


Some of the pLcBox methods are complex, having to cope with a variety of cases. For most purposes it is 
not necessary to understand these methods in detail - the essential information for most uses will be found 
in this chapter. The Dialog Controls chapter contains the essential information about the standard dialog 
components that are supplied in the HWIM library. 


In an HWIM application the most common use of a dialog is as a result of the user selecting a command 
from a command menu. In response to an Open file command, for example, a dialog would be presented 
to allow the user to specify the name (and possibly the type) of the file to be opened. 


There are a number of system dialogs that may be run by specific wsERv methods, such as 
ws_error_dialog, ws_query_dialog and ws_format_dialog, described in the WSERV Class chapter of 
the HWIM Reference manual. Application-specific dialogs are usually started by means of the window 
server object's ws_do_dial method, or the equivalent hLaunchDial utility function. 


All HWIM dialogs are modal, that is, while the dialog is visible the user can interact with the application 
only via that dialog; the application enters a 'mode' such that all attempts to interact with, say the menu 
bar are disallowed. This mode terminates when the user satisfactorily completes the dialog. 


An HWIM dialog box consists of a bordered window containing between one or more lines, or items. Each 
item may be plain text, a control, or a combination of a plain text prompt and a control. Each control may 
be an instance of one of many different classes, including: 


e achoice list, allowing selection of one of a list of options 
e an action list, containing one or more buttons (this may only be used as the last item in a dialog) 
e an integer (WORD or LONG) numeric editor 


e a floating point numeric editor 


OBJECT ORIENTED PROGRAMMING GUIDE 


e a time editor 

e a date editor 

e ascrolling or non-scrolling text editor 
e a secret data input box 

e a filename choice list 

e a filename editor 

e an application-specific control 


A dialog written for the Series 3 (or for the Series 3a in compatibility mode) may contain up to seven 
items, which may be divided into two groups by a single underline. A Series 3a dialog can display up to 
nine items, any number of which may be underlined. Note that the inclusion of an action list, which 
occupies two lines, reduces the number of items that may be displayed. On the Series 3a the inclusion of 
more than one underline may also reduce the possible number of items. 


In the majority of dialogs a single underline is used to separate the first item, designated to be the dialog 
title, from those that follow. The title is normally, but not necessarily, plain text. 


Dialog box items are generally accessed by an index number. The items are numbered, with the first item 
being item 0, in the order in which they are added to the dialog box (which is also the order in which they 
are displayed). 


Using dialog boxes 


In all cases, the dialog must ultimately be started up by means of the wsERV ws_do_dial method. This 
may, however, be indirect, such as when using the hLaunchDial utility function or an equivalent 
application-specific function. 


All sample code in this section assumes that the application category file is myapp.cat. 


Default dialog behaviour 


One item in the dialog box generally has focus, that is, it receives all keys (via a wN_KEY message to its 
control) directed to the dialog box, except for those, listed below, that are processed by the dialog box 
itself. On receipt of a key, a control may elect to absorb all further keys directed to the dialog box. 


Provided that one of the dialog's component controls has not elected to absorb all keys directed to the 
dialog box: 


e the up and down cursor keys respectively move focus to the previous or the next item in a cyclic 
fashion 


e the page up and page down keys respectively move the focus to the first or the last item that is 
capable of receiving focus 


e the Enter key terminates the dialog. If the dialog has a result buffer (that is digbox.rbuf is not 
NULL) the value of digbox.current is written to the first worp of this buffer 


e the Esc key terminates the dialog. If the dialog has a result buffer (that is digbox.rbuf is not 
NULL) a value of w_KEY_ESCAPE is written to the first worp of this buffer 


If no control is absorbing all keys and the dialog contains an action list of buttons as its last item, this 
behaviour is modified. In this case all incoming keys are first offered to the action list and if the key 
matches one of the buttons in the action list the dialog is terminated. If the dialog has a result buffer (that 
1S dlgbox.rbuf is not NuLL) the uppercased key code is written to the first worp of this buffer. Unless it 
matches a button in the action list, the Enter key is ignored. Only if the key is not recognised by the action 
list is it then offered for processing as described above. 


7 DIALOGS 


This action may be further varied by other digbox. flags values as follows: 


e if the pLGBox_NoTIFY_ALL_act flag is set, keys that do not match a button in the action list also 
cause the dialog to terminate 


e if the DLGBox_REPORT_ACT_HORIZz flag is set, the value written to the result buffer will be the 
index of the matching button (the leftmost button has an index of zero, and non-matching keys 
give a value of -1) rather than the key code 


Other variations are generally accomplished by subclassing pLGBox, and some such variations are 
described later. 


Dialogs and resource files 


The content of a dialog is normally specified by a resource file item, using dialog box resource file 
structures that are defined in hwim.rh. These resources also use definitions of constants (in hwim.rg) that 
are derived from the HWIM category file. All resource files that contain dialog resources must contain the 
following two lines before the definition of any dialog resource: 


#include <hwim.rh> 
#include <hwim.rg> 


The piatoc resource struct is defined in hwim.rh as: 


STRUCT DIALOG 
{ 
WORD flags=0; 
TEXT title=""; 
LEN BYTE STRUCT controls[]; /* array of CONTROL resource items only */ 


} 


and the contro. resource struct is defined as: 


STRUCT CONTROL 


LEN { 

WORD flags=0; 

BYTE class; /* class of control */ 

TEXT prompt=""; /* Prompt for item */ 

STRUCT info; /* eg CHLIST, TXTMESS, EDWIN, or NCEDIT */ 


} 


A simple dialog, consisting of a title, a fixed message and a 'Continue' button, might be defined by the 
following resource, assumed to be in a myapp.rss source file: 


RESOURCE DIALOG simple_dialog 
{ 
flags=DLGBOX_NO_DDP; 
title="Dialog title"; /* an empty string means that the dialog has no title */ 
controls= 
{ 
CONTROL 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE |DLGBOX_ITEM_DEAD; 
info=TXTMESS 
{ 
flags=IN_TEXTWIN_AL_CENTRE; 
str="This is a message"; 
ad 
} és 
CONTROL 
{ 
class=C_ACLIST; 
info=ACLIST 
{ 
rid=-SYS_AC_CONTINUE; /* the ID of a system action list resource */ 


hi 


OBJECT ORIENTED PROGRAMMING GUIDE 


In use, this will display (in compatibility mode on the Series 3a) the following dialog, which exits when 


the user presses the Esc key. 


Dialog title 


This if 3 message 


Continue 


Esc | 


The dialog resource uses a one-button action list defined by the the system resource sys_Ac_CONTINUE (see 
the HWIM Resource Files chapter) and is based on the following hwim.rh resource structs: 


STRUCT TXTMESS /* Initialising struct for a text window */ 


{ 


WORD flags=0; 
TEXT str=""; /* message defaults to empty string */ 


} 


STRUCT ACLIST /* Initialising struct for action list */ 


{ 


LINK rid; 


} 


together with the pIALoc and contro structs, described earlier. 


Note that, in general, a dialog resource contains three levels of flags data: 


the highest level, associated with the whole dialog box, can contain a combination of the 
DLGBOX_Xxx flags that may appear in digbox. flags. Exceptionally, the example sets 

D 
is no advantage in writing its handle to patDialogptr (but it would do no harm to omit this flag). 
Although the dialog contains an action list as its last item, there is no need to set 
DLGBOX_ACTION_LIST since this happens automatically 


LGBOX_NO_ppP. This is done because the dialog does not use any dialog utility functions, so there 


the second level, associated with a particular control, can be a combination of the 
DLGBOX_ITEM_xxx flags that may appear in the flags field of each of the dialog's component 
items. In the above example, the text message control is to be centred in the dialog box and not 
selectable with the cursor keys 


finally, there may be a set of flags (usually 1n_xxx) specific to the initialisation of the particular 
class of which the control is an instance. In the example, the IN_TEXTWIN_AL_CENTRE flag 
ensures that the text is centre aligned in its control (a text window) 


Launching a dialog 


A dialog is launched by means of the wsERV ws_do_dial method. This loads the dialog data from a 
resource file, and then initialises and runs the dialog. It is used as shown below: 


p_send5 (w_ws,O_WS_DO_DIAL, p_getlibh(cat),class,pdata) ; 


where cat and class are respectively the category and class numbers of the dialog box class (DLGBox or a 
subclass of pLGBox) that is to be run, and pdata is a pointer to a pL_pata struct, defined in hwimman.g as: 


typedef struct 


{ 
UWORD id; /* resource ID of a DIALOG resource*/ 


VOID *rbuf; /* address of result buffer, or NULL */ 
PR_DLGBOX **pdlg; /* address of where to write handle of dialog, or NULL */ 
} DL_DATA; 


An alternative is to use the hLaunchDial utility function: 


INT hLaunchDial(P_CATID cat, INT class, DL_DATA *pdata) ; 


This function is described in the HWIM Utility Functions chapter of the HWIM Reference manual, and 
examples of its use appear in the following text. 


7 DIALOGS 


Simple dialogs 


A simple dialog, for the purposes of this section, is one that uses the piGBox class, rather than subclassing 
it. Such a dialog is easy to define and run, but suffers from the following limitations: 


e the initial values it displays are entirely determined by the (static) resource file data: the dialog 
data may not be determined dynamically from current values stored in the application 


e knowledge of the final state of the dialog is limited to the default information that is written to a 
result buffer - effectively only indicating which keypress terminated the dialog 


Despite these restrictions, such dialogs may be useful, say, to make a specific warning with a 
characteristic appearance, or to elicit multiple choice responses (but bear in mind the system-supplied 
Error and Query dialogs, run by wszrv methods). 


As an example, the dialog defined earlier may be run using code as follows: 


#include <hwimman.g> 
#include <dlgbox.g> 
#include <myapp.rsg> /* resource file generated header file */ 


LOCAL_C VOID RunDlgNoResponse() 


{ 
DL_DATA data; 


data.id=SIMPLE_DIALOG; 

data. rbuf=NULL; 

data.pdlg=NULL; 

hLaunchDial (CAT_MYAPP_HWIM, C_DLGBOX, &data) ; 
} 


Should you wish to know whether the dialog was terminated by pressing Enter or Esc, you could use the 
following alternative: 


INT RunDlgWithResponse() 


{ 
WORD result; 
DL_DATA data; 


data.id=SIMPLE_DIALOG; 

data.rbuf=&result; 

data.pdlg=NULL; 

hLaunchDial (CAT_MYAPP_HWIM, C_DLGBOX, &data) ; 
return (result) ; 


} 
The return value is w_KEY_EScapE only if the dialog was exited by pressing Esc. 


To go beyond the range of possibilities discussed above, pLGBox must be subclassed. In the majority of 
cases this will involve no more than supplying replacements for one or both of the di_dyn_init and 
dl_key methods. 


Dynamically initialised dialogs 


The di_dyn_init method is intended to be used for setting the initial values of dialog box controls 
dynamically, as opposed to the static initialisation, from data in a resource file, used by simple dialogs. 


Suppose, for example, that an application needs to use a dialog, similar to the one described earlier, but 
able to display one of two alternative text messages. Such a dialog might use two string resources and a 
dialog resource as follows: 


OBJECT ORIENTED PROGRAMMING GUIDE 


RESOURCE STRING dial_msg_1l { str="Message one"; } 
RESOURCE STRING dial_msg_2 { str="Message two"; } 


RESOURCE DIALOG message_dialog 
{ 
controls= 
{ 
CONTROL /* the title */ 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD | DLGBOX_ITEM_UNDERLINED; 
info=TXTMESS 
{ 
flags=IN_TEXTWIN_AL_CENTRE; 
str="Dialog title"; 
i 
hy 
CONTROL /* the message */ 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD; 
info=TXTMESS 
{ 
flags=IN_TEXTWIN_AL_CENTRE; 
‘i 
ae 
CONTROL 
{ 
class=C_ACLIST; 
info=ACLIST 
{ 
rid=—-SYS_AC_CONTINUE; 
i 


hi 
} 


Note that the control that is to contain the message does not specify a message, and so has the default of 
an empty string, since (in the case of this example) the message text is always replaced. 


In addition, this dialog resource uses an alternative way of specifying the dialog title, compared with the 
previous example. Although it takes up a few more bytes than in the previous case, it allows the possibility 
for the title to be left as the default empty string in cases where, for example, the dialog title itself is to be 
generated. 


The dialog could subclass pucBox as follows: 


CLASS msgdial dlgbox 
{ 
REPLACE dl_dyn_init 
} 


with the corresponding message function to set the message text: 


METHOD VOID msgdial_dl_dyn_init (PR_MSGDIAL *self) 
{ 
INT flag; 
TEXT buf[40]; 


flag=* (WORD *) self-—>dlgbox.rbuf; 

hLoadResBuf (flag?DIAL_MSG_1:DIAL_MSG_2, &buf[0]); 
hDigSetText (1, &buf[0]); 

} 


This method uses the dialog utility function npigsetText to set the text for the TexTw1n item with index 
number | (the message). This function, which is described in the Dialog box utilities section of the HWIM 
Utility Functions chapter of the HWIM Reference manual, assumes that the dialog's handle is stored in 
DatDialogPtr. Thus, in contrast with the previous example, the resource for this dialog must not set the 


DLGBOX_NO_ppp flag. 


7 DIALOGS 


In this case, the flag to select the required message string is passed to the dialog code in the dialog box 
result buffer. A possible means of running this dialog is: 


LOCAL_C INT RunMessageDlg(INT flag) 
{ 
WORD result; 
DL_DATA data; 


result=flag; 

data.id=SIMPLE_DIALOG; 

data.rbuf=&result; 

data.pdlg=NULL; 

hLaunchDial (CAT_MYAPP_MYAPP,C_MSGDIAL, &data) ; 
return (result) ; 


} 


On completion of the dialog, the worp pointed to by data. rbuf (that is, result) will be overwritten, as in 
the previous example, with a value indicating how the dialog was terminated. 


The essential aspect of this example is that a dialog item can be modified by sending the appropriate 
WN_SET message (in this case, encapsulated in an HWIM utility function) from the dialog's di_dyn_init 
method. This technique is not limited to setting the text of a message, but can be used to modify the 
content of almost any dialog control (some exceptions are discussed in the following section). Examples of 
such dynamic initialisation are: 


e setting the currently selected item in a choice list, 

e — setting the initial value to be displayed in a numeric editor, 
e — setting the initial content of a text editor, 

e supplying replacement text for an item's prompt. 


A dialog that needs to dynamically initialise several controls may use the technique described above, with 
a dlgbox.rbuf that points to a structure containing the initialisation data for all the controls, although this 
may require a considerable amount of code to set up the data. An alternative would be to allow the 
dl_dyn_init method to obtain its data directly from other objects (ideally, via sensing methods) or from 
static variables. A possible technique is to use the result buffer pointer to point to a particular structure, 
which may be in the property of some other object. The method chosen in a particular circumstance may 
depend on the trade-off between such factors as the amount of code needed, the clarity of the code and the 
preservation of modularity. 


Further dynamic initialisation 


In addition to changing an item's content, a number of other operations may be carried out in the dialog's 
dl_dyn_init method. In addition to allowing a dialog's initial appearance to match the current status of 
the application, some of these additional techniques enable two or more similar dialogs to share the same 
resource and/or code. 


Operations that may be performed in the a1_dyn_init method include: 
e locking or unlocking an item by sending the dialog a pL_1TEM_Lock message, 
e dimming or undimming an item by sending the dialog a pL_1TEM_pIM message, 
e replacing an item by sending the dialog a pL_ITEM_REPLACE message, 


¢ appending one or more additional items to the end of a dialog by sending the dialog pL_1TEM_app 
Of DL_ITEM_APPEND messages. 


Note that there is no explicit means of removing an item from a dialog, once it has been added. It is, 
however, possible, during the initialisation of a dialog, selectively to prevent an item in the dialog's 
resource from being added, and this effectively implements the removal an item. To do this, you will need 
to replace the dialog's d1_item_adda method. The technique is illustrated by example code in the 
description of the d1_item_add method in the Dialog Boxes chapter of the HWIM Reference manual. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Changing some aspects of a dialog may require a little ingenuity. It is not, for example, apparent that the 
button resource for an action list (referenced by its resource ID in the dialog's resource) can be changed 
dynamically during the start-up of a dialog. This resource can, however, be replaced in the following way. 


Suppose that the button resource shown below is to be used as a replacement in some dialog's action list 
control. 


RESOURCE ACLIST_ARRAY ac_replace 
{ 
button = 


{ 
PUSH_BUT 


{ 
keycode=W_KEY_SPACE; 
str="First"; 
hy 

PUSH_BUT 


{ 
keycode=W_KEY_DELETE_LEFT; 


str="Second"; 
} 
de 
} 


An opportunity to replace the button resource in a dialog's action list control is provided by setting 
DLGBOX_ITEM_APPL_CAT in the flags field of the relevant conrRo resource, as shown below: 


RESOURCE DIALOG aclist_dialog 
{ 


controls= 


{ 
CONTROL 


{ 
flags=DLGBOX_ITEM_APPL_CAT; 
class=C_ACLIST; 
info=ACLIST 
{ 
rid=—-SYS_AC_CONTINUE; 
i 


‘i 
} 


Normally this flag is set only if the dialog control class is defined in the application's category. In the 
absence of this flag, the control is assumed to be one provided by HWIM and an instance is created from 
the HWIM category. Setting the pLcBox_ITEM_appL_cat flag causes the dialog's d1_item_new method to 
be called after the loading of the dialog resource. Normally this method is used to create an instance of the 
class from the application's category, rather than from HWIM. 


In the current example, the pLcBox_1TEM_APPL_catT flag is set even though the acurst class is in the 
HWIM category. The normal intention is subverted and the pL_1TEM_NEW message is used principally as a 
means of allowing application code to run between the loading of the resource and the creation of an 
instance of the control. The code required to overwrite the button resource ID and create the instance of 
ACLIST, assuming that the application's category is called MYAPP, is as follows: 


METHOD PR_LODGER *mydial_dl_item_new(AD_DLGBOX *par) 
{ 


TEXT *p; 

if (...) /* application-dependent condition */ 
{ 
p=&par->prompt [0]; /* par points to the item's loaded resource */ 
pt=p_slen(p) +1; /* p points to the button resource ID */ 
((IN_ACLIST *)p)->rid=AC_REPLACE; /* overwrite the resource ID */ 


} 
return (f_new(CAT_MYAPP_HWIM,C_ACLIST)); /* create an instance of ACLIST */ 


} 


This technique may be used in any case where modifications need to be made to the resource data between 
loading the resource and creating an instance of a control, provided such changes to not affect the length 
of the resource. 


7-8 


7 DIALOGS 


Retrieving dialog results 


In general, it is not sufficient merely to know which keypress caused the dialog to terminate. Most dialogs 
gather information from the user and this information must be communicated to the rest of the 
application. Such a dialog will generally set pLGBox_RBUF_FILLED iN digbox. flags, to prevent system 
code from writing to *digbox.rbuf. The dialog may then safely write its own specific results to that 
location. The collection of the results is normally done by a replacement di_key method. 


The di_key method is called from the wn_key method when the dialog box is potentially about to 
terminate, in the following circumstances: 


e when the keypress w_KEY_RETURN is received, provided digbox. flags contains 
DLGBOX_NOTIFY_ENTER, but not DLGBOX_ACTION_LIST OF DLGBOX_SMALL_ACTION_LIST 


e when the keypress w_kEY_EscapE is received, provided digbox. flags contains 
DLGBOX_NOTIFY_ESCAPE 


e the dialog contains an action list, dlgbox. flags contains DLGBOX_ACTION_LIST (or 
DLGBOX_SMALL_ACTION_LIST) and a keypress matches one of the buttons in the action list 


e for all keypresses, provided the dialog contains an action list and digbox. flags contains 
DLGBOX_NOTIFY_ALL_AcT as well as DLGBOX_ACTION_LIST (Of DLGBOX_SMALL_ACTION_LIST) 


A typical di_key method will sense the data in one or more of the dialog's component controls, write this 
data into a buffer or structure pointed to by digbox.rbuf and return wN_KEY_CHANGED. The choices 
available for transferring information to other objects within the application are similar to those already 
discussed for the di_dyn_init method. 


The method may test the keypress that caused it to be called, since this is passed as a parameter. It may, as 
a result of this test - or of other tests on the values of one or more component controls - return the value 
WN_KEY_NO_CHANGE to Indicate that the dialog box should not be terminated. 


Dialogs with and without 'WAIT' 


By default, the wszERV ws_do_diai method (and hence the hhaunchDial utility function) will not return 
until the dialog terminates and the dialog box has been destroyed. 


The processing of the dialog's results may therefore be divided between the d1_key method (which could, 
in this case, be viewed as a simple collector of the data) and code that immediately follows a call to, say, 
hLaunchDial. Most of the system-supplied dialogs use this technique since it allows application-specific 
code to follow the generic processing performed in the dialog's di_key method. 


If digbox. flags contains DLGBOX_NO_WwAIT, the ws_do_dial method (and hiaunchDial) will return as soon 
as the dialog is launched, rather than waiting until the dialog is destroyed. In such a case none of the 
processing of the dialog completion can be performed by code following a call to, say, hLaunchDial, since 
the dialog is still running at that time. All the completion processing must be done in the dialog's di_key 
method. 


Although this technique is more difficult to handle, it does have a number of advantages for the 
application writer, one of the more significant being that it is less expensive in terms of stack use. 


Controlling the width of a dialog 


The width of a dialog is normally set - following its initialisation and before it is made visible, in the 
dl_set_size method - so that it exactly fits the widest of its components. In cases where the size of a 
component may vary after the dialog becomes visible, this may not be adequate. 


An example of such a situation would be a dialog containing a centred text message that shows a page 
count used, say, to report the progress of document printing. The message may have to display page 
numbers up to 999, but would normally start at page 1. If the dialog box width is determined for the initial 
message "Page 1", it may be too narrow to display "Page 999". Two possible solutions are described 
below. 


OBJECT ORIENTED PROGRAMMING GUIDE 


If the maximum width of an item can be easily determined, the dialog can be forced to the required width 
by replacing the di_ing_minsize method. Using the above example, the dialog resource could contain the 
following initialisation data for a page count control: 


CONTROL 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD; 
info=TXTMESS 


{ 
flags=IN_TEXTWIN_AL_CENTRE; 


str="Page 1"; 
hi 
iy 


A suitable di_ing_minsize method could be of the form: 


METHOD VOID mydlg_dl_inq_minsize(PR_MYDLG *self, INT *pCent, INT *pPmpt,INT *pCtl); 


{ 
*pCent=gTextWidth (WS_FONT_SYSTEM, G_STY_NORMAL, "Page 999",8); 


i 


In an application that may have to be translated into one or more different languages, the width should be 
dtermined from text that is contained in the application's resource file. An alternative, if suitable for a 
particular application, would be to hard code an explicit maximum width. In this case it may be necessary 
to publish the maximum width (say, by means of a comment in the source of the application's resource 
file) for the benefit of translators or future developers. 


An alternative solution is to replace the d1_set_size method itself. In the present example this is possibly 
a better solution, since it reuses the code used elsewhere to set the page number. In this case the resource 


file initialises the control to its maximum size: 


CONTROL 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD; 
info=TXTMESS 


{ 
flags=IN_TEXTWIN_AL_CENTRE; 


str="Page 999"; 
he 
ian 


Assuming that this control immediately follows a dialog title (that is, it has an index number of 1) and 
that the resource file also contains a string resource of the form: 


RESOURCE STRING page_num_str {str="Page %u"; } 
the required code could then be: 


LOCAL_C VOID SetPage(INT pagenum) 


{ 
TEXT buf[10]; 


hAtos (&buf [0] ,PAGE_NUM_STR, pagenum) ; 
hDigSetText (1, &buf[0]); 
} 


#pragma METHOD_CALL 


METHOD VOID mydlg_dl_set_size(PR_MYDLG *self); 


{ 
p_supersend2 (self,O_DL_SET_SIZE); /* sets width for largest case */ 
SetPage(1); /* now set initial page number value */ 


} 


On first being made visible the dialog displays the text "Page 1" but is wide enough to display "Page 999". 


Subdialogs 


7 DIALOGS 


A dialog box may contain one or more items that can 'explode' into a separate subdialog, displayed over 
the main dialog. An example is the Margins line of the Print setup dialog as used, for example, in the 
Word application. The controt resource for this dialog item is as follows: 


CONTROL 
{ 
class=C_TEXTWIN; 


prompt="Margins"<WS_SYMBOL_ELLIPSIS>; 


info=TXTMESS 
{ 
stra"; 
flags=IN_TEXTWIN_POPOUT; 
ad 

Ir 


The dialog is shown, with the Margins item highlighted, in the following diagram: 


‘Page size... 
‘Header... 
‘Footer... 
‘Paging control... 
‘Printer model... 
‘Printer device... 


Print setup 


1, No, 1,2,3 
Canon BJ-18e 
Plis 


The significant points about the above contro. resource are that the control is an instance of the TExTWwIN 
class and that it is initialised with the In_TExTwIN_PopPout flag. By convention, the prompt (which is also 
an instance of TexTwin) for such an item terminates with an ellipsis. 


The text of the control, by convention, shows a summary of the current values of the information that may 
be modified by the subdialog and should therefore be set dynamically, in either the d1_dyn_init or the 
dl_set_size methods. Which method is the most appropriate depends on the nature of the summary text 
to be displayed. If it is of fixed width the resource text may be a null string (as it is in the above example) 
and the replacement text may safely be set in the d1_dyn_init method. If the summary text is of variable 
size, the resource file should contain a string that is guaranteed to be the longest that can be displayed. 
This text should be replaced in the di_set_size method, after supersending the pL_sET_s1zEz message, so 
that the dialog is guaranteed to be wide enough. A contrRot resource that uses this technique is shown 
below. 


CONTROL 
{ 
class=C_TEXTWIN; 
prompt="Font"<WS_SYMBOL_ELLIPSIS>; 
info=TXTMESS 
{ 
str="MMMMMMMMMMMMMMMMMMMM 00"; 
flags=IN_TEXTWIN_POPOUT; 
i 


/* max font name + space + max font size */ 


by 


The IN_TEXTWIN_Popourt flag ensures that the dialog item will respond to the Tab key by sending the 
dialog a DL_LAUNCH_SUB message (and to any other key by displaying the sys_popouT_HELP information 
message which, in English, is "Press Tab to change this item"). The main dialog's d1_launch_sub method 
launches the subdialog in exactly the same way as any other dialog is launched, that is by means of either 
the window server object's ws_do_dial method, or the hLaunchDial utility function. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The following diagram illustrates the Margins subdialog launched from the Print setup dialog. 


Print setup 


Margins (inches) 


mm et 


‘Left 1.25 
‘Right 1.25 
‘Bottom = 1.25 


‘Printer device... P.lis 


\. 


A parameter to d1_launch_sub is the index number of the dialog item that launched the subdialog. When 
launching the Margins subdialog, for example, this parameter has a value of 2. 


Processing within the subdialog is no different from that needed for a main dialog. On completion of the 
subdialog, the return to the main dialog is handled automatically by system code. 


CHAPTER 8 


DIALOG CONTROLS 


This chapter describes the use of the dialog controls provided by the HWIM object library. These controls 
are used as components of dialog boxes, allowing a wide variety of dialogs to be constructed. The 
following diagram, for example, shows a simple dialog constructed from a title and a prompted numeric 
editor control. 


Position to-do entry 


A control is implemented as an instance of a dialog control class: dialog control classes are not usually 
subclassed since the base class usually provides sufficient functionality and flexibility for most dialogs. 
Dialog control classes ultimately subclass the LopcEr class and thus inherit its property and methods. 


The initial appearance and behaviour of a dialog control are primarily determined by the (static) content 
of a CONTROL resource (in the application resource file). They may be dynamically modified by an optional 
WN_SET message sent from the d1_dyn_init method of the dialog box itself. The use of dynamic 
initialisation allows the control to be modified to take into account the current state of the application (in 
the above example the initial value will have been set to correspond to the position of an item in the 
current to-do list). 


The preferred method of sensing and setting a dialog control is to use the wn_sense and wn_set methods 
of the dialog box, as in the following code fragment: 


p_send4 (DatDialogPtr,O_WN_SET, control_index, pset) 


The control is identified by its index control_index. It is passed a pointer, pset, to an appropriate sE_xxx 
struct that holds the replacement data. The reserved static DatDialogPtr is assumed to point to the 
dialog; an assumption that is made throughout this chapter. It will always be true unless the dialog that 
owns the control sets the pR_WIN_No_pppP flag in its win. flags property (see the Dialogs chapter for 
further details). 


The alternative method of sensing/setting a dialog control is to use the wn_sense and wn_set methods of 
the particular dialog control, as in the following example code fragment. 


PR_LODGER *hand; 


hand=(PR_LODGER *)p_send3 (DatDialogPtr,O_DL_INDEX_TO_HANDLE, cont rol_index) 
p_send3 (hand, O_WN_SET,pset) 


The first method is clearly preferable. 


On termination of a dialog, the associated data can be sensed by means of a wn_sense method call from 
within the dialog box adi_key method. This is the normal practice for any but the simplest of dialogs. 


The HWIM object library also includes some convenient utility functions that can perform the more 
common dialog control sensing and setting actions. The full set of available functions is described in the 
Dialog box utilities section of the HWIM Utility Functions chapter. 


Each type of component control has an associated sz_xxx struct, used for both setting and sensing the 
control's data. In almost all cases the control allows selective setting of its data: which items of data that 
are set is determined by the value of a flags field in the appropriate sz_xxx struct. The flags field has no 
effect on sensing: the wn_sense method always senses all relevant data. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Text windows 


The textwin.g header file should be included when using a text window control. 


A TEXTWIN text window control is widely used to provide dialog titles, prompts for controls, and simple 
messages (and exceptionally to launch sub-dialogs). 


Hello 
message 


The above dialog could be created with the following pra.oc resource. 


RESOURCE DIALOG demodlg 
{ 
title="Hello"; 
flags=0; 
controls= 


{ 
CONTROL 


{ 

class=C_TEXTWIN; 

flags=DLGBOX_ITEM_CENTRE |DLGBOX_ITEM_DEAD; 
info=TXTMESS 


{ 
flags=IN_TEXTWIN_AL_CENTRE; 


str="message"; 
de 


di 
} 


The first item in this dialog is the title and is specified by the resource line. 


title="Hello"; 
This creates an underlined centred item, with index zero, displaying the appropriate text. 


Note that the title is, in fact, simply a TExTw1n control with index zero. The above line is exactly 
equivalent to including the following controt resource as the first item in the dialog: 


RESOURCE CONTROL demonstration 


{ 

class=C_TEXTWIN; 

flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD | DLGBOX_ITEM_UNDERLINED; 
info=TXTMESS 


{ 
flags=IN_TEXTWIN_AL_CENTRE; 


str="Hello"; 
i 
} 


As a result the title can easily be replaced dynamically using the npigsetText utility function called from 
within, say, the dl1_dyn_init method of the dialog. 


The second item in the demodig resource is a simple text window and is specified by the contro struct: 


CONTROL 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD; 
info=TXTMESS 


{ 
flags=IN_TEXTWIN_AL_CENTRE; 


str="message"; 
i 
} 


where the IN_TEXTWIN_AL_CENTRE flag ensures that the text is centred within the control. Note also the 
DLGBOX_ITEM_CENTRE flag that ensures that the control itself is centred within the dialog. 


8 DIALOG CONTROLS 


A TEXTWIN control can also have a prompt as shown in the following example. 


Hello 
Prompt message 


This dialog could be specified with the following resource: 


RESOURCE DIALOG demodlg 
{ 
title="Hello"; 
flags=0; 
controls= 
{ 
CONTROL 


{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_DEAD; 
prompt="Prompt"; 
info=TXTMESS 
{ 
flags=IN_TEXTWIN_AL_CENTRE; 
str="message"; 


hi 


hi 
} 


As in this example, the text of a prompt is specified by a line of the form: 
prompt="Prompt"; 


The text of a prompt is left aligned automatically, and is normally preceded by a bullet symbol , to 
indicate that the content of the corresponding control can be modified. The bullet symbol can be removed 
permanently by setting the pLcBox_1TEM_pEanp flag. 


Specifying prompt text does not necessarily mean that the prompt will appear. If, in addition to a prompt, 
a control also has the pLGBox_ITEM_CENTRE flag set (as in the earlier example) the prompt will be 
suppressed.This is, however, an abnormal case and is not a recommended technique, since it can lead to 
unexpected behaviour. A dialog set up in this way, and with the pLcBox_1TEM_DEap flag left clear, has the 
rather strange appearance shown in the following diagram. 


By initialising a text window used as dialog box control with the 1n_TExTw1IN_Popout flag, it may be used 
to trigger a subdialog, as described in the previous chapter. 


Initialisation 


The initial content and appearance of a text window control are specified by means of a rxTmEss resource 
struct: 


STRUCT TXTMESS 
{ 
WORD flags=0; 
TEXT str=""; 
} 


The str element specifies the initial text and flags may contain any sensible ored combination of: 


IN_TEXTWIN_AL_LEFT align text left 

IN_TEXTWIN_AL_RIGHT align text right 

IN_TEXTWIN_AL_CENTRE centre text 

IN_TEXTWIN_BOLD text in bold typeface 

IN_TEXTWIN_POPOUT launch a subdialog on receipt of a Tab key 


OBJECT ORIENTED PROGRAMMING GUIDE 


Setting 


The sz_TEXTWIN struct is used for setting and sensing text window property. 


typedef struct 
{ 
INT flags; 
UWORD state; 
TEXT *buf; 
UWORD len; 
}SE_TEXTWIN; 


When setting a text window, a sensible combination of the following values should be ored into the flags 
field, to indicate which aspects are to be set (or cleared). 


SE_TEXTWIN_ALIGN setting or clearing an alignment flag 
SE_TEXTWIN_BOLD setting or clearing the bold typeface flag 
SE_TEXTWIN_TEXT setting the text 


For each flag that is not set, the corresponding data will not be changed by the wn_set method. 


If setting the text, a pointer to the replacement text must be written to *but and the length of the text must 
be written to len. 


Any sensible combination of the following flags may be placed in the state element: 


PR_TEXTWIN_AL_LEFT text is left aligned 
PR_TEXTWIN_AL_RIGHT text is right aligned 
PR_TEXTWIN_AL_CENTRE text is centred 
PR_TEXTWIN_BOLD text is in a bold typeface 


The following example sets, for the item with item number index (assumed to be a text window) the text 
to the string pointed to by str. It also clears any PR_TEXTWIN_BOLD flag and sets PR_TEXTWIN_AL_RIGHT 
(clearing PR_TEXTWIN_AL_LEFT and PR_TEXTWIN_AL_CENTRE in the process). No other flags that may exist 
in the text window's property are affected. 


LOCAL_C VOID SetTextWin(INT index, TEXT *str) 
{ 
SE_TEXTWIN set; 


set. flags=SE_TEXTWIN TEXT |SE TEXTWIN. ALIGN |SE_TEXTWIN_BOLD; 
set.state=PR_TEXTWIN_AL_ RIGHT; /* PR_TEXTWIN_BOLD is not set, so will be cleared 


*/ 
set. buf=str; 
set.len=p_slen(str); 
p_send3 (DatDialogPtr,O_WN_SET, index, &set) ; 
} 


It is rare for application code to set more than the text of an instance of TExtw1n used as a dialog box 
component. In such a case the hp1gSetText utility function may be used. As mentioned earlier, this utility 
function can also be used to replace the text of the title by passing an index of zero. 


The text window's flags are generally either set on initialisation (usually from In_TExTWIN_xxx values set 
in a resource item) or are set or cleared by system code (see, for example, the pLGBox d1_item_lock and 
dl_item_dim methods). 


Sensing 


Sensing a text window writes a pointer to the text and the text length to the but and 1en elements of an 
SE_TEXTWIN Struct. It does not provide any information about the text window's state flags. A typical call is 
as follows: 


SE_TEXTWIN set; 
p_send3 (DatDialogPtr,O_WN_SENSE, index, &set) ; 


A text window must not be sensed if it contains no text. 


8 DIALOG CONTROLS 


Choice lists 
The chlist.g header file should be included when using a choice list control. 


A choice list dialog control presents the user with a list of choice items only one of which is visible and 
hence selected. The selection can be changed by the following means: 


e using the left and right arrow keys 
e using first letter matching 
e via a pop-out expanded view obtained with the tab key 


e or optionally, via incremental matching with a sequence of key presses (the control must be specially 
configured to allow incremental matching by use of the appropriate flag - see below). 


In the following two illustrations a dialog containing three choice lists is shown. In the right hand picture 
the user has pressed the Tab key to obtain the pop-out expanded view for the first, highlighted, choice list. 


Stule for entry Stule for entry 


+No+ c(Noy 


‘Italic No ‘Italic Yes 
‘Underline No ‘Underline Wo 


A choice list control, allowing the user to select one of four presidents of the United States, could be 
defined with the following two resources: 


RESOURCE MENU presidents 

{ 

items= 
{ 
CHOICE_ITEM {str="Kennedy";}, 
CHOICE_ITEM {str="Johnson";}, 
CHOICE_ITEM {str="Nixon"; }, 
CHOICE_ITEM {str="Ford"; } 
i 

} 


RESOURCE CONTROL demonstration 
{ 
class=C_CHLIST; 
flags=DLGBOX_ITEM_NOTIFY_CHANGED; 
prompt="President"; 
info=CHLIST{rid=presidents; }; 

} 


In this case, the dialog control would initially display "Kennedy", surrounded by a pair of little arrows, to 
the right of the "President" prompt. 


Initialisation 


The cuit1st resource struct specifies the initial content and appearance of a choice list: 


STRUCT CHLIST 
{ 
LINK rid=0; 
BYTE nsel=0; 
BYTE flags=0; 
} 


OBJECT ORIENTED PROGRAMMING GUIDE 


The ria element identifies the menu resource that contains the list of selections as an array of cHOICE_ITEM 
structs: 


RESOURCE MENU example_menu 

{ 

items= 
{ 
CHOICE_ITEM {str="zero";}, 
CHOICE_ITEM {str="one";}, 
CHOICE_ITEM {str="two"; } 
‘i 

} 


The cHorck_ITEm structs are indexed according to the order in which they are listed in the menu resource. 
Thus the first has index zero, the second has index one, and so on. In hwim.rh the cHoIcE_ITE™ struct is 
defined as follows: 


STRUCT CHOICE_ITEM /* choice list item */ 
BYTE { 
TEXT str=""; /* identification text */ 
} 


The nse1 element of a cHLIsT resource struct specifies the index number of the initially selected item. 


The flags element may optionally contain the 1n_CHLIST_INCREMENTAL flag, to allow choice list selection 
to be made using incremental key matching. Note that this option can not be set dynamically. 


Setting 


A choice list is set by passing a pointer to an sz_CHLIST struct to the wn_set method 


typedef struct 
{ 


UWORD set_flags; /* which fields are significant */ 
PR_VAROOT *data; /* pointer to array containing data */ 
UWORD nsel; /* index of current item */ 


} SE_CHLIST; 


The property to be set is indicated by oring one or more of the following flags into the flags field of the 
above struct. 


SE_CHLIST_NSEL the index of the current item is to be set. 


SE_CHLIST_DATA the data is to be replaced. The replacement data is a string array - see 
variable arrays in the OLIB Reference manual. 


SE_CHLIST_RETAIN data should not be destroyed on destruction of the choice list control - once 
set this flag cannot be cleared. 


The content of a choice list can be set dynamically, say, from the dialog's di_dyn_init method. However, 
changing the content of a choice list once the dialog has become visible is not recommended, since the 
width of the dialog box is set on initialisation. If the choice list content must be replaced, then care should 
be taken to ensure that the text does not become too wide for the dialog box to display. 


Sensing 


A choice list is sensed by passing a pointer to an sE_CHLIST struct to the wn_sense method. The 
SE_CHLIST struct is defined above. Both nsei and data are sensed. 


8 DIALOG CONTROLS 


Push buttons and action lists 


The aclist.g header file should be included when using an action list control. 


An action list control presents the user with a horizontal list of one or more push-button options, as 
illustrated in the following diagram: 


Alarm details 
¢Yes> 


‘Time before 66:45 
‘Alarm at 45:55 pm 


‘Days previous 64 
‘Sound Leloup 


Test sound Confirm 


[Menu] (_Enter_| 


This action list consists of the two buttons, and their accompanying labels "Test sound" and "Confirm", at 
the bottom of the dialog (note that the action list must either be the last of a series of controls, or it must 
appear on its own).An action list, allowing the user to select either Yes or No, could be defined with the 
following resource: 


RESOURCE ACLIST_ARRAY yes_or_no 


{ 
button= 


{ 

PUSH_BUT 
{ 
keycode=—'n'; 
str="No"; 
}y 

PUSH_BUT 
{ 
keycode='y'; 
str="Yes"; 


} 


‘i 


The push-button is defined by a text string str that appears above the button, and a keycode indicating 
the key to be pressed by the user. For non-special keys, the uppercased key symbol will appear on the 
push-button. For special keys, the keycode is conveniently specified by a symbolic constant. The symbolic 
constants, and the text that will appear on the push-button, are as follows: 


W_KEY_RETURN "Enter" 

W_KEY_ESCAPE "Esc" 
W_KEY_DELETE_LEFT "Del" 

W_KEY_SPACE "Space" 

W_KEY_UP <WS_SYMBOL_UP_KEY> 
W_KEY_DOWN <WS_SYMBOL_DOWN_KEY> 
W_KEY_RIGHT <WS_SYMBOL_RIGHT_KEY> 
W_KEY_LEFT <WS_SYMBOL_LEFT_KEY> 
W_KEY_TAB "Tab" 

W_KEY_MENU "Menu" 


OBJECT ORIENTED PROGRAMMING GUIDE 


For example, the leftmost button in the dialog, illustrated above, could be defined with the following 
PUSH_BUT resource. 


RESOURCE PUSH_BUT test_sound_button 


{ 
keycode=W_KEY_MENU; 
str="Test sound"; 


} 


A negative keycode (as in an earlier example) indicates that the escape key can also be used to obtain the 
same effect (note that this is not possible if the escape key has already been assigned). The yes_or_no 
resource, defined earlier, could be used to create a dialog, asking the user to press 'y' or 'n', as follows: 


RESOURCE DIALOG get_answer 
{ 
title="Accept changes ?" 
flags=DLGBOX_NOTIFY_ESCAPE | DLGBOX_RBUF_FILLED; 
controls= 
{ 
CONTROL 


{ 
class=C_ACLIST; 
info=ACLIST 
{ 
rid=yes_or_no; 


‘i 


‘i 
} 


The same effect could be obtained by using the sys_Nno_ves resource defined in the system resource file. 


There is an alternative form of the above action list, which is useful when space is at a premium, as 
illustrated in the following diagram. 


(E)End (R)Replace (S)Skip (ADA 


As in this case, the compact, or small, action list is not usually accompanied by any other controls. 


A small action list uses the same acLIST_ARRay resource as the standard action list. The example given 
above, yes_or_no, could be used to create a dialog consisting of a small action list, and no other controls, 


as follows: 


RESOURCE DIALOG get_answer 
{ 
flags=PR_WIN_FORCE_BOTTOM; 
controls= 
{ 
CONTROL 
{ 
class=C_SMACLIST; 
info=ACLIST 
{ 
rid=yes_or_no; 


di 


‘i 
} 


The only difference, between the dialog resources for the two action lists, is the class: the standard action 
list is an instance of the acuist class whereas the small action list is an instance of the smactist class. 


Initialisation 


The appearance and behaviour of a push button are specified with a pusH_BuT resource. 


STRUCT PUSH_BUT 
BYTE { 
WORD keycode; 
TEXT str; /* text associated with button */ 


} 


8 DIALOG CONTROLS 


One or more buttons are in turn collected into an array: the first button in the array has index zero, the 
second has index one, and so on. The first button will appear on the far left of the control. 


STRUCT ACLIST_ARRAY rid 


{ 
LEN BYTE STRUCT button[]; /* array of push_buttons */ 


} 


This array is included as a dialog control by means of the acurst struct. 


STRUCT ACLIST /* Initialising struct for action list */ 


{ 
LINK rid; 


} 


The resources are the same for both the standard and the small action lists. 


There are no flags associated with this control, since there is nothing to change on initialisation, or via 
setting. 


Setting and sensing 


There is nothing that can usefully be set or sensed. 


Edit boxes 


The edwin.g header file should be included when using an edit box control. 


An edit box control presents an editable string which may be wholly or partially visible. A wide range of 
options are available for customising this control: see the resources section. In the following example an 
edit box is displaying "rabbit burrow". 


Find 
rabbit burrow 


‘Direction Forwards 
‘Case sensitive No 


An edit box control accepting up to 40 characters, including tabs, with 20 visible at any one time could be 
created using the following resource: 


RESOURCE CONTROL 
{ 
class=C_EDWIN; 
prompt="string"; 
info=EDWIN 
{ 
Sstrarns 
£lags=IN_EDWIN_VULEN_CHARACTERS | IN_EDWIN_ACCEPT_TABS; 
maxlen=40; 
vulen=20; 
i; 
} 


Initialisation 


The initial content and appearance of an edit box are specified by an Epw1N resource struct. 


STRUCT EDWIN /* edit box */ 


{ 
WORD vulen; /* ignored unless either _VULEN_ flag set */ 


WORD flags=0; 
WORD maxlen; 
TEXT str=""; 
} 


where maxien specifies the default edit box width, and the maximum length of string that may be edited. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The behaviour of an edit box is specified by oring one or more of the following flags into the flags 
member of the Epwin struct. 


N_EDWIN_DIALLABLE indicates that the edit box should accept the telephone symbol via the 
shift+dial key combination. 
N_EDWIN_ACCEPT_TABS indicates that the edit box should accept tabs. 
N_EDWIN_AUTO_CUR_END indicates that the text, when first displayed, is not to be highlighted 
and that the cursor is to be placed at the rightmost position. 
N_EDWIN_NO_AUTOSELECT indicates that the text, when first displayed, is not to be highlighted 
and that the cursor is to be placed at the leftmost position. 
N_EDWIN_VULEN_CHARACTERS indicates that the width of the edit box is specified, in characters, in 
member vulen of the EpwIn struct. 
N_EDWIN_VULEN_PIXELS indicates that the width of the edit box is specified, in pixels, in 
member vulen of the EpwIn struct. 
Setting 
The sz_EDwIN struct is used for setting an edit box. 
struct 
{ 
TEXT *buf; 
UWORD len; 
} SE_EDWIN; 


The following code fragment demonstrates the setting of an edit box, assumed to be the item with index 2: 


SE_EDWIN set; 
TEXT buf[11]; 


p_scpy (&buf[0],"Hello world"); 

set .buf=&buf [0]; 
set.len=p_slen(set.buf); 

p_send4 (DatDialogPtr,O_WN_SET,2,&set) ; 


Sensing 


The sz_Epwtn struct defined above is also used for sensing an edit box. The following code fragment 
demonstrates the sensing of an edit box, again assuming that it is the item with index 2: 


SE_EDWIN sense; 


p_send4 (DatDialogPtr,O_WN_SENSE,2,é&sense) ; 


LONG numeric editor 


The ncedit.g header file should be included when using a long numeric editor control. 


A long numeric editor control presents an editable long integer value. In the following diagram, the long 
numeric editor control has a current value of five hundred thousand. 


Demonstration 


‘LONG 566648 


A numeric editor control could be created using the following resource: 


RESOURCE CONTROL 

{ 

class=C_LNCEDIT; 

prompt="LONG"; 

info=LNCEDIT 
{ 
low=1; 
high=700000; 
current=500000; 
i 


8 DIALOG CONTROLS 


Initialisation 
The initial content of a long numeric integer is specified by means of an LNCEDIT resource struct. 


STRUCT LNCEDIT /* long number edit box */ 
{ 
LONG current = 0; 
LONG low = 0; /* lowest allowed value */ 
LONG high = 10000000; /* highest allowed value */ 
} 


where the current value may not be less than iow or greater than high. 


Setting 
The sz_LNcEDIT struct is used for setting a long integer numeric editor. 


typedef struct 
{ 
LONG value; 
LONG low; 
LONG high; 
UWORD flags; 
} SE_LNCEDIT; 


The property to be set is indicated by oring one or more of the following flags into the flags field of the 
above struct 


SE_LNCEDIT_VALUE indicates that the current value is to be set 
SE_LNCEDIT_LOW indicates that the lower limit is to be set 
SE_LNCEDIT_HIGH indicates that the upper limit is to be set 


The following code fragment illustrates the setting of a long integer numeric editor, assumed to be the 
item with index 4: 


SE_LNCEDIT set; 


set .flags=SE_LNCEDIT_VALUE|SE_LNCEDIT_HIGH; 
set.value=100; 

set .high=50000; 

p_send4 (DatDialogPtr,O_WN_SET, 4, &set) ; 


Sensing 


A pointer to a Lone is used for sensing the current value. The low and nigh values can not be sensed. 
The following code fragment demonstrates the sensing of a long numeric editor, again assuming it to be 
the item with index 4: 


LONG value; 


p_send4 (DatDialogPtr,O_WN_SENSE, 4, évalue) ; 


Integer numeric editor 
The ncedit.g header file should be included when using an integer numeric editor control. 


An integer numeric editor control presents an editable unsigned integer value. In the following diagram 
the integer numeric editor has a current value of one. 


Position to-do entry 


An integer numeric editor control could be created using the following example resource: 


RESOURCE CONTROL 
{ 
class=C_NCEDIT; 
prompt="Number of cars"; 
info=NCEDIT 
{ 
low=0; 
high=6; 
current=4; 


‘i 


OBJECT ORIENTED PROGRAMMING GUIDE 


Initialisation 
The initial content of an integer numeric editor are specified by means of an ncEDIT resource struct: 


STRUCT NCEDIT /* UWORD number edit box */ 
{ 
UWORD current = 0; 
UWORD low = 0; /* lowest allowed value */ 
UWORD high = 65535; /* highest allowed value */ 
} 


where the current value may not be less than 1ow or greater than nigh. 
Setting 
The sz_NcEpDIT struct is used for setting an integer numeric editor. 


typedef struct 
{ 
UWORD value; 
UWORD low; 
UWORD high; 
UWORD flags; 
} SE_NCEDIT; 


The property to be set is indicated by oring one or more of the following flags into the fags field of the 
above struct. 


SE_NCEDIT_VALUE indicates that the current value is to be set. 
SE_NCEDIT_LOW indicates that the lower limit is to be set. 
SE_NCEDIT_HIGH indicates that the upper limit is to be set. 


The following code fragment demonstrates the setting of an integer numeric editor, assuming it to be the 
item with index 4: 


SE_NCEDIT set; 


set .flags=SE_NCEDIT_VALUE | SE_NCEDIT_HIGH; 
set .high=100; 

set.value=10; 

p_send4 (DatDialogPtr,O_WN_SET, 4, &set) ; 


Sensing 


A pointer to a uworp is used for sensing the current value. The low and nigh values can not be sensed. 
The following code fragment demonstrates the sensing of an integer numeric editor, again assuming that 
it is the item with index 4: 


UWORD value; 


p_send4 (DatDialogPtr,O_WN_SENSE, 4, &évalue) ; 


WORD numeric editor 


The ncedit.g header file should be included when using a word numeric editor control. 


A word numeric editor control presents an editable signed integer value. In the following example the 
word numeric editor has a current value of minus one hundred. 


A word numeric editor control could be created using the following resource: 


RESOURCE CONTROL 
{ 
class=C_WNCEDIT; 
prompt="Temperature"; 
info=WNCEDIT 
{ 
current=-100; 
i 


8 DIALOG CONTROLS 


Initialisation 
The initial content and appearance of a word numeric editor are specified by means of a wNcEDIT resource 


struct: 


STRUCT WNCEDIT /* numeric control edit box (signed words) */ 


{ 

WORD current = 0; 

WORD low = -32768; /* lowest allowed value */ 
WORD high = 32767; /* highest allowed value */ 


} 


where the current value may not be less than 1ow or greater than high. 


Setting 


The sz_wNcEDIT struct is used for setting the word numeric editor. 


typedef struct 


WORD value; 
WORD low; 

WORD high; 
WORD flags; 


} SE_WNCEDIT; 


The property to be set is indicated by oring one or more of the following flags into the fags field of the 
above struct. 


SE_WNCEDIT_VALUE indicates that the current value is to be set. 
SE_WNCEDIT_LOW indicates that the lower limit is to be set. 
SE_WNCEDIT_HIGH indicates that the upper limit is to be set. 


The following code fragment demonstrates the setting of a word numeric integer, assuming it to be the 
item with index 3: 


SE_WNCEDIT set; 


set .flags=SE_WNCEDIT_VALUE|SE_WNCEDIT_LOw; 

set.low=4; 

set.value=10; 

p_send4 (DatDialogPtr,O_WN_SET, 3, &set) ; 
Sensing 


A pointer to a worp is used for sensing the current value. The low and nigh values can not be sensed. 
The following code fragment demonstrates the sensing of a word integer numeric editor, again assuming 
that it is the item with index 3: 


WORD value; 


p_send4 (DatDialogPtr,O_WN_SENSE, 3, évalue) ; 


Range numeric editor 
The rgedit.g header file should be included when using a range numeric editor control. 


A range numeric editor control presents two editable unsigned words specifying upper and lower values of 
a range. In the following example a range numeric editor is shown with a lower value of thirty and an 


upper value of sixty. 


A range numeric editor control could be defined using the following resource: 


OBJECT ORIENTED PROGRAMMING GUIDE 


RESOURCE CONTROL 
{ 
class=C_RGEDIT; 
prompt="Age range"; 
info=RGEDIT 
{ 
value_1=100; 
value_2=200; 
de 
} 


Initialisation 


The initial content of a range numeric editor is specified by means of an RcEDIT resource struct: 


STRUCT RGEDIT /* range editor */ 
{ 
WORD low=1; /* lowest allowed value */ 
WORD value_1=1; /* lower value of range */ 
WORD value_2=9999; /* higher value of range */ 
WORD high=9999; /* highest allowed value */ 


} 


where value_1 must be less than or equal to value_2 and both values may not be less than 1ow or greater 
than high. 


Setting 


The sz_RGEDIT Struct is used for setting the range numeric editor. 


typedef struct 
{ 
UWORD value[4]; 
UWORD flags; 
} SE_RGEDIT; 


The value array is indexed as follows: 

IX_RGEDIT_LOW index of lower limit in value array. 
IX_RGEDIT_VALUE_1 index of lower current value in value array. 
IX_RGEDIT_VALUE_2 index of upper current value in value array. 
IX_RGEDIT_HIGH index of upper limit in value array. 


The property to be set is indicated by oring one or more of the following flags into the flags field of the 
above struct. 


SE_RGEDIT_LOW indicates that the lower limit is to be set. 
SE_RGEDIT_VALUE_1 indicates that the current lower value is to be set. 
SE_RGEDIT_VALUE_2 indicates that the current upper value is to be set. 
SE_RGEDIT_HIGH indicates that the upper limit is to be set. 


The following code fragment demonstrates the setting of a range numeric integer, assuming that it is the 
item with index 2: 


SE_RGEDIT set; 


set. flags=SE_RGEDIT_VALUE 1|SE RGEDIT_VALUE_2; 
set.value [IX_RGEDIT_VALUE_1]=4; 

set .value [IX_RGEDIT_VALUE_2]=10; 

p_send4 (DatDialogPtr,O_WN_SET,2,&set); 


Sensing 


The sz_RGEDIT struct (see above) is used for sensing the range numeric editor: all four members of the 
struct are sensed. The following code fragment demonstrates the sensing of a range numeric editor, again 
assuming it to be the item with index 2: 


SE_RGEDIT sense; 


p_send4 (DatDialogPtr,O_WN_SENSE, 2, &sense) ; 


8-14 


8 DIALOG CONTROLS 


Floating point editor 
The fltedit.g header file should be included when using a floating point editor control. 
A floating point editor control presents an editable floating point number. In the following example two 


floating point editors are shown with current values of 21.00 and 29.70. 


Page size (cm) 
‘Page size Custom 


‘Width 
‘Height 29.76 
‘Orientation Portrait 


A floating point editor control could be defined using the following resource: 


RESOURCE CONTROL 


{ 
class=C_FLEDIT; 
prompt="Width"; 
info=FLEDIT 
{ 
current=21.00; 
low=0.0; 
high=40.0; 
ndec=2; 
‘i 
} 


Initialisation 
The initial content and appearance of a floating point editor are specified by means of an FLTEDIT 
resource struct: 


STRUCT FLTEDIT /* floating point edit box */ 


{ 

DOUBLE current=0.0; 

DOUBLE low =-9.9999999999e99; /* lower bound */ 

DOUBLE high =9.9999999999e99; /* upper bound */ 

BYTE vulen=5; /* width of editor in characters */ 
BYTE ndec=0; /* P_DTOB_GENERAL */ 


} 


where the current value may not be less than 1ow or greater than high. The current value is displayed to 
ndec decimal places in a box of width vuien characters. If ndec is zero, the current value is displayed in 
general format as defined for the PLIB routine p_dtob (see the Plib Reference manual). 


Setting 


The sz_FLEDIT struct is used for setting the floating point editor. 


typedef struct 
{ 


DOUBLE current; /* current value */ 
DOUBLE low; /* lower bound */ 
DOUBLE high; /* upper bound */ 


WORD set_flags; /* which fields to set */ 
} SE_FLTEDIT; 


The property to be set is indicated by oring one or more of the following flags into the set_flags field of 
the above struct. 


SE_FLTEDIT_VALUE indicates that the current value is to be set. 
SE_FLTEDIT_LOW indicates that the lower limit is to be set. 
SE_FLTEDIT_HIGH indicates that the upper limit is to be set. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The following code fragment demonstrates the setting of a floating point numeric integer, assuming that it 
is to be the item with index 2: 


SE_FLTEDIT set; 


set .set_flags=SE_FLTEDIT_VALUE|SE_FLTEDIT_LOW; 
set.low=4; 

set.value=10.3; 

p_send4 (DatDialogPtr,O_WN_SET,2,&set) ; 


Sensing 


A pointer to a pouBLz is used for sensing the current value. The low and high values can not be 
sensed. The following code fragment demonstrates the sensing of a range integer numeric editor, again 
assuming it to be the item with index 2: 


DOUBLE current; 


p_send4 (DatDialogPtr,O_WN_SENSE,2,&current) ; 


Date/time editor 


The dtedit.g header file should be included when using a date/time editor control. 


The date/time editor presents either an editable date, an editable time or an editable duration. In the 
following example the upper date/time editor is showing the time in am/pm format in hours, minutes and 
seconds, the lower editor is showing the date. 


Set time and date 


Goo 28:03 pm 
‘Date 11/18/1993 


A date/time editor showing a duration of one hour and ten minutes, with upper and lower limits of 
23h59m and OhOm could be defined using the following resource: 


RESOURCE CONTROL 

{ 

class=C_DTEDIT; 

prompt="Time left"; 

info=DTEDIT 
{ 
flags=IN_DTEDIT_HHMM_D]|IN_DTEDIT_INIT; 
current=4200; 


low=0; 
high=86340; 
i 

} 


Initialisation 


The initial content of a date/time editor is specified by means of a pTEDIT resource struct: 


STRUCT DTEDIT /* combined date/time editor */ 


{ 

WORD flags; 

LONG current; 

LONG low; /* lowest allowed value */ 
LONG high; /* highest allowed value */ 
} 


where the current value may not be less than 1ow or greater than high. Note that any supplied values of 
current, low and high will be ignored, and a set of default values used, if f1ags does not contain 
IN_DTEDIT_INIT. 


The format of the date/time editor must be specified by including one of the following values in the flags 
member. 


8 DIALOG CONTROLS 


IN_DTEDIT_DDMMYYYY initialise as a date editor to display a date in day, month and year format: the 
current, low and high dates are specified as days elapsed since 01/01/1900. 
Note that the control can not display dates before 01/01/1980. See the 
MFNE subclass examples section of the Numeric Editors chapter of the 
HWIM Reference manual for an example of a date editor with an extended 
range. 


IN_DTEDIT_HHMMSS initialise as a time editor to display a time as hours, minutes and seconds. 
The current, low and high times are specified in seconds elapsed since 
midnight. 


IN_DTEDIT_HHMM initialise as a time editor to display a time as hours and minutes. The 
current, low and high times are specified in seconds elapsed since 
midnight. 


N_DTEDIT_HHMMSS_D initialise as a time editor to display a duration as hours, minutes and 
seconds. The current, low and high durations are specified in seconds. 


N_DTEDIT_HHMM_D initialise as a time editor to display a duration as hours and minutes. The 
current, low and high durations are specified in seconds. 


N_DTEDIT_HHMMSS_ND __initialise as a time editor to display a negative duration as hours, minutes 
and seconds. The current, low and high durations are specified in seconds. 


N_DTEDIT_HHMM_ND initialise as a time editor to display a negative duration as hours and 
minutes. The current, low and high durations are specified in seconds. 


Setting 


The sz_pTED1IT struct is used for setting the date/time editor. 


typedef struct 


UWORD flags; 
LONG value; 
LONG low; 
LONG high; 
} SE_DTEDIT; 


The property to be set is indicated by oring one or more of the following flags into the fags field of the 
above struct. 


SE_DTEDIT_VALUE indicates that the current value is to be set. 
SE_DTEDIT_LOW indicates that the lower limit is to be set. 
SE_DTEDIT_HIGH indicates that the upper limit is to be set. 


The interpretation of value, low and high depends on the type of data that the control is set to display, as 
specified in the Jnitialisation section above. 


The following code fragment demonstrates the setting of a date/time editor, assuming that it is the item 
with index 2: 


ULONG stime; 
P_DAYSEC ds; 
SE_DTEDIT set; 


stime=p_date(); 

p_sttods (&stime, &ds) ; 
set.value=ds.days; 

set. flags=SE_DTEDIT_VALUE; 

p_send4 (DatDialogPtr,O_WN_SET,2,&set); 


Note that this code assumes that the date/time editor has been initialised to display a date, by setting the 
initialisation flag IN_DTEDIT_DDMMyyyy. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Sensing 


The sz_DTEDIT Struct is used for sensing the date/time editor: 
typedef struct 


UWORD flags; 
LONG value; 
LONG low; 
LONG high; 

} SE_DTEDIT; 


Note, however, that the pTEDIT wn_sense method only writes to the value member. The low and high 
values can not be sensed. 


The following code fragment demonstrates the sensing of a date/time editor, again assuming it to be the 
item with index 2: 


SE_DTEDIT sense; 


p_send4 (DatDialogPtr,O_WN_SENSE, 2, é&sense) ; 


The Latitude/Longitude editor 


The /ledit.g header file should be included when using a latitude/longitude editor control. 


A latitude/logitude editor presents either an editable latitude or an editable longitude. Both the latitude 
and the longitude are expressed in degrees and minutes followed by a single character specifying the 
cardinal point i.e. N, W, S or E. 


In the following example the upper latitude/longitude editor is showing a longitude and the lower editor is 
showing a latitude. 


‘Add city 


‘Country Denmark 
‘Longitude H1B°1HE 


‘Latitude 656°88 N 
‘Area code 

‘GMT offset 1:46 
‘Zone European 


A latitude editor control that initially displayed 56° 8 N could be defined using the following resource: 


RESOURCE CONTROL 

{ 
class=C_FLEDIT; 
flags=IN_LLEDIT_LATITUDE; 
prompt="Latitude"; 
info=LLEDIT 

{ 

value=3368; 

i 
} 


Initialisation 


The initial content and appearance of a latitude/longitude editor are specified by means of an LLEDIT 
resource struct: 


STRUCT LLEDIT /* lat long editor */ 
{ 
WORD flags; /* Latitude or longitude */ 
WORD value=0; /* The default value */ 


} 


where the latitude/longitude value is in minutes of arc: a positive sign indicating a latitude/longitude in 
the northern /western hemisphere, and a negative sign indicating a latitude/longitude in the southern 
/eastern hemisphere. Thus -604 corresponds to a latitude of 10° 4 S or a longitude of 10° 4 E. 


8-18 


8 DIALOG CONTROLS 


The behaviour of a latitude/longitude editor is specified by assigning one of the following flags to the flags 
member. 


IN_LLEDIT_LATITUDE display value as a latitude. 
IN_LLEDIT_LONGITUDE display value as a longitude. 
Setting 


The sz_LLEDIT struct is used for setting the latitude/longitude editor. 


typedef struct 
{ 
WORD value; 
} SE_LLEDIT; 


The following code fragment demonstrates the setting of a date/time editor, assuming it to be the item 
with index 2: 


SE_LLEDIT set; 


set .value=3368; 
p_send4 (DatDialogPtr,O_WN_SET,2,&set); 


Sensing 


A pointer to an sz_LLEDIT struct is used for sensing the current value. The following code fragment 
demonstrates the sensing of a latitude/longitude editor, again assuming that it is the item with index 2: 


SE_LLEDIT sense; 


p_send4 (DatDialogPtr,O_WN_SENSE, 2, é&sense) ; 


File name editor 


The files. g header file should be included when using a file name editor control. 


A file name editor presents the user with a file name that may be edited with the keyboard or with the 
pop-out file selector (via the Tab key). A wide range of options is provided for customising the behaviour 
of file name editors: these options are described in the resources section below. It is commonly used when 
saving an edited file, for example a document created with the Word application. It is not usually used for 
opening an already existing file for which a file name choice list is better suited. 


A file name editor is generally supplied together with a pack selector, that is placed immediately below, as 
in the following example: the selector is specified by oring the pLGBox_NEEDS_PAcx flag into the flags 
member of the contro. struct (see below). 


Save icon as pic file 


t Name JF asel.pic| 


* Disk Internal 


A file name editor control could be defined using the following resource: 


RESOURCE CONTROL 

{ 

class=C_FNEDIT; 

flags=DLGBOX_ITEM_NEEDS_PACK; 

prompt=""; 

info=FNEDIT 
{ 
flags=IN_FNEDIT_STANDARD; 
fname="easel.pic"; 


hi 


OBJECT ORIENTED PROGRAMMING GUIDE 


Initialisation 


The initial content and appearance of a file name editor are specified by means of an FNEDIT resource 
struct: 


STRUCT FNEDIT /* filename editor */ 
{ 
BYTE flags=0; 
TEXT fname=""; 
} 


The behaviour of the file name editor is specified by oring a suitable combination of the following flags 
into the £1ags member of the above struct. 


N_FNEDIT_STANDARD set fname to be the file name pointed to by patUsedPathNamePtr: typically 
the name of the application's current file. 

N_FNEDIT_ALLOW_DIRS allow the name of a directory with the file name. 

N_FNEDIT_JUST_DIRS allow only the name of a directory and no file name. 

N_FNEDIT_FORCE_NXIST disallow existing files. 

N_FNEDIT_NO_AUTOQUERY do not query on an existing file (by default the control prompts the user 


before accepting the name of an already existing file, to help prevent 
accidental deletion/overwriting). 


IN_FNEDIT_ACCEPT_NULL allow the null string. 


IN_FNEDIT_SET_DEFEXT set the default file extension to that of the file name specified in the 
FNEDIT resource struct. 


IN_FNEDIT_CAN_WILDCARD allow wildcards. 


Setting 


The file name may be set by passing a pointer to a character string to the wn_set method. The character 
string should contain the file name terminated by a zero character. 


Note that any extension present in the name string will be displayed in the control, even if it is the 
control's default extension. To obey the guidelines concerning the display of an application's default 
filename extension, the caller is responsible for checking for the presence of the default extension in the 
string and removing it before setting the file name editor control. 


The default extension may be set by passing a pointer to a character string to the wn_set method. The first 
character must be 0x01. The following characters can either be a file name, or a file extension preceded by 
a full stop character. Only the extension is significant. 


The following code fragment demonstrates the setting of the default extension, assuming that the rNEDIT 
control is the item with index | in the dialog: 


TEXT buf [P_FNAMESIZE]; 


buf [0]=1; 
p_scpy (&buf[1],".DBF") ; 
p_send4 (DatDialogPtr,O_WN_SET,1,é&buf[0]); 


Sensing 


A buffer, of size at least p_rnames1zz bytes, is used for sensing the current full file specification from a 
file name editor control, as illustrated by the following code fragment, again assuming it to be the item 
with index 1: 


TEXT buf [P_FNAMESIZE]; 


p_send4 (DatDialogPtr,O_WN_SENSE,1, &buf[0]); 


8 DIALOG CONTROLS 


File name choice list 


The files. g header file should be included when using a file name choice list control. 


A file name choice list presents the user with a choice of files with the selection being made with the 
keyboard arrow keys, via letter matching or by pressing Tab to display the pop-out file selector. A wide 
range of options is provided for customising the behaviour of a file name choice list: these options are 
described in the resources section below. 


A file name choice list is always supplied with a pack selector that is placed immediately below as in the 
following example: the selector is specified by oring the pLGBox_NEEDS_pack flag into the flags member 
of the contRox struct (see below). 


Open file 


¢ gelacuti+ 


' Disk Internal 


A file name choice list is commonly used when opening an existing file. It is not usually used for saving 
an edited/modified file for which a file name editor is better suited. 


A file name choice list control could be defined using the following resource: 


RESOURCE CONTROL 
{ 
class=C_FNSELWN; 
flags=DLGBOX_ITEM_NEEDS_PACK; 
info=FNSELWN 
{ 


fname=""; 
i 
} 


Initialisation 


The initial content and behaviour of a file name choice list control are defined by an rNSELWN resource 
struct as follows: 


STRUCT FNSELWN /* filename selector */ 
{ 
BYTE flags=0; 
TEXT fname=""; 
} 


The behaviour of a file name choice list control is specified by oring one or more of the following flags 
into the £1ags member of the above struct 


N_FNSELWN_STANDARD select the file specified by the patusedPathNamePtr reserved static: 
typically points to the application's current file. 


N_FNSELWN_SHOW_DIRS show directory names. 

N_FNSELWN_HIDE_FILES hide directory names. 

N_FNSELWN_RESTRICT_LIST display only files with extension matching the default. 
N_FNSELWN_CAN_TAG allow file tagging: file tagging is carried out with the file name choice 


list - pressing the '+' and '-' keys tags and untags a file respectively. 


N_FNSELWN_ACCEPT_NULL accept the null string. 

N_FNSELWN_SET_DEFEXT set the default extension to that of the file specified in the rnsELWN 
struct. 

N_FNSELWN_CAN_WILDCARD allow wildcards. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Setting 


The file name may be set by passing a pointer to a character string to the wn_set method. The character 
string should contain the file name terminated by a zero character. Wildcards in the file name are allowed. 


The default extension may be set by passing a pointer to a character string to the wn_set method. The first 
character must be 0x01. The following characters can either be a file name, or a file extension preceded by 
a full stop character. Only the extension is significant. 


The following code fragment demonstrates the setting of the file name in an rnsELwn control, assuming 
that it is the item with index 3: 


TEXT buf [P_FNAMESIZE]; 


p_scpy (&buf [0], "DATABASE.DBF") ; 
p_send4 (DatDialogPtr,O_WN_SET, 3, &buf[0]); 


Note that, in normal usage, it should not be necessary to set the file name in the di_dyn_init method of a 
dialog. 


Sensing 


A buffer, of size at least p_rNames1zz bytes, is used for sensing the full file specification of the currently 
selected file. 


The following code fragment demonstrates the sensing a file name choice list, again assuming the control 
to be the item with index 3: 


TEXT buf [P_FNAMESIZE]; 


p_send4 (DatDialogPtr,O_WN_SENSE, 3, &buf[0]); 


CHAPTER 9 


ACTIVE OBJECTS 


An active object represents an event source and is, by definition, an instance of any class that has acTIvE 
as an ancestor in its inheritance chain. This chapter provides a simple introduction to the use of active 
objects: for further information on the precise mechanisms involved, see The APPMAN Application 
Manager Class, The Active Class and Active Objects, and following chapters in the OLIB Reference 
manual. 


All HWIM applications contain at least one active object, which is a subclass of the HWIM wserv window 
server active object class. This active object represents the source of events directed to the application by 
the window server process. 


An application may create additional active objects to represent other event sources. A simple example 
would be to implement a timer, so that the application will, from time to time, receive timer expiry events. 


A prioritised queue of an application's active objects is maintained by the application manager. A 
significant part of the application manager's function is in its event loop, which manages this queue, 
associating the occurrence of an event with the appropriate active object and sending it a message. 


An active object must be added to this queue when it is created. This may be done explicitly by the code 
that creates the active object, or it may be included in the initialisation (that is, in the ao_init method) of 
the active object itself. 


Active objects and asynchronous requests 


Before an event can occur it must be requested by a program. The way to do this is to make an 
asynchronous request, as explained in the Asynchronous Requests and Semaphores chapter of the PLIB 
Reference manual. In consequence there is a strong connection between the making of asynchronous 
requests and active objects. 


An active object is, in fact, the standard way of making and processing asynchronous requests in HWIM 
applications. Since the application manager contains a mechanism with the express purpose of scheduling 
the events marking the completion of asynchronous requests that are encapsulated in active objects, you 
are recommended to use active objects for all asynchronous operations in an HWIM application. 


The diagram opposite illustrates the general form of any event loop. 
Testing for the event that has completed is usually done by polling the 
status words of all the possible requests. 


The application manager's event loop follows this general pattern, except 
that the code that requests an event and the code that processes its 
completion are provided by the ao_queue and ao_run methods 
respectively of one or more active objects. On completion of a request, 
the application manager polls the active objects in its queue to determine 
which one can process the event, and explicitly sends it an ao_RuUN 
message. In contrast, the application manager's event loop contains no 
explicit code to request an event and relies on its active objects to do this. 
Since an HWIM application always has at least one active object - its 
window server active object - there will always be something to make 
such a request. 


Note that there is an implied restriction that there should be only a single 
asynchronous request in an ao_queue method, and there should be no 
such request made in the ao_run method. 


OBJECT ORIENTED PROGRAMMING GUIDE 


All ao_run methods must return the value RUN_ACTIVE_uUsED (defined in appman.g) to signify that the 
event has been consumed. 


An active object has a status word (active.status) in its property and it is this status word that the 
application manager polls to determine which active object is to be sent an ao_RuN message. In order to 
assist the polling mechanism, an active object has a further item of property, active.isactive, Which 
must be set to a TRUE value when an asynchronous request is made. It is automatically cleared when the 
active object is sent an ao_RUN message. 


Active object priorities 


When an active object is created, it must be given a priority (by setting its active.priority property) 
before it is added to the application manager's queue. If, at any time, the requests from two or more active 
objects have completed, the one with highest priority will be the first to be sent an ao_RuN message. 


A priority is a signed byte and therefore must lie in the range +127 (highest priority) to -128 (lowest 
priority). A number of standard priorities are defined in appman.g and some of the more significant of 
these are explained below. 


PRIORITY_ACTIVE_IPCS +80 - for inter-process communication 
PRIORITY_ACTIVE_WSERV +60 - the priority of the window server active object 
PRIORITY_ACTIVE_SERIAL +20 - for serial port communications 
PRIORITY_ACTIVE_FILES -20 - for reading or writing files 


PRIORITY_ACTIVE_REPEATER  -40 - for timers, animation, etc. 


PRIORITY_ACTIVE_PRINT -60 - for communication with a printer 


PRIORITY_ACTIVE_COMPUTE -100 - for background computation 


These values are supplied as guidelines and you are not compelled to follow them precisely. However, in 
order to ensure the application's responsiveness to redraws and the keyboard, most active objects should be 
given priorities that do not exceed that of the window server active object. 


Application responsiveness 


An active object can not be given a chance to run (by being sent an ao_RuN message) until the execution of 
a previous ao_RUN message (by this, or any other active object) has terminated. Thus an active object, even 
one of low priority, that performs extensive processing in its ao_run method will reduce the application's 
responsiveness to other events. 


It is the programmer's responsibility to ensure that the processing done in any one call to an active object's 
ao_run method is restricted to a reasonable amount. An active object that, for example, is being used to 
write a file to an SSD should not write the whole file in one operation, but should divide the writing into a 
number of relatively small sections. 


One way of doing this is to construct a buffer containing a section of the file and write the contents of this 
buffer to the file by means of an asynchronous write request in the ao_queue method. On receipt of an 
AO_RUN message, signifying that the write has completed, the process can be repeated, provided there is 
still part of the file that has not been written. 


An alternative approach would be to use a technique similar to the background processing mechanism, 
described below. In this case writing to the file would be performed synchronously, from within the 
ao_run method. A disadvantage to this second technique is that, if writing to a remote device, the write 
could take an extended time to complete and thus may compromise the responsiveness of the application. 


Background processing 


An active object is ideally suited to breaking down a long computation into a sequence of small sections 
and may be used for this purpose, even if the process does not involve making asynchronous requests. 
Examples of where this technique may be useful are the formatting of a large amount of text, or the 
recalculation of all the cells of a spreadsheet. 


The arp. class is supplied in the OLIB library as a basis for this type of use. The supplied class 
subclasses acTIve to replace the ac_init method with code that sets a priority of 
PRIORITY_ACTIVE_comPuTE and adds itself to the application manager's active object queue. It uses the 
default (active class) ao_queue method, which simply sets active.isactive to TRUE and generates an 
event by calling p_iosignal. 


9-2 


9 ACTIVE OBJECTS 


The arpue class must be subclassed to replace the ao_run method with one to perform a unit of processing 
and, if processing is not complete, send itself an ac_quEUE message. 


Errors 


Apart from errors during initialisation (for, example, failure to open a channel to a device) errors that 
result in p_leave being called are expected only to occur in the ao_run method. Thus, all operations that 
could potentially fail, such as the allocation of memory from the heap, should be performed from within 
this method, with a call to p_leave if an error occurs. Where possible, it is more effective to use the £_xxx 
functions, such as £_alloc, f£_new OF f£_send. 


If failure is possible in any asynchronous request that is made in the ac_queue method, the value of 
active.status should be checked for an error value in the ao_run method. Any such error should result 
iN p_leave being called, passing the error value. 


Any call to p_leave from within the ao_run method is handled by system code to perform standard error 
handling, as described in the Error Handling and Error Recovery chapter. Part of this standard 
mechanism is to send the active object an ao_ABRUN message. 


The ao_abrun method supplied by the acrrve class provides fail-safe reporting of the error and so, by 
default, no active object needs to take any explicit action to report errors to the user. The ao_abrun method 
is intended to be replaced by subclassers to provide any application-specific error recovery (again, see the 
Error Handling and Error Recovery chapter). In most cases the replacement method will, in addition to 
any other action, supersend the ao_aBRuNn message to report the error. Depending on the specific 
circumstances, the replacement ao_abrun method may, as its final action, send the active object a pesTRoY 
message. This would normally be appropriate if the active object was created by a command manager 
method, called as a result of the user's selection of a command menu option. 


A simple timer 


The Timer demonstration application is installed into the \sibosdk\oopdemo directory. It may be built 
from that directory by typing: 


make timer 


It is a simple example that uses a timer to print an information message every two seconds, but clearly 
illustrates the way in which an active object is set up and used. 


The category file, timer.cat contains, in addition to classes that will be familiar from the "Hello World" 
example, the class definition of a timer: 


CLASS mytimer active 
{ 
REPLACE ao_init 
REPLACE ao_queue 
REPLACE ao_run 
PROPERTY 
{ 
UWORD count; 
} 
} 


Note that, in order to show clearly the active object mechanisms, the myT1mer class subclasses active. In a 
real case it would be more efficient to subclass the OLIB timer class, which supplies some of the 
functionality that is duplicated in myTIMER. 


During the initialisation of the application's window server active object, an instance of myTImER is created 
and initialised as illustrated below: 


METHOD VOID timerws_ws_dyn_init (PR_TIMERWS *self) 
{ 
self-—>wserv.cli=f_new (CAT_TIMER_TIMER, C_TIMERBW) ; 
p_send2 (self—>wserv.cli,O_WN_INIT)j; 
f_newsend (CAT_TIMER_TIMER, C_MYTIMER, O_AO_INIT, "TIM:",-1); 
} 


OBJECT ORIENTED PROGRAMMING GUIDE 


The ao_init method first supersends the ao_1n1T message to be processed by the acTIvE ao_init 
method, which opens a channel to the r1m: device. It then sets itself to a reasonably low priority and adds 
itself to the application manager's active object queue. Its final action is to send an ao_QuEUE message. 


METHOD VOID mytimer_ao_init (PR_MYTIMER *self,TEXT *devname, INT mode) 
{ 
p_supersendé4 (self,O_AO_INIT, devname, mode) ; 
self—>active.priority=PRIORITY_ACTIVE_REPEATER; 
p_send3 (w_am, O_AM_ADD_TASK, self) ; 
p_send2 (self, O_AO_QUEUE) ; 
} 


The ao_queue method, listed below, simply makes an asynchronous request on the timer, using 
active.stat as its status word. Also, as required, it sets active.isactive to TRUE. An event will be 


signalled after the requested two-second delay. 


METHOD VOID mytimer_ao_queue (PR_MYTIMER *self) 


{ 
LONG delay; 


delay=20; 
p_ioc4 (self-—>active.pcb, P_FREAD, &self->active.stat, &édelay) ; 


self—>active.isactive=TRUE; 


} 


When the event has occurred myTimer will be sent an ao_RuUN message to signify that the requested event 
has completed. The ao_run method increments a counter in its property and uses this value to display an 
information message at the bottom right hand corner of the screen. It then sends an ao_QuEUE message to 
restart the timer before returning RUN_ACTIVE_USED. 


METHOD INT mytimer_ao_run(PR_MYTIMER *self) 
{ 


self—>mytimer.count+=1; 

hInfoPrint (TIMER_INFO, self-—>mytimer.count) ; 
p_send2 (self, O_AO_QUEUE) ; 

return (RUN_ACTIVE_USED) ; 

} 


The timer continues to run for the lifetime of the application. 


In a real application an active object could be created elsewhere in the program - frequently from a 
command manager method, executed in response to the selection of a menu option. The ao_run method 
would include a test for the completion of processing and, when complete would terminate itself by not 
sending an ao_QUEUE message (it might send itself a ppstRoy message instead). 


CHAPTER 10 


ERROR HANDLING AND ERROR RECOVERY 


Psion's Object Oriented programming system and the associated libraries provide considerable support for 
reporting and recovering from error conditions. In consequence, a typical HWIM application contains 
very little explicit error-handling code. 


The cornerstones of error reporting and recovery are: 
e the use of p_enter and p_leave to centralise the handling of errors 
e¢ using PROPERTY n Statements in category files, for the automatic destruction of component objects 
e =the OLIB cizanup class, to provide for the freeing of temporary resources 
e standard error reporting in the ao_abrun method of all active objects 
This chapter gives a brief summary of the range of techniques that are available. 


One point that needs to be emphasised at the outset is that the error handling built into Psion's Object 
libraries does depend on p_ieave being called whenever a run-time error occurs. This means, for 
example, that an application should not permanently disable calls to p_leave from the window server by 
use of the window server function wDisableLeaves. If, for some specific purpose, application code does 
need to disable window server calls to p_leave, it should do so only temporarily. Any application code 
that calls woisableLeaves (TRUE) should ensure that it subsequently calls woisableLeaves (FALSE), before 
execution returns to system code. 


Errors during initialisation 


One simple precaution makes the handling errors during the initialisation of an application very simple: 


The application should be written so that all resources that the application needs in 
order to draw its initial view are created from within the ws_dyn_init method of its 
subclass of wSERV. 


If this condition is met then, assuming there are no coding errors that cause p_panic to be called, the 
application can not fail between the return from the ws_dyn_init method and its appearance on the 
screen. 


All that the application has to do in the event of a failure to create one of its start-up resources from 
anywhere in the ws_dyn_init method, or any other functions or methods that this calls, is to call p_leave 
with a suitable (negative) error number. System code handles the recovery and the reporting that the 
application has failed to start before terminating the application. Note that the application can call 
p_leave (-1) to terminate without a system-generated error report. The value -1 is the error number 
E_GEN_FAIL, and is also defined in hwimman.g as RUN_ACTIVE_CLEANUP_NONOTIFY. 


The most common cause of failure during initialisation is that there is insufficient memory available. In 
many applications this is the only error that could occur during start-up initialisation. An error of this 
nature can normally be precipitated by setting the application's initial heap size requirement to be 
sufficiently large. This is done by setting the heapsize variable in the applications .pr file, as described in 
the An HWIM Application - Hello World chapter. Should insufficient memory be available, the application 
will then fail at a very early stage, before any application-specific code is executed, and this failure will be 
handled by system code. 


10-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


To avoid excessive memory use by an application you should avoid allowing for the worst case. You 
should not, for example, set an initial heap size for a file-based application so that it is guaranteed to be 
able to load an exceptionally large file. 


This technique may not prevent application code from running if, for example, the out-of-memory failure 
occurs when the window server is creating server-side resources for the application, or if a file-based 
application fails while opening a large file. It will, however, cause the application to fail earlier rather 
than later in the majority of cases. 


General error recovery 


One of the most important aspects of the handling of errors is to understand that virtually all application- 
specific code is executed from within the ao_run method of some active object or other. For example, all 
code that is executed in response to the receipt of a window server event is executed from within the 
ao_run method of the application's subclass of the wsErv object. This includes all system-generated 
redrawing and all keypress processing, which itself includes both the receipt of a wn_kzy message by any 
window and, more indirectly, the processing of a command by means of a command manager method. 


There are two main areas of code that are exceptions to this general case: 
e the start-up initialisation of an application 
e code executed in response to an error, such as an active object's ao_abrun method. 


Errors occurring in the first of these two areas are handled as described earlier, while the code executed in 
response to an error should be written so that it can never, itself, generate errors. 


In consequence an error can always be reported simply by calling p_leave. When called within an active 
object's ao_run method, this will be caught by the application manager's event handler and will, cause the 
application manager to be sent an aM_cLEAN_up message. This provides standard resource clean-up and 
also makes a call-back to the active object's own ao_abrun method, to provide standard error reporting 
(which is, itself, designed so that it will not fail). For further details, see the description of appman's 
am_start method, and the further topics that it references, in the APPMAN Application Manager Class 
chapter of the OLIB Reference manual. 


An application that has already reported an error in application-specific code may conclude its error 
response by calling p_leave (RUN_ACTIVE_CLEANUP_NONOTIFY). All the error recovery will be perfomed as 
described above, but there will be no system-generated error report. 


Except when under the explicit protection of a call to p_enter in application-specific code, an application 
should avoid calling p_leave (0). The application manager's event handler can not distinguish this from a 
return value of zero (RUN_ACTIVE_UNUSED) from an ao_run method and will cause the application to fail 
with a 'stray signal’ panic. 


Note that an application that has one or more global actions to execute on the occurrence of all types of 
error can subclass the am_clean_up method to provide these actions as well as the method's standard 
functionality. The Record application, which is described in the Application Design chapter, and whose 
source code is supplied with the SDK, provides an example of the use of this technique. 


The roll-back principle 


All operations that can fail should be written so that failure causes roll-back to a previously safe state. 
Failure to ensure adequate roll-back is one of the most common defects in application software and usually 
evidenced by a monotonically increasing use of memory as some operation is repeated. Such an 
occurrence can generally be detected faily easily, for example, by the use of spy.app (described in the 
Series 3/3a Programming Guide). 


A simple example is the insertion of records into a variable array (see, for example, the va_insertm 
method of the variat class, described in the Variable Arrays chapter of the OLIB Reference manual). If 
an error occurs before the insertion is complete, any partially inserted data is removed before the error is 
propagated by calling p_leave. 


10-2 


10 ERROR HANDLING AND ERROR RECOVERY 


In general, any operation that consists of a sequence of stages, where any stage could fail, must be written 
to release any resources that have been created in earlier successful stages. The general model is 
illustrated in the following code: 


VOID multi_stage() 
{ 


INT error; 


stage_one(); /* this creates a resource, calling p_leave on failure */ 
error=p_enter(stage_two); /* catch any error in the second stage */ 
if (error) 
{ 
undo_stage_one(); /* release the resource created in stage one */ 
p_leave (error) ; /* propagate the error to system code */ 
} 
} 


Note that this code assumes that any non-zero value of error is a negative error number. If there is a 
possibility that a positive value could be returned then, depending on its meaning, the code may have to 
test the sign of the value. If only negative values need to be propagated to system code, this can most 
conveniently be accomplished by calling £_leave (error) since this does not call p_leave if error is zero 
or positive. 


If possible, such error-handling code should be written so that the roll-back is as simple as possible. For 
example, suppose a large amount of data has to be inserted into a buffer, and that the data has to be read 
in segments. Recovery may be complex if each segment is read and inserted separately, since a failure will 
require the deletion of all previously inserted segments. A simpler approach, which requires no explicit 
error recovery code, is to pre-allocate space for the entire insertion in a single operation. If this fails 
(presumably by calling p_1eave) no further action is necessary. If it succeeds, the data can be written into 
the allocated space, segment by segment, with no risk of subsequent failure. If there could be a failure in 
reading the data segments then this approach still simplifies matters since there is always only a fixed size 
of allocated memory to be released, regardless of how many segments have been copied into it. 


The following sections give a number of different means of ensuring that roll-back recovers resources that 
have been created before an error occurs. In a real case it is likely that a mixture of these techniques will 
be used. 


Roll-back for component objects 


The creation of an object that contains a number of components could fail during the creation of the object 
itself or during the creation or initialisation of any of its components. Any failure should result in the 
destruction of the object and any partially created components. This situation is particularly simple since it 
can make use of the built-in mechanisms for component destruction. 


Suppose, for example, that a MYAPP category contains the myciass class, with component classes 
COMPONENT1 and componEnt2. The class definition for mycuass could be as follows: 


CLASS myclass root 

{ 

ADD init 

PROPERTY 2 
{ 
VOID *comp1; 
VOID *comp2; 
} 

} 


where its init method function might be: 


VOID myclass_init (PR_MYCLASS *self) 
{ 
self—>myclass.compl=f_new (CAT_MYAPP_MYAPP,C_COMPONENT1) ; 
self—>myclass.comp2=f_new (CAT_MYAPP_MYAPP, C_COMPONENT2) ; 
} 


If a mycLass instance is created with: 


VOID *hand; 


hand=f£_newsend (CAT_MYAPP_MYAPP, C_MYCLASS, O_INIT) ; 


10-3 


OBJECT ORIENTED PROGRAMMING GUIDE 


then a failure (by means of a call to p_ieave) at any stage will cause the object and any created 
components to be sent a pDesTRoy message, and the call to p_leave is then propagated. 


Note the use of £_new and £_newsend, to guarantee a call to p_leave on failure. 
The principle may be applied to the creation of components of the components, and so on. 


Other resources in an object's property 


Resources that are not component objects, but whose handles are stored in an object's property must be 
explicitly released in the object's destroy method. This is illustrated in the following example, which 


extends the one given above. 


In this case, mycnass has a cell of allocated memory, with its handle stored in its property, according to 
the class definition: 


CLASS myclass root 

{ 

REPLACE destroy 

ADD init 

CONSTANTS 
{ 
ALLOC_SIZE 100 
} 

PROPERTY 2 
{ 
VOID *comp1; 
VOID *comp2; 
BYTE *alloc; 
} 

} 


Its init method function could then be: 


VOID myclass_init (PR_MYCLASS *self) 


{ 
self-—>myclass.comp1l=f_new (CAT_MYAPP_MYAPP,C_COMPONENT1) ; 


self-—>myclass.comp2=f_new (CAT_MYAPP_MYAPP, C_COMPONENT2) ; 
self—->myclass.alloc=f_alloc (ALLOC_SIZE) ; 
} 


Again, note the use of the £_xxx functions, to guarantee a call to p_leave on failure. 
The destroy method function would be: 


VOID myclass_destroy(PR_MYCLASS *self) 
{ 


if (self->myclass.alloc) 


{ 
p_free(self—>myclass.alloc) 
self->myclass.alloc=NULL; /* not strictly necessary in this case */ 


} 
p_supersend2 (self,O_DESTROY) ; 


} 


Again, creating an instance of mycuass with: 
VOID *hand; 


hand=f£_newsend (CAT_MYAPP_MYAPP, C_MYCLASS, O_INIT) ; 


will result in total roll-back (and an error report) in the event of any failure. 


Note that it is good practice to zero the property corresponding to the handle of a resource when that 
resource is released. Although not strictly necessary in the above example, in general it is useful as it 
prevents an attempt being made to release a resource that does not exist. 


10-4 


10 ERROR HANDLING AND ERROR RECOVERY 


Using the CLEANUP list 


An application may create temporary resources whose handles are stored on the stack, rather than in an 
object's property. Alternatively, even if the handles are stored in property, the resources may not be 
created or destroyed at the same time as the 'owning' object, and the roll-back on failure to create the 
resource may not need an object to be destroyed. 


In such cases, roll-back can be performed by use of the cLzanup object that is present in every HWIM 
application. This object is described in The CLEANUP Class, in the OLIB Reference manual. 


Suppose that, the file afile.txt needs to be opened temporarily, together with the temporary creation of two 
allocated memory cells. On failure to create all of these three resources, any of them that have been 
created must be released and the error reported. Suitable code would be as follows: 


VOID CreateResources (VOID) 
{ 
VOID *fcb; 
UBYTE *p1,p2; 
INT cleanl,clean2; 


f_open(&fcb, "AFILE.TXT") ; /* just leave on error */ 
cleanl=cl_add_iochan(fcb) ; /* add file handle to cleanup list */ 
pl=f_alloc(100); /* create first cell */ 

clean2=cl_add_alloc(pl1); /* add first cell to cleanup list */ 
p2=f_alloc(100); /* if this succeeds, all resources are created */ 
cl_remove (cleanl1) ; /* so we can remove both items... */ 

cl_remove (clean2) ; /* ... from the cleanup list */ 


/* processing that can not fail */ 


p_close(fcb); 
p_free(pl); 
p_free(p2); 

} 


Remember that resources on the cleanup list will be removed by system code if p_leave is called in the 
ao_run method of any active object. 


Interactions with system code 


Care should be taken when errors arise in application-specific code when system code also needs to 
perform error recovery. A typical case is during the initialisation of a dialog, since it is system code that 
controls the roll-back from any partially complete creation of dialog objects. 


In general this is not a problem since the application code does not normally need to take any specific 
action on an error condition. The application code can just call p_leave and leave the system code to 
perform all necessary error recovery. 


This might not be the case if, exceptionally, the application-specific code needs to perform some specific 
action on detection of an error, such as reporting the error in a non-standard way, or sending some form of 
notification to another process. In such a situation the application code will normally trap errors by calling 
p_enter and perform local error handling when this call returns an error. 


The preferred solution in such a case is to propagate the error to system code by calling p_1eave (with the 
same error number as was returned from the call to p_enter) after the local error handling is complete. 
This will allow system code to perform its own error recovery, including reporting the error in a standard 
way. If the error has already been reported by application code, the error can be propagated by calling 
p_leave (RUN_ACTIVE_CLEANUP_NONOTIFY). This will enable any required error recovery in system code 
but will disable the standard error reporting. Note that RuN_ACTIVE_CLEANUP_NoNoTIFY is defined in 
hwimman.g to be the value -1 (which is the same as the error number &_GEN_FAIL). 


A difficulty arises in the (fortunately rare) case where it is essential, for some reason, that the application 
code does not call p_1eave. If, in such a case, system-owned resources may need to be released. then the 
application's error recovery code should at least send the application manager a cL_CLEAN_LEVEL message, 
which will normally be sufficient. There is, however, no guarantee that this will always be totally 
successful: such a situation should be avoided if at all possible. 


10-5 


CHAPTER 11 


FILE-BASED APPLICATIONS 


The general aspects of file-based applications for the Series 3 range of machines are described in the 
Communicating with the System Screen chapter of the Series 3 Programming Guide. This chapter assumes 
a basic familiarity with that material and concentrates on those aspects that are of significance to an object 
oriented application. The three main topics that are discussed are: 


e — start-up initialisation 
e opening and creating files 
e saving files 


To maintain consistency with the built-in applications, all file-based applications should obey the general 
guidelines for such applications. They should, for example, store their files in a suitable subdirectory and 
applications that use record-based files should write their records in a flash-friendly manner (see, for 
example, the Database Files chapter of the PLIB Reference manual). It is a general rule that an 
application must keep its current file open, even if it is not actually reading from or writing to the file. 


Start-up initialisation 


As is described in the Series 3 Programming Guide, the command line that is passed to a file-based 
application when it is started contains the name of a file to be opened or created, the default file extension 
and any 'alias' information. System initialisation code analyses the command line and writes the 
information that it contains to a number of standard locations. Thus, by the time the application receives a 
WS_DYN_INIT message, to perform application-specific initialisation, the data is set up as follows: 


e the full path name of the file to be opened or created is pointed to by the magic static 
DatUsedPathNamePtr 


e whether the file is to be created or opened is determined by the uByTE accessed by 
w_am->hwimman.command, which will contain either H_COMMAND_CREATE_FILE or 
H_COMMAND_OPEN_FILE (defined in hwimman.g) 


e the default file name extension for the application's files is pointed to by an item of the 
application manager's property and is accessed by the TExT pointer w_am->hwimman.defext 


e the alias information, if required, is accessed via the TEXT pointer w_am->hwimman.aliasinfo 


A convenient way of opening or creating the required file from within the application-specific 
initialisation code is to send the command manager a coM_FILE_CHANGE message of the form: 


p_send4 (w_ws->wserv.com, O_COM_FILE_CHANGE, w_am->hwimman.command, DatUsedPathNamePtr) ; 


The required behaviour of this method is described in the following section. 


11-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


Opening and creating files 


A file-based application will normally have New and Open command menu options, to create a new file 
and to open an existing file. The corresponding command manager method functions would present 
suitable dialogs to specify a file name and any other relevant parameters. 


On successful completion of the dialog, the opening of an existing file or the creation of a new file could 
conveniently be performed by calling a replacement of the com_file_change method of the application's 
subclass of the comman command manager. A typical replacement would have the form indicated by the 
following code: 


GLDEF_D TEXT filename [P_FNAMESIZE] ; 


METHOD INT mycman_com_file_change(PR_MYCMAN *self, INT command, TEXT *pname) 


{ 
SaveCurrentFile(self); 
p_scpy (&filename[0],pname) ; 
switch (command) 
{ 
case H_COMMAND_CREATE_FILE: 
hEnsurePath (&filename[0]); 
CreateNewFile (self, &filename[0]); 
break; 
case H_COMMAND_OPEN_FILE: 
OpenExistingFile(self, &filename[0]); 
break; 
} 
p_send3 (w_am, O_AM_NEW_FILENAME, &filename[0]); 
return(0); /* there has been no call to p_leave */ 


} 


where SaveCurrentFile, CreateNewFile and openExistingFile represent application-specific code to 
perform the corresponding actions. 


The following points must be noted regarding this code: 


the method is also called by system code, under the protection of p_enter, and must return zero 
on successful completion. There is thus an implicit assumption that any failure within the method 
should result in p_1eave being called. Application code may, if desired, take advantage of this, to 
trap and explicitly handle errors, by sending the message by means of p_entersend. An 
application will normally have to handle a failure to open or create a file by attempting to reopen 
the previously open file. 


it is standard practice to call the utility function hEnsurePath at any point where it is possible 
that the directory specified by a file specification might not exist. Although this is not guaranteed 
to succeed, and does not report an error on failure, it reduces the likelihood that the following 
operation (in this case, the creation of a file) could fail, merely because a directory has not yet 
been created. 


whenever an application switches to a new file it must, on successful completion of the operation, 
send the application manager an aM_NEW_FILENAME message, passing a pointer to a permanent 
buffer containing the full file specification of the new file. System code sets patUsedPathnamePtr 
to point to this name, as required for correct operation of the System Screen. Note that, in the 
example, this buffer is, for clarity, implemented as static data. In a real application it would 
normally be part of the property of some object that remained in existence for the whole time that 
this file is the application's current file. In a simple application this object could be the command 
manager itself, but would normally be an object that represents the current file. 


A common alternative scheme is illustrated by the Record application (whose code is supplied and is 
discussed in the Application Design chapter). In this case, the application's command manager has, for 
example, a com_open_file method that does not use the com_file_change method. Instead, both methods 
call common application-specific code. 


11-2 


11 FILE-BASED APPLICATIONS 


Switchfiles messages 


As discussed in the Series 3 Programming Guide, the System Screen can, at any time, send a Switchfiles 
message to a file-based application. System code within the application converts such a message to a 
COM_FILE_CHANGE message, sent to the application's command manager. The receipt of this message may 
be handled in exactly the same way as described above. 


An application that is temporarily unable to process a Switchfiles message may set the magic static 
DatLocked to a non-zero value, clearing it when it is again able to process such a message. 


If opening a file takes an extended time, it would be sensible for an application to set Dat Locked for the 
duration of this operation. In this case, the application must ensure that patLocked is cleared on 
termination, even if the operation terminates on an error (which will generally result in p_leave being 
called). 


Saving files 


Saving a file is subject to many of the considerations already discussed in the previous section. An 
application will generally support at least Save and Save as menu options, with only the second of these 
requiring a dialog to select a file name. 


If, after saving the file with a specific name, the current file takes the new file name, this must be reported 
by sending the application manager an aM_NEW_FILENAME message, as described above. 


Saving the file may be an extended operation and should similarly be protected against Switchfiles 
messages by setting Dat Locked. 


Application termination 


On termination of a file-based application by means of an Exit menu option, the command manager's 
com_exit method should save any outstanding changes to the current file automatically, without any 
notification to the user. Should an error occur during any such saving, the application should come to the 
foreground (see below). The user should then be notified of the error and offered the option of cancelling 
the Exit. Once the file is successfully saved (or, on failure, the user has elected to terminate the 
application) the com_exit method should either supersend the com_zx1T message or, equivalently, call 
p_exit (0). 


The following code illustrates a possible replacement com_exit method: 


METHOD VOID mycman_com_exit (PR_MYCMAN *self) 
{ 


INT error; 


error=p_enter2 (SaveChanges, self); 
if (error) 
{ 
wClientPosition(0,0); /* come to foreground */ 
hErrorDialog(error,0); 
if (h2LineConfirm(-SYS_LOSING_CHANGES, -SYS_CONFIRM_CONTINUE) ) 
return; 
} 

p_supersend2 (self,O_COM_EXIT); 

} The code assumes that the application-specific function savechanges returns zero if no changes 
have been made or if saving the changed file was successful. Any p_leave caused by an error while saving 
the file is trapped by calling savechanges under the protection of p_enter and is explicitly reported (by 
use of the hErrorDialog utility function). 


Shutdown messages 


The System screen may, at any time, send an application a Shutdown message. System code within the 
application converts such a message to a coM_EXIT message, sent to the application's command manager. 
The receipt of this message may be handled in the same way as described above. 


An application may receive a Shutdown message while it is a background process. To ensure that the user 
can see and respond to any error notification, the process must therefore come to foreground, as 
mentioned above. 


Note that setting DatLocked disables Shutdown messages as well as Switchfiles messages. 


11-3 


CHAPTER 12 


EDIT WINDOWS 


This chapter explains how to use the HWIM epwrtn class to create edit windows that (to name but a few 
features) 


e handle all standard cursor movement, selection, typing, and deletion keys 

e can be either single-line or multi-line 

e¢ automatically scroll vertically and/or horizontally, whenever required 

e¢ automatically word-wrap, whenever required 

e provide common editing functionality such as Copy, Insert, Bring, Evaluate, Find, and Replace. 


All the features of Hwif edit boxes, available through the Hwif nesxxx functions, are also available to 
HWIM programmers using EDWIN directly. (In fact, as can be confirmed by consulting the module ehwif-c 
in the optional \sibosdk\hwifsrc directory, the hEBxxx functions are just thin layers over calls to various 
methods of zpw1n.) However, programming directly at the Epw1n level opens up many additional 
possibilities. Some of these additional possibilities are: 


e more efficient handling of larger amounts of text 

e edit windows (and edit-like windows) which support “labels” in the left-margin 
e edit windows with tabs and variable tabstops 

e edit windows with multiple fonts and font styles. 


In fact, the main editing window in the Word Processor application built into the Series 3 is a subclass of 
EDWIN - as are the main windows of the Database and Program Editor applications. 


In order to achieve effects like this, programmers need to become acquainted with some of the component 
objects utilised by Epw1n - for example the EPpoc document object, the scrimc screen image object, and 
the scrLay screen layout object. Later sections of this chapter provide an introduction to these additional 
objects (which are all instances of classes in FORM). However, many of the aspects of Epwin can be 
accessed without any knowledge of the internal structure of the class. These aspects are described in the 
earlier sections of this chapter. 


Introduction to EDWIN 


Dialogs and edit windows contrasted 


The first use a programmer normally makes of EpwIn is by having an edit box in a dialog. (See the 
chapter Dialog Controls for information on how to program edit boxes in dialogs.) 


In this case, the initialisation of the edit window is taken care of by system code. System code likewise 
ensures that 


e keypresses are passed to the edit box at the right time 


e the edit box is always displayed in the correct “emphasis” state (ie with its cursor flashing or not, 
as the case may be, and with any select region highlighted when appropriate). 


12-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


The responsibility of the programmer in this case is merely to 
e choose which initialisation flags to define 
e set text into the edit box when needed 
e sense the contents of the edit box, after the user has edited them. 


In contrast, when an application has an edit window outside a dialog box, the programmer has to accept 
the following additional responsibilities 


¢ creating the edit window to start with - by filling in fields in the 1n_Epwrn data structure (and, 
optionally, in the 1n_Epw1n_x auxiliary data structure) 


e deciding when the edit window should receive keys - and passing these keys onto the edit window 
e deciding when the edit window should be emphasised. 


The programmer also has to draw any border required for the edit window; the Epwtn class has, itself, no 
notion of a border. 


The NOTES example program 


The examples in the first half of this chapter are mainly based on the example application Notes.app. The 
source code of this application is placed in the directory \sibosdk\notes if the optional OOPDEMO 
component of the SIBO C SDK is installed. This application may be recognised as an HWIM version of 
one of the Hwif example programs. It creates three different edit windows, as can be seen in the following 
screen shot: 


This is the title (single line editor) 


This is the main body of the note. 
Itis a multi-line editor, supporting clipboard functionality, and 
>the lines of text wrap automatically when required| 


Find: <Find string here> 274 


In this screen shot, the emphasis is currently with the middle editor - as can be seen from the flashing 
cursor at the end of the third line, and also by the “margin cursor” in the left margin. These visual 
indications as to which editor has the emphasis will of course disappear when the emphasis (sometimes 
also called the “keyboard focus”) is moved elsewhere. 


The three editors are each drawn within their own border - which is provided by creating the editor within 
an instance of the Bw1n bordered window class. As suggested above, the EpwIn class itself does not make 
any call to any variant of gBorder: its concern is purely with the text inside the border. 


The screen shot is of this application running on a Series 3a. The application also runs on a Series 3, and 
in this case, there are fewer lines in the middle editor. In fact, as well as illustrating use of the Epw1n 
class, the application provides an example of how to write a fully-resizeable application, that can run on 
either the Series 3 or the Series 3a (exactly the same image program runs on the two different models, and 
would also run intelligently on machines with intermediate screen sizes). However, this feature of this 
application is incidental to the main theme of this chapter and will not be mentioned again. 


The “Hello World” program for edit windows 


Because Notes is a fairly well developed example program, the basic architecture of programming an edit 
window may to some extent be hidden, in its source code, by the lots of other concerns that have to be 
taken care of by that program. For this reason, installing the optional OOPDEMO component of the SDK 
also places the source code for a much simpler example, ehello.img, into the \sibosdk\ehello directory. 


This program simply draws a one-line edit window in the middle of the screen, and diverts most incoming 
keypresses to that window. The only exception is the ENTER keypress, which throws up a simple dialog 
confirming the text currently in the editor. The following two screen shots demonstrate, respectively, the 
main state of the application, and the confirmation dialog: 


12-2 


12 EDIT WINDOWS 


Hello hiorld 


Hello 
World 


Ehello 


Edit window 


Current text Hello world 


[Hello Wt 


The EHELLO category file 


The category file for the ehello application, ehello.cat, defines only three classes: 
IMAGE ehello 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE hwimman.g 
INCLUDE edwin.g 


CLASS ehwserv wserv 
{ 
REPLACE ws_dyn_init 
} 


CLASS ehbwin bwin 
{ 
REPLACE wn_init 
REPLACE wn_emphasise 
REPLACE wn_key 
REPLACE wn_draw 
PROPERTY 

{ 
PR_EDWIN *edwin; 
} 


} 


CLASS ehdlg dlgbox 
{ 
REPLACE dl_dyn_init 
} 


The bulk of what little code there is in the application resides in the EHBwIn class. As can seen, EHBWIN is 
a custom-subclass of swin, and owns a component EpwIn object. In technical terms - elaborated below - 
EHBWIN Is the “landlord” for the edit window in this application. 


12-3 


OBJECT ORIENTED PROGRAMMING GUIDE 


Initialisation code in EHELLO 


The code in main has the standard form: 


GLDEF_C VOID main (VOID) 
{ 
IN_HWIMMAN app; 
IN_WSERV wserv; 


p_linklib(0); 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE|FLG_APPMAN_CLEAN; 
app.wserv_cat=p_getlibh (CAT_EHELLO_EHELLO) ; 
app.wserv_class=C_EHWSERV; 

wserv.com_cat=p_getlibh (CAT_EHELLO_HWIM) ; 
wserv.com_class=C_COMMAN; 

p_send4 (p_new (CAT_EHELLO_HWIM, C_HWIMMAN) ,O_AM_INIT, &app, &wserv) ; 

} 


After main, application code is next called in the ws_dyn_init method of the Exwserv class: 


METHOD VOID ehwserv_ws_dyn_init (PR_EHWSERV *self) 
{ 
wsEnable(); 
self->wserv.cli=f_newsend (CAT_EHELLO_EHELLO, C_EHBWIN, O_WN_INIT) ; 
} 


Evidently, this creates and initialises the client window for the application - an instance of rHBwIN. In 
turn, the wn_init method of expwrtn is as follows: 


METHOD VOID ehbwin_wn_init (PR_EHBWIN *self) 
{ 
W_WINDATA wd; 
IN_EDWIN_X initx; 
struct 
{ 
IN_EDWIN e; 
TEXT rest [21]; 
} init; 


wd.extent .width=240-50; /* extent calculation presupposes SERIES 3 screen */ 


wd.extent.height=3+10+5; /* matches flags set for bwin below */ 
wd.extent.tl.x=0; 
wd.extent.tl.y=31; /* centred vertically */ 


p_send5 (self, O_WN_CONNECT, NULL, W_WIN_EXTENT, &wd) ; 

hLoadResBuf (EHSTR_INIT, &init.e.contents[0]); 

init.e.vulen=240-50-3-1-5-1; /* one extra pixel clearance each end */ 

init.e.maxlen=50; 

init.e.flags=IN_EDWIN_VULEN_PIXELS | IN_EDWIN_POSITION_SUPPLIED; 

initx.pos.x=3+1; 

initx.pos.y=3; 

self—>ehbwin. edwin=f_newsend (CAT_EHELLO_HWIM, C_EDWIN, O_WN_INIT, 
&init,self, &initx) ; 


self—>win.flags=IN_BWIN_SHADOW 2|IN BWIN_CUSHION; 
p_send3 (self, O_WN_EMPHASISE, TRUE) ; 

hiInitVis (self); 

} 


Without going into details at the moment, the basic form of this method can still be pointed out: 
¢ connect the window to the Window Server (by sending se1f a wn_connect message) 
¢ create and initialise the Epwrn component object 


e make this window tree visible. 


12-4 


12 EDIT WINDOWS 


Other code in EHELLO 


The other three methods of zEHBw1n mainly just delegate responsibility appropriately, between the Epw1N 
component and the pwrn superclass (see later for a fuller explanation of what is going on here): 


METHOD VOID ehbwin_wn_emphasise(PR_EHBWIN *self,INT flag) 


{ 
p_supersend3 (self,O_WN_EMPHASISE, flag) ; 
p_send3 (self—>ehbwin.edwin, O_WN_EMPHASISE, flag) ; 


} 


METHOD VOID ehbwin_wn_draw(PR_EHBWIN *self) 


{ 
p_supersend2 (self,O_WN_DRAW) ; 
p_send2 (self—>ehbwin. edwin, O_WN_DRAW) ; 


} 


METHOD VOID ehbwin_wn_key(PR_EHBWIN *self, INT keycode, INT mods) 


{ 
SE_EDWIN sense; 


if (keycode!=W_KEY_RETURN) 
p_send4 (self—>ehbwin. edwin, O_WN_KEY, keycode, mods) ; 
else 


{ 
p_send3 (self—>ehbwin.edwin, O_WN_SENSE, &sense) ; 


LaunchDialog(C_EHDLG, EHDLG, sense.buf) ; 
} 
} 


The utility routine LaunchDialog has the standard form 


LOCAL_C VOID LaunchDialog(INT class, INT resid,VOID *rbuf) 


{ 
DL_DATA dld; 


did.id=resid; 

dld.rbuf=rbuf; 

dld.pdlg=NULL; 

hLaunchDial (CAT_EHELLO_EHELLO, class, &dld) ; 
} 


and, in turn, the dl1_dyn_init method of the zxpuc dialog box class merely sets the text of the edit window 
into a text window in the dialog: 


METHOD VOID ehdlg_dl_dyn_init (PR_DLGBOX *self) 


{ 
hDlgSetText (1, self-—>dlgbox.rbuf) ; 


} 


Simple use of EDWIN 


This section explains: how to initialise an edit window, how to pass keys to it, how to set text into it and 
sense text out of it, how to pass wn_draw and wn_emphasise messages onto it, and the basic format of the 


text stored in it. 


Initialising an instance of EDWIN 


Often, the hardest aspect of incorporating an edit window in an application is initialising it correctly. 
Once the edit window has been set up properly, it handles most features automatically. 


In order for an edit window to be drawn on the screen, the following steps are required: 


1. 
2 


an instance of the zpwin class (or a subclass thereof) has to be created 
a wn_init message must be sent to the object, with suitable parameters (see below) 


the window tree of which the editor is part must be made visible, usually via a call to the utility 
function hInitVis. 


12-5 


OBJECT ORIENTED PROGRAMMING GUIDE 


These steps may take place in code such as the following: 


IN_EDWIN init; 
IN_EDWIN_X initx; 


edwin=f_newsend (CAT_NOTES_HWIM, C_EDWIN, O_WN_INIT, &init, landlord, &initx) ; 


hiInitVis (landlord); 
The landlord of the edit window 


In the above code fragment, the variable 1andiord is the handle of an instance of (a subclass of) the 
HWIM wrn class which has connected to the Window Server. In the language of the Windows chapter in 
this manual (to which the reader is referred for background information on such concepts as “lodger 
windows’), the landlord object contains a notional wswin component. In other words, a wn_connect 
message has been sent to the 1andlora object, and the field 1andlord->win.id has been filled in as a 
result. 


Note that the value of 1andlord->win.ia must be filled in before the wn_init message is sent to the 
EDWIN object. 


Frequently, the 1andlord object is an instance of a (subclass of) the HWIM bwin class. 


Incidentally, whereas in the Notes example application, each of the three editors has its own unique 
landlord window, there is no general requirement for a given landlord window to contain only one editor. 
For example, if there are three editors in one dialog, that dialog window (an instance of digbox) is the 
common landlord of all three editors. 


As with all lodger windows, an zpw1n object requires to know 
e the top left offset within the landlord, to the rectangle occupied by the lodger 
e the width of the rectangle occupied 


e the height of this rectangle: 


\ _ top left offset 


LANDLORD 


In the case of EDWIN: 


e the height is worked out, inside the wn_init method, from a knowledge of the number of lines 
that are to be visible at one time (this defaults to one), the font in which the text in the editor is to 
be displayed, and the vertical leading to be applied with this font 


e the width is also worked out, by one of a variety of different means, depending on which flags are 
set on initialisation 


e the top-left offset always has to be supplied explicitly, in pixels - although as is explained below, 
it is possible to defer providing this value until later in the overal initialisation process. 


The IN_EDWIN and IN_EDWIN_X data structs 


The 1n_EDWIN and IN_EDWIN_x data structs are defined as follows in edwin.cl: 


typedef struct 
{ 


UWORD vulen; viewing length or width 

UWORD flags; autoselect etc 

UWORD maxlen; maximum number of characters allowed 
TEXT contents[1]; rest of initial contents follows in line 
} IN_EDWIN; 


12-6 


12 EDIT WINDOWS 


typedef struct 
{ 
WORD total; 
WORD top; 
} EDWIN_LEADING; 


typedef struct 
{ 


UWORD vislines; number of lines visible in window 
P_POINT pos; top left offset relative to landlord 
WORD font; font id 

UWORD style; font style 

EDWIN_LEADING leading; total and top-only vertical leadings 
VOID *doc; document object to use 

VOID *clip; possible clipboard to use 

} IN_EDWIN_X; never used in edwins in dialogs 


Whilst the address of an IN_EDWIN struct must always be passed to the wn_init method of Epwtn, it is 
optional whether to pass the address of an IN_EDwIn_x struct. This is because, depending on various bit 
values that can be set in the flags field in the 1n_Epwin struct, default values are assumed for the fields 
that can be supplied in the 1n_Epw1n_x struct. More precisely: 


e unless IN_EDWIN_VISLINES_SUPPLIED is set in flags, the value of vislines is taken as 1 


e the value of pos is ignored unless IN_EDWIN_POSITION_SUPPLIED Is Set (if this bit is clear, the 
value of pos must be supplied by a subsequent call to 1g_set_id_pos - see below) 


e unless IN_EDWIN_FONT_SUPPLIED is Set, the value of ws_rFonT_systTEM is assumed for font, and 
the value Gc_sTy_NoRMAL is assumed for style 


e unless IN_EDWIN_LEADING_SUPPLIED is Set, values of 2 and 1 are assumed for leading.total and 


leading.top 


e unless IN_EDWIN_DOC_SUPPLIED Is set, any supplied value of doc is ignored, and the edit window 
automatically creates a suitable document object (see later in this chapter for further discussion of 
document objects) 


e the value of clip is ignored unless IN_EDWIN_CLIPBOARD is Set (specifying that the editor is to 
support clipboard functionality). 


As can be appreciated, edit boxes in dialogs are never passed an address of an IN_EDwIn_x struct; all the 
corresponding flags are clear. This reflects the fact that the Epw1n resource struct, defined in hwim.rh, 
corresponds just to the 1N_EDwtIn struct, and does not have any fields matching the 1n_EDWIN_x struct. 


The Ig_set_id_pos method 


In case it is impossible (or particularly inconvenient) to give the value of pos, the top-left offset of the 
editor within its landlord, at the time when the wn_init method has to be called (this is the case when 
editors are created within dialogs, as the dimensions of a dialog cannot be determined until all the items 
in the dialog have been initialised), this can be given later, by sending the editor an 1g_set_id_pos 
message as follows: 


P_POINT pos; 


p_send5 (edwin, O_LG_SET_ID_POS, landlord->win.id, &pos, width) ; 
where width is the width of the region the editor is to display itself upon. 
Other edit window initialisation flags 


In addition to the six IN_EDwIN_xxx flags mentioned above, there are four other groups of possible values 
that the flags field in the 1n_EpwIn struct can contain: 


e values governing the interpretation of the vulen field, if set 


e values influencing the behaviour of the wn_key method of the editor (ie influencing the way the 
editor responds to various keypresses passed to it) 


e values governing the initial cursor position and initial highlighting (more precisely, these control 
the cursor position and the highlighting following initialisation and following any call to wn_set 
to set text into the editor) 


e miscellaneous other values governing attributes the edit window may or may not possess. 


12-7 


OBJECT ORIENTED PROGRAMMING GUIDE 


In detail, these ten additional values are as follows: 


IN_EDWIN_VULEN_CHARACTERS 


IN_EDWIN_VULEN_PIXELS 


IN_EDWIN_ACCEPT_TABS 


IN_EDWIN_ACCEPT_SOFT_HYPHENS 


IN_EDWIN_DIALLABLE 


IN_EDWIN_NO_AUTOSELECT 


IN_EDWIN_AUTO_CUR_END 


IN_EDWIN_LEFT_CURSOR 


IN_EDWIN_TEXT_SEGMENTED 


IN_EDWIN_PAGINATABLE 


if this is set in flags, the value of vulen is multiplied by 
the max_width value for the font to be used by the editor, 
and the result is used for the width of the editor 


if this is set in flags, the value of vulen is taken directly 
as the width to be used by the editor (if neither this flag or 
the previous one is set, then the width is set to the product 
of the max_width value of the font and the maxlen value 
from the In_EDwrIN struct) 


unless this is set, the wn_key method of the editor will 
reject the TAB key 


unless this is set, the wn_key method will reject the 
CONTROL-hyphen key (which would otherwise insert a so- 
called “soft” hyphen - which would be invisible in most 
cases) 


unless this is set, the wn_key method will reject the SHIFT- 
DIAL and the CONTROL-SHIFT-DIAL keys (if this is set, the 
wn_key method will, respectively, insert an 0x05 telephone 
symbol, or run the system ‘Append country markup’ 
dialog) 


if this is set, the initial cursor position is set to the start of 
the text, and there is no initial select region 


this has the same effect as the previous flag, except that the 
initial cursor position is set to the end of the text (if neither 
this flag or the preceding one is set, the cursor is placed at 
the end of the text and the entirety of the text is selected) 


if this is set, a triangular pointing cursor is displayed down 
a left-hand margin (as in the middle of the three editors in 
the Notes example application), using the same font as the 
main application but with all bits of the font style cleared 
apart from the c_sty_pousBLe bit (if set) 


if set, this means that the editor will create an EPSEG 
document object whenever needed (as opposed to the 
EPFLAT Object that is created by default - see later in this 
chapter for more discussion of document objects) 


(for advanced use only - see later). 


A note on the CONTENTS field in the IN EDWIN struct 


Evidently, the 1n_zpwtn struct only defines one element in a possible contents [] array. In order to 
specify initial contents different from just the null string ("" - specified when init.contents [0] 1S Zero), 
the application needs to make a declaration such as 


struct 
{ 
IN_EDWIN e; 
TEXT rest [LENGTH-1+1]; 
} dna ts 


init.e.maxlen=...; 


p_scpy (&init.e.contents[0],pInitString) ; 


edwin=f_newsend(C_APP_HWIM, C_EDWIN, O_WN_INIT, &init,...); 


Note that the array contents[] is ignored altogether if the flag IN_EDWIN_DOC_SUPPLIED Is Set in flags. 
Apart from this, sinit..contents[0] is always interpreted as a pointer to a zero terminated string. 


12-8 


12 EDIT WINDOWS 


Alternative means of defining initial text for an editor (these methods can circumvent the limitation to 
setting text that is only one paragraph long) include: 


e using the wn_set or other methods of the Epw1n class, possibly repeatedly, before the editor is 
made visible 


e setting the text directly into the document object. 


Values of special characters in the text 


The following values are used to represent special characters within an editor: 


7 (SCRLAY_SYM_HARD_HYPHEN) a minus sign (sometimes called a “nonbreaking hyphen’) 
that does not count as a word delimiter - normally entered 
at the keyboard using SHIFT-CONTROL-hyphen 


8 (SCRLAY_SYM_SOFT_HYPHEN) a soft (sometimes called an “optional”) hyphen - normally 
invisible, but will transform into a visible hyphen to allow 
a word to wrap over two lines at the point, if required - 
normally entered at the keyboard using CONTROL-hyphen 


15 (SCRLAY_SYM_HARD_SPACE) a space that does not count as a word delimiter - normally 
entered at the keyboard using SHIFT-CONTROL-SPACE 

0 ("\0') a paragraph end - normally entered at the keyboard using 
ENTER 

10 ("\n') a forced line break - normally entered at the keyboard 
using SHIFT-ENTER 

9 ("\t') a tab character - normally entered at the keyboard using 
the TAB key 

5 (WS_SYMBOL_PHONE) a telephone character - normally entered at the keyboard 


using SHIFT-DIAL. 


Apart from the above values, characters with values less than 32 should in general not be set into edit 
windows. Note in particular that characters with values less than 4 are all treated, by low-lying code (in 
the OLIB library) as paragraph delimiters - with potentially bizarre results, given that the formatting code 
in FORM only recognises the character value 0 as a paragraph delimiter. 


Note that no characters are written to the buffer to denote the location of line ends caused merely by word- 
wrap. These locations (sometimes called “soft carriage returns”) have no fundamental significance: 


e they change whenever the window size changes (eg when the status window is altered) or the 
display font is “zoomed” 


e they are calculated dynamically when needed, and are held in a different part of property of the 
edit window (actually in the scriay screen layout component). 


A note on the MAXLEN field in the IN_EDWIN struct 


For clarity, it should be emphasised that the maxien field in the 1n_zpwtn data struct specifies the 
maximum number of characters the editor can contain, not counting any final terminating character. 


For example, an editor with maxien set to 6 could contain the string "abcdef" (where the editor would in 
fact also store a terminating zero at the end of the string. 


However, if the editor was multi-line, it could not store the text "ab\ocde£" (representing a paragraph of 
two characters followed by one of four characters): that would require a maxlen of (at least) 7. 


The wn_sense method 


For many uses of edit windows, the following model is sufficient to explain the storage of text within the 
editor: the text is stored as a zero terminated string, and the wn_sense method of Epw1n provides access to 
this buffer, as follows: 


typedef struct 
{ 
TEXT *buf; 
UWORD len; 
} SE_EDWIN; 


12-9 


OBJECT ORIENTED PROGRAMMING GUIDE 


SE_EDWIN sense; 


p_send3 (edwin, O_WN_SENSE, &sense) ; 
p_scpy (&store[0],sense.buf) ; 


Note however that the buffer whose address (sense .buf) 1s obtained in this way must always be regarded 
as read only. In order to change the contents of this buffer, the various methods of Epw1n (or methods of 
components of EpwrNn) have to be used. 


Second, note that the buffer used by the editor may move as more text is added. This is because the buffer 
is resized according to how much text it contains. Therefore, it would be a grave mistake to hold onto the 
address of this buffer, and assume that this will still be valid after more text could have been added into 
the editor. 


Third, as mentioned above, paragraph ends (in multi-line editors) are internally represented as zeros. 
However, code such as 


p_send3 (edwin, O_WN_SENSE, &sense) ; 
p_scpy (&store[0],sense.buf) ; 


will only succeed in copying out text as far as the first embedded zero. For this reason, the Notes example 
application essentially uses the following code instead: 


SE_EDWIN sense; 


p_send3 (edwin, O_WN_SENSE, &sense) ; 
p_bcpy (&store[0],sense.buf,sense.len) ; 


Finally, note that this still assumes that the text is stored in a flat buffer. This applies by default, but the 
initialisation flag IN_EDWIN_TEXT_SEGMENTED can be used to specify that the text is stored in a segmented 
buffer. (Roughly speaking, the larger the quantity of the text, the more pressing the need to store it in a 
segmented buffer - to cut down on the amount of data that needs to be shuffled along each time a single 
character is typed into the middle of the document.) If the text is stored segmented, the result of the 
wn_sense method is undefined, and other means are required to sense the contents of the editor. 


The wn_set method 
The wn_set method can be used to completely replace the contents of an EDwin object. 
It uses the same sE_EDWIN struct as does the wn_sense method: 


SE_EDWIN set; 


p_send3 (edwin, O_WN_SET, &set) ; 


After this method, the contents of the editor are the set .1en characters in the buffer pointed to by 

set .buf. (The operation of the method involves copying these characters into the internal storage buffers 
of the edit window.) All previous contents are discarded. The highlight and the cursor position are 
adjusted according to the IN_EDWIN_AUTO_CUR_END and IN_EDWIN_NO_AUTOSELECT flags specified on 
initialisation. 


The wn_key method 

The way to pass keypresses onto an edit window is to send a wn_key message as follows 
p_send4 (edwin, O_WN_KEY, keycode, modifiers) ; 

The method actually returns one of the following two values: 
@ WN_KEY_CHANGED - the contents of the edit window changed as a result of the keypress 


@ WN_KEY_NO_CHANGE (which is the same as FaLsE) - the contents of the edit window did not change 
as a result of the keypress. 


In most cases, however, this value will be ignored by application code that passes the key to the window. 


Something else that is automatically suitable in most uses of EpwIN is the behaviour of the wn_key method 
when the keypress passed to the editor 


¢ causes an out-of-memory error, or 
e would cause the maximum capacity of the editor to be exceeded. 


See the discussion of the ew_leave method below for more information on how EpwrIn copes with these 
two cases. 


12 - 10 


12 EDIT WINDOWS 


The wn_emphasise method 


Although the need for the wn_key method is clear, the need for the wn_emphasise method may be less so. 
However, as mentioned several times in this manual, there is a definite need for applications to track the 
“emphasis” as it moves around an application: 


e into the menu bar and out again 
e into the Help subsystem and out again, or into dialogs and out again 
e around various parts of the main viewing screen of the application. 


The parameters to the wn_emphasise method of Epw1n are the same as those for any other window class 
within HWIM: 


p_send3 (edwin, O_WN_EMPHASISE, flag) ; 


where flag is either rausz, to indicate that emphasis is moving away from the edit window, or TRuz, to 
indicate that emphasis is moving to the edit window. 


The wn_draw method 


The wn_draw method shares with wn_key and wn_emphasise the feature that landlord windows for edit 
windows invariably have to pass these messages onto their EpwIN components. 


As far as EDWIN is concerned, there are no additional parameters to the wn_draw method (in particular, the 
entire visible portion of the editor has to be redrawn every time). 


Additional EDWIN methods 


This section explains some additional methods of Epw1n: 
e methods for inserting text, finding text, and replacing text 
e the copy and insert (“paste”) clipboard methods 
e =the ew_evaluate method 
e additional setting and sensing methods 
e how to detect if the contents of an edit box has been changed 
e edit boxes set to be “read only” 
e the ew_leave method for notifying run-time errors. 


The ew_insert method 


The following code will result in the bien characters at *buf being inserted into the editor with handle 


edwin: 
p_send4 (edwin, O_EW_INSERT, buf,blen) ; 


The characters are inserted at the cursor position (any selection being cancelled first), and the cursor is 
advanced to the end of the characters inserted. 


The ew_insert method calls ew_leave if any error occurs. 


The ew_find method 


It is possible to request an editor to search for given text within itself. The following code is used 
val=p_send4 (edwin, O_EW_FIND, pstr, flags) ; 
where pstr points to a zero-terminated string of the text to match, and possible bit values in f1ags are: 


@ EWF_BACKWaARDs to search backwards from the cursor position (the default is to search forwards 
from the cursor position) 


@ EWF_CASESENS to make the search case sensitive (the default is for the search to be case 
insensitive). 


12-11 


OBJECT ORIENTED PROGRAMMING GUIDE 


The ew_find method returns rause if no match was found, and otherwise TRUE - in which case the 
matched text is highlighted as the new select region. The ew_find method is intelligent enough to code 
with repeated calls to ew_find without finding the same text repeatedly. 


Note that matches across paragraph boundaries are not possible (this is consistent with the search string 
being zero terminated: it cannot contain an embedded zero). 


The ew_replace method 


The ew_replace method is in some ways similar to the ew_insert method, but it is designed primarily to 
implement a ‘Replace’ menu command (in conjunction with the ew_find method, which is designed to 
implement a ‘Find’ menu command). Whereas ew_insert cancels any selection before inserting the 
specified characters, ew_replace Starts by deleting any selection. Another difference is that ew_insert 
takes its insertion text in the (buf, 1en) form, whereas ew_replace expects a zero terminated string. 
Finally, whereas ew_insert always leaves cursor at the bottom end of the selection region consisting of 
the text just inserted, ew_replace allows the cursor to be positioned at either end of this selection - in 
order to support repeated forward or backward text replacement, without entering an infinite recursion: 


p_send4 (edwin, O_EW_REPLACE, replace, backwards) ; 


The replacement string is specified by the zero-terminated string *replace, and the flag backwards 
specifies whether the cursor should be placed at the top end of the selection (if backwards 1S TRUE) or at 
the bottom end. 


The method attempts to insert the replacement text first, before deleting the existing selection (if any), so 
that any out-of-memory error is handled automatically without the loss of any text. 


The ew_replace method calls ew_leave if any error occurs. 


The ew_replace_clip method 


The ew_replace_clip method is provided to implement a ‘Copy text’ menu command, in conjunction 
with the ew_paste_clip method (discussed next), which is provided to implement an ‘Insert text’ menu 
command (sometimes called a ‘Paste’ menu command). 


Both these methods presuppose that the flag 1N_EDWIN_CLIPBOARD was set on initialisation - otherwise the 
editor will panic (panic 55). Note however that the 1n_zpwIn_cLIpBoarp flag should not be set 
unnecessarily (ie if no calls to ew_replace_clip Of ew_paste_clip are to be made), since this entails an 
additional memory overhead. 


If IN_EDWIN_CLIPBOARD 1s Set on the initialisation of the editor, the value of the clip field in the 
IN_EDWIN_X initialisation struct becomes significant: 


e if this is non-nuLL, it is taken as the handle of a suitable clipboard object to be used by the editor 


e otherwise, the editor creates a clipboard object for its own use, which is in fact an instance of the 
OLIB eprtat class (unless the flag IN_EDWIN_TEXT_SEGMENTED was set on initialisation, in which 
case an instance of the OLIB epsze class is used). 


In most cases, setting clip to nuLL will be perfectly sufficient. The main exception is if the clipboard 
object has to persist beyond the lifetime of the editor (or if the clipboard is to be shared between two 
editors that both exist at the same time). Note here that the destroy method of zpwin sends a destroy 
message in turn to any clipboard object that the editor itself created - whereas clipboard objects specified 
via a non-NnuLL value of the clip field of the 1n_Epw1n_x struct passed at initialisation do not get destroyed 
in this way (that responsibility falls to the owner of the editor). 


An application that wishes to create a clipboard for external purposes can use code such as 
clip=f_newsend (CAT_APP_OLIB, C_LEPFLAT,O_EP_INIT,maxlentl) ; 


where the reason why 1 is added to maxien (the value used to initialise the editor itself) is that space has to 
be reserved, in the document object, for the final paragraph delimiter as well. In general, any realisable 
subclass of the OLIB class epRoot can be used as the clipboard. 


Note that there is no Epwin method corresponding directly to any ‘Cut’ menu command. There is of 
course no such menu command on the ROM-resident Series 3 applications; the way that text is “cut” into 
the clipboard is that the user highlights the text and simply presses DELETE. This keypress is received by 
the Epwin code in the wn_key method, and this is the location of code to copy the deleted text into the 
clipboard (if present). 


12-12 


12 EDIT WINDOWS 


Having said all that, in practice use of the ew_replace_clip method is simplicity itself. For example, the 
corresponding code in the Notes example application is just 


METHOD VOID nocomman_ncoe_copy (PR_NOCOMMAN *self) 


{ 

CheckEditing(self) ; 

if (! (p_send2 (DatApp3, O_EW_REPLACE_CLIP) )) 
hInfoPrint (NOSTR_NO_TEXT_COPIED) ; 

else 
hinfoPrint (NOSTR_TEXT_COPIED) ; 


} 
In this example, patapp3 holds the handle of the editor. 


The ew_replace_clip method in fact returns the length of the current selection - which is therefore zero if 
there is nothing to copy. 


The ew_paste_clip method 
Use of the ew_paste_clip method is just as simple. The corresponding code in the Notes application is 


METHOD VOID nocomman_ncoe_insert (PR_NOCOMMAN *self) 


{ 

CheckEditing(self) ; 

if (! (p_send2 (DatApp3,O_EW_PASTE_CLIP) ) ) 
hinfoPrint (NOSTR_NO_TEXT_INSERTED) ; 

} 


The ew_paste_clip method returns the length of the text in the clipboard - which is zero if there is 
nothing to insert. 


The ew_evaluate method 


The ew_evaluate method can usefully be discussed alongside ew_replace_clip and ew_paste_clip 
because 


e an ‘Evaluate’ menu command would normally be found alongside those for “Copy text’ and 
“Insert text’ 


e the usage of this method is, in practice, equally as straightforward (although, in all three cases, a 
great deal happens behind the scenes). 


The ew_evaluate method can, however, be used without the editor having been initialised with 
IN_EDWIN_CLIPBOARD. 


The code in the Notes application that implements the ‘Evaluate’ menu command there is 


METHOD VOID nocomman_ncoe_evaluate (PR_NOCOMMAN *self) 


{ 
CheckEditing(self) ; 
p_send2 (DatApp3, O_EW_EVALUATE) ; 


} 


Note that whereas it is the responsibility of the application to detect “errors” (such as Nothing to insert) in 
the case of ew_replace_clip and ew_paste_clip, syntactical errors within the string to be evaluated are 
signalled by code within the ew_evaluate method - with the cursor being positioned to the error and an 
appropriate hInfoPrint being executed. 


The ew_set method 


For some purposes, the additional control provided by the ew_set method (as compared to the wn_set 
method) may be helpful: 


typedef struct 
{ 
UWORD flags; 
SE_EDWIN txt; 
UWORD cursor; cursor (moving point of select) 
UWORD anchor; anchor point (fixed end of select) 
} SET_EDWIN; 


SET_EDWIN set; 


p_send3 (edwin, O_EW_SET, &set) ; 


12 - 13 


OBJECT ORIENTED PROGRAMMING GUIDE 


As well as containing an szE_EpwIN struct within itself, the seT_zpwin struct evidently also allows the 
cursor and select region to be defined more precisely. This is governed by the possible values in f1ags: 
e if sET_EDWIN_EMPTY is Set, this is merely a convenient way to empty the contents of the editor 
e if SET_EDWIN_TXxT Is set, a call to the wn_set method of the editor is effectively made 
e if SET_EDWIN_SEL_ALL is set, the entire contents of the editor are selected 
e if SET_EDWIN_CUR_END is Set, the cursor is set to the end of the document 


e if SET_EDWIN_ANCHoR is Set, the anchor point of the selection (the non-moving end) is set to 
document offset set .anchor 


e if SET_EDWIN_cuRSOR is set, the cursor (which is also the moving end of the selection, if one 
exists) is set to document offset set. cursor. 


The ew_sense method 


Whereas the ew_set method allows more control, compared to wn_set, over features of an edit window 
that can be set, the ew_sense allows additional editing details (namely, the two ends of the select region) 
to be sensed: 


typedef struct 
{ 
UWORD cursor; cursor (moving point of select) 
UWORD anchor; anchor point (may equal cursor) 
} SENSE_EDWIN; 


SENSE_EDWIN sense; 
p_send3 (edwin, O_WN_SENSE, &sense) ; 


In fact, this method always returns the top end of the select region (if any) in sense. anchor, and the 
bottom end in sense.cursor. Ifthe length of the select region is presently zero, both the two fields in the 
SENSE_EDWIN return the cursor position. 


The concept of document offset 


Both the ew_set and ew_sense methods make use of the concept of document offset. In fact, this is a 
fundamental notion for the epwrn class as a whole. The idea is that although features like line number 
and line offset vary according to the zoom state and status window setting, the document offset of a given 
location with the editor remains constant, regardless of how the contents are viewed. 


For example, suppose an editor has its width decreased and its display font zoomed larger, causing the 
word-wrap to change. Then the possible cursor location just in front of, say, the ‘f’ of “four” 


i ae eeee Cae 
| One two three four | ,One two | 
| five six | three four | 
| | five six 


is on the first line in one case but on the second line in the second case. The line offset of this location 
also changes, but the document offset remains constant: the location has document offset 14 in both cases. 


Allowed values of document offset 


If there are n characters in a document - not counting the final terminating character zero - then there are 
precisely n+1 allowed character offsets, ranging in value from 0 to n. 


Note that the cursor cannot be positioned beyond the final terminator (ie at document offset n+1). Nor in 
fact can the cursor ever be positioned beyond the final character on any line - it always repositions 
automatically in these cases to the very beginning of the following line. 


12-14 


12 EDIT WINDOWS 


The EDWIN.CHANGE property 


Although many parts of the property of zpw1n should be regarded as private, the change field is open to 
direct read-write manipulation by application code. 


For example, the following routine in the Notes application checks whether any change has been made to 
the editor in a given window, and if so 


e senses the new text, and records it 
e resets the value of edwin. change tO FALSE: 


LOCAL_C VOID RecordChange(PR_NOWIN *win, LENBUF *1b) 


{ 
PR_EDWIN *edwin; 
SE_EDWIN sense; 


edwin=win->nowin.edwin; 
if (! (edwin->edwin.change) ) 

return; 
p_send3 (edwin, O_WN_SENSE, &sense) ; 
lb->buf=f_realloc(lb->buf,sense.len) ; 
lb->len=sense.len; 
p_bcpy (1lb->buf, sense. buf, sense.len) ; 
edwin->edwin. change=FALSE; 


} 


In fact there are two different ew_cuancE_xxx bit flags defined in edwin.cl: EW_CHANGE_SINCE_SAVED and 
EW_CHANGE_SINCE_PAGINATE, with the values 0x01 and 0x02 respectively. Further, whenever any code 
inside a method of epw1n changes the contents of the editor, al/ the bits (oxf£££, symbolically rw_cHancE) 
are set iN edwin.change. This allows an application greater control over monitoring the extent to which 
changes may or may not have taken place. 


“Read-only” edit boxes and the ew_readonly method 


Before epw1n code ever allows a change to be made, by the user, to the contents of the edit box, the value 
of the pR_EDWIN_READONLY bit in the edwin. flags field in property is tested. If this is set, by default a 
beep is emitted and the thread of execution is terminated - as can be seen from the following utility routine 
called frequently internal to Epw1n code: 


LOCAL_C VOID CheckNotReadOnly(PR_EDWIN *self) 


{ 
if (self->edwin.flags&PR_EDWIN_READONLY && p_send2(self,O_EW_READONLY) ) 


{ 
hBeep (); 
p_leave (RUN_ACTIVE_USED) ; 
} 
} 


Note that there is no formal mechanism whereby the pR_EDWIN_READONLY bit in edwin. flags can be set or 
cleared, other than by the application directly manipulating this bit. (Care must be taken, however, to 
leave all other bits in this field well alone.) Note further that this bit is cleared at the end of 
edwin_wn_init, so that application code should only ever attempt to set it after the return of the wn_init 
message to the editor. 


The ew_readonly method is declared in edwin.cl to be equal to p_true - in other words, it always returns 
TRUE. An application can subclass this to make some additional tests. For example, code shared between 
the Program Editor and Word Processor applications supplies the following replacement: 


METHOD INT oplwin_ew_readonly(PR_OPLWIN *self) 
{ 
if (IsOutlined() ) 
{ 
KillOutline (self); 
return (FALSE) ; 


} 
return (TRUE) ; 


} 


where the effect is to cancel any outline state before allowing any change to take place in the document, 
whereas other reasons for the document being read-only continue to result in TRUE being returned. 


12-15 


OBJECT ORIENTED PROGRAMMING GUIDE 


The ew_leave method 


Whenever there is a run-time failure following user action that causes characters to be added to the 
document (eg inside the wn_key, ew_insert, OF ew_paste_clip methods), a call is made to ew_leave. 


For example, the code for edwin_ew_paste_clip is as follows: 


METHOD INT edwin_ew_paste_clip(PR_EDWIN *self) 
{ 
UWORD len; 
INT err; 


CheckNotReadOnly (self); 
len=p_send2 (self->edwin.clip,O_EP_SENSE_LEN) ; 
if (len) 
{ 
if ((err=p_entersend4 (self-—>edwin.doc,O_EP_PASTE, 
&self—>edwin.cpos, self->edwin.clip) ) !=0) 
p_send3 (self, O_EW_LEAVE, err) ; 
self—>edwin.clen+=len; 
EdwinFwdChange (self) ; 
SetEdwinSelect (self,self—>edwin.cpos-len, len) ; 
} 
return(len); 


} 


The significance of this is so that one particular error message can be trapped - namely the “Overflow” 
error, E_GEN_OVER, that document objects such as EPFLaT generate when an attempt is made to insert more 
than the allowed maximum number of characters. Code in the ew_leave method translates this particular 
error into something more meaningful to the user: 


METHOD VOID edwin_ew_leave(PR_EDWIN *self, INT err) 

{ 

if (err==E_GEN_OVER) 
{ 
hBeep () ; 
if (self->edwin.flags&PR_EDWIN_NOTIFY_OVERFLOW) 

hInfoPrint (-SYS_EDIT_NCHARS) ; 

err=RUN_ACTIVE_CLEANUP_NONOTIFY; 
} 

f_leave(err); 


} 


(If desired, an application could modify this behaviour by subclassing this method.) 


In English, the text for the system resource -sys_EDIT_NCcHaRs is “Maximum number of characters 
reached”. As can be seen, this message is displayed only if the pR_EDWIN_NOTIFY_OVERFLOw flag is set in 
edwin.flags. By default, this is set for all editors which set either of the 1n_EDwIN_vULEN_xxx flags on 
initialisation. 


Controlling the layout and formatting 


The mechanisms described in this section require various measures of knowledge of the scrimc and 
SCRLAY components of Epwin. In fact, on the whole, these mechanisms are not methods of Epwrn itself, 
but involve sending messages to the scrime and/or scriay objects. 


Amongst other things, these mechanisms allow: 
e changes in which kinds of hidden symbols are displayed 
e — setting the width of the cursor (eg to turn it off completely - if desired) 
e changes in the font used to display the text 
e changes in the size of window 
e changes in the margins applying to paragraphs (including the ability to disable word-wrapping) 


e setting tabstops. 


12 - 16 


12 EDIT WINDOWS 


An introduction to SCRLAY 


As mentioned already, the character content of an edit window is stored within a so-called “document 
object”, which is an instance of a subclass of the FORM eppoc class. The document object can, for the 
most part, be thought of as simply an extended buffer (possibly segmented), containing all the characters 
in the document, together with paragraph delimiters, tab characters, and forced line breaks stored in line. 


By contrast, the document object has no knowledge of 
e tab stop positions 
e whether various special characters (spaces, tabs, carriage returns, etc) are shown or hidden 
e left, right, and first-line margins applying to paragraphs 
e interline and interparagraph spacing 
e the “keep with next’, “keep together’, and “start new page” attributes of paragraphs 
e the fonts to be used to display (and/or print) characters. 
These attributes of an editor are supervised by a scruay (“screen layout”) sub-component. 
More precisely, from the point of view of scriay, screen layout consists of 
e a linked list of paragraphs, each of which consist of 
e a linked list of lines, each of which consists of 
e a linked list of so-called thoxes. 
There are three reasons why a line can be split into different tboxes: 


¢ physical segmentation - the characters making up the line happen to be stored in two different 
physical buffers at that point (this can only ever apply, for editors, if the flag 
IN_EDWIN_TEXT_SEGMENTED is Set on initialisation) 


¢ — stylistic segmentation - where there is a change of font, font-style, or character visibility 


¢ enforced segmentation - where a limit of 236 characters per tbox is applied, to simplify visual 
display of the text of a tbox on the screen by means of the Window Server function 
gPrintBoxText (the value 236 is symbolically known as ws_PRINT_BOX_TEXT_MAX_LEN). 


SCRLAY structure definitions 


The screen layout is held in property of scriay using the following structures (defined in scrlay.cl); 


typedef struct que_tbox 
{ 
struct scrlay_tbox *next; 
struct scrlay_tbox *prev; 
} QUE_TBOX; 


typedef struct scrlay_tbox 
{ 
QUE_TBOX hd; 
WORD width; /* width of box in pixels */ 
UWORD tlen; /* number of doc positions, with mask info */ 
} SCRLAY_TBOX; 


typedef struct que_line 
{ 
struct scrlay_line *next; 
struct scrlay_line *prev; 
} QUE_LINE; 


12-17 


OBJECT ORIENTED PROGRAMMING GUIDE 


typedef struct scrlay_line 
{ 
QUE_LINE hd; 
QUE_TBOX tboxs; 


WORD indent; x pixel position of left of lst tbox 
UWORD len; number of addressible content positions 
UBYTE islast; TRUE if last line in paragraph 

UBYTE new_page; TRUE if start of page 


} SCRLAY_LINE; 


typedef struct que_para 
{ 
struct scrlay_para *next; 
struct scrlay_para *prev; 
} QUE_PARA; 


typedef struct scrlay_para 
{ 
QUE_PARA hd; 
QUE_LINE lines; 
} SCRLAY_PARA; 


An important efficiency measure is that scriay only contains the layout information for the visible 
portion of the data - ie the data currently visible on the screen (though it turns out simpler also to 
maintain the data for any portion of the first visible paragraph that is off the top of the screen). 


Thus scriay property contains the document offset of the top of the layout structure it currently possesses. 
From this, it is possible to calculate the document offset of the start of any line, or the start of any tbox, 


within the screen layout. 


As well as containing the screen layout data, scriay contains the logic for re-calculating screen layout, 
according to changes in, for example, document content or paragraph styling. Finally - and this is of 
particular concern to users of edit boxes - scrLay property contains a scRLAY_STYLE “global style 
definition” data structure: 


typedef struct 


{ 


UWORD fid; 
UWORD style; 
UWORD height; 
} SCRLAY_FONT; 


typedef struct 


{ 


font id for wserv or typeface for printer 
font style (eg bold) 
height of printer font in decipoints 


UWORD left; Left margin 

UWORD right; Right margin 

UWORD indent; Left margin of first line in para 

UWORD align; Alignment (left, right, centre or justified) 


} SCRLAY_MARGINS; 


typedef struct 


{ 


UWORD line; 

UWORD above; 
UWORD below; 
UWORD flags; 


} SCRLAY_SPACING; 


typedef struct 


{ 


UWORD x; 
UWORD type; 


} SCRLAY_TABSTOP 


typedef struct 


{ 


UWORD ntab; 
SCRLAY_TABSTOP tab[SCRLAY_NTABS_MAX]; 


} SCRLAY_TABS; 


12 - 18 


Space between paragraph lines 

Space above paragraph 

Space below paragraph 

Keep together/next and start new page 


tab position 
tab type (left, right, centre or repeated) 


number of tabs 


12 EDIT WINDOWS 


typedef struct 
{ 


SCRLAY_MARGINS *margins; Paragraph margins 
SCRLAY_TABS *tabs; Paragraph tabs 
SCRLAY_SPACING *spacing; Paragraph spacing 


} SCRLAY_PDATA; 


typedef struct 
{ 


UBYTE options; Layout options 

UBYTE printer; TRUE if printer layout 
SCRLAY_PDATA pd; Global margins, tabs, spacing 
SCRLAY_FONT *font; Global font id and style 

UBYTE *fwtab; Global font table or NULL 

UWORD scrpwidth; Width of screen in printer units 


SCRLAY_FONT *sfont; Global screen font id and style 
} SCRLAY_STYLE; 


Example: changing visibility of special characters 


The following code shows an example of interaction with the scrLay_styLe data structure inside scriay. 
The code either hides or shows specified so-called “special characters”: 


LOCAL_C VOID ShowSymbols(PR_EDWIN *edwin, INT symbols) 
{ 
SCRLAY_STYLE style; 


p_send3 (edwin->edwin.scrlay,O_SL_SENSE, &style); 
style.options=symbols; 

p_send3 (edwin->edwin.scrlay,O_SL_SET, &éstyle) ; 

p_send3 (edwin->edwin.scrimg,O_SI_STYLE_CHANGED, SCRIMG_STCHNG_DOC) ; 
} 


The basic pattern here is: sense the global layout data, make the required changes, set the new values, and 
then notify scrime of the change (see later for further discussion of scr1IMc). 


The given routine is complete in its own right, but for it to be used, the allowed values of the options field 
in SCRLAY_STYLE property have to be known. These can be found out from scrlay.cl: 


SCRLAY_SHOW_TABS 0x01 

SCRLAY_SHOW_SPACES 0x02 

SCRLAY_SHOW_CRS 0x04 

SCRLAY_SHOW_HYPHENS 0x08 Show optional hyphens 

SCRLAY_SHOW_LFS Ox10 

SCRLAY_WIDOW_ORPHAN 0x20 Set to enable widow & orphan suppression 


Default values of SCRLAY_STYLE in edit windows 


Note that many of the fields in scrLay_styLz are stored by indirection. By default, the indirected data 
exists in suitable slots within EpwIn property - which contains a SCRLAY_MARGINS margins field anda 
SCRLAY_FONT font field. 


The following extract from edwin_wn_init shows how this works: 


SCRLAY_STYLE style; 

SCRLAY_DOC doc; 

style.options=SCRLAY_SHOW_TABS; 

style.printer=FALSE; 

style.pd.margins=(&self-—>edwin.margins) ; 
style.sfont=style.font=(&self->edwin.font) ; 
style.pd.tabs=(SCRLAY_TABS *) (&self—->edwin.font.height) ; /* ntab = 0 */ 
style. fwtab=NULL; 

style.scrpwidth=0; 

p_send3 (self,O_EW_INIT_STYLE, &style); /* chance for subclassers */ 
p_send4 (self->edwin.scrlay=h_fnew(C_SCRLAY) ,O_SL_INIT, &édoc, &style) ; 


(see later in this chapter for a discussion of the scrLay_poc structure). 


Note in particular that, by default, no tabstops are set up. (There is a minor piece of trickery here, relying 
on the fact that, for screen display purposes, the font .height field is always zero.) 


12-19 


OBJECT ORIENTED PROGRAMMING GUIDE 


Changing from the default layout style 


As can be seen above, one way to change from the default layout style is by a call to s1_sense followed by 
one to sl_set. 


Another approach is to subclass the ew_init_style method of pwn - since a call to this is made just 
prior to the scruay object is actually created and initialised (see the code fragment given earlier). By 
default, the ew_init_style method equals p_dummy, and does nothing. 


Finally, all the above concerns so-called global style for the editor - style applying by default to all 
portions. However, the formatting code within scruay is open to the possibility of local variations in 
style. This is discussed further in the section on document objects below. 


An introduction to SCRIMG 


As noted above, changing the layout style by means of a call to si_set is not, by itself, sufficient to cause 
an actual change in the formatted layout of the editor. In addition, the screen image scrime object has to 
be notified - hence the si_style_changed message in the example given. 


In general, scrime caters for the concepts of 
e cursor position and select region 
e emphasis on or off 
e knowledge of the (lodger) window to be drawn to 


e knowledge of how this window region may break down into a possible left gutter (“labels’’) 
region, and a possible “line cursor” margin, as well as the main drawing area 


e width of the text cursor, when displayed, as well as the style of any margin line cursor 
e parameters affecting the way horizontal scrolling takes place 


e the state of background reformatting (ie which parts of the layout are up-to-date, and which need 
to be re-evaluated as soon as time allows). 


Additionally, scrrme contains the logic for the actual displaying (drawing and redrawing) of the edit 
window, for recalculating layout information (ie for driving the scruay object), for freeing layout 
structures no longer required (since the visible portion of the document has altered), and for smoothly 
scrolling the display vertically whenever appropriate. 


In fact, it may well appear that scrimc contains the core logic for Epwt1n itself, and there is much to be said 
for this view. Several methods of Epw1n simply delegate responsibility to scrime by passing on an 
appropriate message. However, it may be worth pointing out a few of the general differences between the 
overall epw1n object and its scrImG component: 


¢ scrime (and scruay and indeed any class in FORM) is completely independent of any of the 
concepts in HWIM, and can be utilised eg on the MC range of computers, where the front-line 
user interface library (WIMP) is significantly different from the HWIM library 


¢ scrime can be utilised independently of zpwrn, to provide so-called “edit-like windows” 


¢ pwn may be viewed as an organiser of the cooperation between a scRIMG, a SCRLAY, and an 
EPDoCc; the wn_init method of Epw1n involves a substantial amount of “form filling”, in which 
these sub-components are properly initialised in a suitable relationship to one another 


¢ EDWIN contains an extensive wn_key method, which is actually one of the longest methods in the 
whole of the HWIM library 


e¢ pwn adds on significant clipboard functionality, evaluation functionality, link-paste 
functionality, and find and replace functionality 


e amazingly (as discussed in more detail later in this chapter), scrime has no direct knowledge 
whatsoever about the document object. 


12 - 20 


SCRIMG structure defin 


12 EDIT WINDOWS 


itions 


The “window” or “drawing environment” aspects of a scrime object are stored in property in an 


SCRIMG_WIN data structure: 


typedef struct 
{ 
UWORD wid; 
P_POINT tl; 
WORD nlines; 
UBYTE lheight; 
UBYTE lascent; 
WORD width; 
WORD margin; 
WORD lcfont; 
UBYTE cwidth; 
UBYTE lcstyle; 
UBYTE lccode; 
UBYTE hscrlx; 
UBYTE hscrlm; 
UBYTE drawplabs; 
} SCRIMG_WIN; 


window ID 
top left corner of area being drawn to 
number of text lines 
line height in pixels 
distance from top of line to text base line 
total width in pixels (margin, line cursor,text) 
width of label margin in pixels 
line cursor font (or zero for no line cursor) 
text cursor width 
line cursor style 
line cursor character code 
horizontal scroll x jump 
horizontal scroll margin 
draw trailing para labels after formatting if set 


Just as the scrLay_sTyLE data held by a scriay object can be sensed via an s1_sense method and set via 
an si_set method, so also is there an si_sense method to sense the scrrmc_win data held by a scrime 
object, and an si_set method to set this data (see later for examples of these calls). 


In fact, as the code for scrimg_si_set makes clear, the si_set method takes two parameters - the first 


being (if non-nut1) the poin 


ter to a SCRIMG_WIN Struct, and the second being (if non-nuLL) the handle of 


the associated scriay object: 


METHOD INT scrimg_si 
/* 
Optionally set the w 
and return the width 
Also waits for an ba 
ef: 
{ 
(PR_SCRIMG *)Dat 
CompleteFormatti 
if (lay) 
self-—>scrimg 
if (win!=NULL) 
{ 
self—>scrimg 
self—>scrimg 
self—>scrimg 
if (self->sc 
self->sc 
self 
self—>scrimg 


self-—>scrimg. 
self—>scrimg. 


} 


if (self->scrimg. 
self-—>scrimg. 
if (self->scrimg. 


p_send3 (self 
return (self->scr 


} 


During initialisation, EDwINn 


_set (PR_SCRIMG *self,SCRIMG_WIN *win,VOID *lay) 


indow and the layout object (win and lay may be NULL) 
(in pixels) of the text area. 
ckground formatting to die down. 


Scrimg=self; 
ng(); 


.lay=lay; 


-gc.font=WS_FONT_BASE; 

-gc.style=G_STY_NORMAL; 

-win=*win; 

rimg.win.lcfont) 

rimg.lcwidth=gTextWidth (self—>scrimg.win.lcfont, 
—>scrimg.win.lcstyle, &self-—>scrimg.win.lccode,1)+2; 
-mrwidth=self—>scrimg.win.margint+self—>scrimg.lcwidth; 
xo=self—>scrimg.mrwidtht+self—>scrimg.win.tl.x; 
txwidth=self—>scrimg.win.width-self-—>scrimg.mrwidth; 


crs.line>self-—>scrimg.win.nlines-1) 
crs.line=self-—>scrimg.win.nlines-1; 

lay) 

—>scrimg.lay,O_SL_SET_LINES, self->scrimg.win.nlines) ; 
img.txwidth) ; 


takes care of setting up appropriate values for the scrimc_win data structure - 


based (as can be imagined) on the data in the In_EDwIN and IN_EDWIN_x Structures. 


12-21 


OBJECT ORIENTED PROGRAMMING GUIDE 


Example: changing the width of the text cursor 


The following code shows an example of interaction with the scrimc_wtn data structure inside scrimc. 
The code adjusts the width that the flashing text cursor will have, when shown (eg, it could be used to set 
the width to zero - effectively to hide the cursor altogether): 


LOCAL_C VOID SetCursorWidth(PR_EDWIN *ebH, INT cwidth) 


{ 
SCRIMG_WIN win; 


p_send3 (edwin->edwin.scrimg,O_SI_SENSE, &win) ; 
win. cwidth=cwidth; 
p_send4 (edwin->edwin.scrimg,O_SI_SET, &win, NULL) ; 


} 
Changing the font used by an editor 


The ew_set_font method of Epwrn can be used to change the font used for the display. The code follows: 


METHOD VOID edwin_ew_set_font (PR_EDWIN *self,INT font,UINT style, EDWIN_LEADING 
* leading) 

/* 

Expected to be accompanied by call to ew_set_size 


a 


{ 
SCRIMG_WIN win; 
G_FONT_INFO finfo; 


self—>edwin.font.fid=font; 

self—>edwin.font.style=style; 

gFontInfo(self—>edwin.font.fid, self—>edwin.font.style, &finfo) ; 
p_send3 (self—->edwin.scrimg,O_SI_SENSE, &win) ; 
win.lheight=finfo.height+leading->total; 
win.lascent=finfo.ascent+leading->top; 

p_send4 (self—>edwin.scrimg,O_SI_SET, &éwin, NULL) ; 


} 


Note however that, as the comment in the code states, this call by itself will generally be insufficient to 
effect the font change. Additionally: 


e in many cases, the size of the window region may change (quite likely when the font change has 
been triggered by a ‘Zoom’ menu command) 


e a suitable request message will have to be passed in due course to scrime to request it to 
recalculate the screen image - which will involve invalidating some or all of the formatting 
information maintained by scruay. 


See later for more information about notification and request messages to scrimc. The main point here is 
that adjusting the scrimc_wt1n data does not, by itself, trigger a recalculation; rather, this is delayed until 
all necessary adjustments have been made, to avoid needless repeated re-calculations. 


Note incidentally that the new font details do not have to be passed on explicitly to scruay. Recall that 
the various scRLAY_FonT data structures required by scruay are referenced indirectly: the scRLAY_STYLE 
data structure actually contains, by default, pointers to the edwin. font structure inside EDWIN property. 


Note moreover that there is no compulsion to use the ew_set_font method, in order to change the font 
used by an editor. Rather, the code given above can be used as a template (in conjunction with more code 
to be listed shortly) for application-specific code to achieve a similar result. 


Note finally that editors with local variations in font - ie with some portions of text being displayed in one 
style, and with other portions being displayed in another style - require alternative document objects to be 
used - as is discussed later in this chapter. 


12 - 22 


12 EDIT WINDOWS 


The ew_sense_size and ew_set_size methods 


Code that can be used to resize an editor - for example in response to the status window changing, or in 
response to a ‘Zoom’ menu command - includes the ew_sense_size and ew_set_size methods. These 
are normally used as a pair, for obvious reasons: 


METHOD VOID edwin_ew_sense_size(PR_EDWIN *self,P_EXTENT *pext) 


{ 
SCRIMG_WIN win; 


p_send3 (self—>edwin.scrimg,O_SI_SENSE, &win) ; 
pext—>tl=win.tl; 

pext—>width=win.width; 
pext—>height=win.nlines; 


} 


METHOD VOID edwin_ew_set_size(PR_EDWIN *self,P_EXTENT *pext,VOID *hand, INT method) 
{ 
SCRIMG_WIN win; 
INT twid; 


p_send3 (self-—>edwin.scrimg,O_SI_SENSE, &win) ; 
self—>lodger.offset=win.tl=pext—>t1l; 
self—>lodger.width=win.width=pext-—>width; 
win.nlines=pext—>height; 
twid=p_send4 (self-—>edwin.scrimg, O_SI_SET, &éwin, NULL) ; 
if (hand) 

p_send3 (hand, method, twid) ; 
p_send3 (self—->edwin.scrimg, O_SI_STYLE_CHANGED, SCRIMG_STCHNG_DOC) ; 
} 


Note that the height field in the p_extent data accessed by both these methods refers to the number of 
lines in the screen window - in contrast to the values in each of the other three fields in the p_ExtEnt data, 
which all represent numbers of pixels. Evidently, scrimc always assumes that there are a whole number 
of lines visible. 


Next, note that the si_set method returns the number of pixels in the width of the text area of the editor, 
after the resize. This value may or may not be useful - see below for its use within the wn_init method of 
EDWIN itself. 


Finally, note that the ew_set_size method supports a possible “soft” call-back - if the parameter nana is 
non-nuLu - before making the si_style_changed request to scrime to trigger a layout recalculation. 


Changing the paragraph margins 


The following example could be used to set the “right” margin to the arbitrary large value of 4096 - and 
thereby to disable word-wrap, in effect (as in the Program Editor). 


Alternatively, the example could be extended to adjust the other paragraph margins used by paragraphs - 
ie the “left” and “first line” margins: 


LOCAL_C VOID SetRightMargin(PR_EDWIN *edwin,UINT right) 
{ 
edwin->edwin.margins.right=right; 
p_send3 (edwin->edwin.scrimg,O_SI_STYLE_CHANGED, SCRIMG_STCHNG_DOC) ; 
} 


Notifying SCRIMG of a change in style 


The si_style_changed method causes scrime to recalculate some or all of the layout in the associated 
SCRLAY object, and to redraw the screen (intelligently - ie minimising the amount of redrawing that 
actually is done). The cursor position and any select region are maintained, and as far as possible, the 
cursor is drawn on the same line number of the screen (eg the second line down from the top of the visible 
portion) as before. 


Exactly how much work is carried out depends on the scrimG_sSTCHNG_xxx parameter passed (but note that 
this parameter is ignored on the Series 3, with notice of it being taken only on the Series 3a and on the 
MC): 


e if the parameter is scrIMG_sTCHNG_Doc, the entire layout is rebuilt and redrawn (subject, as 
always, to only building as much layout as is required to cover the visible portion of the 
document) 


12 - 23 


OBJECT ORIENTED PROGRAMMING GUIDE 


if the parameter is scRIMG_STCHNG_PaARA, layout above the top of the paragraph containing the 
cursor (or the top of any selected region) is not recalculated or redrawn, and any layout below the 
bottom of the paragraph containing the cursor (or the foot of any selected region) is merely 
scrolled vertically (if required) - this is the action appropriate when, for example, paragraph 
styling is applied in the word processor by a key sequence such as CONTROL-BT 


if the parameter is scRIMG_STCHNG_LINE, a similar optimisation is made, appropriate this time to 
the application of phrase style (sometimes called “emphasis’”) in the Word Processor - this can 
result in even less work being carried out, if for example the cursor is in the third or subsequent 
line in a paragraph. 


Initialising the SCRIMG_WIN data structure 


For general background interest, here is an extract from edwin_wn_init, containing the code that creates 
and initialises the scrrme subcomponent: 


METHOD VOID edwin_wn_init (PR_EDWIN *self,IN_EDWIN *init,PR_WIN *whand, IN_EDWIN_X 
*initx) 


12 - 24 


{ 

SCRIMG_WIN win; 
SCRLAY_DOC doc; 
SCRLAY_STYLE style; 
G_FONT_INFO finfo; 
EDWIN_LEADING leading; 


self-—>lodger.landlord=whand; 


win.lcfont=0; /* no left cursor by default */ 
if (init->flags&IN_EDWIN_LEFT_CURSOR) 


win.lcfont=WS_FONT_BASE; 
win.lcstyle=self—>edwin.font.style&G_STY_DOUBLE; 
win. lccode=WS_SYMBOL_MARGIN_CURSOR; 


leading.total=2; /* by default, one pixel leading at top and at bottom */ 
leading.top=1; 
if (init->flags&IN_EDWIN_LEADING_SUPPLIED) 

leading=initx—>leading; 
win.lheight=finfo.height+leading.total; 
win.lascent=finfo.ascentt+tleading.top; 


win.wid=whand->win.id; 
win.tl=initx-—>pos; /* may be garbage but no harm done */ 
win.nlines=(init-—>flags&IN_EDWIN_VISLINES_SUPPLIED? initx->vislines: 1); 
win.width=self-—>lodger.width; 
win.margin=0; 
win.cwidth=2; 
if (win.nlines==1) 

{ 

win. hscr1lx=0; 

win. hscrlm=win.width>>2; 

} 
else 

win. hscrlx=win.hscrlm=30; 
self—>edwin.scrimg=h_fnew(C_SCRIMG) ; 
self—>edwin.margins.right=p_send4 (self-—>edwin.scrimg,O_SI_SET, 

éwin, self-—>edwin.scrlay) -finfo.max_width; 

if (win.nlines==1) 


self—>edwin.margins.right=4096; /* any large value will do */ 
if (init->flags&IN_EDWIN_POSITION_SUPPLIED) 
{ /* else defer until following lg_set_id_pos */ 


self—>lodger.offset=win.tl; 
self—>win.id=win.wid; 

p_send4 (self—>edwin.scrimg,O_SI_INIT,0,0); 
SelectAl1lOrCurEnd (self); 

} 


12 EDIT WINDOWS 


Direct interaction with document objects 


A given EpwIn object interacts with up to two document objects: the document object where its own 
character content is stored, and (optionally) the clipboard document object. 


The clipboard document object is usually an instance of either EPFLAT or EPSEG. See the OLIB Reference 
manual for a description of these two classes - which are each a subclass of EPRooT. 


The main document object for an Epwin has to be a subclass of the FORM eppoc class. This is a 
specialised subclass of EPpRoot, and like EPRooT, it supports both “flat” and “segmented” concrete 
subclasses, namely eEpriat and Epsec. Methods of eproor all carry over to Eppoc - although the 
implementation may differ, in places. 


Although many of the methods of zpw1n manipulate the document objects on behalf of the application, 
there can be occasions where it is more appropriate for the application to interact directly with these 
objects. Afterwards, of course, the editor has to be informed that a change has taken place. 


Another reason for wishing to understand document objects more deeply is in order to support local 
variations in style data. Yet another is in order to supply “labels” for paragraphs. All these topics are 
discussed in the following subsections of this chapter. 


Setting text directly into the document object 


Suppose for example that an application wishes to set a large amount of text into an edit window - text 
that can be read in stages from a file. The following steps could be taken. 


First, the handle of the document object needs to be obtained. This can be read out of the edwin.doc 
property field of the Epw1n, or the document object may have been created separately - in which case the 
handle will already be known. 


Next, it may be appropriate (especially in the case of a flat document object) to set the capacity of the 
document object in advance - if the overall size is known. The ep_capacity method (refer to the OLIB 
Reference manual for details) can be used to this end. (The ep_capacity method is left as p_dummy for all 
segmented document objects.) 


Following that, repeated calls of the following sort can be made: 
p_send5 (doc, O_EP_INSERT,pos,buf,1len); 


having the effect, each time, of inserting the 1en characters at *bur into the document, at document offset 
pos. It would be usual to track the value of pos as this operation proceeds, with 1en being added to it each 
time 1en more characters are inserted. 


Finally, code of the following sort is required: 


LOCAL_C VOID NotifyDocChanged(PR_EDWIN *edwin) 


{ 
UINT doclen; 


doclen=p_send2 (edwin->edwin.doc, O_EP_SENSE_LEN) ; 
edwin->edwin.clen=doclent+1; 

p_send3 (edwin->edwin.scrimg,O_SI_DOC_CHANGED, doclen) ; 
} 


It is this last routine that stands most in need of comment here (the earlier steps, after all, only require 
knowledge of the Eproot class). In general 


e scrime level data has to be adjusted - by means of, for example, the si_doc_changed method 


¢ and, at the same time, Epwtn level data has to be adjusted - usually by direct manipulation of the 
relevant property fields. 


There are actually two key epw1n property fields in this context: 


¢ edwin.clen, giving the total “character length” of the document (including the final paragraph 
delimiter) 


@ edwin.cpos, giving the cursor position, as a document offset. 


For simplicity, the above routine, Not ifyDocChanged, omits making any change in the cursor position - 
which may be appropriate in some cases, but it will not be appropriate in other cases, and will even result 
in program crashes in yet other cases (eg if there are fewer characters in the document after the change 
than before the change). 


12 - 25 


OBJECT ORIENTED PROGRAMMING GUIDE 


Dual variables at the EDWIN and SCRIMG levels 


As can be seen, copies of, effectively, the total character length of the document are held by both scrime 
and Epwin. Likewise, dual copies are also kept of the cursor position. There is also a select field within 
EDWIN property (which is TRuE if there is a non-nuLL select region, otherwise FaLsE), which must, once 
again, be kept in synchronisation with the status of the select region as known to scrime. 


The reason for this duplication of data storage is to increase the speed at which various critical 
manoeuvres within edit boxes can be executed. However, it should be pointed out that failure to keep 
these dual variables appropriately in harmony is a common cause of bugs in programming edit windows. 


Adjusting the cursor position 


One way that the cursor position of an edit window (and, with it, the select region) can be adjusted is via 
the ew_set method documented earlier in this chapter. 


For many purposes, however, the scrimc method si_move_cursor may prove more suitable. In fact, 
si_move_cursor is used frequently within Epw1n code (for example, within the ew_set method), often via 
the following utility routine: 


LOCAL_C VOID MoveCursor(PR_EDWIN *self,INT shift, INT type) 


{ 

self—>edwin.select=p_send5 (self->edwin.scrimg, O_SI_MOVE_CURSOR, 
shift,type, &self-—>edwin.cpos) ; 

} 


Subclasses of zpw1n often contain a duplicate of this utility function - such is its use. 
The meaning of the shift parameter to si_move_cursor is as follows: 


e ifthe shift parameter is non-zero, it means to extend (or create) a select region, with the 
movement specified by type being applied to the moving end of the selection 


e if shift is zero, it means to cancel any existing select region, and to move the cursor as specified 
by type. 


The return value from si_move_cursor 1S TRUE if there is a non-NULL select region after the movement, 
and otherwise FALSE. 


The possible meanings of type are as follows: 


SCRIMG_LINEDN move the cursor down one line 

SCRIMG_LINEUP move the cursor up one line 

SCRIMG_PAGEDN move the cursor down one page 

SCRIMG_PAGEUP move the cursor up one page 

SCRIMG_LINBEG move the cursor to the beginning of the current line 

SCRIMG_LINEND move the cursor to the end of the current line 

SCRIMG_SETPOS move the cursor to the document offset specified by the final parameter. 


In all cases, the final position of the cursor, as a document offset, is written to the address specified by the 
final parameter to the si_move_cursor call. 


Logical cursor movement and physical cursor movement 


Most of the type values in the above table cater for so-called “physical” cursor movement - where the 
actual movement of the cursor is determined by reference to the current layout. (In order to work out 
where to position the cursor, scriMe interrogates the data structures maintained by the associated scrLay 
object.) 


In many other cases - for example, in response to the CONTROL-LEFT key, which moves the cursor back to 
the next beginning of a word - the movement of the cursor is instead determined by reference to document 
content, and results in a so-called “logical” cursor movement. Methods of EPRoot, such as ep_scan_word, 
may be of use in this case. Once the required document offset is known, a call to si_move_cursor iS 
required, specifying scrImMG_sETPos as the type. For this reason, subclasses of epw1n often contain a 
routine such as 


12 - 26 


12 EDIT WINDOWS 


LOCAL_C VOID SetCursor(PR_EDWIN *self) 


{ 
MoveCursor (self,0,SCRIMG_SETPOS) ; 


} 


which, evidently, layers over the Movecursor utility routine described earlier. 


On this subject, yet another routine that may be worth duplicating is the following, whose effect is to set 
up a select region with given ends (this routine is called, in effect, from inside edwin_ew_set): 


LOCAL_C VOID SetEdwinSelect (PR_EDWIN *self,UINT ancpos,INT sellen) 
{ 


self—>edwin.cpos=ancpos; 
MoveCursor (self,0,SCRIMG_SETPOS) ; 
self—>edwin.cpos+=sellen; 
MoveCursor (self, TRUE, SCRIMG_SETPOS) ; 


} 
Notifying SCRIMG of a change in document content 


The scrime class supports in all four different methods for reporting to it that there has been a change in 
the document. These methods differ primarily in how much reformatting is required - to avoid incurring 
unnecessary work re-evaluating layout data that cannot possibly have changed. (That is a very important 
consideration when the user is typing in the middle of a sizeable paragraph.) 


The si_doc_reset method can be called as follows: 
p_send5 (scrimg, O_SI_DOC_RESET, doclen,cpos, line) ; 


with the following effect: 


¢ scrime is notified that the document has totally changed, and now has document length docien 
e any existing select region should be discarded 


e the existing layout data should be discarded 


e the layout should be rebuilt so that the curspor is put at document offset pos and is displayed at 
line number 1ine on the visible screen (subject to that line being reachable). 


Note that 1ine can be set to the value -1 in order to pick up the current line number (so that the cursor 
remains, if possible, on the same line of the screen as before). 


Note again that the docien value includes the final terminating paragraph delimiter in the document. 


A simple example of the use of si_doc_reset 1s in the following utility routine called inside 


edwin_wn_set: 


LOCAL_C VOID EdwinSet (PR_EDWIN *self,SE_EDWIN *set) 


{ 
p_send4 (self—>edwin.doc, O_EP_SET_TEXT, set—->buf, set-—>len) ; 


self—>edwin.cpos=0; 
self—>edwin.clen=set-—>len+1; 
p_send5 (self—>edwin.scrimg, O_SI_DOC_RESET, self->edwin.clen,0,0); 


} 
(the code in edwin_wn_set goes on to set the cursor position and select region depending on the value of 
the initialisation 1N_EDWIN_xxx flags). 
Similar in effect to si_doc_reset, the si_doc_changed method differs on in that the cursor position is 


maintained the same as before the change was made. Accordingly, whilst a docien parameter is still 
needed, this method has no cpos or line parameters. The way to call si_doc_changed in general is 


p_send3 (scrimg, O_SI_DOC_CHANGED, doclen) ; 


For example, edwin_ew_replace calls si_doc_changed, indirectly, as follows (this code also illustrates use 
of the scrimc method si_sense_select): 


LOCAL_C VOID EdwinDocChanged(PR_EDWIN *self) 


{ 
self—>edwin. change=EW_CHANGE; 
p_send3 (self->edwin.scrimg,O_SI_DOC_CHANGED, self->edwin.clen) ; 


} 
12-27 


OBJECT ORIENTED PROGRAMMING GUIDE 


METHOD INT edwin_ew_replace(PR_EDWIN *self, TEXT *replace, INT backwards) 
{ 
UWORD replen; 
UWORD sellen; 
UWORD pos; 
INT err; 


CheckNotReadOnly (self); 

replen=p_slen (replace) ; 

sellen=p_send3 (self-—>edwin.scrimg,O_SI_GET_SELECT, &pos) ; 

if ((err=p_entersend5 (self—>edwin.doc, O_EP_INSERT, pos, replace, replen) ) !=0) 
p_send3 (self,O_EW_LEAVE, err) ; 

self—>edwin.cpos=pos+treplen; 

if (backwards) 
self—>edwin.cpos=pos; 

SetCursor (self); 

p_send4 (self—>edwin.doc, O_EP_DELETE, pos+replen, postreplentsellen) ; 

self—>edwin.clen+=(replen-sellen) ; 

EdwinDocChanged (self) ; 

return (0); /* confirm success */ 


} 
Notifying SCRIMG of a local change in document content 


The si_fwd_change method of scrime is called as follows: 


p_send3 (scrimg, O_SI_FWD_CHANGE, doclen) ; 


This takes exactly the same parameters as si_doc_changed, and as in that case, the method has the effect 
of 


e cancelling any select region 


e keeping the cursor at the same document offset as before. 


However, for si_fwd_change, ScRIMG makes the assumption that the layout cannot change in paragraphs 
earlier in the document than that containing the cursor; nor can it change in lines in the current paragraph 
more than one above that containing the cursor. Briefly (though, as can be appreciated, not completely 
accurately), only layout forward from the cursor can have changed. 


For example, edwin_ew_paste_clip Calls si_fwd_changed, indirectly, as follows: 


LOCAL_C VOID EdwinFwdChange(PR_EDWIN *self) 
{ 
self—>edwin. change=EW_CHANGE; 
p_send3 (self—>edwin.scrimg,O_SI_FWD_CHANGE, self-—>edwin.clen) ; 
} 


METHOD INT edwin_ew_paste_clip(PR_EDWIN *self) 
{ 
UWORD len; 
INT err; 


CheckNotReadOnly (self); 
len=p_send2 (self->edwin.clip,O_EP_SENSE_LEN) ; 
if (len) 
{ 
if ((err=p_entersend4 (self—>edwin.doc,O_EP_PASTE, 
&self—>edwin.cpos, self->edwin.clip) ) !=0) 
p_send3 (self, O_EW_LEAVE, err); 
self—>edwin.clen+=len; 
EdwinFwdChange (self) ; 
SetEdwinSelect (self,self-—>edwin.cpos-len, len) ; 
} 
return(len); 


} 


The si_para_changed method takes stages one step further by restricting the extent of possible layout 
change to the current paragraph (whereas a change notified by si_fwd_change can effect regions on the 
screen arbitrarily far below the current paragraph). More precisely, si_para_changed assumes that the 
only possible change in layout for paragraphs below the current paragraph is vertical scrolling. 


12 - 28 


12 EDIT WINDOWS 


Another change between si_fwd_change and si_para_changed is in the form of the parameters passed. 
In general, a call to si_para_changed has the form 


SCRLAY_PLX old; 
p_send4 (scrimg, O_SI_PARA_CHANGED, keycode, &01d) ; 


The precise description of the method is that the paragraph containing the cursor has changed at the 
cursor position as a result of one of: 


e asingle character insertion of a content character, where keycode is either a character code 
(which is assumed to be printable) or zero (paragraph end) or '\t' (W_KEY_TAB) Or '\n' 


e a left delete, where the character code is '\b' (W_KEY_DELETE_LEFT) 
e aright delete, where the character is 127 (W_KEY_DELETE_RIGHT). 


The method immediately redraws the current line to reflect the input, and completes the production and 
the drawing of the layout as a background task. The final parameter, which is a pointer to a scRLAY_PLX 
struct, has significance only when keycode 18 W_KEY_DELETE_LEFT. This is required in order for scrimc 
code to be able to make a safe judgement about whether the deletion has effects that extend over more 
than one line (the problem being that scrime cannot in this calculate the old cursor position after being 
notified of the change in the document). 


An example of code that sets up the appropriate scrLay_pLx structure, prior to calling si_para_changed, 
is in the following extract from edwin_wn_key, for the case when a w_KEY_DELETE_LEFT key has been 
received: 


case W_KEY_DELETE_LEFT: 
CheckNotReadOnly (self); 
if (self->edwin.select) 
goto delsel; 
if (self->edwin.cpos==0) 
break; 
p_send3 (self—>edwin.scrimg,O_SI_DELPREP, &p1x) ; 
self—>edwin.cpos-=1; 
p_send4 (self-—>edwin.doc, O_EP_DELETE, self—>edwin.cpos, self->edwin.cpost1l) ; 
self—>edwin.clen-=1; 
p_send4 (self—>edwin.scrimg, O_SI_PARA_CHANGED, keycode, &p1x) ; 


As can be seen, there is no need to pass a doclen parameter to si_para_changed. 


When there is a change of content and a change in cursor position 


The above few sections have touched (and hinted) at one potential problem when orchestrating 
notification to scrime that the document has altered, whilst at the same time trying to take advantage of 
incremental updates in the layout information (for speed purposes). 


The problem is that scrimc cannot be left with a cursor position that no longer exists in the document. 
More precisely, recall that scriay ultimately views the document as consisting of a series of so-called 
tboxes. For example, a given tbox may refer to n characters starting at document offset dort. As 
mentioned earlier, these n characters will all be stored contiguously within a buffer inside the associated 
document object. But suppose, as a result of a change in the document, these characters are no longer 
stored contiguously. Then any subsequent attempt to access these characters - by reference - will fail. But 
this is precisely the kind of thing that scrimc and scruay will, between them, attempt to do - so long as 
they believe that part of the layout structure is still valid. 


Without going into any more details, the moral is clear: position the scrime cursor to the beginning of any 
region where change is about to occur, before making that actual change. Various aspects of the EDwIN 
code given above can be seen, upon inspection, to be obeying this principle. 


12 - 29 


OBJECT ORIENTED PROGRAMMING GUIDE 


The SCRLAY_DOC data structure 


The si_init method, described earlier, actually requires the address of a scrLay_poc data structure, as 
well as the address of a scRLAY_STYLE data structure. The scriay_poc structure informs scrLay about 
some very important aspects of the associated document object: 


typedef struct 
{ 


UWORD len; Length of doc (one greater than max position) 
VOID *content; Object containing document content 

WORD sensechars; Method to sense character segments 

WORD sensepdata; Method to sense paragraph layout data 

WORD senseplabel; Method to sense paragraph label 

WORD toparst; Method to scan start of paragraph 

WORD enqpage; Method to enquire for a page break 


} SCRLAY_DOC; 


Here is how these values are filled in during edwin_wn_init: 


SCRLAY_DOC doc; 


doc.enqpage=0; 

if (init->flags&IN_EDWIN_TEXT_SEGMENTED) 
{ 
doc.sensechars=O_EPDOC_SENSE_CHARS; 
doc.toparst=O_EPDOC_PARA_START; 
if (init->flags&IN_EDWIN_PAGINATABLE) 

doc.enqpage=O_EPDOC_ENQ_PAGE; 

} 


else 
{ 
doc.sensechars=0O_EPFDOC_SENSE_CHARS; 
doc.toparst=O_EPFDOC_PARA_START; 


} 
if (init->flags&IN_EDWIN_DOC_SUPPLIED) 
self—>edwin.doc=initx->doc; 
else 
{ 
self—->edwin.doc=NewEdwinDoc (CAT_HWIM_FORM, C_EPFDOC, init) ; 
p_send4 (self—>edwin.doc, O_EP_SET_TEXT, &init->contents[0],p_slen(é&init- 
>contents[0])); 
} 
doc.sensepdata=0; 
doc.senseplabel=0; 
doc.content=self->edwin.doc; 
doc. len=self-—>edwin.clen=p_send2 (self-—>edwin.doc, O_EP_SENSE_LEN) +1; 


Subclasses of zpw1n (or other window objects providing edit-like windows) may wish to alter some of the 
values of these fields, to achieve affects such as paragraphs with associated labels. 


The five soft method numbers in SCRLAY_DOC 


It is time to point out one key design decision embodied in the relationship between scrimc, scRLay, and 
EPpoc. In fact, scrnay only ever communicates with the document object via the five soft methods (also 
known as “call-backs’’) whose numbers are contained within the scruay_poc data structure. And scrimeG 
never communicates directly with the document object. (For example, when scrime needs to display text 
on the screen - eg in response to a redraw request - it reads the characters it has to draw, by sending the 
associated scrLay object an s1_read message.) 


This allows for diverse powerful objects to be built up, using scrLay and scrimc as components. In some 
cases, the associated document object will continue to be a subclass of Eppoc - as is always the case when 
EDWIN is involved. But there is no fundamental requirement for this to be the case. Instead, the only 
requirement is to provide methods for some of the slots in scRLAY_poc. 


The SENSECHARS call-back 
The protocol of the sensechars call-back can be seen from the following excerpt from scriay code: 


GLDEF_C VOID SenseChars (SCRLAY_SENSECHARS *ps,SCRLAY_FONT **pf,UBYTE **pfw) 
{ 


p_send5 (DatScrlay-—>scrlay.doc.content, DatScrlay-—>scrlay.doc.sensechars,ps,pf,pfw); 


} 


12 - 30 


12 EDIT WINDOWS 


This uses the scRLAY_SENSECHARS Struct which is defined as follows: 


typedef struct 
{ 


UWORD pos; document position to sense 
WORD printer; TRUE for printer data else screen data 
TEXT *buf; address of character block 
WORD blen; length of character block 


} SCRLAY_SENSECHARS; 


One concrete realisation of the sensechars call-back is provided by the epdoc_sense_chars method of the 
EPDoc class: 


METHOD VOID epdoc_epdoc_sense_chars (PR_EPDOC *self,SCRLAY_SENSECHARS *sense) 
{ 
sense->blen=p_send5 (self, O_EP_SENSE_CHARS, 
&sense—>buf, sense-—>pos, WS_MAX_PRINT_BOX_TEXT_LEN) ; 


} 


It will be immediately apparent that this realisation of sensechars has ignored the final two parameters, 
pf and pfw. That is entirely deliberate. The reason for this is that, prior to scrLay sending the 
sensechars message, it pre-loads the pf and pfw variables to point to the corresponding global style 
fields, inside the scrLay_styLe data structure. Accordingly, for editors in which there is no local 
variation in style, there is no need for the sensechars call-back to write to the passed pf and pfw 
variables. 


For interest, here is an extract from the sensechars call-back provided by the Word Processor document 
class (known simply as doc): 


METHOD INT doc_epdoc_sense_chars(PR_DOC *self,SCRLAY_SENSECHARS *pss, 
SCRLAY_FONT **pf,UBYTE **pfw) 
{ 
TAGLIST_ITEM *pi; 
LOG_POSITION *plog; 
UINT len; 
UBYTE *pw; 


pi=doc_dc_sense_position(self,pss-—>pos) ; 
plog=(&self—->doc.t->taglist.lpos) ; 
if (pf) /* if pss->printer is TRUE, get printer data */ 
{ 
if (pss->printer) 
{ 
SensePrnFont (pi->par,pi->phr, &self—>doc.pf); 
*pf=&self->doc.pf; 
} 
else 
{ 
pw=SenseScrFont (self,pi->par,pi->phr, &self—>doc.sf); 
*pf=&self->doc.sf; 
} 
} 
if (pfw) /* if pss->printer is TRUE, get printer data */ 
{ 
if (pss->printer) 
*pfw=SensePrnWidthTable(self,pi-—>par,pi->phr) ; 
else 
*pfw=pw; 
} 
len=pi->link-plog->offset; 
pss->blen=p_send5 (self, O_EP_SENSE_CHARS, &pss->buf,pss-—>pos, len); 
return (pss—>blen==len) ; 


} 
Without going into details, some points can be noted: 


e the significance of the printer field in the scrLAy_sENSECHARs Struct is that, if it is TRUE, printer 
font width tables and printer font details should be provided - otherwise data for screen display 


e atest should be made on pf and pfw, in case they are nut, which means that no data should be 
written to them in that case. 


12 - 31 


OBJECT ORIENTED PROGRAMMING GUIDE 


Structure of SCRLAY font width tables 


Reference has been made above to font width tables. As can be seen, the default value of the font width 
table pointer fwtab - as set up IN edwin_wn_init - is actually nunu. This clarifies that an explicit font 
width table is not always required. In this case, widths of pieces of text are calculated by calling the 
Window Server function gtextwidth. But for some purposes, having a font width table on the client side 
significantly increases the speed at which formatting can take place. Furthermore, for printing purposes, 
having font width tables loaded is essential. See the Printing chapter in this manual for more information 
about font width tables. 


The TOPARST call-back 


The protocol of the toparst call-back can be seen from the following excerpt from scruay code: 


LOCAL_C VOID ToParStart (UWORD *pos) 
{ 


p_send3 (DatScrlay-—>scrlay.doc.content,DatScrlay->scrlay.doc.toparst,pos) ; 


} 


In contrast to the sensechars call-back, this is a completely straightforward routine, with only one 
parameter, which is a pointer to a document offset that needs to be converted to that for the start of the 
paragraph containing it. 


The implementation of toparst by Eppoc is as follows: 


METHOD VOID epdoc_epdoc_para_start (PR_EPDOC *self,UWORD *ppos) 

/* 

Convert *ppos to point to the beginning of the paragraph containing *ppos 

*/ 
{ 
p_send4 (self, O_EP_SCAN_PARA, ppos, EP_SCAN_BACKWARDS | EP_SCAN_STAY | EP_SCAN_TO_BEGIN) ; 
} 


The ENQPAGE call-back 


The enqpage call-back, if non-zero, is used by scriay to determine whether a page-break is to occur at a 
given line in a paragraph. This fact is indicated by setting the new_page field TRuz, in the corresponding 
SCRLAY_LINE data structure (see earlier in this chapter for the definition of this structure). 


By default, this call-back is left as zero, in edit windows, but Epwrn sets it to epdoc_eng_page if the 
initialisation flag IN_EDWIN_PAGINATABLE Is Set. 


A full discussion of the operation of epdoc_eng_page requires detailed knowledge of the Eppoc class. 


The SENSEPDATA call-back 


The sensepdata call-back plays a role analogous to sensechars call-back: it caters for local variations in 
paragraph styling (whereas sensechars Caters for local variations in phrase emphasis). 


The protocol for the sensepdata call-back can be seen from the following extract from scriay code: 


if (DatScrlay->scrlay.doc.sensepdata) 
p_send5 (DatScrlay-—>scrlay.doc.content, DatScrlay-—>scrlay.doc.sensepdata, 
posl,DatScrlay->scrlay.st.printer, &DatScrlay-—>scrlay.st.pd); 


In other words, if the call-back is non-zero, the document object has to provide the scrLay_ppata data for 
the paragraph containing the document offset posi (and paying due respect to whether the printer 
parameter is TRUE Or FALSE). 


12 - 32 


12 EDIT WINDOWS 


For interest, here is how the Word Processor doc class provides the sensepdata Call-back: 


METHOD VOID doc_dc_sense_pdata(PR_DOC *self,UINT pos,INT printer,SCRLAY_PDATA *pd) 


/* 


Sense the margin, tab and spacing data for the current paragraph. 
If printer is TRUE, return printer values. 


af 


{ 
PAR_STYLE *par; 


par=doc_dc_sense_position(self,pos) —>par; 

if (!printer) 
{ 
SenseScrTabs (self,par, &self-—>doc.tabs) ; 
pd->margins=&par->scr_mar; 
} 

else 
{ 
SensePrntTabs (self,par, &Self—>doc.tabs) ; 
pd->margins=&par->prn_mar; 
pd->spacing=&par->prn_spc; 
} 

pd->tabs=&self->doc.tabs; 

} 


The SENSEPLABEL call-back 


If non-zero, the senseplabe1 call-back is assumed to provide information about labels to be drawn 
alongside the starts of paragraphs, in a left margin area. Two examples of paragraph labels are 


field names in the Database application 


style short-codes in the Word Processor application. 


The protocol of the call-back can be seen from the Word Processor implementation of the method: 


METHOD VOID doc_dc_sense_plabel(PR_DOC *self,UINT cpos,INT printer, SCRLAY_PLABEL 
k* 
ppl) 


{ 
PAR_STYLE *par; 
XWP_LAY *pxlay; 


pxlay=SenseLay (self); 

*ppl=&pxlay-—>label; 
par=doc_dc_sense_position(self,cpos) ->par; 
pxlay->label.buf=&par->ph.sc[0]; 
pxlay->label.blen=2; 

} 


As with many of these call-backs, two of the parameters passed are 


the document offset, cpos, of the position of interest 


a flag, printer, specifying whether the information is required for screen-display purposes or for 
printing purposes. 


The final parameter involves the scRLAY_PLABEL struct, whose definition is as follows: 


typedef struct 


{ 
SCRLAY_FONT font; font ID, height (printer only) and style 


UWORD align; alignment 
TEXT *buf; address of character block 
WORD blen; length of character block 


} SCRLAY_PLABEL; 


12 - 33 


OBJECT ORIENTED PROGRAMMING GUIDE 


Some examples of edit-/ike windows 


Admittedly, there is a formidable learning curve to gaining full familiarity with the scope and power of 
the scriay and scrime classes. In particular, despite its length, this chapter has only touched peripherally 
on such topics as page breaks and special support for printing (ie using approaches other than the 
LPRINTER and xPRINTER Classes). 


However, it turns out in practice that many displays can be programmed more quickly and more efficiently 
using scrimc and scruay, than using any alternative mechanisms. Furthermore, in many cases only a 
small amount of the rather detailed full interface to scrimc and scruay needs to be appreciated. 


One example is the “Synonyms” screen in the Spellchecker application. This consists of a list of words 
which can be navigated by standard keypresses. Thus if the word “Bang” is looked up, the screen can 
look like: 


ban 

cities: hit, bell, ringer, bull’s-eye, gong, slam, smash; 

crash, boom, clang, clap, resound, roar, rumble, shake, 
under; 

Bees blast, blare, boom, discharge, noise, pop, report, 


rOar; 

hit, knock, blow, box, bump, chop, clap, conk, crack, crash, 
cuff, impact, jar, jolt, lick, punch, rap, slap, slug, smack, 
smash, swat, swipe, tap, wallop, whack. 

crack, pop, snap, thump; 

dent, hollows, dimple, indent; 


In this example, ROM code handles: 
e automatically wrapping the lists of words at the edge of the window 
e¢ automatically smoothly scrolling the display vertically when needed 
e navigating by word - using the ep_scan_word method 


e displaying the relevant “labels” at the side of each group of words (in this example, “noun” and 
“verb’). 


ROM code even supports the notion of a “hard space”, eg between “bell” and “ringer” on the second line 
in the above screen, to prevent paired groups of words being split over a line. 


Another example worth mentioning is the main display window of the “Berlitz” five-language translator 


@ 


Berlitz 


application: 


Eng pitch-dark fadj? 
Fre knoir comme dans un four 
{adj} 


Ger stockdunkel fad Jj} 
Spa oscuro como bocs de lobo 
{adj} 


{74h 
(Engipitch sav] Thu i6 


General comments on creating edit-like windows 


In all examples like this, a significant proportion of the work is in the initialisation. The various 
initialisation structs - which are quite long - have to be filled in appropriately, and in the correct order. 
But once the system of objects is in place, it largely runs itself. 


The main extra responsibility of the programmer is to pass messages onto the scrime object (and, less 
frequently, to the scriay object) at appropriate times. Many of these messages have already been 
discussed in the course of this chapter, but a few remain to be covered. 


12 - 34 


12 EDIT WINDOWS 


The si_redraw method 


The entire contents of the wn_draw method of Epwtn is to pass on a corresponding message to scRIMG: 


METHOD VOID edwin_wn_draw(PR_EDWIN *self) 


{ 
p_send3 (self—>edwin.scrimg, O_SI_REDRAW, NULL) ; 
} 


The final nui parameter means to redraw the entirety of the region. 


On the SERIES 3a, the final parameter can meaningfully be other than nut (in fact, on the Series 3, the 
final parameter is ignored), in which case it should point to a p_ReEct structure indicating the portion of 
the region that needs to be redrawn. 


The si_emphasize method 


The entire contents of the wn_emphasise method of EpwIn is, again, to pass on a corresponding message to 
SCRIMG: 


METHOD VOID edwin_wn_emphasise(PR_EDWIN *self,INT flag) 


{ 
p_send3 (self-—>edwin.scrimg,O_SI_EMPHASIZE, flag) ; 
} 


Any application that makes direct use of scrimc (ie without the benefit of an intermediate Epw1n object) 
would have to possess a corresponding message. 


The si_pan method 


The si_pan method provides a means for a window display to be scrolled horizontally, even though it is 
not displaying any visible cursor. 


An example of this behaviour is when the user presses RIGHT or LEFT when viewing the “Found” screen in 
the “Wrap off” state of the Database application. 


Code calling the si_pan method will look like 
p_send4 (scrimg, O_SI_PAN, func, par) ; 
The si_pan method actually combines three different functions, depending on the value of func: 


e if func is SCRIMG_PAN_SETNOPAN, the nopan property value of scrimc is set to par (which should 
be either TRUE Or FALSE) 


e if func 1S SCRIMG_PAN_DELTA, the effect is to scroll the display horizontally by par pixels (where 
par can be either positive or negative) 


e if func is ScRIMG_PAN_aBS, the effect is the scroll such that par is at the left of the view. 


Any scrolling is contrained to reasonable limits. For example, the display will not scroll past the right 
hand end of the longest visible line. 


The purpose of the nopan mode is to control whether, any time the view is scrolled vertically, it also tries 
to scroll horizontally (ie to “pan’’) in order to keep the cursor position visible. 


12-35 


CHAPTER 13 


PRINTING 


This chapter describes the basics of access to the WDR print system from HWIM applications. 


See the chapter WDR Printing in the Additional System Information manual for background information 
about the scope of the SIBO WDR print system. 


Although partial access to the WDR print system is available to Hwif programmers, full access is only 
possible via object oriented techniques, such as are explained in this chapter. 


As far as HWIM applications that wish to print are concerned, three different levels of approach can be 
identified, in increasing order of sophistication (and difficulty): 


1. applications create and use a subclass of the HWIM iprinter class, and REPLACE only the one 
method 1pr_sense_text 


2. applications create and use a subclass of 1printer, and REPLACE other methods, such as 
lpr_read 


3. applications avoid using lprinter, and instead interface more directly with classes such as pages 
in the FORM library (the interaction with pages is one of the things that 1printer handles 
automatically, for less demanding applications). 


For many applications, the first of these three levels is perfectly sufficient, and in this case, only a limited 
acquaintance with the material in this chapter is required. 


Print preview 


On the Series 3a, it is relatively straightforward for applications using the WDR print system to provide 
‘Print preview’ menu commands, in addition to ‘Print’ commands. 


The basic step involved, in most cases, is to subclass the xprinter class, rather to subclass 1printer. 
(The xprinter class is in the XADD library and is not available on the Series 3.) The interface to 
xprinter Is very similar to that of lprinter (xprinter is actually a subclass of printer). The vast 
majority of application-level print preview code is exactly the same as the application code that 
implements print. 


The basic model of WDR printing 


Regardless of whether an application implements print preview as well as print, and regardless of the 
level of sophistication the application brings to printing, the first basic requirement, in use of the WDR 
print system, is an understanding of the woR_PRINT struct. This is defined as follows in prdrv.cl: 


typedef struct 


WORD flags; WDR_PRINT_XXX 

WORD typf; Typeface number for WDR_PRINT_FONT 
WORD fheight; Font height for WDR_PRINT_FONT (twips) 
WORD style; Font style for WDR_PRINT_FONT 

WORD down; Line down for WDR_PRINT_LINE 

WORD indent; Line indent for WDR_PRINT_LINE 

WORD height; Line height for WDR_PRINT_LINE 

WORD right; Right movement for WDR_PRINT_RIGHT 
TEXT *buf; Text to print for WDR_PRINT_TEXT 
UWORD blen; Length of data at buf for WDR_PRINT_TEXT 
} WDR_PRINT; 


13-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


(Hwif programmers will recognise this as the same as the h_print struct.) 
Essentially, the basic model of WDR printing can be summarised as follows: 
e the application creates and initialises a suitable top-level printing object 


e in due course, system code repeatedly calls the application back, passing, each time, the address 
of a WDR_PRINT struct 


e the application fills in various fields in this struct, each time, to describe the next “print 
element’, and lets the flow of execution return into system code 


e eventually, printing comes to an end - either because it has finished normally, or because the user 
has cancelled it, or because an error condition has arisen 


e the top-level printing object gets destroyed. 


Thus printing can be viewed as a sequence of “print elements”, each described by a woR_pRINT struct. The 
possible types of print elements can be seen from the list of possible bits that the application can set, each 
time, in the £1ags field of the woR_PRINT struct: 


WDR_PRINT_PAGE this print element is to go on a new page (ie a page break will be forced, if 
not already on a new page) 

WDR_PRINT_LINE this print element is to go on a new line (ie a line break will be forced, if not 
already at the start of a line) 

WDR_PRINT_FONT this print element is to be in a specified font and font style 

WDR_PRINT_RIGHT this print element consists, in part, of moving the print position right by a 


specified amount 


WDR_PRINT_TEXT this print element consists, in part, of text to be printed (in cases where 
WDR_PRINT_RIGHT Is set as well, the movement right takes place before the 
printing of the text) 


WDR_PRINT_END this print element terminates the print process 


WDR_PRINT_KEEP the line containing this print element is to be kept, if possible, on the same 
page as the following print element 


WDR_PRINT_IDLE (for advanced use only - see later). 


Calculation of page breaks 


Note that applications in general have no need to calculate the positions of page breaks, as they print. 
System code, inside the pacrs class in FORM, automatically calculates when page break instructions 
should be emitted to the printer. This calculation takes all the following into account: 


e =©Any explicit instructions from the application, on account of print elements with the bits 
WDR_PRINT_PAGE and/or WOR_PRINT_KEEP set 


e The length of the page, as supplied by the user in the ‘Print setup’ dialog for the application 


e  =The height of each line, and (in effect) the spacing between each line, as supplied in the height 
and down fields in print elements with woR_PRINT_LINE Set. 


System code (in pacEs) also takes care of printing relevant headers and footers at the top and bottom of 
each page - with the contents of the header and footer being supplied by the user in the ‘Print setup’ 
dialog for the application. 


Calculation of line breaks 


The situation as regards calculation of line breaks is rather different from that of the calculation of page 
breaks. Whereas the pacss class handles “vertical” formatting (such as pagination), it is up to other 
software (eg that in LPRINTER) to handle “horizontal” formatting (such as word-wrap). 


Thus the only time pacss repositions the print position back to the beginning of a line is when 
WDR_PRINT_LINE is explicitly set in the flags field of a print element. 


Moreover, Paces never prints any text, when moving horizontally, other than that supplied to it in a print 
element, which contrasts (again) with the case when moving vertically - since headers and footers are 
added in automatically, by paces, whenever they are required. 


13-2 


13 PRINTING 


Printer units 


Values set in the down, indent, height, and right fields in a print element must be supplied in printer 
units. These units vary from printer to printer (corresponding to the different degrees of resolution these 
printers support). Now it might at first seem that having to deal with printer units conflicts with the 
general philosophy of WDR printing, in which application code doesn't have to worry about which printer 
has been selected by the user. However 


e there are system services, such as the 1pr_sense_buf_width method of LpRINTER, to calculate the 
width, in current printer units, of a specified buffer of text 


e there are other system services, such as the wdr_twips_to_xy method of the wor class (see later 
for more details of this class), to assist with the transformation of printer independent units (ie 
“twips”, where there are 1440 twips to an inch) into printer dependent ones 


e simple uses of LPRINTER can avoid the need to specify units altogether, since they can accept the 
default values of the down, indent, height, and right fields in any print element. 


Note that the reason why word wrap and pagination calculations must take place in printer units is to 
avoid unnecessary rounding errors in the use of any other units. 


The difference between INDENT and RIGHT, and between DOWN and HEIGHT 


Superficially, the indent and right fields in the woR_PRINT struct may seem to serve the same purpose. 
However, the indent field is only relevant when the woR_PRINT_LINE bit is set in flags, and the right 
field is only relevant when the woR_PRINT_RIGHT bit is set in flags: 


e when woR_PRINT_LINE is Set, the current print position is moved back to the beginning of the 
line, then moved down by the height of one line, and then moved right by indent 


e when woR_PRINT_RIGHT is Set, the current print position is simply moved right by right (if a 
print element contains both a woR_PRINT_LINE and a WOR_PRINT_RIGHT, the former is executed 
before the latter). 


The indent field is intended for use, as its name implies, as the “left indent” (or “first line indent’’) 
parameter of a paragraph of formatted text, whereas the rignt field is intended for use as the spacing 
between two columns of data, or (more simply) as the representation of tab characters. 


Note that the action of any right movement adds on to the current horizontal offset of the print position - 
as established by earlier indents, rights, and text printing on that line. 


The difference between the down and height fields may also require some attention. Both fields are 
relevant only when woR_PRINT_LINE is set. Normally, the two values are simply added together, and the 
sum is taken as the amount, in printer units, to advance the print position vertically, before starting to 
print the given line of text. However, code in paces automatically zeros the supplied value of down if the 
given line of text would be the first on a page. 


For example, applications that wish to implement some measure of inter-paragraph spacing additional to 
inter-line spacing (or which wish, more generally, to separate related groups of printed lines by extra 
spacing in between these groups) should place this additional spacing in the down field. This will ensure 
that extra spacing is not printed, unnecessarily, at the tops of pages. (Blank spacing at page tops, of this 
form, would be seen, on occasion, if extra spacing between groups of lines were implemented, at the 
application level, simply as blank lines.) 


Margins and page size 


There is no need for application code to attempt to set the indent value in such a way as to include the 
margin at the left hand edge of the page (as set, by the user, inside the ‘Print setup’ dialog for the 
application). Likewise, nor is there any need to try to set the first height or down values to try to include 
the margin at the top of the page. 


Rather, these adjustments are automatically made by system code. In other words, zero is a suitable 
default value for both indent and down. 


As mentioned before, there is no need, either, for applications to determine the length of the printing area 
of the page (ie the total page length, minus the sum of the top and bottom margins), since pagination is 
handled by code in paczs. Nor, in general, does an application that uses LPRINTER need to investigate the 
width of the printing area of the page, since LPRINTER handles all (simple) word-wrap automatically. 


13 -3 


OBJECT ORIENTED PROGRAMMING GUIDE 


The PRINTER class and storage of the ‘Print setup’ dialog settings 


Whenever a standard ‘Print setup’ dialog is invoked in an application, the values displayed and edited in 
this dialog are stored within the property of an instance of the PRINTER class. The PRINTER class is in the 
FORM library and, in addition to allowing these data values to be stored, this class also contains general 
supervisory logic to do with printing. (The printer class also encapsulates knowledge of read/write 
access to the p$? printing environment variables, described in the WDR Printing chapter of the Additional 
System Information manual.) 


Some applications may choose to create a PRINTER instance as part of their standard initialisation. Other 
applications only create one such instance when they are about to: 


e print (or print preview) 
e run the print setup dialog. 


HWIM code relies on the handle of any instance of PRINTER being written to the wserv.printer field 
within the property of w_ws. On the Series 3a, this handle is also written to the appman. spare! field of 
w_am, where it can be accessed by FORM code (eg low level print preview code). 


However, HWIM applications have no need to write, themselves, the handle of the PRINTER object into 
either of these places. This is handled by system code, mainly inside the ws_ens_print_context method 
of wsErv. The contents of this method (which has no parameters) are, effectively, 


METHOD VOID wserv_ws_ens_print_context (PR_WSERV *self) 
{ 


if (!self->wserv.printer) 
self—>wserv.printer=f_newsend (CAT_HWIM_FORM, C_PRINTER, O_PR_INIT) ; 
} 


Note that HWIM code automatically sends w_ws a ws_ens_print_context message in the following cases: 


e at the beginning of the ws_edit_print_context method of wserv - the method which 
applications invoke (see below) to run the standard ‘Print setup’ dialog suite 


e at the beginning of the 1pr_init method of LpRINTER - the method which applications invoke 
(see below) in order to print or to print preview. 


Consequently, most applications never need to call ws_ens_print_context directly. The exception is if 
an application wishes, for various reasons, to maintain an instance of PRINTER at other stages of its 
lifetime. For example, an application may store some of the print context (the property of the PRINTER 
class) to file, and restore that context when the file is opened again. In this case, typical action would be 
for the application to call ws_ens_print_context itself, during its file loading code, and then to set 
various parts of the in-memory print context, using methods of the printer class, passing data from file as 
parameters. (An example of code to achieve this is given near the end of this chapter.) 


Changing font or font style while printing 


One of the aspects of the “print context” is the so-called “default printing font”. Code in LPRINTER sets 
this font (and its associated style) by default into the typf£, fheight, and style fields in the woR_PRINT 
struct. 


Here, note that a font, for printing purposes, is identified by a combination of its “typeface number” (the 
typf value) and its “font height” (the fheight value). For background information about fonts and 
typefaces, see the chapter WDR Printing in the Additional System Information manual. 


Most applications will find it convenient not to adjust the typf or fheight values in any print element. 
However, applications may well wish to augment the style field, on occasion. For example, certain parts 
of the printed output might benefit from being emphasised in bold or in italics. In this case, note that the 
allowed values in the style field are bit combinations of woR_sTYLE_NORMAL, WDR_STYLE_UNDERLINE, 
WDR_STYLE_BOLD, WDR_STYLE_ITALIC, WOR_STYLE_SUPER, and wDR_STYLE_suB - where all the meanings are 
obvious from their names. 


Note also that variant style values should be in general be orred into the default ones supplied by system 
code, rather than completely overwriting these values. This is to preserve the freedom of the user, in the 
‘Print setup’ dialog, to choose a “default printing font” with style other than normal. 


13-4 


13 PRINTING 


Applications that support multiple fonts (or multiple font heights) should be aware of the set of fonts (and 
font heights) supported by the current printer model (eg Epson, HP, Postscript), as selected by the user 
in the ‘Print setup’ dialog. Thus if the application provides the user with a “palette” of possible “character 
styles” (or whatever), the set of fonts from which the user is allowed to choose ought to match the set 
known to the loaded printer model. There are methods of the wor class that allow access to this 
information (see later in this chapter for more details). 


The text referenced in a print element 


There are a couple of potential errors that applications should watch out for, regarding the lifetime of the 
buffer referenced by the buf and bien fields of a print element. One is somewhat obvious; the other less 
so (but it only applies to applications that make explicit use of the woR_PRINT_KEEP flag). 


In the first place, this buffer must, obviously enough, continue in existence after return of the application 
callback routine (eg the 1pr_sense_text method) that sets up the print element. Thus it would be a grave 
error to assemble a print buffer on the stack of this routine. 


More subtly, consider a line of printed output with the wor_PRINT_KEEP flag set. Clearly, system code 
cannot print this line straightaway, on the current page of output, since it must first process at least one 
additional line, to see whether the first line should instead be held over for a new page (eg if there is no 
room to place this line and the following one on the current page). For this reason, any print buffer 
associated with a line with woR_PRINT_KEEP set must have a more permanent existence, and can only be 
re-used with due care. 


Limitations with the WODR_PRINT_KEEP flag 


Note incidentally that the wor_pRINT_kKEEP flag only has effect if it is set in the first print element in a line 
of output. Setting this flag for print elements other than those which also contain woR_PRINT_LINE has no 
effect. 


Applications which require more complicated pagination scenarios will need to perform a “pagination 
pass” prior to actually printing (this happens, in different ways, in both the Series 3 Word Processor and 
the Series 3 Spreadsheet) and will thereafter only set the woR_PRINT_pPaGE flag, and never the 
WDR_PRINT_KEEP flag. 


The need to specify font and style for each line 


One more potential problem should be pointed out (though this will be of concern only to programmers 
who interact more deeply with the paczs class). On the face of things, if the font never changes, 
throughout the course of printing, there oughtn't to be any need to specify the typf, fheight, and fstyle 
values anew for every print element. However, it must be borne in mind that two print elements which the 
application regards as being contiguous may, in the actual course of printing, end up on two different 
pages, with a footer and a header separating them. Given that the user is, in general, free to specify the 
print fonts of the header and the footer to differ from that used in the body of the page, it can now be 
appreciated why, as far as pacEs is concerned, it is important for application-generated print elements to 
have their print font specified explicitly each time. 


Programmers needn't worry about any particular inefficiency this might entail. Font change instructions 
are sent to the printer itself only when there is an actual change in between two adjacent print elements. 


Use of WDR_PRINT_IDLE 


A small proportion of applications may find themselves unable to generate print elements immediately (ie 
sufficiently quickly) in response to a system callback. For example, fetching and assembling the data to 
print may involve one or more application-specific active objects. This kind of application may wish to 
make use of print elements with woR_PRINT_IDLE Set. 


If paces finds that woR_PRINT_IDLE is set in any print element, the rest of that print element is disregarded 
and the print subsystem is effectively placed into a state of suspension. Thus no more application 
callbacks will take place until the print system is restarted by an explicit application call. 


The way that the application lets system code know to restart the print subsystem is to send the paces 
object an ao_queue message. Applications using LPRINTER can find the handle of the paces object in the 
lprinter.pages field in its property. 


13-5 


OBJECT ORIENTED PROGRAMMING GUIDE 


Using LPRINTER for standard printing purposes 


The printing requirements of most applications can be met by them defining an application-specific 
subclass of LPRINTER, in which they 


e ~=REPLACE the method ipr_sense_text (which is pererred at the LPRINTER level) 
e declare sufficient property to be able to keep track of the progress of printing. 
Then when the user invokes the ‘Print’? menu command in the application: 


e the application may choose to present a dialog (a so-called ‘Print details’ dialog), to collect 
parameters describing which portions of its data are to be printed, and (possibly) with what 
options 


e next, the application creates its subclass of LPRINTER and sends it an 1pr_init message 


e this message does not return until printing has completed (internally, a call to am_start is 
made); accordingly, the next lines of code can destroy the LPRINTER subclass object 


e however, in the meantime, the ipr_sense_text message will have been called repeatedly. 
Thus typical command manager code, in the print method, might look like 


RunPrintDetailsDialog(); 
hDestroy (f_newsend (CAT_APP_APP, C_APP_LPRINTER, O_LPR_INIT) ); 


Note: this code fragment assumes that the results of the “print details” dialog is available to the LPRINTER 
subclass via global data. An alternative approach would of course be to REPLACE the ipr_init method 
too, so that the command manager code would become something along the lines of 


PRINT_DETAILS_RBUF rbuf; 


RunPrintDetailsDialog(&rbuf) ; 
hDestroy (f_newsend (CAT_APP_APP, C_APP_LPRINTER, O_LPR_INIT, &rbuf) ); 


with the contents of the 1pr_init method looking like 


METHOD VOID app_lprinter_lpr_init (PR_APP_LPRINTER *self,PRINT_DETAILS_RBUF *prbuf) 
{ 
self—>app_lprinter.prbuf=prbuf; 
p_supersend2 (self,O_LPR_INIT); /* calls am_start */ 
} 


The syntax of the LPR_SENSE_TEXT callback 


(Hwif programmers may note that this is the same as the syntax of the printLine callback function, where 
PrintLine is the function name passed as the parameter to nhprint.) 


This method passes the single parameter pr, which is a pointer to a woR_PRINT data structure. Application 
code adjusts one or more or the fields in *pr, as described above. 


The return value from 1pr_sense_text has the following significance: 


e a zero return value means that all print elements have already been defined, and that the print 
subsystem should now terminate 


e¢ anon-zero return value means that the application has not finished printing yet, and expects 
additional calls to ipr_sense_text to be made in due course. 


Note that LPRINTER fills in many of the parts of «pr before sending seit the 1pr_sense_text message: 
e the flags field is set to woR_PRINT_FONT | WDR_PRINT_LINE |WDR_PRINT_TEXT 


e the typf, fheight, style, and height fields are all set to values reflecting the default font and 
default font style (which the user can specify in the ‘Print setup’ dialog suite 


e =the down and indent fields are both set to zero. 


Thus all that needs to be filled in, in most cases, are the but and bien fields. On some occasions, the 
value of flags may need to be adjusted; likewise the down, indent, and right fields. Finally, a value will 
need to be provided for right in any case when wOR_PRINT_RIGHT is Set in flags. (See below for some 
examples.) 


13 - 6 


13 PRINTING 


LPRINTER and word-wrap 


As mentioned above, LPRINTER takes care of word-wrap automatically on behalf of applications: if the text 
at pr->buf is too wide to fit on the space remaining to the end of the line, LpRINTER splits it up into two or 
more sections, and thus creates two or more print elements (without any intervention being required to 
this end from the application). For the print elements formed in this way: 


e for new lines, the indent value is taken from the subsqind field within LpRINTER property 
(subclasses can write directly to this field within application code - see below for an example) 


e unless the user has disabled widows/orphans control in the ‘Print setup’ dialog suite, the flag 
WDR_PRINT_KEEP is orred into all but the last of these print elements. 


Note that the word-wrap calculation presupposes use of the default font and the default style throughout. 
If an application prints using multiple fonts or font-styles, it may need to subclass the 1pr_read method of 
LPRINTER, instead of the 1pr_sense_text method - see later for more details. 


Working out widths 


If, unusually (eg to support printing in columns), an application needs to know more about relative widths 
of various strings of text, the 1pr_sense_buf_width method may be used. This takes two parameters: 


¢ aTExt* pointer to the buffer of text 
¢ an InT giving the length of the buffer. 
This method returns the width of the buffer, in current printer units. 


Note that this method cannot be accessed until inside one of the 1pr_sense_text callbacks - since it relies 
on fields in LPRINTER property that are not set up until just before the first such callback. 


Note also that this calculation presupposes use of the default font and the default style. See later for how 
to calculate widths of text in other fonts and styles. 


Another width value that may be of interest to applications is stored in the width property field of 
LPRINTER. This is the width, in printer units, of the printing area of the page (ie the complete page width 
less its left and right margins). Again, this value is not available prior to the first callback to 
lpr_sense_text. 


Launching the print setup dialog suite 


If (as normal) an application that supports print also supports a ‘Print setup’ menu command, the code 
that is required in the command manager method for print setup is usually just the single line: 


p_send2 (w_ws, O_WS_EDIT_PRINT_CONTEXT) ; 


For interest's sake, the entire contents of the ws_edit_print_context method of wsErv is given here: 


METHOD VOID wserv_ws_edit_print_context (PR_WSERV *self) 
{ 
p_send2 (self,O_WS_ENS_PRINT_CONTEXT) ; 
runHwimDialog (-SYS_PRINT_CONTROL_DL, C_PRNCTRL, NULL) ; 
} 


Examples of use of LPRINTER 


This section presents two related examples of the use of LpRINTER. Both are complete applications in their 
own right. Each example allows the user to specify a start date, and a number of days, and then prints the 
list of dates specified, in expanded form, eg as follows: 


Monday, 18th October 1993 
Tuesday, 19th October 1993 
Wednesday, 20th October 1993 


The user is also able, in each case, to specify a “separation” between months, which can be either “No 
gap’, “Half a line”, or “Complete line”. Also in each case, the user is given an opportunity, before the 
printing actually takes place, to alter the values in the ‘Print setup’ dialog. 


13-7 


OBJECT ORIENTED PROGRAMMING GUIDE 


The second example builds on the first, adding additional formatting to produce printed output in two 
columns, with effect as in: 


Monday, 18th October 1993 
Tuesday, 19th October 1993 
Wednesday, 20th October 1993 


On installation of the OOP component of the SDK, the source code for the second example is copied into 
a \sibosdk\tprint directory. 


Framework of the example applications 


Unusually, these applications have no menu bar or client window. This is possible because, throughout 
their lifetimes, a dialog is always presented: 


e first, the ‘Print details’ dialog of the application 
e = next, the ‘Print setup’ dialog 
e finally, the HWIM ‘Printing’ dialog, that keeps track of the progress of printing. 


In implementation terms, all this happens inside the ws_dyn_init initialisation callback to the wsERv 
subclass of the application. Code never returns from here, since, after the printing is complete, a call to 
p_exit 1s made. 


The entire contents of the category file, demo.cat, of the first example, are as follows: 


IMAGE demo 


EXTERNAL olib 
EXTERNAL hwim 


INCLUDE hwimman.g 
INCLUDE dlgbox.g 
INCLUDE lprinter.g 
INCLUDE time.g 


CLASS dewserv wserv 
{ 
REPLACE ws_dyn_init 
} 


CLASS dedlg dlgbox 
{ 
REPLACE dl_key 
TYPES 
{ 
typedef struct 
{ 
UWORD dayno; 
UWORD ndays; 
UWORD gap; 
} RBUF_LP; 


} 


CLASS delp lprinter 
{ 
REPLACE lpr_init 
REPLACE lpr_sense_text 
PROPERTY 1 
{ 
VOID *time; 
RBUF_LP rbuf; 
TEXT buf [LN_TIME_DATE_STR]; 
} 


13-8 


13 PRINTING 


From this, it can be seen that there are four key objects in the application: 
e =the dewserv object, which provides the ws_dyn_init method 


the pepe object, which supervises the ‘Print details’ dialog of the application 


e the pELp object, which is a subclass of LPRINTER 


¢ atime object, which is used to obtain the textual representations of the dates printed. 


The ‘Print details’ dialog 
The following resources define the ‘Print details’ dialog of the application (in demo.rss): 


RESOURCE MENU delp_gap_chlist 

{ 

items = 
{ 
CHOICE_ITEM { str="No gap";}, 
CHOICE_ITEM { str="Half a line"; }, 
CHOICE_ITEM { str="Complete line"; } 
he 

} 


RESOURCE DIALOG delp_dlg 
{ 
title="Print list of days"; 
flags=DLGBOX_NOTIFY_ENTER | DLGBOX_RBUF_FILLED; 
controls= 
{ 
CONTROL 


{ 
class=C_DTEDIT; 


prompt="Start date"; 
info=DTEDIT { flags=IN_DTEDIT_DDMMYYYY; }; 
hy 

CONTROL 


{ 
class=C_NCEDIT; 
prompt="Number of days to print"; 
info=NCEDIT 
{ 
low=10; 
current=20; 
high=200; 
}; 
hy 
CONTROL 
{ 
class=C_CHLIST; 
prompt="Space between months"; 
info=CHLIST { rid=delp_gap_chlist; }; 
} 
M; 
} 


When run, the dialog looks like 


Print list of days 


‘Start date FE} 16-1993 


"Humber of days to print 2A 
"Space between months Ho gap 


13-9 


OBJECT ORIENTED PROGRAMMING GUIDE 


Code in the di_key method of the piGBox subclass senses the values in the fields in this dialog into an 
RBUF_LP result buffer: 


#include <demo.g> 
#include <hwim.h> 


#pragma METHOD_CALL 


METHOD INT dedlg_dl_key (PR_DLGBOX *self) 
{ 
RBUF_LP *prbuf; 


prbuf=self-—>dlgbox.rbuf; 
prbuf->dayno=hDlgSenseDtedit (1); 
prbuf->ndays=hDlgSenseNcedit (2) ; 
prbuf->gap=hDlgSenseChlist (3) ; 
return (WN_KEY_CHANGED) ; 

} 


Startup code and WS_DYN_INIT code 


This dialog code is invoked (indirectly) from the code in the ws_dyn_init method of the application: 


#include <demo.g> 
#include <demo.rsg> 
#include <hwim.h> 


LOCAL_C INT LaunchDialog(INT class, INT resid,VOID *rbuf) 
{ 
DL_DATA dld; 


dld.id=resid; 

dld. rbuf=rbuf; 

dld.pdlg=NULL; 

return hLaunchDial (CAT_DEMO_DEMO, class, &dld) ; 
} 


#pragma METHOD_CALL 


METHOD VOID dewserv_ws_dyn_init (PR_DEWSERV *self) 


{ 
RBUF_LP rbuf; 


if (LaunchDialog(C_DEDLG, DELP_DLG, érbuf) ) 
{ 
p_send2 (self,O_WS_EDIT_PRINT_CONTEXT) ; 
hDestroy (f_newsend (CAT_DEMO_DEMO, C_DELP, O_LPR_INIT, &rbuf) ); 
} 
p_exit (0); 
} 


In turn, this code is invoked (again indirectly) from that in main: 


#include <demo.g> 


GLDEF_C VOID main(VOID) 
{ 
IN_HWIMMAN app; 
IN_WSERV wserv; 


p_linklib(0); 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE|FLG_APPMAN_CLEAN; 
app.wserv_cat=p_getlibh (CAT_DEMO_DEMO) ; 

app.wserv_class=C_DEWSERV; 

wserv.com_cat=p_getlibh (CAT_DEMO_HWI™) ; 

wserv.com_class=C_COMMAN; 

p_send4 (p_new (CAT_DEMO_HWIM, C_HWIMMAN) ,O_AM_INIT, &app, &wserv) ; 

} 


13 - 10 


13 PRINTING 


Note incidentally that the resource file for this application only contains three resources in all: the two 
listed above, which define the ‘Print setup’ dialog, plus a “dummy” wseERv_1nro resource defined as 
follows: 


#include <hwim.rh> 
#include <hwim.rg> 


RESOURCE WSERV_INFO demo_accs 
{ 
menbar_id=0; 
first_com=0; 
accel={'x'}; 


} 


(Although the application has no true menu bar - since it never returns to a “base state” without a dialog 
being present - it is a requirement of alJ HWIM applications that the first resource in their own resource 
file is a wsERv_InFo. This resource is loaded by system startup code, and must be present.) 


The LPRINTER initialisation code (first example) 


The LPRINTER subclass initialisation code has two parts: 
e that which takes place outside of any 1pr_sense_text callback 


e that which takes place during the first callback to 1pr_sense_text (by which time, all required 
system initialisation will be complete). 


These take place, respectively, in the routines delp_lpr_init and InitTimeobject, with the former being 
called from code in the ws_dyn_init method (see above), and the latter from code in 1pr_sense_text (see 
below): 


#include <demo.g> 


#define TIME_FMT_FLAGS (PR_TIME_MONTH NAME | PR TIME_SUFFIX_NAME | PR_TIME_DAY_NAME) 


LOCAL_C VOID InitTimeObject (PR_DELP *self) 
{ 
P_DAYSEC ds; 
SE_TIME_ FORMAT fmt; 


ds.day=self->delp.rbuf.dayno; 

ds.sec=0; 

self—>delp.time=f_new (CAT_DEMO_OLIB,C_TIME) ; 

p_send4 (self-—>delp.time, O_TO_SET, SET_TIME_DAYSEC, &ds) ; 

fmt .flags=TIME_FMT_FLAGS; 

p_send4 (self->delp.time, O_TO_SET_FORMAT, &fmt, TIME_FMT_FLAGS) ; 


} 


#pragma METHOD_CALL 


METHOD VOID delp_lpr_init(PR_DELP *self,RBUF_LP *prbuf) 
{ 
self—>delp.rbuf=(*prbuf) ; 
p_supersend2 (self,O_LPR_INIT); 
} 


13-11 


OBJECT ORIENTED PROGRAMMING GUIDE 


The LPR_SENSE_TEXT method (first example) 


METHOD INT delp_lpr_sense_text (PR_DELP *self,WDR_PRINT *pr) 


{ 
P_DATE dt; 


if (!self->delp.time) 
InitTimeObject (self); 
else if (!self—->delp.rbuf.ndays) 
return (FALSE) ; 
else 
p_send4 (self—>delp.time,O_TO_ADD_DAYS,1,0); 
p_send4 (self-—>delp.time, O_TO_SENSE, SENSE_TIME_DATESTR, &self-—>delp.buf[0]); 
pr->buf=(&self—>delp.buf[0]); 
pr->blen=p_slen(pr->buf) ; 
if (self->delp.rbuf.gap) 
{ 
p_send4 (self—>delp.time, O_TO_SENSE, SENSE_TIME_DATE, &dt) ; 
if (!dt.day) 
{ 
pr->down=pr->height; 
if (self->delp.rbuf.gap==1) 
pr->down>>=1; 


} 
self—>delp.rbuf.ndays--; 
return (TRUE); 

} 


Second example: additional initialisation code 

To modify the example so that the printing takes place in columns, all that has to change is 
e the definition of pzLp in the category file 
e the pexp code (of course) 
¢ one new sTRING resource is added to the resource file. 


The additional initialisation determines the values of some new property fields in pELp. The new class 
definition of pzLp becomes 


CLASS delp lprinter 
{ 
REPLACE lpr_init 
REPLACE lpr_sense_text 


CONSTANTS 
{ 
DELP_STATE_COL1 0 
DELP_STATE_COL2 1 
DELP_STATE_COL3 2 
} 

PROPERTY 1 


{ 

VOID *time; 

UWORD colwid; 

UWORD right; 

UWORD state; 

RBUF_LP rbuf; 

TEXT dname [E_MAX_DAY_NAME] ; 
TEXT buf [LN_TIME_DATE_STR]; 
} 


13 - 12 


13 PRINTING 


The new initialisation routine FindWidthFirstColumn, called from within the first visit to 
delp_lpr_sense_text (Just after Init TimeObject is called) calculates the required width, in printer units, 
for the first column of printed output. This works as follows: 


LOCAL_C VOID FindWidthFirstColumn(PR_DELP *self) 
{ 
INT i; 
TEXT buf [E_MAX_DAY_NAME]; 
UWORD wid; 


for (i=0; i<7; itt) 
{ 
p_nmday (&ébuf[0],i); 
wid=SenseBufWidth (self, &buf[0]); 
if (wid>self-—>delp.colwid) 
self—>delp.colwid=wid; 
} 
self->delp.colwid+=SenseBufWidth(self," "); /* two spaces */ 
if (self->delp.colwid>(3*self->lprinter.width/4) ) 
{ 
hinfoPrint (DELP_PAPER_NARROW) ; 
p_sleep (20); 
p_leave (RUN_ACTIVE_CLEANUP_NONOTIFY) ; 
} 
self—>lprinter.subsqind=self->delp.colwid; 


} 


The routine senseBufWidth, used within rindwidthFirstColumn, 1s just a convenience layer over the 
lpr_sense_buf_width method of LpRINTER: 


LOCAL_C UINT SenseBufWidth(PR_DELP *self,TEXT *pb) 
{ 
return (p_send4 (self,O_LPR_SENSE_BUF_WIDTH, pb, p_slen (pb) )); 
} 


Note one more feature of the code in FindWidthFirstColumn - the check that the width required for the 
first column does not leave too little room left for the remaining column. A more professional application 
may wish to take more sophisticated action in this kind of situation. (For example, the Series 3 
Spreadsheet application performs a pre-printing “pagination” run that determines how many pages 
horizontally are required, to print a given range of spreadsheet columns.) 


The three states in printing a two-column display 


In this example, where there are only two columns per date to be printed, the process of printing each date 
string is split into three separate visits to the lpr_sense_text method, resulting in three different print 
elements: 


e the first visit prints the text of the first column: since this starts a new line, the default PRINTER 
flags of woR_PRINT_LINE and woR_PRINT_TExT are left as they are; however, the width of the text 
actually printed (which will not exceed that of the column itself) is determined, by making 
another call to senseBufWidth, in order that the amount by which the print position should be 
moved right, in the next print element, can be known 


e the second visit consists just of moving the print position forward from the end of the text printed 
in the first column to where the text in the second column should start; the default flags 
WDR_PRINT_LINE and woR_PRINT_TEXT must be removed in this case, and the flag 
WDR_PRINT_RIGHT Set instead 


e finally, the third visit consists of printing the text for the second column; in this case, the flag 
WDR_PRINT_LINE has to be removed, but woR_PRINT_TEXT remains. 


(The way this mechanism would be extended to printing more than two columns should be clear enough.) 


The LPRINTER subclass keeps track of what it has to do next, in any particular callback, by using the state 
property field, which rotates around the three possible pELP_sTaTE_xxx values. 


13 - 13 


OBJECT ORIENTED PROGRAMMING GUIDE 


The code for the entire 1pr_sense_text method therefore becomes 


METHOD INT delp_lpr_sense_text (PR_DELP *self,WDR_PRINT *pr) 
{ 
P_DATE dt; 
P_DAYSEC ds; 


if (!self->delp.time) 
{ 
InitTimeObject (self); 
FindWidthFirstColumn (self) ; 
} 
else if (!self-—>delp.rbuf.ndays) 
return (FALSE) ; 
switch (self—>delp.state++) 
{ 
case DELP_STATE_COL1: 
if (self->delp.rbuf.gap) 
{ 
p_send4 (self-—>delp.time, O_TO_SENSE, SENSE_TIME_DATE, &dt) ; 
if (!dt.day) 
{ 
pr->down=pr->height; 
if (self->delp.rbuf.gap==1) 
pr->down>>=1; 


} 
p_send4 (self—>delp.time, O_TO_SENSE, SENSE_TIME_DAYSEC, &ds) ; 
p_nmday (&self-—>delp.dname[0],P_WEEK(ds.day) ); 
self—>delp.right=self—>delp.colwid-SenseBufWidth (self, &self—>delp.dname[0]); 
pr->buf=(&self—>delp.dname[0]); 


break; 
case DELP_STATE_COL2: 
pr->flags&=(~ (WDR_PRINT_LINE|WDR_PRINT_TEXT) ); 


pr->flags |=WDR_PRINT_RIGHT; 
pr->right=self—>delp.right; 
return (TRUE) ; 

case DELP_STATE_COL3: 
p_send4 (self-—>delp.time, O_TO_SENSE, SENSE_TIME_DATESTR, &self—>delp.buf[0]); 
pr->buf=(&self—>delp.buf[0]); 
pr->flags&=(~WDR_PRINT_LINE) ; 
pr->indent=self-—>delp.colwid; 
self—->delp.rbuf.ndays--; 
p_send4 (self—>delp.time,O_TO_ADD_DAYS,1,0); 
self—>delp.state=DELP_STATE_COL1; 
} 

pr->blen=p_slen(pr->buf) ; 

return (TRUE) ; 

} 


More details about printing in columns with LPRINTER 


As mentioned above, LPRINTER checks all text passing through it, to ensure that it fits within the 
appropriate part of the page width. In the context of the above example of printing in columns, the text in 
the first column is guaranteed always to fit (otherwise the check on the relative width of the first column 
and the page width would have failed). However, it is certainly possible that the text in the second 
column might end up being wrapped. It was with an eye on this possibility that the following two lines of 
code were included in pELp code above: 


e self->lprinter.subsqind=self->delp.colwid; (iN FindWidthFirstColumn) 


@ = pr->indent=self->delp.colwid; (in the DELP_STATE_coL3 case in the 1pr_sense_text 
method). 


The first of these lines of code tells the superclass LpRINTER code that, if text passed to it ever does need to 
be word-wrapped, the second line should be positioned with a “subsequent indent” (subsqina value) as 
specified. (Were this left at its default value of zero, any new line required would start off in the space 
intended to be reserved for the left hand column.) 


13-14 


13 PRINTING 


The second of these lines of code is somewhat more obscure. The point is that LPRINTER code does not 
track the horizontal offset where the print position has reached. For this reason, in any word-wrap 
calculation, the only sensible width value for LPRINTER to wrap text within is equal to the expression 


self—>lprinter.width-pr->indent 


Now in principle LPRINTER could be used to create printed displays of the “hanging bullet” variety, in 
which text in the “left-hand column” always fits on just one line, whereas text in the “right-hand column” 
is typically longer, and can flow over several lines. However, a couple of words of caution are appropriate 
here: 


¢ support for this kind of “hanging bullet” printing by LpRINTER Is only available, effectively, in the 
Series 3a version of the ROM code 


e in any case, no “widows and orphans” control takes place, since, as mentioned earlier, the 
WDR_PRINT_KEEP flag is effective only when present in the first print element of a line. 


In short, programmers requiring this kind of printed output would be best advised to dig deeper into the 
WDR print system possibilities - such as are featured in the remaining sections of this chapter. 


Advanced uses of LPRINTER - and beyond 


In this section, more features of the code in LPRINTER are gradually introduced - up to the point where it 
should be possible to see how to print without any use of LPRINTER - should that be desired. 


This section can be skipped altogether by programmers whose needs are met by the information in the 
preceding sections. 


The LPR_READ method of LPRINTER 
Consider the code in the ipr_read method of LPRINTER: 


METHOD VOID lprinter_lpr_read(PR_LPRINTER *self,INT x,WDR_PRINT *pr) 
{ 
if (!self->lprinter.wdr) 
ReadWdrData (self); 
if (self—->lprinter.defer.blen) 
{ 
*pr=self—>lprinter.defer; 
pr->indent=self-—>lprinter.subsqind; 
} 
else 
{ 
pr->flags=WDR_PRINT_FONT |WDR_PRINT_LINE WDR_PRINT_TEXT; 
pr->typf=self->lprinter.f.fid; 
pr->fheight=self->lprinter.f.height; 
pr->style=self->lprinter.f.style; 
pr->height=self->lprinter.lheight; 
pr->down=0; 
pr->indent=0; 
if (!p_send3(self,O_LPR_SENSE_TEXT,pr) ) 
{ 
pr->flags=WDR_PRINT_END; 
return; 
} 
} 
self—>lprinter.defer=(*pr) ; 
if (pr->indent>=self-—>lprinter.width) 
pr->indent=0; /* guard against incorrect use */ 
pr->blen=fit_line(self-—>lprinter.wtab, pr->buf,pr->blen, 
self—>lprinter.width-pr-—>indent, GetTextPrinterWidth) ; 
if (pr->blen==self—>lprinter.defer.blen) 
self—>lprinter.defer.blen=0; 
else 
{ 
self—>lprinter.defer.blen-=pr-—>blen; 
self—>lprinter.defer.buft+=pr—>blen; 
self->lprinter.defer.flags|=WDR_PRINT_LINE; /* line added for Series 3a 
version */ 
if (!((PRINTER_PARAMS *) 
p_send2 (w_ws-—>wserv.printer,O_PR_GET_PARAMS) ) ->d.wo_control) 
pr->flags|=WDR_PRINT_KEEP; 


13 - 15 


OBJECT ORIENTED PROGRAMMING GUIDE 


As can be seen, this is the routine which contains the call to 1pr_sense_text - the deferred method that 
all LPRINTER subclasses have to provide. The role of 1pr_read is to provide a layer in between print 
elements defined by application code and print elements as required by the paczs class (see later for 
further discussion of paczs - for the moment it suffices to explain that the 1pr_read message Is sent to 
LPRINTER by paces). The 1pr_read method sets up defaults that are suitable for most LPRINTER 
subclasses, and also performs word-wrapping (via the call to the £it_1ine routine) that is, again, suitable 
for most subclasses. 


However, in some cases, this behaviour is no longer so helpful, and has to be changed - in which case the 
lpr_read method will, itself, have to be REPLAcEd. 


LPRINTER property introduced 


To make sense of the code in 1printer_lpr_read, reference will have to be made to various features of 
the full class definition of LPRINTER: 


CLASS lprinter root 
{ 
REPLACE destroy 
ADD lpr_init 
ADD lpr_read 
ADD lpr_sense_buf_width 
DEFER lpr_sense_text 


PROPERTY 
{ 
VOID *pages; Copy for reference only 
VOID *wdr; Copy for reference only 
SCRLAY_FONT f; The default font 
WDR_PRINT defer; In case previous data was too wide 
UBYTE *wtab; Width table 
UWORD lheight; Line height in printer units 
UWORD width; Width of region to print to 
UWORD subsqind; Indent for subsequent lines (if wrapped) 


} 
} 


The war field in property is used, amongst other ways, as a flag as to whether this is the first call to the 
method (in the current print session). If it is (in which case the war property field is nuuL), the 
initialisation routine ReadWdrData is called. See later for more details of the initialisation of the LPRINTER 
data. 


Apart from testing the value of war, the code in 1printer_lpr_read splits into two cases: 


e if defer.blen is non-zero, it means that the last text buffer specified by the subclass did not all fit 
on the space remaining on the previous printed line, and that some of this buffer remains to be 
processed; in this case, this call to ipr_read will not result in a call to lpr_sense_text 


e otherwise, there is no “deferred” text remaining, and the subclass has to be asked, by means of 
sending self an lpr_sense_text message, to supply another worR_pRINT “print element”. 


In either case, the next buffer of text is submitted to £it_line, to see whether this will all fit within the 
remaining page width. 


If an application subclasses 1pr_reaa, it may choose, however, to do one or more of the following: 
e skip the word-wrap calculation altogether 


e calculate “clipping”, whereby text that is too wide to fit within an allocated region of the page 
does not get word-wrapped, but rather is printed in a truncated form (with trailing or leading 
characters being omitted, as appropriate to the application) 


e make the word-wrap calculation (or a clipping calculation) more sophisticated, by allowing 
variable fonts or variable font styles. 


13 - 16 


13 PRINTING 


The default word-wrapping algorithm 


The routine fit_line is hardly sophisticated: 


(as well as being used within LPRINTER code, fit_line is also utilised within the ws_wrap_para method of 
WSERV) 


GLDEF_C INT fit_line(VOID *wrap,TEXT *buf, INT blen, INT wid, INT (*f) (VOID *, TEXT 
*, INT) ) 

/* 

Returns the number of characters, out of the buffer passed, 

that can form a line of up to the preset width. 

bd 


INT nchars; 
INT thischar; 
INT safe; 

INT seenbreak; 


nchars=0; 
seenbreak=FALSE; 
while (blen--—) 
{ 
thischar=(*buf) ; 
wid-=(*f) (wrap, buf++,1); 
nchars++; 
if (thischar==' ') 
{ 
safe=nchars; 
seenbreak=TRUE; 
} 
else if (wid<0) /* right margin burst */ 
return(seenbreak? safe: nchars-1l); 
} 
return (nchars) ; 


} 


As is evident, the only word delimiter recognised by £it_1ine is the space character. 


Now a fundamental limitation of £it_1ine is that all the characters in the buffer passed to it are assumed 
to belong to the same font (and to have the same style). Thus the callback function £ (which is 

Get TextPrinterWidth in the case when fit_line is called from 1iprinter_lpr_read) disregards the 
offset of characters within the buffer (for example, the width of the first ‘P in “ITALIC” would always be 
taken as the same as that of the second ‘T’ in that word). 


Subclasses that REPLACE 1pr_read may in fact consider any of the following three kinds of modifications 
to this method of reckoning widths: 


e text in one column may have a different font and/or style than that in another column 


e the text within one column (or, in the case when there is only one column across the whole page, 
the text within one line) may itself contain more than one font and/or style 


e special characters, such as tabs, may need additional consideration. 


The third of these possibilities is beyond the scope of this chapter. However, for each of the first two 
possibilities, it is clear that a generalisation of the Get Text Printerwidth routine will be required. 


Calculating widths of text with variable font 


The ipr_sense_buf_width method of LpRINTER simply consists of a call to this same routine, 
GetTextPrinterWidth: 


METHOD INT lprinter_lpr_sense_buf_width(PR_LPRINTER *self,TEXT *buf,INT len) 


{ 
return (GetTextPrinterWidth (self—>lprinter.wtab,buf,len)); 
} 


13-17 


OBJECT ORIENTED PROGRAMMING GUIDE 


In turn, the code for Get Text Printerwidth 1s as follows (note: this code is also the same as the 
wdr_sense_width method of the wor class): 


LOCAL_C INT GetTextPrinterWidth(VOID *pwid, TEXT *buf,INT len) 


/* 
Return the printed width (in printer units) of buf, len 
*/ 

{ 

INT sum; 


if (* (UBYTE *) pwid==0) 
return (len* (* ((UBYTE *)pwid+t1l))); 
for (sum=0; len--;) 
sumt+=* ((UBYTE *)pwid+*buftt+) ; 
return (sum) ; 


} 


From this code, the structure of WDR printer font width tables can be seen. These fall into two types: 


e for monospaced fonts, the table is only two bytes long, with the first byte being o and the second 
being the common width, in printer units, of any of the characters in the font 


e for proportional fonts, the table is 256 bytes long, with the width of character 'a' (which has 
ASCII value 65), say, being the 65th byte in the table, and so on. 


Where printer font width tables come from 


The field wt ab In LPRINTER property is the address of the font width table of the default font for the 
currently selected printer model. The value of wt ab is filled in by the following line of code in the 
ReadWdrData routine called during the first visit to lprinter_lpr_read: 


self—>lprinter.wtab=(UBYTE *)p_send5(self->lprinter.wdr, 
O_WDR_GET_WIDTH_TABLE, self->lprinter.f.fid, 
self—>lprinter.f.height,self—>lprinter.f.style); 


As can be seen, printer font width tables are accessed by means of the wdr_get_width_table method of 
the wor class. Briefly, the wor class encapsulates the logic of reading printer driver .wdr files. (See the 
WDR Printing chapter in the Additional System Information manual for more information about .wdr 
files.) 


There are three parameters to the wdr_get_width_table method: 
e the typeface number of the required font 
e the height identifier of that font 
e the particular font style required. 


In the case when this method is called by tpRintTER, these three parameters are taken from details of the 
default font associated with the current printer, and therefore are bound to be valid. But even if values are 
passed that are not directly known to the current printer model - for example, there could be an enquiry 
concerning an unknown typeface - the method will follow the standard wor customs of “font mapping” 
and “font substitution”: details will be supplied of the “nearest” font, font height, and style actually known 
to the printer model. 


LPRINTER initialisation - phase one 


As has been mentioned, the initialisation of an LPRINTER instance takes place in two stages: 
e some takes place within the LpR_inrT method 


e another portion of the initialisation cannot, however, proceed until other parts of the WDR print 
system have been put into a full state of readiness, and has to wait until the first print element is 
requested, by paczs, before taking place. 


13 - 18 


13 PRINTING 


This section looks at the first of these two stages. 
The code in the LPR_1in1T method of LPRINTER is as follows: 


METHOD VOID lprinter_lpr_init (PR_LPRINTER *self) 
/* 
Returns only when printing is complete 
*/- 
{ 
INT preview; 
PRINTER_PARAMS *par; 
RBUF_PRINTING rbuf; 


preview=self->lprinter.subsqind; 
self—>lprinter.subsqind=FALSE; 
p_send2 (w_ws, O_WS_ENS_PRINT_CONTEXT) ; 
par=(PRINTER_PARAMS *)p_send2 (w_ws-—>wserv.printer,O_PR_GET_PARAMS) ; 
self—->lprinter.width=par->p.pg.body.width; /* in twips at the moment */ 
self—>lprinter.f=par-—>d.f; 
if (preview) 
return; 
rbuf.calls.hread=self; 
rbuf.calls.mread=O_LPR_READ; 
rbuf.ppages=(&self->lprinter.pages); /* to take pages handle when known */ 
runHwimDialog(-SYS_PRINTING_DIALOG, C_PRINTING, &rbuf) ; 
} 


In fact, this is the Series 3a version of the code; the Series 3 version omits the lines 


preview=self-—>lprinter.subsqind; 
self—>lprinter.subsqind=FALSE; 


and 


if (preview) 
return; 


These are connected with support for print preview (which is not available on the Series 3) and will be 
discussed in more detail later in this chapter. 


Apart from these lines, this code 


e sends w_ws a ws_ens_print_context message, for reasons discussed earlier in this chapter 
(essentially, to ensure that an instance of the prinTER class has been created and initialised) 


e senses the width of the printing portion of the page, from the PRINTER instance, and also the 
SCRLAY_FONT structure describing the default font for the current printer model 


e begins to set up an appropriate initialisation struct for the pacEs active object 


e launches an Hwim standard dialog (the printine dialog), whose di_dyn_init method will, in 
turn, set more code in motion that will create and initialise pacEs 


e since the printinc dialog calls am_start, the LpR_inrT method does not return until this dialog 
has completed. 


A brief description of the PAGES active object class 


Clearly there has to be some active object involved with printing - in order that the lengthy process of 
printing can be interleaved with calls to the ac_run method of w_ws (for example, to service redraws, or 
handle keypresses); pacss is this active object. pacers in fact lies at the heart of the WDR printing 
subsystem; whereas it is possible for an application (for example, the Series 3 Word Processor and 
Database application) to perform WDR printing without making any use of LPRINTER, it is not possible to 
avoid using PAGES. 


Whilst a full description of pacrs is beyond the scope of this manual, various points should be noted. 


For its initialisation, pacEs requires to be initialised with, among other items, two object handles and two 
method numbers that collectively make up the pacEs_caLts struct: 


typedef struct 
{ 


VOID *hread; Callback handle for reading print elements 
WORD mread; Callback method for reading print elements 
VOID *hdone; Callback handle for %Sdone & completion 
WORD mdone; Callback method for %Sdone & completion 


} PAGES_CALLS; 


13 - 19 


OBJECT ORIENTED PROGRAMMING GUIDE 


Whenever paces requires another print element, for the body area of a page, it sends hread an mread 
message. In the case when printing is started by LPRINTER, hread Is set to the handle of the LPRINTER 
object itself, whereas mread is set equal to o_LPR_READ - aS can be seen in the above code from 
lprinter_lpr_init. 


Whenever pacss has an event to report to the user, it sends hdone an mdone message. These events 
include: the end of a page, the end of a printing session, and an error condition. In fact, when printing is 
started by LPRINTER, hdone is set, in due course, to the handle of the printine dialog, and mdone is set 
equal to 0_PRINTING_PRINTING. For interest, part of the code of the HWIM printine class follows: 


LOCAL_C UINT GetFirstPageToPrint (VOID) 


return(((PR_PRINTER *) (w_ws—>wserv.printer) )->printer.p.p.pgbeg) ; 
} 


LOCAL_C VOID SetPageNumDisplay (INT num, INT resid) 
{ 
TEXT buf [40]; 


hAtos (&buf [0], resid, num) ; 
hDlgSetText (1, &buf[0]); 
} 


LOCAL_C VOID SetPageNumDisplayCheck(PR_PRINTING *self, INT num) 
{ 
SetPageNumDisplay (num, (self-—>win.flags&PR_WIN_WILL_SKIP && 
GetFirstPageToPrint()>num)? -SYS_SKIPPING_PAGE: -SYS_PAGE_IS); 
} 


#ifdef JPIC 
#pragma METHOD_CALL 
#fendif 


METHOD VOID printing_printing_done(PR_PRINTING *self,PAGES_DONE *d) 

{ 

switch (d->event) 
{ 

case PAGES_DONE_PAGE: 
SetPageNumDisplayCheck (self,d->page) ; 
break; 

case PAGES_DONE_ERROR: 

case PAGES_DONE_END: 
self—>printing.pages=NULL; 
p_send2 (self,O_DESTROY) ; 
break; 
} 

} 


Note that, after sending hdone a PAGES_DONE_END Of PAGES_DONE_ERROR event, paces destroys itself. This 
is the reason why the above printiNc code nulls its copy of the handle of pacrs (otherwise, the destroy 
method of print1Nc would attempt to destroy pacEs a second time, in the standard procedure of automatic 
destruction of component objects). Finally note that a destroy message can also be sent to PRINTING as a 
result of the user pressing the ESC key - this happens automatically on account of standard picBox level 
code; in this case, it is appropriate for pacEs to be destroyed as a consequence of PRINTING being 
destroyed; this is how printing terminates in response to the user's “Abandon” request. 


More about the interface to and from PAGES 
Two cases where application code might send messages directly to pacrs are as follows: 


e if the application makes use of the flag woR_PRINT_IDLE when it prints - in which case, as 
explained earlier in this chapter, it needs to send the paces object an ao_queue when it has 
determined what the next print element is 


e if the application needs to destroy the pacrs object (but this will only arise if the application takes 
over the code that is, by default, handled by the destroy method of the printine class). 


In most cases, however, application code will never have any reason to send a message directly to the 
Paces object. Instead, the parts of the interface to and from paczs that are more likely to need to be 
understood are: 


e how to create and initialise the paczs object 


e the nature of the mreaa and the mdone callbacks from pacEs. 


13 - 20 


13 PRINTING 


A paces object is actually created by sending a message to the PRINTER object. There are in fact three very 
similar methods, all of which create and initialise paces in one way or another: 


e the pr_print method creates and initialises pacgs in printing mode 
e the pr_paginate method creates and initialises pacEs in paginating mode 
e the pr_preview method (Series 3a only) creates and initialises pacrs in print preview mode. 


In each case, there is one additional parameter - the address of a paczESs_caLus Struct, as described earlier. 
In each case, the method returns the handle of the pacgs object created. 


Thus code called from inside the ai_dyn_init method of the HWIM print1nc class (which is itself called 
from code inside the 1pr_init method of the LpRinTER class) contains the following: 


METHOD VOID *printing_printing_do_print (PR_PRINTING *self,PAGES_CALLS *pcalls) 
{ 
return((VOID *)p_send3 (w_ws->wserv.printer,O_PR_PRINT,pcalls) ); 
} 


METHOD VOID printing_dl_dyn_init (PR_PRINTING *self) 
{ 
RBUF_PRINTING *rbuf; 


rbuf=self-—>dlgbox.rbuf; 
rbuf->calls.hdone=self; 
rbuf-—>calls.mdone=O_PRINTING_DONE; 
self—>printing.pages=(VOID *)p_send3(self,O_PRINTING_DO_PRINT, &rbuf->calls) ; 
if (rbuf->ppages) 
*rbuf—>ppages=self—>printing.pages; 


} 


Note that the paginating mode of pacss is provided specially for use by window objects based on the 
FORM scruay and scrime classes - such as the main windows of the Word Processor and Database 
applications. The fact that the pagination logic is so similar to printing logic needn't particularly concern 
most users of paces. The only potentially significant point concerns the parameters passed back to each 
mread callback. As can be seen from the listing given above for the 1pr_read method of LpRINTER, a 
somewhat mysterious second parameter (called x in that listing) is passed. The actual significance of this 
is in whether or not paces is being run in paginating mode. Various optimisations can be made in this 
case within scruay callback code. (To be completely accurate, the callback in this case is to the pRNLAY 
subclass of scruay.) However, as is clear, this parameter is totally ignored when the callback is made, 
instead, to LPRINTER code. 


A brief description of the WDR class 


The wor class shares with pacss the feature of lying at the heart of the WDR printing subsystem. Whilst 
PaGEs Is the active object class that actually drives page formatting and printing, wor supports various 
query functions concerning the current printer model. For example, the wdr_get_width_table method 
has already been mentioned. 


Another useful service of the wor class is that of converting a null-terminated sequence of uworp values 
from twips into current printer units. The method involved here is war_twips_to_xy. An example of the 
use of this method is in the ReadWwdrData function already mentioned: 


LOCAL_C VOID ReadWdrData(PR_LPRINTER *self) 
{ 
UWORD *1x[2]; 
UWORD *ly[2]; 


self—>lprinter.lheight=self—->lprinter.f.height; 

ly [0]=&self-—>lprinter.lheight; 

ly [1]=NULL; 

1x[0]=&self—>lprinter.width; 

1x [1]=NULL; 

self—>lprinter.wdr=((PR_PAGES *)self-—>lprinter.pages) —>pages.in.wdr; 

p_send4 (self-—>lprinter.wdr,O_WDR_TWIPS_TO_XY, &1x[0],&ly[0]); 

self—>lprinter.wtab=(UBYTE *)p_send5(self->lprinter.wdr, 
O_WDR_GET_WIDTH_TABLE, self->lprinter.f.fid, 
self—>lprinter.f.height,self—>lprinter.f.style); 


13 - 21 


OBJECT ORIENTED PROGRAMMING GUIDE 


In order to find out the number of typefaces supported by the current printer model, code such as the 
following can be used: 


nt=((WDR_MODEL *)p_send2 (wdr,O_WDR_SENSE_MODEL) ) ->num_typefaces; 


where the definition of the woR_mMopEL struct is (refer also to the the WDR Printing chapter in the 
Additional System Information manual) 


typedef struct 
{ 


UWORD minx; minimum delta x (in twips, unless MINX_IS_DPI flag set) 
UWORD miny; minimum delta y in twips 

UWORD skipx; amount printer auto indents 

UWORD skipy; amount printer auto feeds 

UWORD flags; orientation 

UWORD num_typefaces; number of typefaces supported by model 


WDR_TYPEFACE *typeface[1]; list of typeface rids/pointers to typeface 
} WDR_MODEL; 


For any given typeface, referred to by index number (0, 1, ...), the corresponding name and typeface 
number, among other things, can be found out by sending the wor object a war_typeface message, which 
takes the index number as a parameter, and which returns a pointer to a wdr_typeface struct: 


typedef struct 
{ 


TEXT name [WDR_FONT_NAME_LEN]; Typeface name 

UWORD typeface; RTF/Word compatible typeface 

UWORD type; WDR_TYPF_XXX 

WORD trans_rid; rid of translates record 

UWORD num_heights; Number of different typeface heights 
WDR_FONT font[1]; List of different heights 


} WDR_TYPEFACE; 


Another approach to finding a typeface with a given typeface number is to send the wor object a 
wdr_search_typeface message, which has the following definition: 


METHOD INT wdr_wdr_search_typeface(PR_WDR *self,INT typf,WORD *pix,WDR_TYPEFACE **ppt) 
/* 

Write the index and address of the typeface struct with RTF/Word 

typeface number typf to *pix and *ppt respectively and return TRUE 

if a matching typeface was found. 

Otherwise return FALSE and write a recommended typeface 

or -1 if no such typeface number exists in the current model. 

Either pix or pt may be NULL if that part of the return is not required. 

*/ 


As will be appreciated, this method contains the “font mapping/ substitution” logic of the wor class. 


Finally, for a given typeface, the way to determine the range of heights available (and also the 
recommended way of matching a desired font height) can be seen from the following code - which is 
actually an extract from the standard Hwim “Font selector” dialog code: 


LOCAL_C VOID ResetSizes(PR_FONTSEL *self,INT typfix) 
{ 
UWORD n,i; 
WORD height; 
TEXT buf[12]; 


p_send2 (self-—>fontsel.sizes,O_VA_RESET) ; 
n=((WDR_TYPEFACE *)p_send3(self-—>fontsel.wdr,O_WDR_TYPEFACE, typfix) )->num_heights; 
for (i=0;i<n;it++) 

{ 

buf [p_itob (&buf [0],p_send4 (self- 

>fontsel.wdr,O_WDR_FONT_HEIGHT,typfix,i)/20) ]=0; 

p_send3 (self-—>fontsel.sizes,O_VA_APPEND, &buf[0]); 

} 
height=self->fontsel.pf-—>height; 
hDlgSetChlist (2,p_send4 (self—>fontsel.wdr,O_WDR_SEARCH_HEIGHT,typfix, &height) ); 
} 


(Note that the values returned by the wdr_font_height method are in twips: hence the multiplication by 
20, to convert into points prior to presenting the values in a dialog for inspection by users.) 


13 - 22 


13 PRINTING 


Creating and destroying WDR objects 


Evidently, LpRINTER code - and any other WDR printing code - relies on a suitable wor object having been 
created and initialised. The paczs class in particular contains a property field given the handle of a wor 
object, and the ao_init method of paces requires to be passed this handle as part of its initialisation data. 


There is also a slot for the handle of a wor object within PRINTER property. For this reason, PRINTER code 
called inside the pr_print method (also called by the pr_paginate and pr_preview methods) contains the 
following lines: 


if (!self->printer.wdr) 
p_send2 (self,O_PR_OPEN_WDR) ; 


The pr_open_wdr method of PRINTER is as follows: 


METHOD PR_WDR *printer_pr_open_wdr(PR_PRINTER *self) 
{ 
INT model; 
TEXT name [P_FNAMESIZE]; 


model=p_send3 (self,O_PR_SENSE_MODEL, &name[0]); 

printer_pr_close_wdr (self); 

self—>printer.wdr=f_newsend (CAT_FORM_FORM, C_WDR, O_WDR_INIT, &name[0],model) 
return (self—>printer.wdr); 


} 


In turn, the pr_sense_mode1 method obtains the appropriate printer model 


e by preference, from data set in PRINTER property by a prior call to pr_set_mode1 (see the end of 
this chapter for an example of code sending a pr_set_model message) 


¢ otherwise, from the psm print model environment variable 
e failing that, from the hard-wired default of rom: :Bg. wor. 


Note that this mechanism leaves open the possibility of the application creating and initialising a wor 
object, for its own purposes, well before the user selects any print menu command. Evidently, the way to 
do this is to 


e ensure that the PRINTER object (handle at w_ws->wserv.printer) has been created and initialised 
e send this object a pr_open_wdr message. 


Incidentally, it is perfectly possible for there to be more than one wor object in existence, at the same time 
in the same application (although only one of them can have its handle written into PRINTER property). 
Thus when dialogs inside the HWIM “Print setup” dialog suite are operational, a “scratch” wor is used at 
various times, in order to enquire details of .wdr printer model files other than the one to which the 
application is currently “logged”. 


On the other hand, wor objects should be destroyed as soon as they are no longer required. (For example, 
a considerable amount of memory may be tied up by all the font width tables that may have been loaded.) 


To achieve this, simply send pRINTER a pr_close_wdr message. Thus the destroy method of LPRINTER 1s 
as follows: 


METHOD VOID lprinter_destroy(PR_LPRINTER *self) 
{ 
if (w_ws->wserv.printer) 
p_send2 (w_ws-—>wserv.printer,O_PR_CLOSE_WDR) ; 
p_supersend2 (self,O_DESTROY) ; 
} 


(Note that the test of whether w_ws->wserv.printer is non-null is necessary because the 1pr_init 
method of LpRInTER could fail prior to the completion of the call to ws_ens_print_context.) 


13 - 23 


OBJECT ORIENTED PROGRAMMING GUIDE 


In turn, the code in the pr_close_wdr method of pRinTER is, naturally enough, 


GLDEF_C VOID DestroyRef(PR_ROOT **ref) 
{ 
if (*ref) 
{ 
p_send2 (*ref,O_DESTROY) ; 
*ref=NULL; 
} 
} 


METHOD VOID printer_pr_close_wdr(PR_PRINTER *self) 


{ 
DestroyRef (&self—>printer.wdr) ; 
} 


Using XPRINTER for print preview 


Just as there are various levels at which the subject of printing can be approached, so also are there 
various levels at which the subject of print preview can be approached. However, 


e like printing, the requirements of most applications for print previewing can be met very simply, 
by means of creating and using a subclass of LPRINTER - except that this time a subclass of 
XPRINTER is required (xPRINTER itself being a subclass of LPRINTER) 


e in these cases, what makes application coding particularly easy is the fact that exactly the same 
subclass will suffice both for printing purposes and for print preview purposes 


e underlying this similarity is the fact that printing and print previewing are both driven by the 
PAGES active object, which requires in both cases to be fed by application code with a series of 
print elements (the same set of print elements in both cases). 


The difference between XPRINTER and LPRINTER 


First, note that xPRINTER is defined in the XADD library, which is not present in the ROM of the Series 3, 
so that print preview support does not exist on the Series 3 - only on the Series 3a. (Further to this, the 
versions of LPRINTER on the Series 3 and the Series 3a are also critically different, in a few small but key 
places - though the calling interface remains exactly the same.) 


Next, witness the entirety of the code of xPRINTER: 


#include <xprinter.g> 
#include <prev.g> 
#include <xadd.g> 


GLREF_D PR_APPMAN *w_am; 
GLREF_D VOID *w_ws; 


#ifdef JPIC 
#pragma METHOD_CALL 
#endif 


METHOD VOID xprinter_destroy(PR_XPRINTER *self) 
{ 
if (self->xprinter.locked) 
p_send3 (w_ws, O_WS_LOCK, FALSE) ; 
p_supersend2 (self,O_DESTROY) ; 
} 


METHOD VOID xprinter_lpr_init (PR_XPRINTER *self, INT commid) 
{ 
if (!commid) 
{ 
p_send3 (w_ws, O_WS_LOCK, TRUE) ; 
self—>xprinter.locked=TRUE; 
} 
self—>lprinter.subsqind=commid; /* communicate with subclass */ 
p_supersend2 (self,O_LPR_INIT); 
if (!commid) 


return; 
f_newsend (CAT_XADD_XADD, C_PRVVIEW, O_PVV_INIT, self, commid, &self—>lprinter.pages) ; 
} 


and the entirety of the corresponding class definition: 


13 - 24 


13 PRINTING 


CLASS xprinter lprinter 


{ 
REPLACE destroy 
REPLACE Ipr_init 
PROPERTY 
{ 
WORD locked; 
} 
} 


Evidently, xPRINTER adds two pieces of functionality to LPRINTER (one rather trivial, and the other more 
fundamental): 


XPRINTER locks the application whilst it is printing, to lessen the chance of “accidents” half-way 

through printing due to the user inadvertently switching files from the System screen (setting the 
application locked means the System screen will block any attempted file switch with a “XXX is 

busy” infoprint) 


XPRINTER expects an extra parameter to its LpR_inrT method; if the value of this is zero, 
LPRINTER code is invoked in printing mode, whereas if it is non-zero, print preview takes place 
instead. 


The actual meaning of this additional parameter - commia - is the method number of the command 
manager that system code will invoke if the user chooses the ‘Print’ menu command from within the 
menubar available inside print preview. 


Extended example of print and print preview using XPRINTER 


To illustrate use of xPRINTER for print and print preview purposes, consider the following variation upon 
the example applications presented earlier: 


once again, the user is given the choice of specifying a range of dates to be printed 
the dates will be printed in two columns (as in the second of the two earlier examples) 


the first column will be printed in bold (by way of illustrating some features described in the 
middle portion of this chapter) 


rather than a series of dialogs following each other irrevocably, the various possible choices in 
the application are available, in more standard style, as choices from the menu bar 


there are four menu commands available: Exit, Print setup, Print preview, and Print 


in order to illustrate another (quite separate) point, the application also attempts to load and save 
its current print setup to file, on startup and on exit (though discussion of this particular feature 
of the application is deferred until the end of the chapter). 


On installation of the OOP component of the SDK, the source code for this application is copied into a 
\sibosdk\wdrprint directory. 


The category file 


IMAGE demo 


EXTERNAL olib 
EXTERNAL hwim 
EXTERNAL xadd 


INCLUDE hwimman.g 
INCLUDE dlgbox.g 
INCLUDE xprinter.g 
INCLUDE time.g 
INCLUDE epoc.h 


CLASS dewserv wserv 


{ 
REPLACE ws_dyn_init 


} 


13 - 25 


OBJECT ORIENTED PROGRAMMING GUIDE 


CLASS decomman comman 
{ 
REPLACE com_init 
REPLACE com_exit 
ADD dec_psetup 
ADD dec_preview 
ADD dec_print=decomman_dec_preview 
ADD dec_print_direct 
TYPES 
{ 
typedef struct 
{ 
UWORD dayno; 
UWORD ndays; 
UWORD gap; 
} DE_PRINT_DETAILS; 
} 
PROPERTY 
{ 
DE_PRINT_DETAILS dets; 
} 


CLASS dedlg dlgbox 
{ 
REPLACE dl_dyn_init 
REPLACE dl_key 
} 


CLASS delp xprinter 


{ 
REPLACE lpr_sense_text 


CONSTANTS 
{ 
DELP_STATE_COL1 0 
DELP_STATE_COL2 1 
DELP_STATE_COL3 2 
} 

PROPERTY 1 


{ 
VOID *time; 
VOID *wtab; 
UWORD colwid; 
UWORD right; 
UWORD state; 
UWORD ndays; 
TEXT dname [E_MAX_DAY_NAME]; 
TEXT buf [LN_TIME_DATE_STR]; 
} 
} 


Command manager 


The four commands in the menu bar - Exit, Print setup, Print preview, and Print - are handled by the 
command manager methods com_exit, dec_psetup, dec_preview, and dec_print. Note that the 
definition 


ADD dec_print=decomman_dec_preview 


means that the single routine decomman_dec_preview handles both the menu commands Print and Print 
preview. This is a common feature of applications supporting print preview as well as print. As in all 
cases of this sort, the code can distinguish which of the two menu commands has actually been chosen by 
testing the value of the additional commia parameter that is always passed, by system code, to command 
manager methods. Thus the code for decomman_dec_preview IS 


METHOD VOID decomman_dec_preview(PR_DECOMMAN *self,INT commid) 


{ 
if (LaunchDialog(C_DEDLG, DELP_DLG, &commid) ) 
PrintOrPreview (commid) ; 


13 - 26 


13 PRINTING 


where LaunchDialog is the same as in previous examples (it is an entirely standard dialog-launching 
utility), and printorPreview is as follows: 


LOCAL_C VOID PrintOrPreview(INT commid) 
{ 
commid= (commid==O_DEC_PREVIEW? O_DEC_PRINT_DIRECT: 0); 
hDestroy (f_newsend (CAT_DEMO_DEMO, C_DELP,O_LPR_INIT, commid) ) ; 
} 


As can be seen, the parameter passed to the 1pr_init method of Exp is either 
¢ 0, in the case when DELP is to print, or 
@ 0_DEC_PRINT_DIRECT, in the case when DELP is to print preview. 


Note that the dec_print_direct method of the command manager is not directly associated with the Print 
menu command from the base state of the application. On the contrary, as already stated, this menu 
command has corresponding command manager method dec_print, which is handled by the same code 
as the dec_preview method. The role of the dec_print_direct method is to service the Print menu 
command from the special Print Preview submenu that is available inside the print preview subsystem: 


Wdrprint 


Show margins 
Pages to display 
Jump to page 


Exit preview 


\ Sun 24 


The code for the dec_print_direct method, in this example, is just 


METHOD VOID decomman_dec_print_direct (VOID) 


{ 
PrintOrPreview(O_DEC_PRINT) ; 
} 


Note: the reason for using the term “direct” is that printing is to proceed directly, without any additional 
‘Print details’ dialog being presented first. The print details are the same as in the dialog that invoked 
the print preview. 


Apart from the methods of the command manager already covered above, pEcomman also has the following 
methods: 


e =the dec_psetup method just has one line, sending a ws_edit_print_context message tO w_ws 


e =the com_init method writes the handle of the command manager into patapp2 (for convenience 
elsewhere in the code) and also calls the routine rryLoadPrintContext 


e =the com_exit method is as follows: 


METHOD VOID decomman_com_exit (PR_DECOMMAN *self) 
{ 
if (p_enterl (SavePrintContext) ) 
p_delete (SAVED_FILE_NAME) ; 
p_exit (0); 
} 


More details of the saveprintcontext and TryLoadPrintContext methods are given nearer the end of 
this chapter. 


Print details dialog 


The code here contains two enhancements compared to the earlier example: 


e if the dialog is visited more than once, it is seeded, in the di_dyn_init method, with the values 
last set by the user (as sensed in the preceding di_key method) 


e the dialog title has to vary, to reflect whether Print preview is to follow, or Print. 


13 - 27 


OBJECT ORIENTED PROGRAMMING GUIDE 


The entire code in the dialog module for the application is 


#include <demo.g> 
#include <demo.rsg> 
#include <hwim.h> 


GLREF_D PR_DECOMMAN *DatApp2; 


#pragma METHOD_CALL 


METHOD VOID dedlg_dl_dyn_init (PR_DLGBOX *self) 
{ 


INT commid; 


commid=(* (INT *) self—>dlgbox.rbuf) ; 
if (commid==0O_DEC_PREVIEW) 

hDlgSetTitleByRid (DELP_PREVIEW_TITLE) ; 
if (!DatApp2->decomman.dets.dayno) 

return; 
hDlgSetDtedit (1, DatApp2->decomman.dets.dayno) 
hDlgSetNcedit (2, DatApp2-—>decomman.dets.ndays) 
hDlgSetChlist (3, DatApp2-—>decomman.dets.gap) ; 
} 


’ 
’ 


METHOD INT dedlg_dl_key (PR_DLGBOX *self) 
{ 
DatApp2->decomman.dets.dayno=hDlgSenseDtedit (1) 
DatApp2->decomman.dets.ndays=hDlgSenseNcedit (2) 
DatApp2->decomman.dets.gap=hDlgSenseChlist (3); 
return (WN_KEY_CHANGED) ; 
} 


Note that the actual pz_pRINT_pETAILs data structure edited in this dialog no longer exists purely on the 
stack (as it did in versions of this example discussed earlier in this chapter). Instead, to give this data 

greater persistence, it now exists within the property of the command manager - whose handle has been 
written to Datapp2 for convenience. (Otherwise, this handle could be obtained from w_ws->wserv.com.) 


7 
’ 


The way the di_dyn_init routine determines whether the dialog has been invoked before - ie determines 
whether there is data in the pzk_PRINT_DETAILS Struct that ought to overwrite the defaults provided by the 
resource controls for the dialog - is by testing the value of patapp2->decomman.dets.dayno, to see 
whether it is non-zero. But note that, of course, the test for whether the dialog title should be changed is 
quite independent of this. 


Application initialisation 


Apart from the code in the com_init method, the other initialisation code for the application is in main 
itself, and in the ws_dyn_init method of the wserv subclass (as can be seen, there is nothing unusual in 
any of it): 


GLREF_D WSERV_SPEC *wserv_channel; 


LOCAL_C INT StatusWindowWidth (VOID) 


{ 
P_EXTENT ext; 


wiInquireStatusWindow (-1, &ext) ; 
return (ext.width) ; 


} 


LOCAL_C VOID InitClientWindow(PR_DEWSERV *self) 
{ 
W_WINDATA wd; 
PR_WIN *cliwin; 


wd.extent.tl.x=wd.extent.tl.y=0; 

wd.extent .height=wserv_channel-—>conn.info.pixels.y; 
wd.extent.width=wserv_channel->conn.info.pixels.x-StatusWindowWidth () ; 
cliwin=f_newsend (CAT_DEMO_HWIM, C_BWIN, O_WN_CONNECT, NULL, W_WIN_EXTENT, &wd) ; 
self—>wserv.cli=cliwin; 

cliwin->win.flags=PR_BWIN_CORNER 4|PR BWIN_SHADOW_1; 

p_send3 (cliwin, O_WN_EMPHASISE, TRUE) ; 

hiInitVis (cliwin) ; 


} 


13 - 28 


13 PRINTING 


#pragma METHOD_CALL 


METHOD VOID dewserv_ws_dyn_init (PR_DEWSERV *self) 


wsSet List (W_STATUS_WINDOW_ICON, NULL, 0) ; 
wStatusWindow (W_STATUS_WINDOW_BIG) ; 
InitClientWindow(self); 

} 


GLDEF_C VOID main(VOID) 


N_HWIMMAN app; 
N_WSERV wserv; 


p_linklib(0); 

app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE | 
FLG_APPMAN_CLEAN|FLG_APPMAN_FULLSCREEN; 

wserv.com_cat=app.wserv_cat=p_getlibh (CAT_DEMO_DEMO) ; 

app.wserv_class=C_DEWSERV; 

wserv.com_class=C_DECOMMAN; 

p_send4 (p_new (CAT_DEMO_HWIM, C_HWIMMAN) ,O_AM_INIT, &app, &wserv) ; 


} 
XPRINTER subclass initialisation 


All the initialisation code for the xPRINTER subclass of the applicaiton is called inside the first 
lpr_sense_text callback. This establishes: 


¢ atime object, suitably prepared (as in previous examples) to render textual versions of given 
dates, and initialised with the start date from the p—_PRINT_DETAILS data structure 


e aprinter font width table (handle written to wt ab) for the bold version of the default print font 
e the value of the colwia property field, that gives the overall width for the first column: 
#include <demo.g> 

#include <demo.rsg> 

#include <hwim.h> 


GLREF_D PR_DECOMMAN *DatApp2; 


#define TIME_FMT_FLAGS (PR_TIME_MONTH NAME | PR TIME_SUFFIX_NAME) 


LOCAL_C VOID InitTimeObject (PR_DELP *self) 
{ 
P_DAYSEC ds; 
SE_TIME_ FORMAT fmt; 


ds .day=DatApp2->decomman.dets.dayno; 

ds.sec=0; 

self—>delp.time=f_new (CAT_DEMO_OLIB,C_TIME) ; 

p_send4 (self—>delp.time, O_TO_SET, SET_TIME_DAYSEC, &ds) ; 

fmt .flags=TIME_FMT_FLAGS; 

p_send4 (self->delp.time, O_TO_SET_FORMAT, &fmt, TIME_FMT_FLAGS) ; 
} 


LOCAL_C UINT SenseBufWidth(PR_DELP *self,TEXT *pb) 


{ 
return (p_send5 (self—->lprinter.wdr,O_WDR_SENSE_WIDTH, self- 


>delp.wtab,pb,p_slen(pb))); 
} 


13 - 29 


OBJECT ORIENTED PROGRAMMING GUIDE 


LOCAL_C VOID FindWidthFirstColumn(PR_DELP *self) 
{ 
INT i; 
TEXT buf [E_MAX_DAY_NAME]; 
UWORD wid; 


for (i=0; i<7; i++) 
{ 
p_nmday (&ébuf[0],i); 
wid=SenseBufWidth (self, &buf[0]); 
if (wid>self-—>delp.colwid) 
self—>delp.colwid=wid; 
} 
self—>delp.colwid+=SenseBufWidth(self," "); /* two spaces */ 
if (self->delp.colwid>(3*self-—>lprinter.width/4) ) 
{ 
hiInfoPrint (DELP_PAPER_NARROW) ; 
p_leave (RUN_ACTIVE_CLEANUP_NONOTIFY) ; 


self—>lprinter.subsqind=self->delp.colwid; 


LOCAL_C VOID InitWidthTable(PR_DELP *self) 
{ 
self—>delp.wtab=(VOID *)p_send5(self->lprinter.wdr,O_WDR_GET_WIDTH_TABLE, 
self—>lprinter.f.fid,self-—>lprinter.f.height, 
self->lprinter.f.style|WDR_STYLE_BOLD) ; 


} 


For more information relevant to parts of this code, see the appropriate sections earlier in this chapter. 
The XPRINTER LPR_SENSE_TEXT callback 


What is particularly noteworthy about the peLp code in this application (referring in fact to the totality of 
the code in this class) is that although there are, admittedly, differences from the pep code given for 
earlier examples in this chapter, these differences have nothing to do with the extra support that this class 
is now providing for Print Preview as well as Print. These differences are purely to do with comparatively 
incidental points, such as the fact that, for example, the first column is now being printed in bold. 


In other words, the extra support for Print Preview is achieved without any change at the LPRINTER 
subclass level - except that the definition of the class now specifies xPRINTER as the superclass, instead of 
just LpRintER. The code itself could survive unchanged: 


13 - 30 


13 PRINTING 


METHOD INT delp_lpr_sense_text (PR_DELP *self,WDR_PRINT *pr) 
{ 
P_DATE dt; 
P_DAYSEC ds; 


if (!self->delp.time) 


self—>delp.ndays=DatApp2-—>decomman.dets.ndays; 
InitTimeObject (self) ; 

InitWidthTable (self); 

FindWidthFirstColumn (self); 


else if (!self-—>delp.ndays) 
return (FALSE) ; 
switch (self—->delp.state++) 


case DELP_STATE_COL1: 
if (DatApp2-—>decomman.dets.gap) 
{ 
p_send4 (self-—>delp.time, O_TO_SENSE, SENSE_TIME_DATE, &dt) ; 
if (!dt.day) 


pr->down=pr->height; 
if (DatApp2-—>decomman.dets.gap==1) 
pr->down>>=1; 


} 
p_send4 (self-—>delp.time, O_TO_SENSE, SENSE_TIME_DAYSEC, &ds) ; 
p_nmday (&self->delp.dname[0],P_WEEK (ds.day) ); 
self—>delp.right=self-—>delp.colwid-SenseBufWidth (self, &self—>delp.dname[0]); 
pr->buf=(&self—>delp.dname[0]); 
pr->style|=WDR_STYLE_BOLD; 


break; 
case DELP_STATE_COL2: 
pr->flags&=(~ (WDR_PRINT_LINE|WDR_PRINT_TEXT) ); 


pr->flags|=WDR_PRINT_RIGHT; 
pr->right=self—>delp.right; 
return (TRUE) ; 

case DELP_STATE_COL3: 
p_send4 (self->delp.time, O_TO_SENSE, SENSE_TIME_DATESTR, &self->delp.buf[0]); 
pr->buf=(&self—>delp.buf[0]); 
pr->flags&=(~WDR_PRINT_LINE) ; 
pr->indent=self-—>delp.colwid; 
self—>delp.ndays-——; 
p_send4 (self—>delp.time,O_TO_ADD_DAYS,1,0); 
self—>delp.state=DELP_STATE_COL1; 
} 

pr->blen=p_slen(pr->buf) ; 

return (TRUE) ; 

} 


Comments on the differences between XPRINTER and LPRINTER 


One minor change that would have to be made, however, between LPRINTER Subclass code and xpRINTER 
subclass code, would be in any replacement 1pr_init method. This simply due to the fact that the 
lpr_init method of xprinteER requires the additional commid parameter - to differentiate between the case 
of Print Preview and the case of Print. 


Note incidentally that, for compatibility reasons, this extra parameter could not be added in at the 
LPRINTER level. This is on account of existing applications (ie pre-Series 3a applications) that interact 
with LPRINTER code without passing any parameter explicitly to the ipr_init method. 


The way around this constraint, in the development of the Series 3a ROM code, can be seen from the code 
given earlier for the 1pr_init methods of both LpRINTER and xPRINTER: 


e the subsqina property field is re-used as a temporary “postbox’”’ between the two classes 
e after its use, in this way, at the initialisation stage, its value is set back to zero 


e the rationale behind this is that the subsqina field cannot be written to, by application subclass 
code, until inside the first callback to ipr_sense_text (OF lpr_read). 


13 - 31 


OBJECT ORIENTED PROGRAMMING GUIDE 


WDR printing miscellany 


WDR printing classes pictorial overview 


The following class diagram shows a possible application state before printing actually starts: 


 P$? variables,” / PRINTER > / WDR ye LWDR file | 


fo * ee EEO. re —_ / ¥ ph Se 
z Z 7 
a Bile , SA 
aS ., \ Sh 
. } 4 
\ J 
\ | pete \ aan. 
\ \ 
\ ee ee 
SS — C Ad genes. Git 
ra - Se r ie Seas a aed 
/ oN. / S / 


/ W_ws (user? a styles C application 
“ interface) O—___ dialogs Cees data 


See By ate 
7. / 


 printsetup } / Print details a application : 
C dialogs | \ dialogs O ———_ data 
Sa . ites 


i 
2 \ 
i 


\ ser 
Nee 


(A typical application need not, however, contain all the application-specific components shown here. For 
example, applications that print will not necessarily contain “styles dialogs”) 


The next diagram shows a possible situation once printing is underway (for clarity, many aspects of the 
previous diagram are omitted from this one): 


physical / PDR > a ACTIVE 
‘printer a : So f 


/ WDR “ PRINTING) 
Ke (done) i 
PRINTER)  LPRINTER >} ra ee ; 
« ——C..__ (read) oC & data 

‘ } ote / os 

* print details > application 


(dialogs O——<_ data 


a 


(Nothing too significant should be read into the type or the direction of the connections shown between 
different classes in these diagrams. As the foregoing chapter has made clear, the connections between the 
actual classes are, at times, rather more complex than could be done justice in any one diagram.) 


The PDR class 


The role of the ppr object is possibly worth mentioning. This receives all “printing output” from pacEs, 
and directs into towards the relevant IO channel - be it the parallel port, the serial port, or just an opened 
file (in the case of printing to file). The ppr class accesses data held by the wor class, to let it know 
exactly which escape sequences (or whatever) are required to effect various results - such as changing 
from one font to another, indenting by a given amount, and so on. 


13 - 32 


13 PRINTING 


For some kinds of printers, the ppr object has to be an instance of an appropriate subclass of the ppr class 
in FORM - rather than just being an instance of ppr itself. This happens when an appropriate flag is set 
in the .wdr file for that printer. In this case, system code looks for a suitably named DYL in the same 
directory as the .wdr file. See the WDR Printing chapter in the Additional System Information manual for 
some more details, or contact Psion directly for more information about writing ppr subclasses. 


The role of the ppr object in the above diagrams is of interest for one additional reason: when the WDR 
print system is driven in preview mode, as opposed to printing mode, this is one of only two parts of the 
diagram to change. Rather than create any instance of ppr (or a printer-specific subclass thereof), system 
code in this case creates an instance of the FORM class prvppr. Rather than direct printing output 
towards any IO channel, this directs it towards a suitable window - the print preview window. 


The other part of the diagram that changes is the PRINTING part: the PAGES mdone callback is directed to a 
different object, namely an instance of prvvizw. This is discussed briefly in the following section. 


Print preview without XPRINTER 


The code given earlier in this chapter for xPRINTER makes it clear what would be required should an 
application, for whatever reason, wish to access the print preview subsystem without using xPRINTER. 
Essentially, something equivalent to the following line of code is required: 


f_newsend (CAT_XADD_XADD, C_PRVVIEW, O_PVV_INIT, self, commid, &self-—>lprinter.pages) ; 
This creates and initialises an instance of the XADD class prvview. 
The code in the pyv_init method of prvview is as follows: 


METHOD VOID prvview_pvv_init (PR_PRVVIEW *self,VOID *xp,INT commid, VOID **ppages) 


{ 
IN_PRVVIEW init; 


init.Calls.hread=xp; /* xprinter */ 
init.Calls.mread=O_LPR_READ; 
init.Calls.hdone=self; 
init.Calls.mdone=O_PVV_FALSE; 
init.PrintMethod=commid; 

init.Sparel = init.Spare2 = 0; 
p_send4 (self, O_WN_INIT, &init, FALSE) ; 
*ppages=self-—>prvview.pPages; 
self—>prvview. Started=TRUE; 
p_send2 (w_am,O_AM_START) ; 

} 


Applications may wish to avoid calling this method, but they cannot practically avoid sending the prvviEw 
object the wn_init message. Note in this context the definition of the 1n_PRvvIEw struct: 


typedef struct 
{ 
PAGES_CALLS Calls; 


WORD PrintMethod; 
WORD Sparel; 
WORD Spare2; 


} IN_PRVVIEW; 
where Spare1 and spare2 should be set to zero. 


The default mdone callback method, pvv_false, always just returns raLse, and will be suitable for almost 
every client of pRvvizw. (Without going into too many details, there are in fact two layers of done 
callbacks when print preview applies: a first level callback from pacEs to pRvviEw, always using the 
method pvv_pages_done, and a possible second level callback from prvview to any specified recipient 
object.) 


Finally, the significance of the final TRuE/raLsr parameter to the wn_init method of prvview can be seen 
in the following code at the very end of this wn_init method: 


if (DoAmStart) 
{ 
self—>prvview.Started = TRUE; 
p_send2 (w_am,O_AM_START) ; 
} 


13 - 33 


OBJECT ORIENTED PROGRAMMING GUIDE 


The only reason that the code in pvv_init cannot pass the DoAmStart parameter as TRUE is in order to 
write back the handle of pacss (effectively in LPRINTER property), prior to the call to am_start being 
made. This allows code in, for example, 1pr_read callbacks (which take place, of course, before the 
am_start call returns) to access pacEs as required. 


Saving and restoring print context from file 


As mentioned earlier, the pRINTER class has methods allowing the print context (the subject matter of the 
‘Print setup’ dialog suite) to be set and sensed. These methods can be utilised to allow the print context to 
be saved to file, if desired, and then restored the next time the file is opened. 


Any application that wishes to save the print context to file has to answer a number of design decisions: 
¢ what actual format should the data be stored in? 


e what kind of integrity check might be performed on file data, before setting it into PRINTER 
property on application startup? 


e ~=what kind of error recovery procedure should be adopted, if there is any run-time error, either on 
saving the data, or on loading it? 


This is not the place to discuss these matters at any length. Accordingly, many aspects of the example 
code, in the WDRPRINT subdirectory, will just be taken for granted in this discussion (though this is not to 
imply that there is anything special about the design decisions embodied therein). 


What can be briefly covered here, however, are various methods of PRINTER, which fall into two 
categories: those sensing the print context, and those setting it. 


The following code senses the print context and writes it out to file: 


LOCAL_C INT SavePrintContext (VOID) 
{ 
VOID *fcb; 
VOID *printer; 
UBYTE *p; 
struct 
{ 
UBYTE model; 
UBYTE name [P_FNAMESIZE+1]; 
} om; 


printer=w_ws-—>wserv.printer; 
if (!printer) 

return (0); 
f_open (&fcb, SAVED_FILE_NAME, P_FREPLACE|P_FSTREAM|P_FUPDATE) ; 
p=(UBYTE *)p_send2 (printer,O_PR_GET_PARAMS) ; 
f_write(fcb,p,sizeof (PRINTER_PARAMS) ) ; 
m.model=p_send3 (printer, O_PR_SENSE_MODEL, &ém.name[0]) ; 
f_write(fcb, &m.model,1+p_slen(&m.name[0])+1); 
p=(UBYTE *)p_send3 (printer, O_PR_GET_HD, PRINTER_HDR_TOP) ; 
f_write(fcb,p,p_slen(p)+1); 
p=(UBYTE *)p_send3 (printer, O_PR_GET_HD, PRINTER_HDR_BOT) ; 
f_write(fcb,p,p_slen(p) +1); 
p_close(fcb); 
return (0); 


} 


This code uses the following PRINTER methods: 


@ pr_get_params: returns the address of the pRINTER_PARams data structure inside PRINTER 
property 


@ pr_sense_model: writes a ZTS specification of the current .wdr file, and returns the index 
number of the current printer model within this file 


@ pr_get_hd: returns the address of a ZTS giving either the header text or the footer text, 
depending on the final parameter passed. 


Note that aspects of for example the alignment and the printer font of the header and footer are stored as 
parts of the fixed-length pRINTER_PaRaMs struct: it is only the (variable length) text of the header and 
footer that requires a separate method to sense it. 


13 - 34 


13 PRINTING 


The code to read the print context from file, and to set it into PRINTER property, is rather longer - but that 


is only because of the integrity tests that it makes: 


LOCAL_C VOID TryLoadPrintContext (VOID) 
{ 
P_INFO junk; 
VOID *fcb; 
VOID *printer; 
UBYTE buf[512]; 
INT blen; 
INT ind; 
UBYTE *pl1,*p2,*p3,*p4; 


if (p_finfo (SAVED_FILE_NAME, &junk) ) 
return; /* eg file does not exist */ 


f_open (&fcb, SAVED_FILE_NAME, P FOPEN |P FSTREAM|P_FSHARE) ; 


blen=f_read(fcb, &buf[0],512); 
p_close(fcb); 
if (blen<=sizeof (PRINTER_PARAMS) +2) 

goto file_corrupt; 
pl=(é&buf[0]); 
blen-=sizeof (PRINTER_PARAMS) 
p2=pl+sizeof (PRINTER_PARAMS) 
ind=p_bloc(p2+1,blen-1,0); 
if (ind<0) 

goto file_corrupt; 
p3=p2+1t+ind+l; 
blen-=1+ind+1; 
if (blen<=0) 

goto file_corrupt; 
ind=p_bloc(p3,blen,0); 
if (ind<0) 

goto file_corrupt; 
p4=p3t+indt+1; 
blen-=ind+1; 
if (blen<=0) 

goto file_corrupt 
ind=p_bloc(p4,blen, 0) 
if (ind!=blen-1) 

{ 
file_corrupt: 

hinfoPrint (DELETING_CORRUPT_FILE) ; 

p_delete (SAVED_FILE_NAME) ; 

return; 

} 
p_send2 (w_ws, O_WS_ENS_PRINT_CONTEXT) ; 
printer=w_ws-—>wserv.printer; 


’ 
’ 


’ 
’ 


p_bcpy((VOID *)p_send2 (printer, O_PR_GET_PARAMS) ,p1, sizeof (PRINTER_PARAMS) ); 


p_send5 (printer,O_PR_SET_MODEL, FALSE, p2+1,*p2); 
p_send4 (printer, O_PR_SET_HD, PRINTER_HDR_TOP,p3) 
p_send4 (printer,O_PR_SET_HD, PRINTER_HDR_BOT, p4) 
} 


The two new PRINTER methods used here are: 


’ 
’ 


@ pr_set_model: the first parameter is a pointer to a ZTS giving the .wdr filename, and the second 


is the printer model index number 


¢ pr_set_ha: the first parameter specifies whether the header text or the footer text is being set, 


and the second gives a ZTS containing this text. 


13 - 35 


CHAPTER 14 


LINK PASTE 


This chapter contains a practical introduction to programming “Link Paste” (also called “Bring”): 
e how to service link paste requests from other applications (the server side of link paste) 
e how to obtain link paste data from other applications (the client side of link paste) 
e = the role of the OLIB classes LINKsv, LINKCL and sYSTEM 
e the definitions of various link data “types” 
e — specific HWIM assistance for link paste involving edit windows. 


For the sake of concreteness, the discussions in this chapter are mainly based around various 
modifications and extensions of the Ehello example application that features in the opening sections of the 
Edit Windows chapter, and which is optionally installed into the \sibosdk\ehello directory. It should be 
stressed, however, that it is possible to grasp the concepts involved in programming link paste 
independently of any appreciation of programming edit windows. 


The modifications required to the original Ehello code, to add link paste functionality, are all given below. 


The server side of link paste 


When the user selects the ‘Bring’ menu command in application X, say, and sees new data added to 
application X, this data has come from another application - Y, say - that was in background at the time 
the menu command was issued. In this example, application X is the “link client” and application Y is 
the “link server’. 


It is important to realise that the data is fetched directly from application Y at the time the ‘Bring’ request 
is issued. That is, the data is not fetched into any independent “clipboard application” at any earlier stage 
(eg when application Y was in foreground). There is no “clipboard application” in the Epoc architecture 
(neither at the OS level nor at the HWIM level - nor at any intermediate level). 


Thus applications which wish to function as link servers have to be prepared to receive requests for data 
whilst they are in background. These requests are in fact interprocess communication (IPC) messages of a 
particular type - but this is largely hidden from HWIM applications, with the details of the IPC being 
handled, on the server side, by the OLIB t1nxsv class. 


Creating a LINKSV subclass instance 


The tinxsv class contains two deferred methods, 1s_set_format and 1s_get_data, that have to be 
supplied by any link-serving application. For this reason, applications never create an instance of LINKsv 
itself, but rather an instance of an application-specific subclass of linksv. 


For example, the following additional class definition could be added into the file ehello.cat (see later for 
the significance of the property fields) 


14-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


INCLUDE ipc.g 


CLASS ehlinksv linksv 
{ 
REPLACE ls_set_format 
REPLACE ls_get_data 
PROPERTY 
{ 
WORD sellen; 
TEXT *pbuf; 
} 
} 


and the following line of code should be added to application start-up code, eg in the ws_dyn_init method 
of the wszrv subclass (in other applications, the code could be placed instead in the com_init method of 
the comman subclass - depending on what was most convenient): 


£_newsend (CAT_EHELLO_EHELLO, C_EHLINKSV, O_SV_INIT) ; 


Note that there is rarely any need to record the handle of the created L1nxsv subclass instance anywhere in 
application code: 


e the object will continue in existence throughout the lifetime of the application, and so there is no 
need to hold onto its handle just in order to send it a dest roy message at some later stage 


e the handle of the object is in fact recorded in the linked-list of so-called “server handles” held by 
the system recs object, and whenever a suitable IPC message is received by the application, the 
Ipcs object automatically redirects it to the L1nxsv object. 


Note also that any sv_init call will fail - with panic 55 - unless the flag rLg_appMaN_IPcs is set in main 
(in fact, this flag is effectively always set for HWIM applications on the Series 3a - but it is good practice 
to set it explicitly, in main, whenever an application creates a sERVER object of its own). Therefore the line 
in ehmain.c that defines the appman flags for the application becomes 


app. flags=FLG_APPMAN_RSCFILE|FLG_APPMAN_SRSCFILE|FLG_APPMAN_CLEAN 
| FLG_APPMAN_IPCS|FLG_APPMAN_LINKING; 


so that w_am is initialised with an 1pcs component (see later for the significance of the 
FLG_APPMAN_LINKING flag). 


Note finally that it is impossible to delay creating the L1nxsv object until an actual link paste data request 
is received - which might at first seem a good idea (in order to cut down on the memory overhead of an 
object that might never actually be needed). The point is that the Linxsv object is needed in order to 
receive the request in the first place (on pain of a panic 158). However, it is common practice to delay the 
allocation of additional data buffers (such as the pbug field of EHL1InKsv will point to) until actually 
required. 


Declaring link paste server status 


In order for an application to receive a link paste data request, two pre-conditions have to be satisfied: 
e the application has created and initialised a L1nxsv object - as described above 


e the application has sent the system component of w_am an sy_link_server message, declaring 
the presence (and type) of linkable data. 


Declaring current link paste server status is done every time the application passes into background. 
Therefore the application has to subclass the ws_background method of wserv. For example, the 
declaration of EHwsERv in ehello.cat becomes 


CLASS ehwserv wserv 
{ 
REPLACE ws_dyn_init 
REPLACE ws_background 
} 


14-2 


14 LINK PASTE 


with the actual contents of the ws_background method being: 


GLREF_D PR_APPMAN *w_am; 
GLREF_D PR_EDWIN *DatApp3; 


METHOD VOID ehwserv_ws_background(PR_EHWSERV *self) 


{ 
INT which; 


if (!(self->wserv.flags&PR_WSERV_RECEIVED_KEY) ) 
return; 
which=0; 
if (DatApp3->edwin.select) 
which=1<<DF_LINK_TEXT; 
p_send4 (w_am->appman.system,O_SY_LINK_SERVER, which, 0) ; 
} 


The significance of the test on pR_WSERV_RECEIVED_FLAG in this code is to prevent the application 
“stealing” the link paste server status just because the user happens to task through it. Suppose that the 
user has, long ago, selected some text in application A, but now wants to link paste some data from 
application B to application C. The user therefore highlights the data in B, and then tasks to C. But 
suppose that A is tasked to foreground first - as may well happen if, in particular, A and C are instances of 
the same application (eg two different Spreadsheet files). So long as the user pressed no keys while 
transiently tasking through A (apart from the task keys themselves), the above test ensures that the 
eventual ‘Bring’ menu command in C fetches data from B and not from A. 


The bulk of the link-paste specific code in an application's ws_background method usually consists of 
determining the set of link-paste “types” that the current state of the application can support. See later for 
a discussion of various different standard link paste types. In the example above, the decision is a 
straightforward choice between two possibilities: either there is some selected text in the editor - in which 
case linkable data of type pr_LINK_TExT is available - or else there is not - in which case no linkable data 
is available. 


In general, an application will often be able to provide more than one type of data at any given time. For 
example, an application will often be able to offer both “plain text” and “native format data” - depending 
on who the recipient of the data is. Thus if the recipient of the data is another instance of the same 
application (eg one spreadsheet link-pasting data from another), a “native format” data transfer will 
generally transfer more information that the kind of “plain text” data transfer that would happen, instead, 
if the recipient application is not the same. 


For this reason, the sy_1ink_server method takes two parameters, which between them form a uLoNG 
“mask” made up of 32 bits. The more different bits that are set, the more different types of formats which 
the link server is prepared to “render” at that moment. To signal that the link server is prepared to render 
format type DF_LINK_TEXT, the bit (1<<pF_LINK_TEXT) should be set in the mask, and so forth. 


Note that if the application currently has no data available for link paste, the mask value of zero should be 
reported. This will clear any record that may be left over from earlier, when the application did have data 
available for link paste. 


In the example above, pat aApp3 has been set to point, for convenience, to the zpw1n object in the main 
window of the application - via the following line added to ehbwin_wn_init: 


GLREF_D PR_EDWIN *DatApp3; 


DatApp3=self-—>ehbwin.edwin; 


The test on DatApp3->edwin.select therefore just detects whether there is any select region in the editor: 
if there is, the application is prepared to render plain text format link paste data; otherwise, it has no data 
to render. 


14-3 


OBJECT ORIENTED PROGRAMMING GUIDE 


Initialising the SYSTEM component of w_am 


Before the application can send a sy_link_server message to w_am->appman. system, it is necessary to 
arrange for the creation and initialisation of the system component of w_am. This is arranged very simply: 
by setting the FLc_APPMAN_LINKING flag in main. 


Note carefully that setting the rLc_appmMan_system flag will not have the desired effect. That would 
succeed in creating and initialising an instance of the system object, but it would be the wrong type of 
SYSTEM object - being oriented towards the process sys$shll.img instead of towards the process 
sys$wsrv.img (see the OLIB Reference manual for more details). 


The anatomy of a link paste transaction (server-side viewpoint) 


In general, a link-paste transaction is seen, at the server end, as 
e one call to 1s_set_format 
e followed by a number of calls (one or more) to 1s_get_data. 


The transaction takes place in a number of stages, in general, since there is a limit to the amount of data 
that can reasonably be transferred in any one stage, and in order that the foreground application can 
remain responsive to redraw requests in the meanwhile. The link server will usually possess some “state 
variables” - generally in the property of its Ltnxsv subclass - in order to keep track of the progress of the 
current transaction. 


The purpose of the 1s_set_format call is 
e to specify which of the profferred data formats is actually being requested 


e to allow the link server to reset its state variables, reflecting the fact that such-and-such a type of 
link paste data transfer is about to start. 


The purpose of each subsequent 1s_get_data call is 


e to assemble data into a suitable buffer (if necessary) and to set a suitable variable (namely the 
linksv.buf property field) to point to this buffer 


e to specify the length of the data to be transferred in this stage of the transaction (this should be 
written to the 1inksv.1en property field) 


e to indicate, by means of the return value (TRUE or FALSE) whether the transaction has completed. 
Notice that a link paste transaction can end either because: 

e the link server has no more data to transmit 

e the link client does not wish to receive any more data. 


In the latter case, system code informs the L1nxsv subclass by means of specifying a negative len 
parameter - see the example code below. (Ordinarily, this parameter gives the size of the buffer in the 
data space of the recipient where the data is to be copied to.) 


Note that the buffer whose address is written to 1inksv.buf must not be on the stack (for obvious 
reasons). 


Example LINKSV code 


The code for the 1s_set_format and 1s_get_data methods of the zxL1nxsv methods is as follows: 


#include <ehello.g> 
#include <p_gen.h> 


GLREF_D PR_EDWIN *DatApp3; 


LOCAL_C VOID LinkServiceOver (PR_EHLINKSV *self) 
{ 
p_free(self—>ehlinksv.pbuf) ; 
self—>ehlinksv.pbuf=NULL; 
self—>ehlinksv.sellen=0; 


} 


#pragma METHOD_CALL 


14-4 


14 LINK PASTE 


METHOD VOID ehlinksv_ls_set_format (PR_EHLINKSV *self, INT type) 
{ 
UWORD top; 
SENSE_EDWIN sense; 
SE_EDWIN se; 


LinkServiceOver (self); 
if (type!=DF_LINK_TEXT) 
p_leave (E_GEN_NSUP) ; 
p_send3 (DatApp3, O_EW_SENSE, &sense) ; 
if (sense.cursor<sense.anchor) 
{ 
top=sense.cursor; 
self—>ehlinksv.sellen=sense.anchor-top; 
} 
else if (sense.cursor>sense.anchor) 
{ 
top=sense.anchor; 
self—>ehlinksv.sellen=sense.cursor-top; 
} 
else 
return; 
self—>ehlinksv.pbuf=f_alloc(self-—>ehlinksv.sellen) ; 
p_send3 (DatApp3, O_WN_SENSE, &se) ; 
p_bcpy (self—->ehlinksv.pbuf,se.buf+top, self—>ehlinksv.sellen) ; 
} 


METHOD INT ehlinksv_ls_get_data(PR_EHLINKSV *self,INT len) 
{ 


if (len<o || !self->ehlinksv.sellen) 
{ 
LinkServiceOver (self); 
return (FALSE) ; 
} 
if (len>self->ehlinksv.sellen) 
len=self->ehlinksv.sellen; 
self—>linksv.len=len; 
self—>linksv.buf=self-—>ehlinksv.pbuf; 
self—>ehlinksv.sellen=0; 
return (TRUE) ; 
} 


In the 1s_set_format method, the code somewhat kindly just calls p_1eave (Z_GEN_NSUP) in any case that 
the client requests data not available for rendering. Arguably, it might be more appropriate to panic the 
client in this case: 


p_ppanic(self->server.cid, xxx); 


with some well-chosen panic number xxx (since the application has at no time ever declared that it could 
supply any other format of data - so there must be a bug in the client program for requesting such data). 


General remarks about link servers 


Note that a request for data to be rendered for link paste can be received even if 
e the application has one or more dialogs showing 
e the application has a menu showing 
e the application has a help screen showing. 


The HWIM architecture handles this completely smoothly: the application receives ws_background, 
1ls_set_format, and 1s_get_data messages completely independently of whether there are dialogs or 
menus (etc) current. This is in contrast with the case of, for example, most Hwif or OPL/w applications, 
when any link paste request IPC messages would go completely unacknowledged in such a case. (And 
since there is an assumption in L1InKcL code that link paste request IPC messages do not remain 
unacknowledged - on pain of the link client application hanging - this is a strong reason why non-HWIM 
applications should, in general, avoid declaring themselves as link paste servers.) 


14-5 


OBJECT ORIENTED PROGRAMMING GUIDE 


Some standard link paste data formats 


In most cases, applications need only consider three standard link paste data formats: 
@ = DF_LINK_TEXT, in which data is transmitted as a series of buffers of purely printable characters 
@  DF_LINK_TABTEXT, in which the buffers of data can contain, in addition, tab characters 
@ DF_LINK_PaRas, in which the buffers of data can also contain embedded paragraph delimiters. 


(In addition to these standard formats, applications may also consider any number of application-specific 
so-called native formats. Native formats are discussed later in this chapter.) 


Now the code discussed so far may have given the impression that a pr_LINk_TExT data transfer only 
consists of one buffer of text. But that would be a mistaken impression. To see this, consider the 
following simple changes in the Ehello code: 


e declare an extra worp property field, ntimes, for EHLINKSV 


e change the ehlinksv_1s_get_data method so that the se1ien property field only gets reset to 
zero after the available data has been link pasted out three times in all. 


Thus the 1s_get_data method becomes 


METHOD INT ehlinksv_ls_get_data(PR_EHLINKSV *self,INT len) 
{ 
if (len<oO || !self->ehlinksv.sellen) 
{ 
LinkServiceOver (self); 
return (FALSE) ; 
} 
if (len>self->ehlinksv.sellen) 
len=self->ehlinksv.sellen; 


self—>linksv.len=len; 

self—>linksv.buf=self-—>ehlinksv.pbuf; 

if (!--(self->ehlinksv.ntimes) ) 
self—>ehlinksv.sellen=0; 

return (TRUE) ; 

} 


and a new line 
self—>ehlinksv.ntimes=3; 


appears at the end of the 1s_set_format method. Highlighting some text in the editor and then choosing 
‘Bring’ in an application such as the Word Processor or the Database now results in the highlighted text 
being transferred three times in all - going into three different paragraphs in the process. 


Note however that some link clients - such as the Series 3a Agenda - will terminate the transaction after 
only absorbing the first buffer of data. This is perfectly within their right (see below for the details of how 
to achieve this result). 


DF_LINK_TEXT and DF_LINK_PARAS contrasted 


Next, consider another change in Ehello code, in which the line in the ws_background method now 
declares the availability of pp_LINK_paRas as well as DF_LINK_TEXT: 


if (DatApp3->edwin.select) 
which= (1<<DF_LINK_TEXT) | (1<<DF_LINK_PARAS) ; 


At the same time, the test on type in the 1s_set_format method has tobeome less restrictive: 


if (type!=DF_LINK_TEXT && type!=DF_LINK_PARAS) 
p_leave (E_GEN_NSUP) ; 


Suppose the text Hello world is highlighted in Ehello and the user tasks to the Word Processor and 
invokes ‘Bring’. The text that appears in the Word Processor window this time is 


Hello worldHello worldHello world 


14-6 


14 LINK PASTE 


ie with all three copies being added into the current paragraph, whereas before, when DF_LINK_TEXT was 
specified, the text appearing in the Word Processor wndow would have been 


Hello world 
Hello world 
Hello world 


with the three copies going to different paragraphs. 


This experiment confirms that different link paste formats can differ not only in the contents of the buffers 
transferred, but also in the interpretation of the data in these buffers. 


When DF_LINK_PaRAs applies, the link paste server agrees to include paragraph delimiters (ie character 
\o's) in place in the text being transmitted, and the link paste client agrees not to infer any additional 
paragraph breaks between separate buffers transmitted. When pF_LINK_TEXT (or DF_LINK_TABTEXT) 
applies, however, the server agrees not to pass any zeros in place, and the client must infer a paragraph 
break in between each pair of buffers of text. 


One limitation of pF_LINK_TExT should now be apparent, involving the size of the buffer used to transfer 
the text. If a paragraph of text is longer than the size of this buffer, it will have to be split into two, with 
the different parts being interpreted as belonging to two different paragraphs. (It is possible to observe 
this effect with the Notes example application - which only uses the DF_LINK_TEXT format.) 


Word wrap and link paste 


Note that soft line breaks on the screen of the link server are generally ignored by link paste protocols. 
Here, a “‘soft line break” is one that is caused purely by the application of word-wrap, and which might 
well fall in a different place were the screen window changed or the screen display font changed. 


In general, word wrap in the client will produce a very different result to word wrap in the server. For 
this reason, none of the standard link paste formats pay any regard to soft line breaks (this matches the 
fact that, as discussed in the Edit Windows chapter, there is no representation of soft line breaks at the 

document level of an EDWIN object). 


For example, the terminal emulation application Comms always requests DF_LINK_PARAS, if it is available, 
and word-wraps the text received according to the “Bring margin” which the user can specify 
independently. Note that specifying pF_LINK_TExT would be /ess satisfactory, for the reason (noted 
earlier) that paragraphs in the link server would sometimes end up split in two - at an apparently random 
position. 


DF_LINK_TABTEXT 


In some ways, DF_LINK_TABTEXT does for embedded tabs what pr_LINkK_PaRas does for embedded 
paragraph delimiters: 


e if aclient asks for pF_LINK_TABTEXT, it is prepared to hunt for tabs in the passed buffers, and 
interpret them as makes sense within the context of the client application 


e if a link server is asked for DF_LINK_TEXT, it must ensure that tab characters are all stripped out 
of the text before it is transmitted. 


For example, the Spreadsheet application regards embedded tabs as column delimiters. Edit windows, on 
the other hand, interpret embedded tabs according to the tabstops (and other relevant parameters) 
applicable to that edit window at the time. 


If tab characters have to be removed before transmission, it is standard simply to convert them into single 
spaces (it makes little sense to attempt to convert them to a variable number of spaces, since in general 
the number of spaces required is going to depend on settings in the client context). 


The hierarchy of text types 


Note that the functionality of pF_LINK_PaARas is assumed to be a superset of that of DF_LINK_TExT. Any 
application which can handle embedded paragraph delimiters is assumed to be able to handle embedded 
tab characters. 


14-7 


OBJECT ORIENTED PROGRAMMING GUIDE 


The client side of link paste 


Just as there is an OLIB class, t1nxsv, which encapsulates most of the functionality of the server side of 
link paste, so also is there an OLIB class, ttnxct, which encapsulates most of the functionality of the 
client side of link paste. Between them, these two classes protect application programmers from needing 
to worry about the details of the IPC messaging involved in the implementation of link paste. 


Determining whether there is suitable data available 


When an application receives a ‘Bring’ menu command, one of the first things it has to do is to find out 
e if any other application has registered data as available for link pasting 
e what formats that data can be rendered into. 

To this end, the system component of w_am has to be sent an sy_link_paste message, eg as follows: 


WORD lkpid; 
ULONG fmt; 


lkpid=p_send3 (w_am—->appman.system,O_SY_LINK_PASTE, &fmt) ; 
if (!lkpid || ! (fmt & (1<<DF_LINK_TEXT))) 

{ 

hiInfoPrint (-SYS_NOTHING_TO_BRING) ; 

return; 


} 


The utone mask of available formats, if any, is written into fmt, and the PID of the process which has 
registered the data is written to 1kpid. 


Before the application can send a sy_link_paste message tO w_am->appman. system, It is necessary to 
arrange for the creation and initialisation of the system component of w_am. This is arranged very simply: 
by setting the FLc_APPMAN_LINKING flag in main. 


Note incidentally that it is perfectly possible for an application to act as a link client but not as a link 
server. Such an application would have to set FLG_APPMAN_LINKING, but would not need to set 
FLG_APPMAN_1Ipcs (unless, of course, it created other kinds of sERvVER objects, ie apart from LINKSv). 


In the above code fragment, a test is made on fmt as well as on 1kpia. A test should always be done on 
fmt, though the nature of the test made will of course depend on which kinds of link paste data the 
application is prepared to accept. 


The text of the system message sys_NOTHING_TO_BRING is, in English, Nothing to bring. Applications are 
free to substitute more specific messages if they wish. 


The anatomy of a link paste transaction (client-side viewpoint) 


Whereas tinxsv is designed to be subclassed - so that applications never create a direct instance of 
LINKSV - LINKCL is useable as it stands. For this reasons, applications have no need to declare any 
subclass of L1nxc1 in their .CAT file. 


Thus application link paste client code will usually contain a line such as 


link=f_newsend (CAT_EHELLO_OLIB, C_LINKCL, O_LC_START, lkpid, DF_LINK_TEXT) ; 


directly creating and initialising an instance of L1nkcu. Here, the PID of the link server is specified as one 
parameter, and the required format type is specified in another. (The format specified in this 1c_start 
message is passed through to the 1s_set_format method processed by the link server.) 


Note another contrast with the case of Ltnxsv: the instance of L1nxc1 1s only created when explicitly 
needed - in response to a ‘Bring’ menu command. The handle of the instance needs to be stored 


e so that subsequent Lc_GET_DATA messages can be sent to it, for each buffer of data to be collected 
e so that an tc_stop message can be sent to it, if required 
e so that the object can be destroyed at the end of the transaction. 


In practice, once created, the n1nkc1 object usually has its handle added to the cleanup list, and the way 
the object is destroyed is by a subsequent call to cl_clean_item. 


14-8 


14 LINK PASTE 


Simple example of use of LINKCL 


Consider the following modification of Ehello: when the key combination PSION-ENTER is received, it is 
regarded as a ‘Bring’ menu instruction (recall that, for simplicity, Ehello has only the barest bones of a 
real menu bar). Code gets added to ehbwin.c as follows: 


#include <olib.h> 
#include <s_.h> 


GLREF_D PR_APPMAN *w_am; 
GLREF_D PR_WSERV *w_ws; 
GLREF_D PR_EDWIN *DatApp3; 


LOCAL_C VOID DoLinkPaste (VOID) 
{ 
WORD lkpid; 
ULONG fmt; 
VOID *link; 
INT cl_link; 
TEXT buf[52]; 
WORD len; 


lkpid=p_send3 (w_am—->appman.system,O_SY_LINK_PASTE, &fmt) ; 
if (!lkpid || ! (fmt & (1<<DF_LINK_TEXT) )) 
{ 
hInfoPrint (-SYS_NOTHING_TO_BRING) ; 
return; 
} 
link=f_newsend (CAT_EHELLO_OLIB, C_LINKCL, O_LC_START, lkpid, DF_LINK_TEXT) ; 
cl_link=cl_add_object (link); 
len=p_send4 (link, O_LC_GET_DATA, &buf[0],50); 
if (len>0) 
{ 
buf [len] =0; 
p_send4 (DatApp3, O_EW_REPLACE, &buf[0],0); 
} 
w_ws-—>wserv.flags&=(~PR_WSERV_RECEIVED_KEY) ; 
cl_clean_item(cl_link); 


} 


#pragma METHOD_CALL 


METHOD VOID ehbwin_wn_key(PR_EHBWIN *self, INT keycode, INT mods) 


{ 
SE_EDWIN sense; 


if (keycode!=W_KEY_RETURN) 

p_send4 (self—>ehbwin. edwin, O_WN_KEY, keycode, mods) ; 
else if (mods&W_PSION_MODIFIER) 

DoLinkPaste(); 
else 


} 


In a more general setting, one call to 1c_start will normally be followed by a sequence of calls to 
lc_get_data, continuing until the 1en return value from one of them is negative (it will actually be the 
value &_FILE_EOF, but it is not necessary to test for this explicitly). 


The client also has the option of terminating the transaction by calling 1c_stop at any stage. This has not 
been done in the above example, simply because the dest roy method of L1nxct (which is triggered by the 
above call to cl_clean_item) automatically sends self an 1c_stop message. 


Note that the client has to specify the amount of data it is prepared to accept, at each stage of the 
transaction, by means of the final parameter to the 1c_get_data message. This value is communicated to 
the link server as the 1en parameter in the 1s_get_data message. 


The significance of the line of code 
w_ws-—>wserv.flags&=(~PR_WSERV_RECEIVED_KEY) ; 


is to avoid the link client “stealing” the link paste server status, when it next passes into background. 


14-9 


OBJECT ORIENTED PROGRAMMING GUIDE 


Special help with link pasting to and from edit windows 
The ew_bring_in method of EDWIN 


In practice, any application that wishes to link paste text into an instance of Epw1n would actually use the 
ew_bring_in method of that class - which encapsulates the stages of 


e creating the L1nxci object 

e sending the relevant 1c_start and 1c_get_data messages 

e sending various messages to itself. 
The ew_bring_in method also encapsulates knowledge of the various standard types of textual link paste 
formats. For interest, the complete code of edwin_ew_bring_in follows (though it will be necessary to 


read the Edit Windows chapter carefully to appreciate some parts of it - eg some of the utility routines 
used): 


METHOD INT edwin_ew_bring_in(PR_EDWIN *self,INT lkpid, INT format) 


PR_ROOT *link; 
INT cl_link; 
INT SingleShot; 
UINT pos; 

UINT totlen; 
INT len; 

INT err; 

INT offset; 
TEXT buf[258]; 


CheckNotReadOnly (self); 

SingleShot=format &EW_BRING_SINGLE_SHOT; 

if (formaté (1<<DF_LINK_PARAS) ) 
format=DF_LINK_PARAS; 

else if (format& (1<<DF_LINK_TABTEXT) ) 
format=DF_LINK_TABTEXT; 

else 


format=DF_LINK_TEXT; 
link=f_newsend (CAT_HWIM_OLIB, C_LINKCL, O_LC_START,1kpid, format) ; 
cl_link=cl_add_object (link) ; 
pos=self->edwin.cpos; 
totlen=0; 
buf [0]=0; 
offset=1; 
while ((len=p_send4 (link, O_LC_GET_DATA, &buf[1],256) ) >=0) 
{ 
if (!offset) 
lent+; 
if ((err=p_entersend5 (self,O_EW_EP_INSERT,pos, &buf [offset],len) ) !=0) 
{ 
p_send4 (self-—>edwin.doc, O_EP_DELETE, self—>edwin.cpos, self- 
>edwin.cpos+totlen) ; 


cl_clean_item(cl_link); 
p_send3 (self, O_EW_LEAVE, err); 
} 

totlent+=len; 

post=len; 

if (SingleShot) 
{ 
p_send2 (link, O_LC_STOP) ; 
break; 
} 

if (format !=DF_LINK_PARAS) 
offset=0; 


} 
self—>edwin.clen+=totlen; 
EdwinFwdChange (self) ; 
SetEdwinSelect (self, self->edwin.cpos,totlen) ; 
w_ws—>wserv.flags&=(~PR_WSERV_RECEIVED_KEY) ; 
cl_clean_item(cl_link); 


return (0); /* confirm no leave */ 


} 


14-10 


14 LINK PASTE 


Simple example of calling EW_BRING_IN 


The code in the ncoe_bring method of the command manager of the example Notes application 
(optionally installed into \sibosdk\notes) shows how simple it can be to call ew_bring_in: 


METHOD VOID nocomman_ncoe_bring(PR_NOCOMMAN *self) 


{ 
INT lkpid; 
ULONG 1kfmt; 


CheckEditing(self) ; 
lkpid=p_send3 (w_am—->appman.system,O_SY_LINK_PASTE, &lkfmt) ; 
if (!lkpid || !(1kfmt & (1<<DF_LINK_TEXT) )) 

{ 

hInfoPrint (-SYS_NOTHING_TO_BRING) ; 

return; 


} 
p_send4 (DatApp3, O_EW_BRING_IN, lkpid, 1<<DF_LINK_TEXT) ; 


} 


Note however that the value zEw_BRING_SINGLE_sHOT can be orred into the specified format mask, to force 
the transaction to terminate after just one stage. If the code given earlier for the Ehello application were 
to be modified to call ew_bring_in instead, Ew_BRING_SINGLE_sHoT would need to be specified in that 
case. 


The EWLINKSV class 


Just as the ew_bring_in method of rpwin provides system support for link pasting into edit windows, so 
also is there system support from link pasting out of edit windows. This is the zwL1nxsv class, from 
HWIM. 


Despite its name, EWLINKSv is not a subclass of Linxsv; rather, it is a subclass of Root, but its name 
indicates its intended use as a component of a L1nKsv (subclass) object. 


For example, consider the declaration of the noLinxsv class in the Notes application category file: 


CLASS nolinksv linksv 
{ 
REPLACE ls_set_format 
REPLACE ls_get_data 
PROPERTY 


{ 
PR_EWLINKSV *ewls; 


} 
} 


The intended use of ewLinxsv can be seen from the following example code (providing the 1s_set_format 
and 1s_get_data methods of NoLinxsv): 


#include <notes.g> 
#include <hwim.h> 


GLREF_D PR_EDWIN *DatApp3; 


LOCAL_C VOID DestroyLinker (PR_NOLINKSV *self) 
{ 


hDestroy (self->nolinksv.ewls) ; 
self—>nolinksv.ewls=NULL; 
} 


#pragma METHOD_CALL 


METHOD VOID nolinksv_ls_set_format (PR_NOLINKSV *self, INT type) 


{ 
DestroyLinker (self) ; 
self—>nolinksv.ewls=f_newsend (CAT_NOTES_HWIM, C_EWLINKSV, O_EWLS_INIT,DatApp3,type) ; 


} 


14-11 


OBJECT ORIENTED PROGRAMMING GUIDE 


METHOD INT nolinksv_ls_get_data(PR_NOLINKSV *self,INT len) 


{ 


if (len>0) 


{ 


if (((INT) (self->linksv.len=p_send4 (self->nolinksv.ewls, 


O_EWLS_EXTRACT, &Sself->linksv.buf, len) ) ) >=0) 


return (TRUE) ; 


} 


DestroyLinker (self) ; 
return (FALSE) ; 


} 


Evidently, the ew1s_init method needs to be passed the handle of the associated Epw1n instance (in this 
case, this is stored at patapp3) and the specified format type. Thereafter the ewis_extract method 
returns the value that should be written into 1inksv.1len (or a negative number, if the transaction has 
terminated), and also writes back, to one of the passed parameters, the value for 1inksv.buf. 


The three text formats revisited 


The code for zEwLinxsv, reproduced below, contains (in conjunction with the code for the ew_bring_in 
method of epwin) what is in effect the implicit definition of the three standard link paste text formats: 


CLASS ewlinksv root 
Component of linksv class, extracts data from edwin 


{ 


ADD ewls_init 
ADD ewls_extract 


PROPERTY 
{ 


VOID *doc; 

WORD state; 

UWORD pos, parend, posend; 
TEXT buf[256]; 


} 
} 


METHOD INT ewlinksv_ewls_extract (PR_EWLINKSV *self,TEXT **ppb,UINT blen) 


14-12 


{ 


UINT len; 


INT striptabs; 


TEXT *pb; 


TEXT *pbend; 


len=self->ewlinksv.posend-self-—>ewlinksv.pos; 


if (!len) 


return(-1); /* finished */ 
if (len>=blen) 
len=blen; 
p_send5 (self—>ewlinksv.doc, O_EP_EXTRACT, self->ewlinksv.pos, 


&self—->ewlinksv.buf[0],len); 


if (self->ewlinksv.state!=DF_LINK_PARAS) 


{ 


striptabs=(self->ewlinksv.state==DF_LINK_TEXT) ; 
pb=(&self->ewlinksv.buf[0]); 


for 


(pbend=pb+len; pb<pbend; pb++) 


(!*pb) 

{ 

self->ewlinksv.post++; /* skip the zero too */ 
break; 

} 

(striptabs && *pb=='\t') 

*pb=" '; 


len=pb- (&self->ewlinksv.buf[0]); /* excludes any trailing zero */ 


} 


self—>ewlinksv.post=len; 
*ppb= (&self—>ewlinksv.buf[0]); 
return (len); 


} 


14 LINK PASTE 


METHOD VOID ewlinksv_ewls_init (PR_EWLINKSV *self,PR_EDWIN *edwin, INT state) 


{ 
UINT len; 


self—>ewlinksv.doc=edwin->edwin.doc; 

len=p_send3 (edwin->edwin.scrimg, O_SI_GET_SELECT, &self—>ewlinksv.pos) ; 
self—>ewlinksv.posend=self—>ewlinksv.pos+len; 
self->ewlinksv.state=state; 


} 


Native formats 


In many cases, applications will wish to support “native” formats of link paste data. For example, the 
Word Processor link pastes styles and emphasis data along with the text selected. Another example is that 
the Series 3a Agenda link pastes the details of an appointment - including the alarm setting and memo 
setting, if any. 


In cases like this, the mask specified in the sy_link_server message should include the bit for 
DF_LINK_NATIVE. 


For example, the ws_background method of the Word Processor is as follows: 


METHOD VOID wpwserv_ws_background(PR_WPWSERV *self) 


{ 
INT type; 


if (self->wserv.flags&PR_WSERV_RECEIVED_KEY) 
{ 
type= (1<<DF_LINK_TEXT) + (1<<DF_LINK_TABTEXT) + (1<<DF_LINK_PARAS) ; 
if (!IsAlias()) /* see whether plain text alias */ 


type= (1<<DF_LINK_TEXT) + (1<<DF_LINK_TABTEXT) + (1<<DF_LINK_PARAS) + (1<<DF_LINK_NATIVE) ; 
if (!((PR_EDWIN *) HandTWin) ->edwin.select) 
type=0; 
p_send4 (w_am—->appman. system, O_SY_LINK_SERVER, type, 0) ; 
} 
} 


Note that the Window Server process - which is the central store, on the Series 3 and the Series 3a, for 
link paste data information - never reports the pF_LINK_NATIVE bit to an application that differs from that 
which registered the data. (This test is based on the result of calling p_pname.) For this reason, when the 
DF_LINK_NATIVE bit is set in the mask of available formats, the application can rest assured that the link 
paste server is indeed the same application as itself (though, possibly, running under a different alias). 


By convention, the top eight bits in the 32-bit wide mask of formats are reserved for applications passing 
more information to each other about which types of their own native format are presently available. 


Final comments 


Note that the link paste server must run unattended. There is no point in presenting the user with a query 
dialog, asking how the link paste service is to proceed. This is because the link paste server is in 
background: the user will not see the dialog, and will just notice that the link paste operation in the 
foreground window has completely stalled. 


If really desired, the link paste server can obtain additional information from its client, by having the 
client present the query dialog on its behalf. The information required to effect this would have to be 
defined and included as part of the native format (bear in mind that each partner in the process has access 
to the PID of the other so that the link paste IPC can be supplemented, if desired, by other forms of IPC at 
that moment). 


One other point that may be worth mentioning is the fact that the link server can happily call p_ieave 
(directly or indirectly) at any stage of its operation. System code (in L1nksv and LINnkcL) ensures that the 
error notification takes place in the client process, and not in the server process. 


14 - 13 


CHAPTER 15 


HWIM RESOURCE FILES 


The basic information about resource files that applies to all SIBO applications is covered in the Resource 
Files chapter of the Additional System Information manual. That chapter includes: 


e the resource file format 

e the different possible locations for resource files 

e advice about multi-lingual applications 

e use of the resource compiler tool rcomp.exe 

e the allowed content of a .rss source file, including struct and constant declarations 


This chapter contains additional information that is specific to HWIM applications. 


The application resource file 


As was mentioned in the Introduction chapter, all HWIM applications must have an application resource 
file that contains at least the resources required to construct the application's menu bar and pull-down 
menus. Simple examples of such resources appear in the Hello World and Commands and Command 
Menus chapters. 


A further, more realistic, example can be found in the source files for the Record application. In addition 
to the resources for the application's command menus, the file record.rss contains a number of resources 
of various types. In particular, it #includes the file record.hlp that contains Help resources. 


Resource file location 


By default, the application resource file is expected to be built into the application's image file, with the 
aid of an add-file list (.aff) file, in the second of the four possible add-file slots. A resource file in this 
location will automatically be opened by system code during the initialisation of the application. 


An application that wishes to load its resource file from a different location may subclass the pwrmman 
application manager, replacing the am_rscname method. This method is passed a pointer to a buffer and 
must write the full file specification of the resource file to this buffer. 


For example, an application that has an application resource file with the same name as the application's 
image file, and resident in the same directory as the image file, could use the following replacement 
am_rscname method: 


METHOD VOID myappman_am_rscname (PR_MYAPPMAN *self,TEXT *pname) 


{ 
p_fparse(".RSC", DatCommandPtr, pname, NULL) ; 


} 


15-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


A multi-lingual application with a resource file for each of a number of languages could use code similar 
to that suggested in the Resource Files chapter of the Additional System Information manual: 


METHOD VOID myappman_am_rscname (PR_MYAPPMAN *self,TEXT *pname) 


{ 
P_INFO £; 


p_atos (pname, "\\app\\archive\\archiv%02d.rsc",p_getlanguage()); 
p_fparse (pname, DatCommandPtr, pname, NULL) ; 
if (p_finfo(pname, &f) <0) 
p_supersend3 (self,O_AM_RSCNAME, pname) ; 
} 


As explained in that chapter, the resource files are assumed to reside in an application-specific 
subdirectory of the \app directory; in this example the directory is \app\archive. The resource file for the 
default language should be built into the image file so that it is available for use on a machine that is set to 
a language not supported by the application. If the call to p_finfo indicates that a resource file for a 
particular language is not available, supersending the am_rscname message ensures that the built-in 
resource file will be used. 


See also the description of this method in the OLIB reference manual chapter 10 page 11. 


Loading an application resource 


Once the resource file is open, any of its resources may be read by sending the application manager either 
an AM_LOAD_RESOURCE Or an AM_LOAD_RES_BUF message (or by use of the equivalent hLoadResource or 
hLoadResBuf utility functions) passing the resource ID of the required resource. Note that the resource IDs 
are published in the .rg file that is generated during compilation of the source file by rcomp.exe. 


Resource Structures 


The basic resource structures used for strings, dialogs and the standard dialog components are defined in 
the include file hwim.rh. This file should always be #included in the application-specific resource file. 
Application-specific resource structures, if required, will need additional structure definitions. These may 
appear in-line in the .rss source file or may be written in a separate header file, to be #included in the 
source file, together with hwim.rh. It is customary to give such a file a .rh extension. 


The system resource file 


The system resource file resides in the ROM, and the (English) source for each of the relevant machines is 
copied into a \sibosdk\resource directory when the OOP option of the SDK is installed. These files are 
supplied for reference only, and should not be used as source files in the building of any application. 


The files are: 


S3_.1SS The system resource file for the Series 3 
SA_.1SS The system resource file for the Series 3a 
SC_.TSS The system resource file for the Series 3c 
SS_.TSS The system resource file for the Siena 
SW_.TSS The system resource file for the Workabout 


These files are broadly similar, and much of the content is common between all three. The files for the 
Series 3a, Series 3c, Siena and for the Workabout contain additional resources compared with the Series 3 
system resource file. All differences from s3_.rss are commented. 


The system resource file contains text strings, dialogs and other resources that are used by system code. In 
addition to supplying ready-made resources that can also be loaded by application code, the source files 
provide a wide range of example templates for constructing application-specific resources. 


The source files contain comments that are primarily supplied to aid translators to produce non-English 
versions. These comments may, however, prove of use to developers by indicating the purpose of, and/or 
the constraints associated with, a particular resource. 


Subject to the caution given below, all system resources are, in principle, available for use by applications. 


15 -2 


15 HWIM RESOURCE FILES 


Loading a system resource 


A resource is identified as a system resource by specifying a negative resource ID. Otherwise, the process 
of loading a system resource (using, for example, hLoadResource OF hLoadResBuf) 1s identical to loading 
an application resource. For example, the following code can be used to allocate memory and load the 
choice list system resource with resource ID sys_No_yEs: 


TEXT *p; 


hLoadResource (-SYS_NO_YES, &p) ; 
Note the explicit minus sign and that, by convention, all system resource IDs start with sys_. 


The resource IDs of system resources are published (in upper case) in the include file s_.rsg, which must 
be #included in any file that makes an explicit reference to one or more system resource IDs. 


Note that s_.rsg contains the resource IDs of all the resources that are contained in all five of the resource 
files s3_.rss, sa_.rss, SC_.rss, sS_.rss and sw_.rss. In consequence, not all of the listed resource IDs are 
valid on all machines. 


Using system resources 


Many system resources are used implicitly by application code. For example, calling hBusyPrint 
implicitly loads the sys_Busy resource string, and an application that runs one of the system dialogs will 
cause the associated dialog resource to be loaded from the system resource file. 


A number of system resources are suitable for explicit use within an application. Perhaps the most 
commonly used group is the various string resources that are used as information messages. After a 
successful copy operation, for example, an application could present confirmation to the user with the 
code: 


hInfoPrint (-SYS_COPIED_PROMPT) ; 


Referencing system resources from an application resource file 


Other useful system resources include dialog box components such as the various choice lists, particularly 
SYS_OFF_oN and sys_No_yes, and the general-purpose action lists, for example, sys_ac_No_yEs and 
SYS_AC_CONTINUE. These are suitable for inclusion in application-specific dialog resources. 


Suppose, for example, that an application dialog resource needed to include a No/Yes choice list. A 
straightforward way of accomlishing this would be to define a suitable MENU resource, such as: 


RESOURCE MENU no_yes 
{ 
items = 
{ 
CHOICE_ITEM { str= "No";}, 
CHOICE_ITEM { str="Yes"; } 
hi 


and reference it in the control as: 


CONTROL 
{ 
class=C_CHLIST; 
prompt="Ignore parity"; 
info=CHLIST{rid=no_yes; }; 
} 


Since a No/Yes menu resource exists in the system resource file, it is more efficient for the application's 
CONTROL resource to refer to the system resource instead of an application-specific replica: 


CONTROL 
{ 
class=C_CHLIST; 
prompt="Ignore parity"; 
info=CHLIST{rid=-SYS_NO_YES; }; 
} 


15 -3 


OBJECT ORIENTED PROGRAMMING GUIDE 


Caution 


In an application it is possible that a system resource could be used in a context that is different from the 
one for which it was designed. This could cause problems in a multi-lingual application, since translations 
of system resources may expose differences in context that are not apparent in a single language. System 
resources should therefore be used cautiously in applications that are intended to run in more than one 
language. If an application writer has any doubt about the intended use of a system resource, he or she 
should use an application-specific resource. 


Help resources 


An application can supply application-specific Help information by means of one or more top-level 
HELP_ARRay resources. An example of such a resource is as follows: 


RESOURCE HELP_ARRAY myapp_help 
{ 

topic="Myapp"; 
topic_id=myapp_help_index; 

} 


It contains a topic item, used to construct the title for a Help screen, and a topic_id which contains the 
resource ID of a Toprc_array resource. Such a resource is illustrated below; it contains an id_ist array of 
the resource IDs of one or more secondary HELP_ARRAY resources: 


RESOURCE TOPIC_ARRAY myapp_help_index 
{ 

id_lst= 

{ 

basics, 
-SYS_HELP_EDIT, 
-SYS_HELP_PRINT, 
-SYS_HELP_FILES, 
-SYS_HELP_NO_SYS_MEM 
‘i 

} 


Note that this array may contain references to any combination of system and application-specific 
resources, in any order. 


A secondary HELP_ARRAY resource contains a topic item, again used in a title, followed by a striist 
array of strings, each of which will be displayed on a single line of a Help screen. 


RESOURCE HELP_ARRAY basics 
{ 

topic="Basics"; 

strist= 


STRING {str="Enter to confirm selection"; }, 

STRING {str="";}, 

STRING {str="To move around:";}, 

STRING {str=ARROWS" to move cursor"; }, 

STRING {str="Psion-"<WS_SYMBOL_LEFT_KEY><WS_SYMBOL_RIGHT_KEY>" go to start/end of 
line"; }, 
STRING {str="Psion-"<WS_SYMBOL_UP_KEY><WS_SYMBOL_DOWN_KEY>" to PageUp/Down"; }, 
STRING {str="Control-Psion-"<WS_SYMBOL_UP_KEY><WS_SYMBOL_DOWN_KEY>" go to 
top/bottom"; } 

‘i 

} 


Each line of text, after compilation, must not include more than 39 characters. 
Using Help resources 


The Help information that is provided when a user presses the Help key may be set within an application 
by writing the resource ID of a top-level HzLP_arrRay resource to the window server object's 
help_index_id property. For example, to use the Help data shown above: 


w_ws-—>wserv.help_index_id=MYAPP_HELP; 


15-4 


15 HWIM RESOURCE FILES 


Application code may set the Help resource ID at any time. If the value of the window server object's 
help_index_id is zero (the default value) system Help will be supplied. Most applications that supply 
their own Help will normally set the Help resource ID during initialisation, in the ws_dyn_init method. 


An application may provide context-sensitive Help by changing the Help resource ID as the application 
context changes. This may be done either by writing a new value to w_ws->wserv.help_index_id, or by 
subclassing the client window to replace its wn_sense_help method. 


The Help supplied for a dialog box may be set independently by creating additional sets of Help resources. 
The Help for a dialog box may be set either by writing to its helprid property, or by supplying a 
replacement wn_sense_help method in a piGBox subclass. Again, default system Help is supplied for 
dialog boxes. 


15-5 


CHAPTER 16 


APPLICATION DESIGN 


It is clearly beyond the scope of this manual to discuss in any detail the principles and practice of 
object oriented design. There are a number of books available on this topic - two that have proved 
useful are: 


Object Oriented Modeling and Design, by James Rumbaugh et al. (Prentice-Hall International, 1991) 


Object Oriented Analysis and Design with Applications, second edition, by Grady Booch (The 
Benjamin/Cummings Publishing Company Inc, 1994) 


This chapter offers some specific guidance on object oriented application design for the Series 3 and 
Series 3a, using the Record application that is built into the Series 3a as an example. The full source 
code of this application is supplied and may optionally be installed into a \sibosdk\record directory. 
Once installed it may be built by making \sibosdk\record the current directory ant typing: 


make record 


The Record application illustrates a range of design techniques and solutions that may usefully be 
transferred to other applications. You should not, of course, take this to mean that Record is presented 
as a perfect example of application design. As with any design, the end result is a compromise 
between the ideal and the realistic. However, it is also true that, as a result of being designed, the 
application is much more robust and comprehensible than it would otherwise have been. 


Basic design 


The design of an application should, in general, separate into two layers: 


e the user interface contains all aspects of the application that are dependent on a particular 
machine 


e the engine (otherwise known as the system model) contains the data and the associated 
mechanisms that are particular to the application 


For Series 3 and Series 3a applications, the user interface contains objects based Pern cecn 
on the HWIM library whereas engines do not. An engine may, however, : rea ip 
optionally use any of the classes in the OLIB library. ee of ate) 
As is illustrated in the accompanying class diagram, the user interface has access 

to the engine, but there is no unsolicited communication in the opposite direction. 

The engine has no detailed knowledge of the classes making the calls. In practice pile 
this means that the user interface source files may include engine header files, but / engine / 
that the engine should not include user interface header files. Base ) 
The division between user interface and engine gives rise to many advantages, aug 


some of the more significant ones being: 


e separating the two aspects of the application reduces the complexity that has to be dealt with 
at any one time by both the designer and the implementer 


e such separation helps the designer to reduce the number of interactions between the various 
component objects, resulting in a 'cleaner' and more maintainable application 


16-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


e the engine does not need to be changed (or, at least, provides a good starting point) if the 
application is ported to another machine with a different user interface 


e the engine may be tested independently - a test harness may be constructed to test all aspects 
of the engine, without the complications of testing via the user interface 


A consequence is that it is in the programmer's interest to put as much of the application code as 
possible into the engine, to improve portability and to protect the investment of effort that the code 
represents. 


A typical application 


The following diagram shows the basic components of a typical application. Most of these 
components will be familiar from the discussion of the basic mechanisms of an object oriented 
application in the /ntroduction chapter. 


—— 
— 


application / 
manager 
fine Oe Sa: Nas paca [ee 
¢ resources / y peices , y menu bar / 
_ ) > ) ~ ) 
Lae Ci“ 
ome aes aon 
comman ialog box client / 
( Smanager?——~“ 60 window ) 
hao Ny _ a 
la engine / 
me ) 


wee 
The diagram concentrates on those aspects of the design that are common to all applications and thus 
omits some relationships that may be present in a particular application. It is, for example, likely that 
a number of classes may make use of utility functions by sending messages to the application manager 
and/or the window server object. 


The handles of the application's instances of the application manager (Hwrmman) and the window 
server object (wsERv), are globally accessible (via the magic statics w_am and w_ws respectively) and 
many application classes may use them - for example, to use the system services that they provide. For 
clarity, such relationships are not shown. 


One of the more significant aspects of this diagram from a design viewpoint is the presence of the 
engine and its relationships with other application classes. As indicated, the engine is commonly 
accessed by the client window, the command manager and the application's dialogs, but not, for 
example, by HWIMMAN or WSERV. 


The user interface 


The HWIM library provides an effective design solution for many aspects of the user interface. As 
described in this manual, the library provides the necessary base classes and mechanisms to process 
the vast majority of messages that may be received from the window server process. For example, 
HWIM provides a ready-made solution for the processing of command options, selected either by an 
accelerator keypress or from pull-down menus. Thus, much user interface design reduces to 
considerations such as what commands options are required and what dialogs are needed to support 
them. 


16-2 


16 APPLICATION DESIGN 


An exception to this is the client window itself, which is used to provide the top-level view of the data 
within the engine. Apart from the positioning and draw/redraw mechanisms, the supplied HWIM 
window classes provide very little in terms of general client window design. There is support, from 
the Epwin class, for windows that display editable text and this is extended to cover printing and 
formatted text by the FORM library. Apart from the material in the Edit Windows and Printing 
chapters, these topics are beyond the scope of this manual. 


The engine 


An application's engine is specific to that application and, since it should be independent of the user 
interface, HWIM provides no significant support. In most cases, engine design will therefore be a 
major component of the design of the whole application. 


Many engine classes will be specific to a particular application, and will directly subclass root. It 
may, however, prove useful to examine the classes supplied by the OLIB library, to see if any of them 
could be used or subclassed. These classes are described in the OLIB Reference manual. 


The Record application 


Record is a reasonably simple, but non-trivial, application and, as such, is ideally suited to being used 
as an example of application design and implementation. 


The application arose from a need to demonstrate the digital sound capability of the Series 3a in an 
intuitive and easy to use manner. The application was required to provide for the recording of custom 
sounds to be attached to alarms and for the recording of voice "notes" for future recall, by playing 
back the sound. For all uses, recording has to use the minimum of key presses. Playing the sound back 
has also to be as easy as possible. 


Constraints on human and machine resources meant that the application had to be capable of being 
produced quickly and be as small as possible. It may be of interest to note that the entire development, 
including requirements, specification, design, implementation and testing took around 90 man-hours 
and that the final application size is about 9 kbytes (against an initial target of 4 kbytes). 


Specification 


Record is a file-based application for the Series 3a. It manipulates one file at a time and keeps its 
current file open. The application operates on sound files that by default are stored in \wve directories 
and have .wve extensions. Such files are suitable for attaching to alarms. The descriptions of Series 3a 
sound files and the services that operate on them are given in the General System Services chapter of 
the PLIB Reference manual. One significant feature of the sound services is that they make direct 
read and write access to sound files. The Record application thus has no need to maintain an in- 
memory copy of its current file. 


The Record application obeys Switchfiles and Shutdown messages from the system screen. It is never 
‘busy’, even when recording or playing a file, so these messages can be received at any time. 


The application has two persistent parameters, stored in environment variables: 
e the sound volume at which to play back files 
e the default file duration. 

Top-level view 

The large status window is on permanently. 


The client window consists of a display of the attributes of the file being edited with buttons to 
perform actions on the file. 


The permanently displayed attributes are: 
e the name component only of the open file 
e the duration of the sound in seconds 


e the length of the file in bytes (so that the user is aware of the memory consumed by this file) 


16 -3 


OBJECT ORIENTED PROGRAMMING GUIDE 


If appropriate, the following attributes are also shown: 
e the number of repeats 
e the duration of the trailing silence in seconds 
e the total playing time 

The buttons are: 


e Record new (Tab) to record to a new file. A dialog is presented for the file name, the disk 
and the maximum duration. A generated file name, of the form recordnn where nn is 01, 02, 
03 etc., is presented by default. The initially suggested disk is the default drive or, if that 
drive contains other than a RAM SSD, Internal. 


e Record over (Space) to re-record to the file, overwriting its existing content. A dialog is 
presented allowing the maximum duration to be set (the suggested duration is that of the 
current file, rounded up to the next 2K bytes). The dialog contains a warning that the current 
data will be replaced. 


e Play (Enter) to play back the file, including any repeats and trailing silences. 
Playing 


When playing the file, the three buttons are replaced by a single Stop (Esc) button. A bar graph is also 
presented, showing the elapsed playing time against the predicted total playing time. 


Recording 


After accepting the dialog presented by Record new or Record over, the three buttons are replaced by 
a single Start recording (Space) button. Pressing Space starts the recording and the button is replaced 
by a single Stop (Esc) button. A bar graph is also presented, showing the elapsed record time against 
the maximum duration. Pressing Esc terminates (but does not abort) the recording, resulting in a 
shorter sound file than would otherwise be produced. If Esc is not pressed, the recording terminates 
when the specified maximum duration has been reached. 


Running for the first time 


The first time the application is run, when no sound files exist under the icon, the application 
immediately presents (over a blank client window) the dialog that is presented when you subsequently 
press the Record new button. In this case the suggested file name is record (not record01) and 
pressing Esc in this dialog causes the application to exit. 


The same behaviour results, but with the specified file name, if you start the application by use of the 
System Screen's New (Psion+N) option. 


Menu 


The menu commands are: 


New file Essentially has the same effect as pressing the Record new button. 

Open file Presents a dialog with standard controls to select a new current file. 

Set repeat Presents a dialog to set the repeat count (1 to 999) and the trailing silence (0 to 
10.00) in the file header. This is intended to be of use for constructing custom 
alarms. 


Adjust for alarm § Automatically sets the repeat and the trailing silence for a short recording (less 
than seven seconds or so) so that it is compatible with being clipped at 15 
seconds by the alarm server. 


Set preferences Presents a dialog to set the volume of playback, the default duration and the 
default disk for new files (the last two being subsequently used by the Record 
to new file dialog). 


Exit There is no need to save any changes - all changes are made to the file directly. 


16-4 


16 APPLICATION DESIGN 


Design 


The overall design of the Record application is shown in the following class diagram. Although the 
diagram is fairly complex, it is interesting to see that the essential design can be captured in a single 
diagram. The Record application represents about the highest level of complexity for which this is 
possible. 


Despite its complexity, the diagram is still a simplification and shows less than the whole truth. It 
does not mention, for example, access to resource files and it omits a number of relationships of lesser 
significance and whose presence would hinder rather than help with an understanding of the design. 
The dialogs, for example, are generally run from within methods of the command manager, but 
including all such relationships would render the diagram totally unreadable. 


The diagram does, however, show all the really significant aspects of the design. As an example, it 
accurately illustrates the relationships between user interface classes and the engine, although the 
relationship between the rEcnew dialog class and the engine is conceptual rather than actual (this is 
explained later in more detail). It is an important part of the design that the file services, digital sound 
services and the environment variables are accessed via the engine and are not referenced directly by 
any user interface component. 


The relationships that are shown are drawn to represent the truth as closely as possible. The using 
relationship between the window server object and the client window is drawn to show that it is 
methods at the WSERV level that send messages to the client window. This reflects the fact the only 
relationships involved are those provided by system code. In contrast, as would be expected, the 
engine is used only by application-specific subclasses. 


SANS ie os 
recbut ; ( bwin 
Hee 


| 5 
recnewdef 
( 


ey 5 } 235 
4 
ae {active} — ie ok | 
a a recnew Nae 
Fs ~™ ee . 2 9! aoe : 


ss, 
(aan a , faetil 
( iat eee! ay 


pee . 
recexist 
( 


( 


S S recopen 5 
setrep ( 
recman : gers hn 
Fe aS Xw RS 


r 
hwimman we o a mes / setpref > 


receng 5 ss | 
oe fare Coe 


reccom > 


site ak 
( 


eee waveao : 
on =z / Fie \ factivel 
L 4 DP 4 < 
woo Ne 2h BS 
Soa ~ . Er ‘) 
File services (i ; / Digital sound 
TSE EoN variables services 
~ (impl.) imp Hinge 


Booch class diagram for record.app 


The following sections describe a number of aspects of the application's design 


16-5 


OBJECT ORIENTED PROGRAMMING GUIDE 


The client window 


The client window forms the application's top-level view of the current file and initially presents a 
view similar to that shown below. 


Bugle 1.1sec 9 Kbyte 


Record new Record over Play 


While recording or playing a file the buttons are replaced by a single button and a bar graph is 
presented to indicate the elapsed time, as illustrated in the next diagram. This diagram also shows the 
additional line of information that is shown for a file that is set to repeat and/or has trailing silence. 


Bugle 1.1sec 9 Kbyte 


Repeats:9 Trailing silence: @.2sec Total: 15.2 sec 


The following diagram shows the application when it is paused before making a recording, with 
another single button being visible. 


Record#2 


Start recording 


The client window thus has, at various times, to display one or two lines of text, one or more of a set 
of five buttons, in various positions, and possibly a bar graph. 


The client window maintains two text strings (the second of which may be a null string) for centred 
display at fixed vertical offsets whenever a wn_draw message is received. 


In contrast, the bar graph and the five possible buttons are component objects of the client window 
and each of them contains the knowledge of its size and its position within the (fixed size) client 
window. Furthermore, each of these components contains a record of whether it is visible or not. Thus 
the client window does not need to maintain a record of which set of components is visible at any one 
time. An operation, such as pressing the Play button only needs to set the visibility of the appropriate 
components. On receipt of a wn_draw message, the client window always sends all six components an 
appropriate drawing message. Each component either acts upon or ignores this message, according to 
its own internal state. The following object diagram illustrates the drawing mechanism, in response to 
a WN_REDRAW message from the window server object. The same sequence is used for drawing that is 
initiated by the client window itself. 


16 - 6 


16 APPLICATION DESIGN 


wn_draw 


wn_redraw 


—_ 


O33 raw 


The client window thus has no direct knowledge of which buttons are visible. It must, however, 
respond to a set of keypresses that correspond to the buttons that are on display. Record accomplishes 
this by taking advantage of the keyboard filtering mechanism (see ws_process_key in the WSERV 
Class chapter of the HWIM Reference manual) to divert the processing of keypresses. 


In its normal, three-button, state, keys are processed by the client window's wn_key method, in the 
normal way. When it switches to either of the other two states, not only is a different set of buttons 
made visible, but a filter is set, redirecting key processing to one of two alternate key processing 
methods - either cl_sound_filter Of cl_pause_filter. The change of state actions are performed by 
the client window's cl_update, cl_begin_sound and cl_pause methods. Note that these two methods 
return wN_KEY_CHANGED, thus ensuring that interaction with the menu bar is disabled in the filtered 
states. Record uses negative values for wserv.filmethod So that Help is still available while keys are 
being filtered. 


It is worth noting that the behaviour in each of the two filtered states is very similar to what could 
alternatively have been achieved by presenting a dialog. The decision to simulate dialog behaviour 
rather than to use dialogs for these states was largely based on cosmetic considerations. 


The bar graph 


While recording or playing a file, Record displays an animated bar graph to show the progress against 
the predicted total time for the operation. Since the sound services provide no information regarding 
their progress, the bar graph animation has to be performed independently of the sound recording or 
playback. 


The Trmepar class therefore takes only a single item of external data - the total predicted time for the 
operation. It uses the free-running counter (Frc:) device in its repeating mode to ensure accurate 
synchronisation of the animation with the sound services. 


Since the Frc: device is a scarce resource, it is essential that Record releases it as soon as possible. 
The device is therefore opened every time it is used, and closed on completion of every record or 
playback operation. It is particularly important to ensure that an error condition does not leave the 
FRC: device open, so its closing is made an essential part of the application's error-handling (see The 
application manager, later in this chapter). 


The engine 


Record's engine is a static instance of the REcENe class, created on initialisation of the application and 
remaining in existence for the application's lifetime. This need not be the case in all applications - in 
many cases it may be appropriate to represent, say, a change of the application's file by destruction 
and recreation of the engine. 


RECENG centralises access to and manipulation of the data associated with the application's current 
file. Its handle is globally available (via the global static variable receng) so that it may be referenced 
by any object in the user interface. The classes that actually use the engine are as indicated in the 
earlier application class diagram. 


16-7 


OBJECT ORIENTED PROGRAMMING GUIDE 


The engine owns instances of the wvEFILE and waveao classes that respectively represent the current 
file and the record/playback process. All access to these classes is via REcENG methods. This reduces 
the engine's 'surface area’, by avoiding the need for any other object to be aware of the existence of 
these two instances. Note that some engine methods add little value (see, for example, the 
eng_sense_file method that senses the file data held by wer Le) and exist simply in order to 
delegate the action to a component. Although this results in a small increase in the size of the 
application, this is outweighed by the design advantages that it brings. 


The essentials of the sound-playing mechanism is illustrated in the following object diagram. Playing 
is initialised by pressing the Play button in the client window, which causes an ENG_SOUND_PLAY 
message to be sent to the engine. 


reccli sound_play 


——— 
begin_sound 
update 


Ney 


ae ge 
done_play 


sense_info 
sense_fname 


waveao 


The name and total sound duration of the current file are sensed by means of wvE_SENSE_FNAME and 
WVE_SENSE_INFO Messages tO WVEFILE and the name is passed to wavEao in a WV_PLAY message to 
initiate the sound. The engine sends a cL_BEGIN_SOUND message to the client window (see later) to 
cause it to change the button display and to initialise and make visible the bar graph. 


WAVEAO 1s an active object which breaks up the playing of the file into a series of sections, allowing the 
application to continue to respond to keypresses (to abort the playing of the sound) and to update the 
growing bar graph (another active object). When wavzao completes the playing of the file it sends an 
ENG_DONE_PLAY message to the engine which, in turn, sends a cL_uPDATE message to the client 
window, causing it to revert to its three-button display. 


An engine should have no knowledge of user interface objects and certainly should not send them 
unsolicited messages. The design of the Record engine, however, requires the engine to send 
messages to the client window in response, for example, to the receipt of an ENG_SOUND_PLAY 
message. This apparent conflict is resolved by passing the client window's handle, and the message 
numbers of the required messages, to the engine as parameters. The parameters are normally sent to 
the engine (as in this case) with its initialisation message and are stored in the engine's property. The 
message can then be sent at a later time, with no knowledge of either the target object or the meaning 
of the message that is being sent. The engine thus preserves its ignorance of, and independence from, 
user interface classes. The special nature of this type of messages is indicated by italicising the 
corresponding message names in the above object diagram. 


Recording to a new file or re-recording an existing file broadly follow the same pattern. The major 
apparent difference in the mechanism arises from the fact that these two operations are also available 
as command menu options. The initiation of these operations from the client window therefore shares 
code with the corresponding command manager methods, but the principles remain the same. Before 
recording to a file starts, the engine deletes any existing file of the same name. 


Dialogs 


As with the majority of applications, there is very little design associated with Record's dialogs since 
they are largely constrained by the system-supplied mechanisms. 


16-8 


16 APPLICATION DESIGN 


om 
— 


a —~ 
¢ digbox / 


~ ) 
fo 
C 


y 
¢ «setrep / setpref / 


oN — 
is 


receng / 
-: ) 
Nee 
The strep and setpreF classes, respectively used by the Set repeats and Set preferences menu options 
are typical subclasses of pLGBox, replacing the dl_dyn_init and di_key methods. As indicated in the 
above class diagram, both classes send messages to the engine to sense and set the relevant data. 


recopen 


The file-related dialog classes, RECOPEN, RECNEW, RECNEWDEF and REcExISsT, are related as shown in the 
above class diagram. The recopen dialog class reports a selected file name by means of a result buffer, 
pointed to by an item of dialog box property, digbox.rbuf. It thus has no direct using relationship 
with any other class in the application. 


The other three dialogs are associated with recording to a file. There is a conceptual relationship 
between these dialogs and the engine, since the current file name is found by means of a message sent 
(by the command manager) to the engine. This initial file name is passed to the dialogs by use of a 
result buffer, so the link to the engine is not represented in the above class diagram (although it is 
shown in the more conceptually orientated overall class diagram shown at the start of the Design 
section). All these dialogs initiate recording to the file by sending a cL_pausE message to the client 
window. 


The application manager 


On error, Record is designed to return to its base state, as on first entry to the application. 


Record's application manager subclasses pwrmman to replace the am_clean_up method. This method is 
called by system code as part of the standard recovery from an error condition (that has caused a call 
to p_leave). Its purpose is to assist roll-back to a safe state by freeing any resources that have been 
allocated since the application was last in a secure state. For further details see The CLEANUP Class 
and the description of the am_clean_up method in The APPMAN Application Manager Class, both of 
which chapters appear in the OLIB Reference manual. 


16-9 


OBJECT ORIENTED PROGRAMMING GUIDE 


The replacement method adds value by also performing all central generalised error recovery needed 
for any error condition. Its actions include, for example, clearing the value of wserv->filter and 
sending a TB_sTop message to the client window's bar graph (to ensure the release of the rrc: device). 
All these actions are guaranteed harmless if they are performed in a case when they are not needed. 


This use of the am_clean_up method may not be so convenient in a more complex application where a 
variety of more specific error recovery procedures may be needed. 


16 - 10 


CHAPTER 17 


SERIES 3A ATTACHED APPLICATIONS 


The Series 3a version of the object libraries introduced the concept of attached applications, that is, the 
ability of one application to make use of the functionality of a second, cooperating, application. This 
feature is used by the Series 3a Agenda application, which uses the Word application to edit memo 
documents that can be attached to Agenda entries. 


This section provides an overview of the basic mechanisms of attaching one application to another. Only 
general guidelines can be given, since the details will be highly dependent on the two applications 
involved and on the particular circumstances under which the attachment is made. 


The following description assumes that process A wishes to attach to and make use of process B. It further 
assumes that the only communication that occurs between the two processes is on start-up and on 
termination of the attached process. Applications are free to implement more sophisticated inter-process 
communications with attached applications. 


Starting the attached process 


The first step is for process A to use a call to p_execc to create process B, passing a suitable command 
line: 


VOID CreateProcess(UWORD *ppid, TEXT *pfspec, UBYTE *pcomline, INT comlen) 
{ 


*ppid=f_leave (p_execc (pfspec, pcomline, comlen) ) ; 


} 


The command line should contain data that can be used by process B to determine that is being started as 
an attached application. One simple way of doing this is to use a non-standard value for the command 
byte, provided it is not needed for its normal purpose of determining how a file-based application obtains 
its initial file. A particularly suitable command byte value to use is H_COMMAND_Bypass ('Z’) since this 
value prevents the HWIM application manager from parsing any following command line data. For 
further details of command line parsing, see the description of the am_init method in the HWIMMAN 
Application Manager chapter of the HWIM Reference manual. 


Following any standard elements of the command line that are needed by the attached application, further 
specific information may be appended. This information must minimally include the process ID of process 
A, but may also contain one or more pointers to data in process A to which process B will require access. 
One such item will normally be a pointer to a status word (possibly of some active object in process A) so 
that process B can notify process A of a successful termination. 


Following the creation of Process B, process A must call p_logona in order to be notified if process B 
terminates unexpectedly, either because of some error condition, or because process B is killed from the 
System Screen. A convenient way is to use an instance of the XADD tocona active object class: 


VOID LogTermination(VOID *hlogona, UWORD pid, VOID *hand, INT method) 


{ 
*hlogona=f_newsend (CAT_MYAPP_XADD, C_LOGONA, O_AO_INIT, pid, hand, method) ; 


} 


When process B terminates, the object with handle hang in process A will receive a message with message 
number method. 


17-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


If any aspect of communication between the two processes is handled by means of an active object in 
process A, this object should be sent an ao_quzuE message at this point, before process B starts to execute. 
This active object is expected to receive an ao_RUN message only when process A is signalled by process B. 


Process A should then set the priority of process B higher than its own and start process B running with a 
call to p_presume: 


VOID RunProcess(UWORD pid) 
{ 
p_setpri (pid, 0x84) ; 
p_presume (pid) ; 


} 


The call to p_presume will not return until process B reduces its priority to be not greater than that of 
process A (or until process B either terminates, or has no other pending activity). The mechanism thus 
relies on the cooperation of process B, which is expected to reduce its priority as soon as possible. 


On return from the call to p_presume, process A uses WSERV'S ws_hide_app method to hide itself from the 
System Screen. 


The whole process may be summarised by the following code: 
#include <hwimman.g> 
GLREF_D PR_WSERV *w_ws; 


LOCAL_D WS_HIDE_APP_DATA hidden; 


VOID AttachProc(TEXT *pame, UWORD *ppid, UBYTE *pcomline, INT comlen, 
VOID *hlogona, VOID *hand, INT method) 

{ 

CreateProcess (ppid, pfspec, pcomline, comlen) ; 
LogTermination (hlogona, *ppid, hand, method) ; 

/* queue any active object here */ 

RunProcess (*ppid) ; 

p_send4 (w_ws,O_WS_HIDE_APP, *ppid, &hidden) ; 

} 


Initialisation of the attached process 


The initialisation code of process B (normally in its ws_dyn_init method) must detect, from the command 
line data, that it is running as an attached application. It must also, minimally, read the process ID of 
process A (and, if supplied, the offset of a status word) from its command line. Process B should send its 
instance of a subclass of wsERV a WS_ATTACH_APP message, passing the process ID of process A. 


Process B may read one or more items of data from process A, using the process ID and other data passed 
to it in the command line. It will usually also make some change to its appearance to indicate that it is 
running as an attached application. 


As soon as possible, after its essential initialisation, process B should call 
wSetPriorityControl (TRUE) ; 


to return control of its priority to the Window Server, thereby allowing process A to resume execution. At 
this point, process A will return from its call to p_presume. 


Termination of the attached process 


This description assumes that, following its successful initialisation, the attached process runs (until its 
normal termination) independently of process A. It may be desirable for process B to log on to process A 
and thus be informed in the event of its abnormal termination. 


If the attached application terminates abnormally, process A will be notified by its instance of the Locona 
class. This notification should cause process A to take any necessary recovery action, including restoring 
itself from the hidden state by sending the message: 


p_send4 (w_ws,O_WS_HIDE_APP, FALSE, &hidden) ; 


17-2 


17 SERIES 3A ATTACHED APPLICATIONS 


The attached process will normally terminate on execution of the com_exit method of its command 
manager. At this point it will generally need to transfer information back to process A. This can most 
simply be accomplished by an inter-process copy (with a call to p_pcpyto) using the process ID of process 
A and one or more offsets into process A's data segment that were communicated to process B via its 
command line. 


Once any data has been successfully transferred, process B must notify process A that it is terminating 
successfully. One way of doing this is to copy a suitable value into a process A status word (the offset of 
which was passed to process B in its command line) and signal process A by calling p_iosignalbypid. 
Following this, process B may terminate. 


If using the method described in the previous paragraph, process A will be notified of the successful 
termination of the attached process by execution of the ao_run method of the appropriate active object. 
Process A should immediately destroy its instance of the Locona class, to prevent erroneous notification of 
an abnormal termination. It should then restore itself from the hidden state, as described earlier. 


This should be followed by any processing of the data sent back by the attached application. 


Termination of process B may need to be postponed until process A has successfully received and 
processed the data, One possible means of doing this is to use the same mechanism that was described 
above for notifying process A of the imminent termination of process B. 


To use this method, process B would create and queue an active object whose ao_run method contains the 
application's termination code, and send the offset of this object's status word to process A. Process A can 
then cause process B to terminate by copying a suitable value into the status word and then signalling 
process B by calling p_iosignalbypid. 


17-3 


CHAPTER 18 


THE SERIES 3A AUTOMATIC TEST SYSTEM 


As its name suggests, the automatic test system (ATS) was conceived as a means of providing automated 
testing of Series 3a HWIM applications. As such it can be used to exercise an application, following a 
predetermined and repeatable sequence of operations. One example of such use is to display, in sequence, 
all an application's dialogs, to check that they do not contain text that is too wide to be displayed. This can 
be particularly valuable when validating the translation of the application's resource file into another 
language. 


The ATS provides a very general set of services. As a result, it can be used for purposes other than testing 
an application. Other uses include: 


e generating rolling demonstrations 
e asimple form of macro recording and playback 
e providing a measure of control for attached applications. 


A process that controls an ATS sequence does not have to be an HWIM application. It can be written in 
either C or OPL and does not need to have a user interface. The process that is running under the control 
of ATS does, however, have to be either a standard Series 3a HWIM application or one that mimics such 
an application's support for ATS. The ATS mechanism can not be used with applications that run on the 
Series 3. 


The ATS mechanism 


The ATS mechanism is implemented by means of standard inter-process messaging, using message types 
in the range TY_ATS_START_RANGE (0x30) tO TY_ATS_END_RANGE (0x3¢£) inclusive. These values, together 
with the message types that are explicitly supported and their corresponding message structures, are 
defined in the header file ats.h. 


A Series 3a HWIM application always supports the receipt of inter-process messages, regardless of 
whether or not the rLc_appMaN_1pcs flag is specified in the application's main (). Part of the Series 3a 
HWIMMAN initialisation is to create and initialise an instance of the XADD arssv class. This is a subclass of 
the OLIB server class and is used to process the receipt of ATS inter-process messages. In addition, the 
HWIMMAN initialisation code sets the gate.atson property of its instance of the caTE class to TRUE (and 
stores the handle of this instance in the DatGate magic static) so that another process can detect that the 
application supports ATS. 


In principle, an ATS controller program simply needs to use p_msendreceivea Or p_msendreceivew to 
send inter-process messages of the appropriate type (or types) to the application that is being controlled. If 
the application is known to support ATS, such as when using ATS to test a specific Series 3a HWIM 
application, this is all that is required. 


In other cases, the controller may have to check that the target application supports ATS before sending 
ATS messages. This is most conveniently done by creating an instance of the HWIM cate class (if it does 
not already exist) and sending it a GT_CHECK_ATS_ON message, passing the process ID of the target 
application. This method returns zero if ATS is supported by the target application and E_cEN_nsup if it is 
not (other errors may be returned if, for example, the specified process does not exist). In order to respond 
positively to this query, the target application must itself have created an instance of the cate class, 
storing its handle in the patGate magic static, and have set its gate.atson property to TRUE. 


18-1 


OBJECT ORIENTED PROGRAMMING GUIDE 


The data for an ATS inter-process message is conrained in an ats_mess struct, consisting of a standard 
E_MESSAGE inter-process message header (defined in epoc.h) and an ats_mEss_Bopy message body. The 
ATS_MEss and aTs_MEss_Bopy Structs are defined in ats.h as follows: 


typedef union 
{ 
UWORD position; 
ATS_KEY_DEF k; 
ATS_DIAL_DEF d; 
UWORD delay; 
VOID *offs; 
WORD par; 
UWORD wid; 
} ATS_MESS_BODY; 


typedef struct 
{ 
E_MESSAGE mess; 
ATS_MESS_BODY u; 
} ATS_MESS; 


The ats_KEY_DEF and atTs_DIAL_DEF structs are also defined in ats.h: 


typedef struct 
{ 
UWORD key; 
UWORD mod; 
} ATS_KEY_DEF; 
typedef struct 
{ 
UWORD main; 
UWORD mainlen; 
UWORD buts; 
WORD butslen; 
} ATS_DIAL_DEF; 


The ATS message types 


When an ATS inter-process message is received by an HWIM application, it is processed by the 
application's instance of the arssv class. This processing, for each type of message, is described in the 
Automatic Test System Classes chapter of the XADD Reference manual. The message types are also listed 
here, but with an emphasis on the way the messages are sent from the controlling process. 


For simplicity, all the examples given in this section send inter-process messages synchronously, using 
calls to p_msendreceivew. If a controlling application wishes to respond to other events, such as 
keypresses, it may use p_msendreceivea to send any of these inter-process messages asynchronously. An 
application is free to use any convenient combination of synchronous and asynchronous calls. 


The completion status, returned by p_msendreceivew or written to the status word used with 
p_msendreceivea, Will be zero (or, in some cases, a positive number) to indicate success, or a negative 
error to indicate failure. 


TY_ATS CLIENT POS Set client position 


Set the position in the task order of the specified process. 


The position is specified by the position member of an ars_mEss_Bopy Struct. Setting a position of zero 
brings the process to the front, as the foreground process. A position of ws_LAST_CLIENT_POSITION 
(defined in wlib.h) positions the process at the back. 


The following example brings the specified process to the front. 


#include <ats.h> 


VOID BringToFront (INT pid) 


{ 
ATS_MESS_BODY u; 


u.position=0; 


p_msendreceivew (pid, TY_ATS_CLIENT_POS, &u) ; 
} 


18 -2 


18 THE SERIES 3A AUTOMATIC TEST SYSTEM 


TY_ATS_ KEY Send a keypress 


Send a keypress to the specified process. 


The keycode and any modifiers are specified by the k.key and k.moda members of an aTS_MESS_BODY 
struct. A receiving HWIM application will process the keypress as if it were received from the keyboard. 


The following example sends a Control-Psion-Enter keypress: 


#include <wlib.h> 
#include <ats.h> 


VOID SendKey (INT pid) 

{ 

ATS_MESS_BODY u; 

u.k.key=W_KEY_RETURN; 
u.k.mod=W_CTRL_MODIFIER|W_PSION_MODIFIER; 


. msendreceivew (pid, TY_ATS_KEY, &u) ; 
} 


TY_ATS_ PAUSE Pause an application 


Pause the specified process. 
The pause, in tenths of a second, is specified by the delay member of an ats_mEss_Bopy struct.. 


If a pause of zero is specified, the application's application manager will be sent an am_yIELD message, 
pausing the application until it has had an opportunity to service any outstanding events. 


The following example pauses an application for half a second: 
#include <ats.h> 
VOID Pause(INT pid) 
{ 
ATS_MESS_BODY u; 
u.delay=5; 


p_msendreceivew (pid, TY_ATS_PAUSE, &u) ; 
} 


TY_ATS_ MESSAGE Display a message 


Display an information message in the top left corner of the screen.. 


The message text is pointed to by the offs (offset) member of an ats_mzss_Bopy struct. It must be 
supplied as a zero terminated string not exceeding 128 bytes in length, including the terminating zero. 


For example: 


#include <ats.h> 


VOID SendMessage(INT pid) 
{ 
ATS_MESS_BODY u; 


u.offs="A remote message"; 
p_msendreceivew (pid, TY_ATS_MESSAGE, &u) ; 
} 


If the inter-process message is sent asynchronously, the buffer containing the string must remain in 
existence until the message completes. 


18 -3 


OBJECT ORIENTED PROGRAMMING GUIDE 


TY_ATS DIALOG Run a dialog 


Display and run a dialog in the specified process. 


The dialog is specified by the contents of the a member of an ats_mEss_Bopy struct. This is an 
ATS_DIAL_DEF Struct, defined in ats.h as: 


typedef struct 
{ 
UWORD main; 
UWORD mainlen; 
UWORD buts; 
WORD butslen; 
} ATS_DIAL_DEF; 


The dialog must be represented by in-memory data, pointed to by d.main and of length d.mainlen. This 
data will usually have been loaded from a resource file, as illustrated below for the error dialog resource: 


RESOURCE DIALOG error_dialog 
{ 
flags=DLGBOX_RBUF_FILLED | DLGBOX_NO_DDP; 
controls= 
{ 
CONTROL 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE | DLGBOX_ITEM_DEAD | DLGBOX_ITEM_UNDERLINED; 
info=TXTMESS 
{ 
flags=IN_TEXTWIN_AL_CENTRE; 
i 
‘yy 
CONTROL 
{ 
class=C_TEXTWIN; 
flags=DLGBOX_ITEM_CENTRE |DLGBOX_ITEM_DEAD; 
info=TXTMESS 
{ 
flags=IN_TEXTWIN_AL_CENTRE; 
i 
a 
CONTROL 
{ 
class=C_ACLIST; 
info=ACLIST 
{ 
rid=ac_continue; 


‘i 


‘i 
} 


An HWIM controlling application could load this resource as shown in the following code fragment: 


ATS_MESS_BODY u; 


u.d.mainlen=p_send4 (w_am, O_AM_LOAD_RESOURCE, ERROR_DIALOG, &u.d.main) ; 


A non-HWIM controlling application might have to create a temporary instance of the OLIB rscriLe 
class to load the resource, as indicated below: 


HANDLE olib; 
VOID *hand 
ATS_MESS_BODY u; 


p_findlib("OLIB.DYL", &0lib) ; 
hand=f_newlibhsend(olib, C_RSCFILE,O_RS_INIT,DatCommandPtr) ; 
u.d.mainlen=p_send4 (hand, O_RS_READ, ERROR_DIALOG, &u.d.main) ; 


p_send2 (hand, O_DESTROY) ; 


Note that this code assumes that the resource file is built into the application file and is thus identified by 
the file specification string pointed to by the magic static DatcommandPtr. 


18-4 


18 THE SERIES 3A AUTOMATIC TEST SYSTEM 


The dialog resource may contain one item that requires an additional resource. The example resource is 
such a case, containing an action list that needs an additional acLisT_array resource, for example: 


RESOURCE ACLIST_ARRAY ac_continue 
{ 
button = 
{ 
PUSH_BUT 
{ 
keycode=W_KEY_ESCAPE; 
str="Continue"; 
} 
ad 
} 


Any such additional resource should be pointed to by the d.buts element and its length should be given by 
d.butslen. The resource can be loaded by the same techique given above for the main dialog resource. 
For an HWIM controlling application, this could be: 


ATS_MESS_BODY u; 


u.d.butslen=p_send4 (w_am, O_AM_LOAD_RESOURCE, AC_CONTINUE, &u.d.buts) ; 


The dialog may not have more than one such item, which may be either an action list (as in the above 
example) a choice list or an edit box.. 


The edit box case is special, in that d.buts and d.butslen are used differently. The value of a.buts 
should be a pointer to a zero terminated string that does not exceed ars_MAX_EDITOR_LEN (256) bytes in 
length, including the zero terminator, and d.butslen should be set to a negative value whose magnitude 
is the index of the edit box item in the dialog box, plus one. The text string will be used to initialise the 
specified edit box. 


If the dialog does not contain an action list, a choice list or an edit box, d.buts and d.butslen should 
both be set to zero. 


Interpretation of the completion status depends on the dialog contents. In all cases, however, a negative 
completion status signifies an error. The error value z_GEN_FaIL (-1) is reserved to indicate that the user 
exited from the dialog box by pressing Escape. In general, this error should be explicitly detected since it 
will usually nor require an error message to be displayed. 


Terminating the dialog by pressing enter gives the following possible results: 
e if the dialog has no action list, choice list or edit box, the result is zero 


e if the dialog contains a choice list, the result is the index of the current item in the choice list 
(selecting the first item in the choice list gives a result of one) 


e if the dialog contains an edit box, the result is zero and the initialising text for the edit box is 
overwritten by the edited text, as a zero terminated string 


The following example runs the dialog defined by the resources given earlier in this section, returning the 
dialog result: 


#include <ats.h> 


INT RunDialog(INT pid) 


{ 
ATS_MESS_BODY u; 


u.d.mainlen=p_send4 (w_am, O_AM_LOAD_RESOURCE, ERROR_DIALOG, &u.d.main) ; 
u.d.butslen=p_send4 (w_am, O_AM_LOAD_RESOURCE, AC_CONTINUE, &u.d.buts) ;u.position=0; 
return (p_msendreceivew (pid, TY_ATS_DIALOG, &u) ; 

} 


18-5 


OBJECT ORIENTED PROGRAMMING GUIDE 


TY_ATS SELF CHECK Run a consistency check 


Instruct the specified process to perform a self-consistency check on its data. 


Sends a ws_SELF_CHECK message to the application's instance of a subclass of wsERv, passing with this 
message the value of the par member of the ats_mess_Bopy struct. It is the application's responsibility to 
provide a meaningful ws_sel£_check method. 


For example: 


#include <ats.h> 


VOID CheckProcess(INT pid,WORD par) 
{ 
ATS_MESS_BODY u; 


u.par=par; 
p_msendreceivew (pid, TY_ATS_SELF_CHECK, &u) ; 
} 


TY_ATS WINDOW_XSUM Perform a window checksum 


Perform a checksum on a specific window, or on the whole screen. 


To checksum a specific window, set the wid member of the ars_mzss_Bopy struct to contain the window 
ID. Use a value of zero to checksum the whole screen. 


A successful result gives a status word containing a checksum in the least significant byte and zero in the 
most significant byte. 


The following example returns either a negative error or a one-byte checksum value for the whole screen: 


#include <ats.h> 


INT ScreenXsum(INT pid) 
{ 
ATS_MESS_BODY u; 


u.wid=0; 
return (p_msendreceivew (pid, TY_ATS_WINDOW_XSUM, &u) ) ; 
} 


TY_ATS_ RECORD Start or stop keypress recording 


Start or stop keypress recording from the specified process, depending on the value of the par member of 
the aTs_MESS_Bopy Struct. 


Starts keypress recording if par is non-zero, otherwise stops keypress recording. When keypress recording 
is on, the controlling application should repeatedly send ty_ats_GET_KkEy inter-process messages. Each of 
these messages will complete when the specified application receives a keypress. 


Attempting to start recording when keypress recording is already on will result in the error z_GEN_INUSE. 
Turning keypress recording off when there is an outstanding Ty_aTs_GET_kEy inter-process message will 
cancel the Ty_ATS_GET_KEY message. 


#include <ats.h> 


VOID SetRecordState(INT pid,INT flag) 
{ 
ATS_MESS_BODY u; 


u.par=flag; 


p_msendreceivew (pid, TY_ATS_RECORD, &u) ; 
} 


18 - 6 


18 THE SERIES 3A AUTOMATIC TEST SYSTEM 


TY_ATS GET KEY Receive a keypress 


Receive notification of the next keypress received by the specified process. 
The details of the keypress will be written into an ars_xey struct, defined in ats.h as: 


typedef struct 
{ 
UWORD time; 
UWORD keycode; 
UBYTE modifiers; 
UBYTE count; 
} ATS_KEY; 


Before sending this inter-process message, the address of this struct must be written to the of fs member 
of the ars_mEss_sopy Struct, as in the following example: 


#include <ats.h> 
LOCAL_D ATS_KEY nextkey; 
VOID GetNextKey (INT pid) 
{ 


ATS_MESS_BODY u; 


u.offs=&nextkey; 
p_msendreceivew (pid, TY_ATS_GET_KEY, &u) ; 
} 


This inter-process message will normally be sent asynchronously, to allow the controlling process to be 
responsive to other events while waiting for notification of keypresses from the specified process. 


It is a programming error to send this inter-process message if keypress recording has not been turned on 
by means of an earlier Ty_aTS_RECoRD inter-process message. 


TY_ATS ALLCOUNT Check allocated memory 


Walk the heap of the specified process and display an information message showing the number of 
allocated cells and the total number of bytes of allocated memory. 


This inter-process message does not require any data to be written into the ars_mEss_sBopy struct. 
#include <ats.h> 
VOID BringToFront (INT pid) 
{ 


ATS_MESS_BODY u; 


p_msendreceivew (pid, TY_ATS_CLIENT_POS, &u) ; 
} 


An example macro recorder 


This example provides a simple macro recording and playback facility for the Series 3a. Buildable code 
for this application can be installed in a \sibosdk\ats directory from the Optional Disk. The resulting 
kats.img file should be installed by transferring it to a \img directory on a Series 3a. 


When installed, the application will appear under the RunImg icon and can be run by selecting the Kats 
item and pressing Enter in the normal way. It runs in background and provides macro recording and 
playback for any foreground HWIM application. 


The macro recorder allows a number of named macros to be created by recording keystrokes received by 
the foreground application. It then allows these macros to be selected by name and played back to the 
foreground application. 


18 -7 


OBJECT ORIENTED PROGRAMMING GUIDE 


The program is intended to be an illustration of the use of the ATS mechanism rather than a full-featured 
application and has, in consequence, a number of limitations. For example, it does not distinguish 
between different applications. This means that a macro recorded from, say, the Word application can be 
played back to a different application, say, the Agenda - where the macro may have a quite different 
effect. It is not a file-based application and, although it supports a 'Save' keypress combination, the code to 
actually save the macros in a file is not implemented. The set of macros are stored in memory for the 
lifetime of the application and will be lost when the application is terminated. 


The program captures the following keypress combinations: 
Cont rol-Psion-Space Start/Stop recording a macro for the current foreground application 


Control-Psion-Enter, Start/Stop playback of a macro to the current foreground application 
Control-Psion-Tab 


Control-Psion-Diamond Save the current set of macros (the save code is not implemented) 


The application itself is written in straightforward C, with a minimum of object oriented code, illustrating 
that an ATS controller does not have to be an HWIM application. The following discussion describes the 
main features of the code, but does not list the entire contents of kats.c. It is intended to be read in 
conjunction with the code itself. 


The application's main() is simply: 


GLDEF_C INT main(VOID) 
{ 
INT ret; 


ret=p_enterl (Initialise); 
if (!ret) 

MainLoop(); 
return (ret); 


} 
The initialisation is carried out under the protection of p_enter, in order to catch any failure. 


#pragma save, ENTER_CALL 


LOCAL_C INT Initialise (VOID) 
{ 


FindOlib(); /* get the category handle of the OLIB library */ 
CreateGate(); /* create an instance of the HWIM GATE class */ 
LoadDialogData(); /* load the dialog resources into allocated memory */ 


CreateMacroStorage(); /* create/initialise an instance of the OLIB VAXVAR class */ 
ConnectToWindowServer(); /* uses the window server wConnect function */ 

CaptureKeys (); /* uses the window server wCaptureKey function */ 

Message ("Keyboard macro support loaded"); /* inform user of successful start-up */ 
return (0); 


} 
#pragma restore 


CreateGate Stores the category handle of the HWIM DYL in a static variable before creating an instance 
of the cate class. The gt_check_ats_on method of this instance is used to as a convenient means of 
checking the validity of an application to receive ATS inter-process messages. 


LOCAL_C VOID CreateGate (VOID) 


{ 
HANDLE hwim; 


p_findlib("HWIM.DYL", &hwim) ; 
DatGate=f_newlibh (hwim, C_GATE) ; 
} 


FindOlib uses the same technique, storing the category handle in a static variable for later use to create 
instances of OLIB classes. Note that communication between many of the routines is by means of static 
variables. 


18-8 


18 THE SERIES 3A AUTOMATIC TEST SYSTEM 


The main event loop of the application is as follows. This is fairly straightforward, at least in terms of 
handling the receipt of ww_kry events: 


LOCAL_C VOID MainLoop (VOID) 

{ 
FOREVER 

{ 

wGetEvent (&event) ; 
wait: 

p_iowait (); 

switch (event.type) 

{ 


case E_FILE_PENDING: 
if (rec_active && rec_stat!=E_FILE_PENDING) 


{ 
rec_active=FALSE; 
if (record) /* else recording has previously been cancelled */ 


Next StageInRecord(); 
} 


else if 


{ 
play_active=FALSE; 
if (playback) /* else playback has previously been cancelled */ 


Next StageInPlayback (); 


(play_active && play_stat!=E_FILE_PENDING) 


} 
goto wait; 
case WM_FOREGROUND: 


wClientPosition(WS_LAST_CLIENT_POSITION,0); /* never come to foreground */ 


break; 
case WM_KEY: 
switch (event.p.key.keycode) 
{ 
case KATS_SAVE: 
if (record) 
Message("Can't save macros while recording"); 


else if (playback) 
Message("Can't save macros while playing back"); 
else if (!changed) 
Message("No macro changes to save"); 
else 
SavelfConfirmed() ; 
break; 
case KATS_RECORD: 
if (record) 
StopRecording(); 
else if (playback) 
Message("Can't record while playing back"); 
else 
StartRecording(); 
break; 
case KATS_PLAYBACK: 
case KATS_PLAYBACK2: 
if (playback) 


{ 
Message ("Playback terminated") ; 


StopPlayback (); 
} 


else if (record) 
Message("Can't play back while recording") ; 


else if (!nmacros) 
Message ("Nothing to play back"); 


else 
StartPlayback (); 


} 


This application is designed to run in background. It may, however, become the foreground application as 
a result of another application either terminating or going into the background. In such a case it will 
receive a WM_FOREGROUND event and simply responds to this event by sending itself into the background. 


18-9 


OBJECT ORIENTED PROGRAMMING GUIDE 


Some of the ATS inter-process messaging is performed asynchronously. The completion of such a 
message signals the application with an event that is separate from the type of event that is requested by 
the call to wcetEvent. Events of this type are recognised by the fact that the weetEvent event type has not 
changed from the value &_F1LE_pENDING. As can be deduced from the above code, the only asynchronous 
inter-process messages are the ones that receive a keypress (ty_aTs_GET_kEy) while recording a macro 
and send a keypress (Ty_aTs_xey) while playing back a macro. 


The application identifies the current foreground client and checks that it is capable of receiving ATS 
messages as follows: 


LOCAL_C INT GetForegroundClient (VOID) 


{ 
UWORD pids[WS_MAX_CLIENTS+1]; 


wGetProcessList (&pids[0]); 
return (pids[0]); 
} 


LOCAL_C INT GetTestForegroundClient (VOID) 


{ 
INT pid; 


pid=GetForegroundClient (); 

if (!p_send3 (DatGate,O_GT_CHECK_ATS_ON, pid) ) 
return (pid) ; /* okay */ 

p_sound (5,320); 

p_sound (5,280); 

p_sound (5,240); 

return (NULL) ; 

} 


This is used before sending most ATS inter-process messages, as illustrated below. In this case the inter- 
process messaging is performed synchronously. 


LOCAL_C VOID ForegroundAtsWait (INT type,ATS_MESS_BODY *pu) 


{ 
INT pid; 


pid=GetTestForegroundClient (); 
if (pid) 
p_msendreceivew (pid, type, pu) ; 


} 


This form of test and wait is used, for example, to send information messages to be displayed by the 
foreground application: 


LOCAL_C VOID Message(TEXT *msg) 


{ 
ATS_MESS_BODY u; 


u.offs=msg; 
ForegroundAt sWait (TY_ATS_MESSAGE, &u) ; 


} 


Information messages of this form are used extensively in the application, as can be seen in the code given 
earlier. It is also used to display error messages, as in the following case: 


LOCAL_C VOID TellErr(INT err) 


{ 
TEXT buf[64]; 


if (err==E_GEN_FAIL) 
return; 

p_errs (&buf[0],err); 

Message (&buf[0]); 

} 


LOCAL_C VOID TellOom(VOID) 
{ /* report failure due to out-of-memory error */ 
TellErr (E_GEN_NOMEMORY) ; 
} 


18 - 10 


18 THE SERIES 3A AUTOMATIC TEST SYSTEM 


Note that Te11Err is also used to report an error in the completion of an ATS dialog. It therefore does not 
report an error for the error number &_GEN_Fatt (the value resulting from pressing Esc when an ATS 


dialog is being presented). 
The application enters the recording state by calling startRecording: 


LOCAL_C VOID StartRecording (VOID) 


{ 
pid=GetTestForegroundClient (); 


if (!pid) 
return; 

if (GetMacroName () ) 
{ 
record=TRUE; 
TransmitRecordState(); 
Message ("Recording..."); 
mac_rec.num_keys=0; 
RequestNextKey (); 
} 

} 


The routine GetMacroName uses an ATS dialog to obtain a name for the macro: 


LOCAL_C INT GetMacroName (VOID) 


{ 
ATS_MESS_BODY u; 
INT ret; 


u.d=dl_edit; 
ret=p_msendreceivew (pid, TY_ATS_DIALOG, &u) ; 
if (ret<0) 

{ 

TellErr (ret); 

return (0); 


} 
return (CheckNotMatching()); 


} 


where CheckNotMat ching scans any existing macros to ensure that the name is not a duplicate of an 
existing name (and offers the option to overwrite the existing macro if the name is a duplicate). The 
notification of keypresses from the foreground application is enabled by setting the static variable 
recording to TRUE and calling TransmitRecordState, which sends a Ty_ATS_RECORD inter-process 


message: 


LOCAL_C VOID TransmitRecordState (VOID) 


{ 
ATS_MESS_BODY u; 


u.par=record; 
p_msendreceivew (pid, TY_ATS_RECORD, &u) ; 


} 
The receipt of notification of the first keypress is handled by RequestNextKey, which writes the keypress 
data into the static struct full_key: 


LOCAL_C VOID RequestNextKey (VOID) 


{ 
ATS_MESS_BODY u; 


u.offs=(&full_key); 
rec_active=TRUE; 
p_msendreceivea (pid, TY_ATS_GET_KEY, &u, &rec_stat) ; 


} 


18 - 11 


OBJECT ORIENTED PROGRAMMING GUIDE 


Subsequent keypresses are received by repeated calls to Next StageInRecord. This copies the keycode and 
modifiers for the previously received key into the keys array of the mac_rec static structure and 
increments the count of received keys before calling RequestNextKey. In addition, it terminates recording 
when the maximum allowed number - max_num_KEys (128) - of keypresses for any one macro has been 
received: 


LOCAL_C VOID NextStageInRecord (VOID) 


{ 
ATS_KEY_DEF *short_key; 


short_key=(&mac_rec.keys [mac_rec.num_keys++]); 
short_key->key=full_key.keycode; 
short_key-—>mod=full_key.modifiers; 
changed=TRUE; 
if (mac_rec.num_keys==MAX_NUM_KEYS) 
StopRecording(); 
else 
RequestNextKey (); 
} 


The recording of a macro is terminated by a call to stopRecording: 


LOCAL_C VOID StopRecording (VOID) 
{ 
record=FALSE; 
TransmitRecordState(); 
if (!'mac_rec.num_keys) 
{ 
Message ("Recording cancelled") ; 
return; 
} 
if (p_enter1l (AppendMacro) <0) 
TellOom() ; 
else if (mac_rec.num_keys==MAX_NUM_KEYS) 
Message ("Maximum number of keystrokes reached") ; 
else 
Message("Finished recording") ; 


} 


The data of the macro, including the macro name and the array of keypresses, is stored by appending it as 
a single record to the application's instance of vaxvar. Note that the macro is stored only if one or more 
keypresses have been received. 


Playback is implemented by a similar technique, using the routines startPlayback, PlayNextKey and 
StopPlayback. 


18 - 12 


APPENDIX A 


CATEGORY FILES 


A category file contains the class definitions for all the subclasses provided by an image or dynamic 
library file. It is expected to have a .cat file name extension. 


The category file defines the group of classes that form an object oriented category. Object oriented classes 
and categories are explained in the /ntroduction chapter of this manual and in the Object Oriented 
Programming chapter of the PLIB Reference manual. 


A category file is the input to the category translator tool, ctran.exe, which generates a number of files, as 


described later in in this chapter. 


Category file content 


The content of a category file is best explained in conjunction with the following example: 


A demonstration cat file 
IMAGE demo 


! External reference to OLIB library 


EXTERNAL olib 


INCLUDE p_std.h 
INCLUDE p_object.h 
INCLUDE varray.g 


CLASS dummy root 
Dummy class definition, 
as an illustration only 
{ 
REPLACE destroy 
ADD dm_init 
DEFER dm_sub 
CONSTANTS 
{ 
! for the buffer 


required for knowledge of VAFLAT 


the class name and its superclass 


Methods follow... 

free buffer and supersend 

create VAFLAT component and allocate buffer 
defined by a subclass... 

auxiliary symbolic constants 


DUMMY_BUF_SIZE 128 allocated buffer size 
! for the VAFLAT component 


DUMMY_GRAN 16 
} 
TYPES 
{ 
typedef struct 
{ 
TEXT *buf; 
UWORD len; 
} DUMMY_BUF; 
} 
PROPERTY 1 
{ 
PR_VAFLAT *array; 
DUMMY_BUF buffer; 
} 


contains auxiliary structs 
/* comments here are exceptional */ 


pointer to allocated buffer 


the component VAFLAT instance 


OBJECT ORIENTED PROGRAMMING GUIDE 


CLASS sub dummy 

Subclass of dummy 
{ 
REPLACE dm_sub ..So that's what it does 
} 


This example is copiously commented, to illustrate where comments are allowed. The general rules are: 


e any number of lines of comment text may be placed at the start of the file, or in the lines 
immediately following a cass declaration 


e any line starting with an exclamation mark, optionally preceded by whitespace, is ignored 


e trailing comment text may be placed on any line (except for a typedef struct line in the tyPEs 
section) provided it is separated by whitespace from significant content. The whole of a typedef 
struct line is output to the .g file. Any comment in this line must, therefore, be suitable for 
inclusion in a C source file. 


Not counting comment lines, the structure of a category file is: 
¢ an IMAGE OF LIBRARY Statement 
e zero or More EXTERNAL Statements 
@ one or more INCLUDE statements 
¢ zero or more cLass statements 
e zero Or More REQUIRE Statements 
The first non-comment line of the file declares the category type and name. The keyword must be one of: 


IMAGE the executable will be an image (.img or .app) file 
LIBRARY the executable will be a dynamic library (.dy/) file 


The category name, in this case, "demo", must be the same as the file name. This category file must, 
therefore have the name demo.cat. 


An EXTERNAL Statement declares an external reference to a dynamic library (DYL). The file may contain 
any number of such external references. There must be an external reference to a DYL before a category 
file class definition may refer to, or subclass - directly or indirectly - a class from that DYL. 


The above example subclasses root, which is in the OLIB library and so must declare an ExTERNAL 
reference to OLIB. The effect is to include an external reference (.ext) file - in this case olib.ext - from the 
designated include directory. External reference files are generated by the ctran.exe category translator, 
and are described later. 


Since all subclasses are ultimately derived from the Root class,! all category files (except that for OLIB 
itself) must contain an EXTERNAL reference to OLIB. 


An INCLUDE statement includes a C language header file from the designated include directory. It is used 
in a similar way to #include in aC source file. All category files must IncLupE, either directly or 
indirectly, p_std.h and p_object.h. The above example also mncLupes the header file varray.g (generated 
by the category translation of the OLIB dynamic library category file and copied to the \sibosdk\include 
directory during installation). This file contains C #defines and typedets relating to the OLIB variable 
array Classes, including the definition of the pR_var.at struct. 


Class definition 


The ciass keyword introduces a class definition. It is followed by the name of the class and then the name 
of the parent superclass. A class name may be up to 15 characters long. The above example defines the 
class pummy which is a direct subclass of the root class. The layout of a class definition is significant; 
apart from leading whitespace, which is ignored, it must follow the pattern illustrated above - and in the 
class definitions given elsewhere. 


Exceptionally, a category may define its own root class and hence require no external reference to OLIB. 


A-2 


APPENDIX A - CATEGORY FILES 


The class definition of each subclass lists its additional methods and any additional property. It may also, 
as in the above example, include the definitions of auxiliary structures and constants used by that class. 
There are many examples of class definitions in the manuals describing object oriented libraries (the OLIB 
Reference manual, for example). 


The class definition may include any number of method declarations,” introduced by the app, REPLACE or 
DEFER keywords. Each of these is followed by a method name, which may be up to 21 characters long. The 
method declarations may be followed by one of each of the constants, Types and PROPERTY keywords. 


The method declaration keywords have the following meanings: 


ADD declare a method in addition to the methods provided by the superclass. The name 
must be unique in relation to all other methods in this category, or any externally 
referenced categories. Although not compulsory, the name conventionally starts with 
a short prefix related to the name of the class in which it is introduced. 


REPLACE declare a method whose functionality is to replace that of a method supplied by a 
superclass. The name must be that of an existing method in the superclass 
inheritance tree. 


DEFER declare an additional method as for app, except that the functionality of the method 
is not defined by the current class and is expected to be provided by a subclass (using 
REPLACE). A class containing perErred methods is known as an abstract class and, in 
general, no instances of such a class will ever be created.3 


It is recommended that each method name be followed by a concise descriptive comment. 


The constants keyword introduces a list of symbolic constant definitions, each consisting of the symbol 
name (conventionally in upper case) followed by the numeric value. The value may be an expression 
involving symbolic constants defined earlier, either in the category file itself, or in any included file. The 
expression must not contain any whitespace. 


The types keyword introduces a list of C language typedef struct definitions, whose layout should follow 
that given in the example category file. (Many further examples may be found in the class definitions 
shown for each class in, say, the OLIB Reference manual.) 


The property keyword introduces a list of data element declarations to be included in the struct that 
defines the class property. 


This keyword may optionally be followed by a literal number (expressions may not be used) that specifies 
how many component items listed in the property are to be sent an automatic pesTRoy message when an 
instance of the class is destroyed. This assumes that, for a value ncomp, the first ncomp items in the 
additional property for the class are either nun or handles (pointers to instances) of component objects. 
The automatic destruction mechanism is described in the Object Oriented Programming chapter of the 
PLIB Reference manual. It is implemented in the destroy method of the root class, described in the 
OLIB Reference manual. (See the Building a Dynamic Library chapter for a description of how a category 
may supply its own root class.) 


In the above example, pummy's component var.at instance will be automatically destroyed when pummy 
recelves a DESTROY Message. 


Sub-category files 


The contents of a category file may be be divided between a number of sub-category files, each of which 
must have a .cl file name extension. 


A sub-category file normally groups together a number of related classes.4 The OLIB category, for 
example, is constructed from a number of sub-category files, including: 


2Subject to a maximum of 255 methods, including those inherited from the superclass tree. 


3There is no formal requirement for all p—ErERred methods to be REPLACcEd and it is acceptable to create an 
instance of such a class provided that it is known that no pEFrERred method will ever be called. See, for 
example, the HWIM picsox class. 


4The relationship may be by function, by inheritance, or by any other means appropriate to a particular 
application. 


OBJECT ORIENTED PROGRAMMING GUIDE 


varray.cl containing the class definitions of the segmented buffer and variable array 
classes described in the SGBUF Segmented Buffer Class and Variable 
Arrays chapters of the OLIB Reference manual 


edit.cl containing the class definitions of the classes described in the Editable 
Documents chapter of the OLIB Reference manual 


time.cl containing the single T1me class definition, described in the TIME Class 
chapter of the OLIB Reference manual 


A sub-category file is included in the category file by means of the REqurRE keyword. For example, 
olib.cat contains the line: 


REQUIRE varray 
to include the sub-category file varray.cl. 
Not counting comment lines, the structure of a sub-category file is: 
@ aNAME Statement 
@ one or more INCLUDE statements 
@ one or more cLass statements 


A sub-category file must start with a Name statement, specifying the sub-category name. This name must 
be the same as the file name. Thus the timer.cl sub-category file must start with the declaration: 


NAME timer 


A sub-category file must not contain EXTERNAL statements and normally will not contain REQUIRE 
keywords. 


During translation of the category file (described in the Category Translation chapter) a separate 
generated header (.g) file is created for each sub-category file. 


Using sub-category files 


A category file may contain in-line class definitions as well as those contained in REqurREd sub-category 
files. For example, the OLIB category file (olib.cat) is: 


LIBRARY olib 


INCLUDE p_std.h 
INCLUDE p_object.h 


CLASS root 
The ultimate superclass - all other classes have root as their 
ancestor. 

{ 

ADD destroy 


PROPERTY 
{ 
P_OBJECT pc; class link 
} 
} 
REQUIRE varray 
REQUIRE appman 
REQUIRE ipc 
REQUIRE factive 
REQUIRE time 
REQUIRE timer 
REQUIRE tlvfile 
REQUIRE edit 


The OLIB category file is unusual in that it contains no references to external categories. It therefore 
contains no EXTERNAL statements and no 1ncLupEs of any .g files. 


The category translation of olib.cat generates olib.g and a further .g file (such as varray.g) for each of the 
sub-category files. 


APPENDIX A - CATEGORY FILES 


The information in a .g file may include: 
e external category numbers 
e includes of .A and .g files 
e class numbers 
e¢ method numbers 
e = property structures 
e = auxiliary class constants 
e = auxiliary class structures 
This information is mainly° required by: 
e¢ C source files that provide the method functions for the classes in the category 


¢ C source files that provide the method functions for classes in another category, if they refer to 
these classes (either by using or by subclassing) 


The definitions of category numbers only appear in the .g file generated from the main category file (such 
as olib.g). If this file contains no in-line class definitions, this is effectively all that the .g file contains. 


The includes of ./ and .g files result from 1NcLUDE statements in either the main category file or a sub- 
category file. The 1ncLupE statements may be tailored to the known dependencies between sub-categories. 
For example, the OLIB appman.cl sub-category file starts as follows: 


NAME appman 


INCLUDE varray.g 
INCLUDE p_que.h 
INCLUDE p_gen.h 
INCLUDE p_file.h 


Note that appman.cl requires a reference to varray.g since the appman class has a cLEANUP component, and 
CLEANUP is a subclass of an array class. Although the classes in appman.cl depend on olib.g and also 
require the inclusion of p_std.h and p_object.h, these do not need to be included explicitly. The classes in 
varray.cl also depend on these files, so varray.g can be relied on to perform the necessary includes. There 
is automatic protection in .g files against including the same file more than once, so explicit inclusion of 
these files, although not necessary, is harmless. 


One of the major advantages of using sub-categories stems from the observation that a particular C source 
file almost never needs to include all the category information. Judicious division of a category file into 
sub-categories to reduce the amount of category data included in each of the source files can significantly 
reduce the time and, more importantly, the memory usage involved in building an application. 


In general, classes should be grouped according to their relationships (which imply dependencies) either 
from subclassing or from usage as a component. For example, the classes in the OLIB varray.cl sub- 
category file (with one exception) are general-purpose array classes, all of which have varoot in their 
inheritance tree. In contrast, the classes in the appman.cl sub-category file are related either by being used 
as components of the application manager, or by the intimate relationship between the application 
manager and active objects. 


It is perfectly acceptable for a sub-category file to contain a single class definition, if that class is isolated 
from other classes in the category. For example, the OLIB trme class has its own sub-category file, 
time.cl. In this case it is particularly worthwhile since the Time class defines a large number of constants 
and structures. 


5 Limited information may also be required by other files. For example, a resource file containing 
command menu items will require the corresponding command manager method numbers. 


OBJECT ORIENTED PROGRAMMING GUIDE 


Category translation 


The category translator tool, ctran.exe, is the principal tool used in the creation of a SIBO object oriented 
program. It generates a number of output files from a single input category file (which may include a 
number of external category references, header files and sub-category files). 


Which output files are generated, and where they are put, is determined by flags passed to ctran. exe, 
whose syntax is: 


ctran <name> [-e<dir> -x[<dir>] -c[<dir>] -g[<dir>] -a[<dir>] -i[<dir>] -l[<dir>] -s - 
k -v] 
where: 
<name> specifies the input category file name, assumed to have a file name extension 
of .cat 
-e<dir> specifies the directory from which files are included by the exTERNaAL and 


INCLUDE category file keywords 


-x [<dir> specifies that an external reference .ext file should be generated in the 
current directory or, if given, the specified directory 


-c[<dir> specifies that a .c C language category source file should be generated in the 
current directory or, if given, the specified directory 


-g [<dir> specifies that one or more .g C language include files should be generated in 
the current directory or, if given, the specified directory 


-a[<dir> specifies that a .asm assembly language category source file should be 
generated in the current directory or, if given, the specified directory. This 
flag is not normally set in the SDK development environment 


-il<dir>] specifies that one or more .ing assembly language include files should be 
generated in the current directory or, if given, the specified directory. This 
flag is not normally set in the SDK development environment 


-l[<dir>] specifies that a .Jis human readable class report file should be generated in 
the current directory or, if given, the specified directory 


-s specifies that output is being generated for the SDK. This flag is normally 
set in the SDK development environment. Omitting this flag causes 
additional information, irrelevant to the SDK development environment, to 
be included in the output .c and .g files 


-k specifies that a set of skeleton method function C source files are to be 
generated. A separate source file is generated for each class in the category 
file. The name of each file is the same as the class name, and has a.c 
extension. Each file is generated only if a file of that name does not already 
exist, so there is no danger of accidentally overwriting an existing source 
file. A warning is given if the file can not be created 


-v specifies verbose on-screen progress reports 


The various output files are best described with reference to the demonstration category file, listed at the 
start of this chapter. 


APPENDIX A - CATEGORY FILES 


The .ext external reference file 


The .ext file publishes the category name and information on the classes it contains. For each class in the 
category the file publishes: 


e the class name 
e the method names at their first appearance (when declared with app or DEFER) 
e flags indicating if the subclass has additional property and if it supplies method functions 


This file should be included (by means of the ExTERNAL keyword) in other category files that make 
external references to the category it describes. There is no need to generate this file if no other category 
makes such references. 


The demo.ext file generated from demo.cat is as follows: 


Generated by Ctran from demo.cat 
IMAGE demo 

CLASS dummy root 

{ 

DECLARE dm_init 

DECLARE dm_sub 

HAS_METHOD 

HAS_PROPERTY 

} 


CLASS sub dummy 


{ 
HAS_METHOD 


} 


The .c C language category source file 


This file contains the C language data definitions and initialisations for each class in the category. It is the 
source of the class descriptors which reside in the same code segment as the method functions (see also 
the Object Oriented Programming chapter of the PLIB Reference manual). 


This file must always be generated, and must be compiled and linked into the application. Before linking, 
the object file must be converted, by means of the ecobj.exe tool, to move the class descriptor data into the 
code segment. This is performed automatically by the ct.bat batch file described in the Building an 
Application chapter. 


The demo.c file generated from demo.cat is effectively as follows: 


/* Generated by Ctran from demo.cat */ 
#include <demo.g> 


/* External Superclass References */ 
#define ERC_ROOT C_ROOT 


/* Class dummy */ 
GLREF_C VOID dummy_destroy(); 
GLREF_C VOID dummy_dm_init(); 
GLDEF_D struct 
{ 
P_CLASS c; 
VOID (*v[2]) (0; 
} c_dummy= 
{ 
{1, (P_CLASS *)ERC_ROOT, sizeof (PR_DUMMY) ,0,0x6b,2,1}, 
{ 
dummy_destroy, 
dummy_dm_init 


} 


OBJECT ORIENTED PROGRAMMING GUIDE 


/* Class sub */ 
GLREF_C VOID sub_dm_sub(); 
GLDEF_D struct 

{ 


P_CLASS c; 
VOID (*v[1]) QO; 
} c_sub= 


{ 
{0, (P_CLASS *) &c_dummy, sizeof (PR_SUB) ,2,0x6b,1,0}, 
{ 
sub_dm_sub 
} 
di 


/* Class Lookup Table */ 
GLDEF_D P_CLASS *ClassTable[]= 
{ 
(P_CLASS *) &c_dummy, 
(P_CLASS *) &c_sub 
i 


/* External Category Name Table */ 
GLDEF_D struct 
{ 
UWORD number; 
UBYTE names[1] [14]; 
} ExtCatTable = 
{1, 


{ 
{NOt Pa Bae By eo Ds T!,040;°09:07:0,'0} 
} 


hi 


Note the expansion of the method function names to include the class name, as well as the declared 
method name. This ensures that, even if a method replaces one supplied by a superclass, the method 
function names are unique. 


The .g C language include file 


A..g include file contains the generated enumerated constants for the categories, classes and methods 
declared in the category file, together with the generated structures that define the property of each class. 
In addition it reproduces the auxiliary constant and structure definitions from the various classes. 


The category numbers section lists the numbers used to refer to categories from within this category (for 
example, when creating an instance with p_new). This section will always contain at least one entry, with 
value zero, for the local category. There will be one additional entry for each externally referenced 
category. 


Unless building an assembly language program, a .g file must always be generated, and must be 
#included in any C source file that refers to any of the items mentioned above. 


Note that the name of a symbolic constant representing a class number is derived from the class name 
with a preceding "c_", and that of a method number is "o_" followed by the declared method name. 


The symbolic constant representing a class number is always of the form cat_xxxx_yyyy, where xxxx is 
the name of the local category and vvvy is the name of the category to which the number refers. Thus the 
DEMO category refers to itself via the constant caT_pEMo_pEMo and refers to the OLIB category by 
CAT_DEMO_OLIB. 


If the category file includes one or more sub-category (.c/) files, an additional .g file is generated for each 
sub-category. The content of each . g file corresponds to the content of the relevant category or sub- 
category file. Note that the .g file corresponding to the main category (.cat) file does not contain 
information relating to the content of any of the sub-category files. 


APPENDIX A - CATEGORY FILES 


The demo.g file generated from demo.cat is effectively as follows: 


/* Generated by Ctran from demo.cat */ 
define DEMO_G 

ifndef P_STD_H 
include <p_std.h> 
endif 

ifndef P_OBJECT_H 
include <p_object.h> 
endif 

ifndef VARRAY_G 
include <varray.g> 
endif 


/* Category Numbers */ 
define CAT_DEMO_DEMO 0 
define CAT_DEMO_OLIB 1 


/* Class Numbers */ 
define C_DUMMY 0 
define C_SUB 1 


/* Method Numbers */ 
define O_DM_SUB 2 
define O_DM_INIT 1 


/* Constants for dummy */ 
define DUMMY_BUFFER_SIZE 128 
define DUMMY_GRAN 16 


/* Types for dummy */ 
typedef struct 

{ 

TEXT *buf; 

UWORD len; 

} DUMMY_BUF; 


/* Property of dummy */ 
typedef struct 
{ 
PR_VAFLAT *array; 
DUMMY_BUF buffer; 
} PRS_DUMMY; 
typedef struct pr_dummy 
{ 
PRS_ROOT root; 
PRS_DUMMY dummy; 
} PR_DUMMY; 


/* Property of sub */ 
typedef struct pr_sub 
{ 
PRS_ROOT root; 
PRS_DUMMY dummy; 
} PR_SUB; 


The .asm assembly language category source file 


This is an assembly language version of the .c category source file, described above. It contains the 
assembly language data definitions and initialisations for each class in the category. It is the source of the 
class descriptors which reside in the same code segment as the method functions (see also the Object 
Oriented Programming chapter of the PLIB Reference manual). 


This file should only be generated, instead of the corresponding .c file, if the application is written in 
assembly language, in which case it must be assembled and linked into the application. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The .ing assembly language include file 


A .ing file is an assembly language version of the .g file, described above. It contains the generated 
enumerated constants for the categories, classes and methods declared in the category file, together with 
the generated structures that define the property of each class. In addition it reproduces the auxiliary 
constant and structure definitions from the various classes. 


It should only be generated if the application is written in assembly language, in which case it must be 
INCLUDEd in any assembly language source file that refers to any of the items mentioned above. 


If the category file includes one or more sub-category (.c/) files, an additional .ing file is generated for 
each sub-category. The content of each .ing file corresponds to the content of the relevant category or sub- 
category file. Note that the .ing file corresponding to the main category (.cat) file does not contain 
information relating to the content of any of the sub-category files. 


The .lis category listing file 


A .lis file is a plain text listing, by sub-category, of all the classes in a category, together with their 
inheritance trees and use of component classes. It may be useful to aid a review of how classes are grouped 
into sub-categories, but is not otherwise required. 


The demo.lis file generated from demo.cat is as follows: 
Generated by Ctran from demo.cat 
IMAGE demo 


KKKKKKKKKK demo KKK KKK KK KK 


dummy 
Derived from root 
References vaflat 
Subclassed by sub 
sub 


Derived from dummy, root 


The .c skeleton method function source file 


A separate .c method function source file is generated for each class defined in either the category file or 
any included sub-category files. The file name is the same as the class name of the class from which it is 
generated (but truncated to the first eight characters in the case of long class names). Generating these 
files avoids the repetitive typing involved in creating method function files by hand. It is expected that a 
set of method function source files will be generated once only for each application. 


A suitable batch file for creating the skeleton method function source files is ctskel. bat: 


@echo off 
ctran %1 -e..\include\ -s -k 


which is used as, for example: 
ctskel demo 


This batch file is copied into the \sibosdk\oopdemo directory on installation of the optional OOP 
component of the SDK. It assumes that the SDK is installed on C: and should, of course, be modified as 
necessary for an SDK installed on a different drive. 


Each source file contains the required include files and a minimal skeleton for each method function that 
must be supplied by the corresponding class. 


The file generated from demo.cat for class pummy is as follows: 


APPENDIX A - CATEGORY FILES 


/* 
dummy .c 
Generated by Ctran from demo.cat 


*/ 
#include <demo.g> 
#pragma METHOD_CALL 


METHOD VOID dummy_destroy(PR_SUB *self) 
{ 
} 


METHOD VOID dummy_dm_init (PR_SUB *self) 
{ 
} 


and that for class sus is: 


/* 
sub.c 
Generated by Ctran from demo.cat 


*/f 
#include <demo.g> 
#pragma METHOD_CALL 


METHOD VOID sub_dm_sub(PR_SUB *self) 
{ 
} 


The general form of these files is described in appendix B of this manual. 


In the generated skeleton files, all method functions are declared as being vorp and are supplied with only 


the first, mandatory, parameter (being the handle of the class instance). It is the responsibility of the 
programmer to make such modifications as are necessary to match the requirements of the method 
functions of a particular application. 


APPENDIX B 


METHOD FUNCTION SOURCE FILES 


For each method included in the category file by means of either app or REPLACE, there must be a 
corresponding method function declared in one of the source files that is compiled and linked into the 
application. 


A set of skeleton source files is generated automatically from the category file if a -k flag is passed to 
ctran.exe, as described in the Category Translation chapter. 


There is no formal requirement as to how method functions should be divided between a number of source 
files. The most common scheme is to place all the method functions for a particular class in a single 
source file, using a separate file for each class (the generated skeleton files follow this scheme). 
Depending on circumstances, however, the method functions of a single class may be divided between two 
or more source files: alternatively, a single file may contain the method functions of more than one class. 
A viable alternative to the above scheme is to group all the method functions of the classes of a single sub- 
category file. The overriding aim should be to enhance the clarity and maintainability of the code. 


The name of each method function is constructed by concatenating the class name with an underscore and 
the method name. Thus, the va_test method of the prru1stT class has a method function with the name 
dirlist_va_test. Method function names may not be more than 31 characters long - which is less than 
the sum of the maximum lengths of the class name (15 characters) and the method name (21 characters). 


Method function parameters 


A method function may have from one to four 16-bit parameters. These correspond, with the omission of 
the message number, to the parameters passed to a message sending function (such as p_sena). Note that 
the message sending mechanism removes the method number from the list of parameters before calling 
the appropriate method function. 


The first parameter of each method function must be the object handle, that is, the handle of the class 
instance. This handle, which is a pointer to the heap cell containing the instance property struct, is 
conventionally given the name seit. The name of the struct itself is derived by adding a leading pR_ to 
the class name. This struct is defined in the .g file corresponding to the .cat or .cl file that contains the 
corresponding class definition. This .g file must therefore be #included in the appropriate source file(s). 


Calling conventions and function order 


As described in the Introduction chapter, a method function should normally be declared with the 
METHOD_CALL calling convention (that is, preceded by a #pragma METHOD_CALL statement). The only 
exception is when a method function is the target of both a message sending function (such as p_send or 
p_entersend) and p_enter. In this case the method function must be declared with the cpEct calling 
convention. 


A method function source file may include any number of local and/or global auxiliary functions. To 
avoid the need to repeatedly switch between different calling conventions, it is recommended that 
functions should appear in the file in a standard order, for example: 


e local and global auxiliary functions, declared with LocAL_c or GLDEF_c 


e local and global auxiliary functions that are the target of p_enter, preceded by a 
#pragma ENTER_CALL statement 


e method functions that are also the target of p_enter, preceded by a #pragma CDECL statement 


e the remaining method functions, preceded by a #pragma METHOD_CALL statement 


OBJECT ORIENTED PROGRAMMING GUIDE 


Where possible, auxiliary functions should be written in ‘topological’ order, that is, a function should 
appear before any reference to that function. Method functions, however, may appear in any order. 


Sometimes, conflicting requirements mean that it is necessary to intermix functions of different calling 
conventions. If, for example, you need to position an auxiliary function that is the target of p_enter 
between other 'normal' auxiliary functions, you can surround the function in question with the pair of 
statements: 


#pragma save, ENTER_CALL 


#pragma restore 


APPENDIX C 


MECHANISMS 


This appendix provides more detail than was presented in the Basic concepts section of the Introduction 
chapter. In addition to giving further insight into the mechanisms involved in Psion's Object Oriented 
system, it may prove useful while debugging errant applications. 


Classes 
A class defines the data (property) and behaviour (methods) of a particular type of object. 
A class is implemented as: 

e a set of method functions 

e aclass descriptor 


The method functions are those functions that implement the class methods. The source code format of 
these functions is described in Appendix B. 


Class descriptor 


A class descriptor is a data structure that resides in the same code segment as the method functions of that 
class. It contains information relevant to the class and consists of a header, followed by an array of 16-bit 
code segment offsets to the method functions. 


The C structure for the class descriptor header is defined as: 


typedef 
{ 
UWORD cat; 
struct p_class *super; /* superclass class */ 
UWORD len; /* length of instance */ 
UWORD base; /* base function number */ 
UBYTE sig_6b; /* signature - should be Ox6b */ 
UBYTE num; /* number of entries in vector table */ 
UBYTE ncomp; /* number of component objects */ 
} P_CLASS; 


The contents of a loaded and dynamically linked class descriptor is as follows: 
cat the handle of the code segment that contains the superclass class descriptor 


super the offset of the superclass class descriptor within segment cat, or zero if 
there is no superclass (i.e. this is the class descriptor of a root class) 


len the length of an instance of the class, including the lengths of inherited 
property (used by, for example, p_new and p_newlibh to create an instance) 


base the base method number corresponding to the first entry in the method table 
that follows the class descriptor 


sig_6b a signature (which should be 0x6) to guard against a bad class reference 
num the number of entries in the method table 
ncomp the number of component objects to be automatically destroyed 


OBJECT ORIENTED PROGRAMMING GUIDE 


The following method table may contain "holes", represented by zeroes, corresponding to those method 
functions that are supplied by a superclass. The following diagram illustrates a typical situation: 


subclass superclass 


header 


class 
descriptor 


where +a, +b, +c and +d represent code segment offsets to method functions. 


In this example, the superclass provides two methods, whose method functions are at code segment offsets 
+a and +b. The subclass replaces the second method of its superclass, with a method function at an offset 
+c in its own code segment. It also adds one new method, with method function code at an offset +d. 


The first method of the subclass is supplied by its superclass, as indicated by the zero in the table of 
offsets. Sending the corresponding message to the subclass will therefore cause the method function at 
offset +a in the superclass code segment to be executed. 


Note that the two classes may be in the same or different code segments. The resolution of the links 
between classes in different code segments is part of the dynamic linkage mechanism, described later. 


Object creation 


An instance of a class is implemented as a cell in the heap and is created by calling p_new, £_new, 
f_newlibh, p_newlibh, f_newsend Or f_newlibhsend. The returned handle is a pointer to the heap cell. 


The first two words of the cell contain the location of the class descriptor of the class of which the object is 
an instance (in exactly the same way as for the superclass reference in a linked class descriptor). 


The remainder of the cell contains the property (if any) of that class, including any property inherited 
from its superclasses. The property contribution of a class always follows the property contribution 
inherited from its superclass, as illustrated in the following diagram. 


handle class descriptor 


property of C1 


property of C2 
property of C3 


In this example, class c1 is subclasses the Root class, c2 subclasses c1 and c3 subclasses c2. The 
property of c3 is made up of the contribution from c3 itself and the contributions inherited from C2, c1 
and the root class. The various contributions are ordered within the cell as shown. 


The pointer to the class descriptor that heads the cell is set up when an instance of that class is created. It 
is, in fact, the property of the Root class, which is the ultimate superclass of all classes. 


APPENDIX C - MECHANISMS 


The property contribution of a class always follows the property contribution inherited from its superclass 
and this ordering stays the same even if the class itself is susequently subclassed. Suppose, for example, 
that class cLassi subclasses the class root. The property of an instance of cLassi is made up of the 
contribution from ciassi1 itself and the contribution inherited from the Root class, as shown in the 
following diagram: 


handle points to: property of Root 
property of cLass1 


If cLass2 in turn subclasses ciass1, the property of an instance of cLass2 is composed of the 
contributions from three classes, ordered within the cell as shown below: 


handle points to: property of Root 
property of cLass1 


property of cLass2 


All the functions (p_new etc) that create an object initialise the property with zeros. 


Categories 


A category is formally defined as being a group of one or more classes. The classes are packaged into a 
load module which, when loaded, occupies a single code segment. A code segment may not contain more 
than one category. There is, therefore, a one-to-one correspondence between a category and the executable 
code in a single code segment.! Note that this implies that an application that occupies a single code 
segment may not contain more than one category. 


Category code segments are shared - there is only one copy of a particular category in memory, however 
many processes are executing it. 


There are two main groups of categories: 


e Image categories contain an entry point at offset zero and are used to implement programs. The 
name of a code segment that contains an image category has the extension .$sc. An image 
category code segment is created by loading an executable using p_execc (as described in the 
chapter Processes and Inter-Process Messaging in the Plib Reference manual). 


e = Dynamic library categories (DYLs) have no entry point, but contain classes that are referenced 
from image categories and other DYLs. The name of a code segment containing a DYL has the 
extension .dyl. A DYL code segment is created by loading a DYL load module (which may be a 
separate file or be embedded in an executable) using p_loadlib OF p_loadfilelib (these 
functions are described in the Object Oriented Programming chapter of the PLIB Reference 
manual). 


Category handles and category numbers 
A category handle identifies a category code segment, which may be in RAM or the ROM, as follows: 
e if the category handle is positive, it is the handle of a moveable RAM-based code segment 
e if the category handle is negative, it is the paragraph address of a ROM category code segment 


A category code segment may also be identified by a category number. This number is known at compile 
time, whereas category handles are only known at run time. A category number is mainly used to create 
an instance of an object class using p_new, f_new Or £_newsend although it is also used by the more 
obscure functions p_exactsend, p_reclass and p_cpycat. 


A local category is defined as the category containing the code that makes a category reference; an 
external category is a category other than the local category. Given these definitions, the local category 
always has the category number zero. An external category number is the index (from 1) into an array 
which, at run time will contain handles to the external categories. The array exists in the local category 
code segment and is generated from the list of =xTERNAL category references in a category file. 


! This is true for all SIBO executables, even if they do not use Object Oriented techniques and thus do not 
explicitly define a category. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The value of an external category number thus depends on the composition and order of the external 
category array in the local category. Different categories will, in general, use different category numbers to 
refer to the same external category. Because of this fact, a category number should not be passed as a 
parameter to an external method (for example, to create a component of variable class). When there is a 
requirement to pass a category as a parameter, the category handle rather than the category number should 
be used. The category handle may always be obtained from the category number by calling p_getlibh. 


For example, consider a situation where an image category cat/.$sc makes external references to dynamic 
libraries cat2.dyl and cat3.dyl, and cat2.dyl contains an external reference to cat3.dyl. 


CAT3.DYL 


The above diagram illustrates that in this case the external category number of cat3.dyl from cat1.$sc is 2, 
but from cat2.dyl it is 1. 


Dynamic linkage 
A reference to an external category by category number occurs when: 
e aclass from an external category is subclassed by the local category 


e the local category contains code that references an external category by a category number (most 
likely for the purposes of creating an instance of an external class using p_new, f_new or 
f£_newsend). 


At run time, each reference by category number (such as in a call to p_new) will generate a reference by 
category handle. Before this can be done, the calling category must be dynamically? linked with all the 
external categories to which it refers. External categories are referenced by their memory segment names 
and it follows that, when a category is dynamically linked, all the referenced categories must be loaded. 


Part of the linking process is the resolution of the reference to a (possibly external) superclass in each 
class descriptor in the category. 


A main image category is linked by calling p_1ink1lib (0). For the predominant case where an application 
is implemented as a single image category referencing only ROM-based DYLs (which do not need to be 
explicitly loaded), the image may be linked by calling p_1ink1ib at any time before the execution of code 
involving an external category reference. The call to p_link1lib is normally early in main. 


A DYL referencing only categories that are already loaded may be linked immediately after it is loaded. 
This may be done by passing a suitable parameter value to the function that loads the DYL (either 
p_loadlib OF p_loadfilelib). For example, this technique is suitable when an application category loads 
a DYL that references only the ROM-based DYLs and the application category. 


2 The term dynamic linkage is used because the link is made at run time. This is as opposed to normal 
(static) linkage between code modules, which occurs at compile time (and is used to produce a category 
load module, such as an executable or, indeed, a DYL). 


C-4 


APPENDIX C - MECHANISMS 


In any case where a category (whether an image category or a DYL) contains references to categories that 
are not yet loaded, it must not be linked immediately after loading. First, all other referenced categories 
must be loaded. Only then can the category be linked by calling p_1inklib. 


Referencing by category handle 


It is possible for a category to reference an external category by category handle - most commonly to 
create an instance of an external class using p_newlibh, f_newlibh Of f_newlibhsend or to call the more 
obscure p_reclassbyhandle. 


In this case, the handle is normally obtained independently of dynamic linkage by one of the following 
means: 


e the category handle is passed as a parameter to a method 
e from the segment name, by calling p_findlib (emulating dynamic linkage) 


e by the local category loading a DYL using p_loadlib Or p_loadfilelib 


Message passing 


In OOP terminology, sending a message to an object means calling a method function of the class or 
superclass of which that object is an instance. 


Method functions are identified by their method number, which must be between zero and 255. The 
method number zero is normally reserved for the method that destroys the object (and its components, if 


any). 


The most common way of sending a message is to use p_sena (or, more efficiently, one of the p_sendn 
variants). This function must be supplied with the handle of the object instance (the address of a cell in 
the heap, as returned by, say, p_new or p_newlibh) and the method number as its first two parameters. Up 
to three additional parameters may be supplied. 


The p_sena function locates the appropriate method function as follows: 


e it locates the class descriptor of which that object is an instance (using the category handle and 
class segment offset at the beginning of the instance) 


e if the method number is in range of the method table that follows the class descriptor, and the 
corresponding entry has a non-zero value, it calls the corresponding method function 


e otherwise it locates the superclass class descriptor and repeats the above 


If the process of trying to find a corresponding method in successive superclass class descriptors 
(sometimes called superclass chaining) fails, the sending function panics with panic number 48. 


The send will also panic (with panic number 55) if the category handle and class segment offset at the 
beginning of the instance points to a class descriptor that does not have the correct signature. This 
catches, amongst other things, the sending of a message to an object that has already been destroyed. 


If successfully located, the method function is passed the object handle and the optional parameters (the 
method number passed to p_send is suppressed). 


Within method function code (including any auxiliary functions) it is possible to 'send a message’ by 
making a normal function call to another method function, rather than using one of the above message- 
sending functions. This is only possible when: 


e the target method function is in the same category as the sending method 


e the target method is monomorphic, that is, the functionality does not depend on the class of the 
instance to which the message is sent 


e there are no calls to p_supersend in the target method 


This technique may be used freely in application-specific classes, since such classes are totally within the 
control of the application writer. When writing general-purpose library DYLs, one has to be more careful 
about calling a local method (rather than using a message sending function such as p_send). Making a 
direct call removes any opportunity for subclassers to divert the send to a subclass method. However, in 
some cases it may be positively desirable to restrict subclassers in this way. 


OBJECT ORIENTED PROGRAMMING GUIDE 


The message sending functions (p_send etc) represent the only mechanism for calling methods when: 


e the method is polymorphic (where a particular send may call different method functions 
depending on the class of the instance to which the method is being sent) 


e the method function is in an external category (for example, a ROM-based DYL) 
e the method function contains a call to p_supersend 


Calling conventions for method functions 


A method function that is the target of any of the message sending functions (eg p_send, p_supersend or 
p_entersend) must use one of the following two calling conventions: 


CDECL where the generated code will take the parameters off the stack 


METHOD_CALL where the generated code will take the parameters from the registers (which 
is more efficient) 


Note that if you call a method function directly, the prototype must be visible to the caller and must, of 
course, indicate the correct calling convention. 


Recall, from the Error Handling chapter of the PLIB Reference manual, that the target of a p_enter must 
use one of: 


CDECL where the generated code will take the parameters off the stack 
ENTER_CALL where the generated code will take the parameters from the registers 


Since the ENTER_CALL convention is different from the MzTHoD_CALL convention, a method function that is 
a target of both p_send (or any other message sending function, including p_entersend) and p_enter 
must be declared as cbEcL. 


Method parameters 


In the calling convention of message sending functions, such as p_send, the function parameters are 
passed in registers. In TopSpeed C this means that no more than five parameters may be passed (including 
the object handle and the method number) with each parameter being limited to a 16-bit value. 


Thus the parameters to a method sending function may not include the types Lonc, FLOAT Or DOUBLE, and 
structures may not be passed by value. The preferred technique is to pass the (16-bit) address of any of 
these types of data. 


In exceptional cases a Lonc may be passed as two worp parameters, where the first contains the least 
significant word and the second contains the most significant word. A very small number of methods in 
the OLIB dynamic library, for example, use this technique. 


INDEX 


.afl files 
oop application, 1-17 
.asm files 
category source file HWIM, A-9 
c files 
category generated source file HWIM, A-7 
skeleton method source file HWIM, A-10 
cat files 
HWIM, A-1 
cl files 
sub category HWIM, A-3 
.dfl files 
DYL add file lists, 3-5 
.ext files 
category file include file HWIM, A-2 
external reference file HWIM, A-7 


.g files 

include file HWIM, A-4, A-8 
.img files 

building illustrated in oop, 2-1 
.ing files 

asm include file HWIM, A-10 
lis files 

category listing file HWIM, A-10 
-pic files 

oop application, 1-17 
re files 


oop applications, 1-16 

resource externals file example, 4-3 
rg files 

oop applications, 1-16 
th files 

in oop, 15-2 

include file in oop, 15-2 

resource header file HWIM, 4-4 
sc files 

oop application, 1-17 
shd files 

oop application, 1-17 
.wve files 

Record application HWIM, 16-3 
abstract class 

in oop, 1-14 
ACLIST 

dialog resource HWIM, 8-9 
ACLIST_ARRAY 

dialog resource HWIM, 8-9 
action list 

dialog control HWIM, 8-7 
action list small 

dialog control HWIM, 8-8 
active objects 

AO_INIT message HWIM, 9-4 

AO_QUEUE message HWIM, 9-4 


AO_RUN message HWIM, 9-4 
application responsiveness HWIM, 9-2 
background processing HWIM, 9-2 
compute intensive tasks HWIM, 9-2 
errors HWIM, 9-3 
introduction HWIM, 9-1 
priority HWIM, 9-2 
Record example app HWIM, 16-8 
RUN_ACTIVE_USED HWIM, 9-2 
timer example HWIM, 9-3 
ADD 
category file statement HWIM, A-3 
oop keyword, 1-14 
add files 
DYLs and, 3-5 
oop application, 1-17 
AIDLE class 
compute intensive tasks HWIM, 9-2 
AM_CLEAN_UP 
errors HWIM, 10-2 
AM_INIT 
message in oop, 1-16 
AM_LOAD_RES_BUF 
message HWIM, 15-2 
AM_LOAD_RESOURCE 
message HWIM, 15-2 
AM_NEW_FILENAME 
switching to a new file, 11-2 
AM_RSCNAME 
replacing resource file load, 15-1 
AO_INIT 
active objects HWIM, 9-4 
AO_QUEUE 
active objects HWIM, 9-4 
AO_RUN 
active objects HWIM, 9-4 
app file 
from img file in oop, 2-2 
app from img 
in oop, 2-2 
application 
add files in oop, 1-17 
attached Series 3a HWIM, 17-1 
automatic test system Series 3a HWIM, 
18-1 
building in oop, 2-1 
building oop example, 4-6 
category file in oop, 1-12 
category file oop example, 4-1, 4-3 
client window oop example, 4-6 
design basics HWIM, 16-1 
design class diagrams HWIM, 16-1 
design engine HWIM, 16-1, 16-3 
design HWIM, 16-1 
design Record app HWIM, 16-1 
design Record example HWIM, 16-3, 16-5 
design typical HWIM, 16-2 
design user interface HWIM, 16-1, 16-2 
DYL accessing built in, 3-5 
DYL building in, 3-5 
example project file in oop, 4-6 
file based oop, 11-1 
HWIM basic class structure, 1-12 
icon file in oop, 1-17 
initialisation specific in HWIM, 5-12 


OBJECT ORIENTED PROGRAMMING GUIDE 


Kats macro recorder example, 18-7 
main function oop example, 4-5 
main in oop, 1-15 
method functions in oop, 1-14 
miscellaneous files in oop, 1-17 
oop and PLIB, 2-6 
oop example - hello world, 4-1 
oop required files, 1-12 
resource externals file in oop, 1-16 
resource externals file oop example, 4-3 
resource file in oop, 1-16 
resource file oop example, 4-1, 4-4 
responsiveness active objects HWIM, 9-2 
shell data file in oop, 1-17 
source code oop example, 4-4 
source files in oop, 1-14 
start up in oop, 1-16 
system resource file in oop, 1-17 
window server object oop example, 4-5 
application components 
in HWIM, 1-9 
application engine 
handle via magic static, 1-11 
application manager 
handle via w_am, 1-9 
HWIM, 1-9 
HWIMMAN class, 1-9 
APPMAN 
OLIB class, 1-9 
array object 
example application prndir in oop, 2-2 
asynchronous requests 
active objects HWIM, 9-1 
ATS 
attached application control HWIM, 18-1 
demonstrations HWIM, 18-1 
macro recorder example HWIM, 18-7 
macros HWIM, 18-1 
mechanism HWIM, 18-1 
message types HWIM, 18-2 
Series 3a HWIM, 18-1 
services general HWIM, 18-1 
structures HWIM, 18-2 
ats.h 
header file HWIM, 18-1 
ATSSV class 
HWIM, 18-2 
attached 


applications Agenda Series 3a HWIM, 17-1 


applications Series 3a HWIM, 17-1 
applications Word Series 3a HWIM, 17-1 
process Series 3a HWIM, 17-2 
automatic test system 
see ATS, 18-1 
Series 3a HWIM, 18-1 
background processing 
active objects HWIM, 9-2 
bar graph 
Record example app HWIM, 16-7 
Berlitz 
edit like windows example HWIM, 12-34 
bring 
See link paste, 14-1 
building 
applications in oop, 2-5 


building application 

oop example, 4-6 
C++ 

contrasted to Psion oop, 1-3 
categories 

external in oop, 1-5 

local in oop, 1-5 

oop, 1-4 
category 

DYL in oop, 1-4 

file translation HWIM, A-6 

handles in oop, 1-5 

image in oop, 1-4 

numbers in oop, 1-5, 1-6 

sub files HWIM, A-3 

sub files using HWIM, A-4 
category file 

application oop example, 4-1, 4-3 

contents HWIM, A-1 

convertion in oop via ct.bat, 2-2 

DYL example, 3-2 

HWIM, A-1 

oop, 1-3 

oop application, 1-12 

structure of HWIM, A-2 

translation DYL example, 3-2 
category listing file 

lis file HWIM, A-10 
category source file 

.asm file HWIM, A-9 

.c file HWIM, A-7 
CHLIST 

dialog resource HWIM, 8-5 
choice list 

dialog control HWIM, 8-5 
CHOICE_ITEM 

dialog resource HWIM, 8-6 
class 

abstract in oop, 1-14 

basic structure HWIM application, 1-12 

constant declaration in oop, 1-14 

diagrams in oop, 1-7 

instance of in oop, 1-3 

method declaration keywords, 1-13 

names in oop, 1-6 

numbers in oop, 1-6 

property declaration in oop, 1-14 

property number declaration in oop, 1-14 

relationships in oop, 1-7 

types declaration in oop, 1-14 
CLASS 

category file statement HWIM, A-2 
class diagrams 

HWIM, 16-1 
classes 

application specific - oop option, 1-8 

introduction to, 1-3 
client window 

HWIM application, 1-10, 6-2 
COM_ACCL_CHECK 

example of use in oop, 5-9, 5-10 
com_cat 

IN_WSERYV field in oop, 1-16 
com_class 

IN_WSERYV field in oop, 1-16 


COM_EXIT 
application termination, 11-3 
Shut down message, 11-3 
COM_FILE_CHANGE 
example of use, 11-1, 11-2, 11-3 
COM_INIT 
example of use in oop, 5-12 
COM_MENU 

example in oop, 5-7 

example of use in oop, 5-10 
COMMAN class 

header file, 5-1 

HWIM library, 1-10, 5-1 

subclassing in HWIM, 5-2 
comman.g 

definition, 5-1 
command byte 

H_COMMAND_BYPASS HWIM, 17-1 
command line 

H_COMMAND_BYPASS byte HWIM, 

17-1 
command manager 

code sharing in HWIM applications, 5-6 

handle, 1-10 

HWIM class, 1-10 

in HWIM, 1-10 

in oop, 5-1 

initialisation in oop, 5-1 
commands 

handling in oop, 5-1 

menus in HWIM applications, 5-2 
component objects 

in oop, 1-3 
compute intensive tasks 

active objects HWIM, 9-2 

AIDLE class HWIM, 9-2 


PRIORITY_ACTIVE_COMPUTE HWIM, 


9-2 
CONSTANTS 

category file statement HWIM, A-3 
ct.bat 

batch file HWIM, A-7 
ct.bat file 

category file convertion in oop, 2-2 
ctran.exe 

category file translation HWIM, A-6 

DYL example, 3-2 

utility in oop, 1-12 

utility program HWIM, A-6 
DatApp1 to 7 

magic statics, 1-11 
DatDialogPtr 

magic static, 1-12, 8-1 
date/time editor 

dialog control HWIM, 8-16 
DatGate 

magic static, 18-1 
DatLocked 

file based apps HWIM, 11-3 
DatUsedPath NamePtr 

magic static, 11-1 
DEFER 

category file statement HWIM, A-3 

oop keyword, 1-14 


INDEX 


designing 


applications HWIM, 16-1 


DESTROY 


message HWIM, A-3 


destruction 


of objects in oop, 1-4 


dfl files 


DYL add file lists, 3-5 


dialog 


adding items HWIM, 7-7 
application specific HWIM, 1-11, 7-1 
behaviour default HWIM, 7-2 
boxes using HWIM, 7-2 

bullet symbol, 8-3 

button text changing HWIM, 7-8 
contrasted with edit windows HWIM, 12-1 
controls HWIM, 7-1, 8-1 
dim/undim items HWIM, 7-7 
dynamic items, 7-5, 7-7 

handle via DatDialogPtr, 1-12 
HWIM, 7-1 

item focus HWIM, 7-2 

items changing, 7-5, 7-7 

items HWIM, 7-1, 8-1 
launching example HWIM, 7-4 
lock/unlock items HWIM, 7-7 
maximum items HWIM, 7-2 
modal HWIM, 7-1 

prompt, 8-3 

removing items HWIM, 7-7 
replacing items HWIM, 7-7 
resource example HWIM, 7-3 
resource files HWIM, 7-3 
resource flags HWIM, 7-4 
resource structures HWIM, 7-4 
resource TEXTWIN HWIM, 8-2 
results retrieval HWIM, 7-9 
simple HWIM, 7-5 

subdialog HWIM, 7-11 

system HWIM, 7-1 

title, 8-2 

title - replacing, 8-4 

wait - with or without HWIM, 7-9 
width controlling HWIM, 7-9 


dialog controls 


action list, 8-7 

action list small, 8-8 

choice list, 8-5 

date/time editor, 8-16 

edit box, 8-9 

file name choice list, 8-21 
file name editor, 8-19 
floating point editor, 8-15 
integer numeric editor, 8-11 
latitude/logitude editor, 8-18 
LODGER class HWIM, 8-1 
long numeric editor, 8-10 
numeric editors, 8-10 

pack selector, 8-19, 8-21 
push button, 8-7 

range numeric editor, 8-13 
text window HWIM, 8-2 
word numeric editor, 8-12 


iii 


OBJECT ORIENTED PROGRAMMING GUIDE 


dialog resource 
ACLIST HWIM, 8-9 
ACLIST_ARRAY HWIM, 8-9 
CHLIST HWIM, 8-5 
CHOICE_ITEM HWIM, 8-6 
DTEDIT HWIM, 8-16 
EDWIN HWIM, 8-9 
FLTEDIT HWIM, 8-15 
FNEDIT HWIM, 8-20 
FNSELWN HWIM, 8-21 
LLEDIT HWIM, 8-18 
LNCEDIT HWIM, 8-11 
MENU and choice list HWIM, 8-6 
NCEDIT HWIM, 8-12 
PUSH_BUT HWIM, 8-8 
RGEDIT HWIM, 8-14 
TXTMESS HWIM, 8-3 
WNCEDIT HWIM, 8-13 
DL_DYN_INIT 
example usage, 8-1 
DL_KEY 
dialog method, 8-1 
document objects 
EDWIN class direct interaction HWIM, 
12-25 
draw/redraw 
window mechanism in HWIM, 6-2 
drawing 
window borders in HWIM, 6-3 
windows in HWIM, 6-3 
DTEDIT 
dialog resource HWIM, 8-16 
DYL 
add file lists, 3-5 
advantages of, 1-4 
building, 3-1 
building example, 3-2 
building into an application, 3-5 
built in accessing, 3-5 
built in example, 3-5 
built in example project file, 4-7 
categories in oop, 1-4 
category file example, 3-2 
category file translation, 3-2 
differences from applications, 3-1 
example, 3-1 
example - using, 3-3 
example runsort.c, 3-3 
example source code, 3-1 
floating point and, 3-1 
loading and linking example, 3-3 
oop option, 1-8 
PLIB, 3-1 
project file example, 3-2 
ROM based, 1-4 
root class suppling example, 3-4 
dynamic libraries 
see DYL, 1-4 
ecobj.exe 
utility program HWIM, A-7 
utility program in oop, 2-1 
edit box 
dialog control HWIM, 8-9 
edit like windows 
Berlitz example HWIM, 12-34 


HWIM, 12-34 
Spellchecker example HWIM, 12-34 
edit windows 
contrasted with dialogs HWIM, 12-1 
document object interactions HWIM, 12-25 
document offset concept HWIM, 12-14 
EDWIN class additional methods HWIM, 
12-11 
features additional HWIM, 12-1 
features of HWIM, 12-1 
HWIM, 12-1 
landlord example HWIM, 12-6 
layout and formatting HWIM, 12-16 
multiple notes example app HWIM, 12-2 
notes example application HWIM, 12-2 
read only HWIM, 12-15 
SCRIMG screen image HWIM, 12-20 
SCRLAY screen layout HWIM, 12-17 
simple example app HWIM, 12-2 
special characters HWIM, 12-9 
edump.exe 
example usage, 3-6 
utility program, 3-6 
EDWIN 
dialog resource HWIM, 8-9 
EDWIN class 
document objects direct interaction HWIM, 
12-25 
document offset concept HWIM, 12-14 
edit like windows HWIM, 12-34 
edit window additional methods HWIM, 
12-11 
edit windows HWIM, 12-1 
edit windows simple use HWIM, 12-5 
layout and formatting HWIM, 12-16 
read only HWIM, 12-15 
SCRIMG screen image HWIM, 12-20 
SCRLAY layout HWIM, 12-17 
ehello 
edit windows simple example app HWIM, 
12-2 
emphasis 
windows in HWIM, 6-5 
emulator 
unsuitable applications, 2-5 
engine 
application design HWIM, 16-3 
handle via magic static, 1-11 
HWIM application, 1-11 
environment variable 
Record application HWIM, 16-3 
EPOC emulator 
unsuitable applications, 2-5 
error 
handling HWIM, 10-1 
handling mechanisms HWIM, 10-1 
recovery HWIM, 10-1 
errors 
active objects HWIM, 9-3 
AM_CLEAN_UP message HWIM, 10-2 
cleanup list HWIM, 10-5 
detection - spy application, 10-2 
general HWIM, 10-2 
handling Record application HWIM, 10-2 
initialisation HWIM, 10-1 


memory HWIM, 10-1 
numbers HWIM, 10-1 
object component rollback HWIM, 10-3 
resource releasing HWIM, 10-4 
roll back principle HWIM, 10-2 
system code interactions HWIM, 10-5 
event 
main scheduling loop HWIM, 1-9 
events 
active objects HWIM, 9-1 
asynchronous requests active objects 
HWIM, 9-1 
example application 
prndir in oop, 2-2 
prndir in oop described, 2-2 
project file in oop, 4-6 
variable array object prndir in oop, 2-2 
example DYL 
built into an application, 3-5 
dynamic libary, 3-1 
using, 3-3 
EXTERNAL 
category file statement HWIM, A-2 
declaration in oop, 1-15 
external reference file 
.ext HWIM, A-7 
file based apps 
alias information HWIM, 11-1 
AM_NEW_FILENAME message HWIM, 
11-2 
COM_EXIT message HWIM, 11-3 
COM_EXIT method HWIM, 11-3 
COM_FILE_CHANGE message HWIM, 
11-1 
COM_FILE_CHANGE method HWIM, 
11-2, 11-3 
DatLocked magic static HWIM, 11-3 
file default extension HWIM, 11-1 
foreground switching to HWIM, 11-3 
hEnsurePath function HWIM, 11-2 
HWIM, 11-1 
initialisation HWIM, 11-1 
open/create command HWIM, 11-1 
opening/creating HWIM, 11-2 
path full HWIM, 11-1 
saving HWIM, 11-3 
shut down message HWIM, 11-3 
switch files HWIM, 11-3 
termination HWIM, 11-3 
file name choice list 
dialog control HWIM, 8-21 
file name editor 
dialog control HWIM, 8-19 
files miscellaneous 
oop application, 1-17 
floating point 
DYL restrictions, 3-1 
floating point editor 
dialog control HWIM, 8-15 
FLTEDIT 
dialog resource HWIM, 8-15 
FNEDIT 
dialog resource HWIM, 8-20 
FNSELWN 
dialog resource HWIM, 8-21 


INDEX 


foreground 
file based apps switching to HWIM, 11-3 
FORM 
library, 1-2 
FRC 
Record example app HWIM, 16-7 
free running counter 
Record example app HWIM, 16-7 
function calls 
in oop, 1-5 
function prototypes 
method functions in oop, 1-7 
GATE class 
Series 3a HWIM, 18-1 
H_COMMAND_BYPASS 
command byte HWIM, 17-1 
handle 
application engine via magic static, 1-11 
application manager via w_am, 1-9 
command manager, 1-10 
dialog via DatDialogPtr, 1-12 
window server via w_ws, 1-10 
handles 
application objects HWIM, 16-2 
as property of an object, 1-3 
category in oop, 1-5 
of objects in oop, 1-3 
help resource 
HELP_ARRAY, 15-4 
identifier help_index_id HWIM, 15-5 
identifiers context HWIM, 15-5 
identifiers dialog HWIM, 15-5 
identifiers HWIM, 15-4 
TOPIC_ARRAY, 15-4 
using HWIM, 15-4 
HELP_ARRAY 
help resource, 15-4 
help_index_id 
resource identifiers help HWIM, 15-5 
hEnsurepath 
file function HWIM, 11-2 
hLoadResource 
resource loading function HWIM, 15-3 
hwim 
resource header .rh files, 4-4 
HWIM 
.asm category source file, A-9 
.c category C source file, A-7 
.c Skeleton method source file, A-10 
.cl sub category files, A-3 
.ext external reference file, A-7 
.g C include file, A-8 
-g include files, A-4 
.ing asm include file, A-10 
lis category listing file, A-10 
active object event sources, 9-1 
active objects introduction, 9-1 
active objects Record example, 16-8 
ADD category file statement, A-3 
agenda attached application, 17-1 
AM_LOAD_RES_BUF message, 15-2 
AM_LOAD_RESOURCE message, 15-2 
application basic class structure, 1-12 
application client window, 1-10 
application components in oop, 1-9 


OBJECT ORIENTED PROGRAMMING GUIDE 


application design, 16-1 

application design basics, 16-1 
application design class diagrams, 16-1 
application design engine, 16-1, 16-3 
application design Record app, 16-1 
application design typical, 16-2 
application design user interface, 16-1, 16-2 
application engine, 1-11 

application manager, 1-9 

asynchronous requests active objects, 9-1 
ATS inter-process messages, 18-1 

ATS macro recorder example, 18-7 

ATS mechanism, 18-1 

ATS message types, 18-2 

ATS Series 3a, 18-1 

ATS structures, 18-2 

ats.h header file, 18-1 

ATSSV class, 18-2 

attached applications Series 3a, 17-1 
attached process Series 3a, 17-2 
automatic test system Series 3a, 18-1 

bar graph Record example, 16-7 
category .ext file, A-2 

category EXTERNAL statement, A-2 
category file contents, A-1 

category file structure of, A-2 

category file translation, A-6 

category files, A-1 

category INCLUDE statement, A-2 
category sub files, A-3 

category sub files using, A-4 

CLASS category file statement, A-2 
client window, 6-2, 16-3 
COM_ACCL_CHECK example of use, 5-9, 
5-10 

COM_INIT example of use, 5-12 
COM_MENU example of use, 5-10 
COM_MENU oop example, 5-7 
COMMAN class, 5-1 

COMMAN class subclassing, 5-2 
command byte HHCOMMAND_BYPASS, 
17-1 

command manager, 1-10, 5-1 
CONSTANTS category file statement, A-3 
ct.bat batch file, A-7 

DEFER category file statement, A-3 
DESTROY message, A-3 

dialog application specific, 1-11, 7-1 
dialog behaviour default, 7-2 

dialog boxes using, 7-2 

dialog button text changing, 7-8 

dialog control action list, 8-7 

dialog control action list small, 8-8 
dialog control choice list, 8-5 

dialog control date/time editor, 8-16 
dialog control edit box, 8-9 

dialog control file name choice list, 8-21 
dialog control file name editor, 8-19 
dialog control floating point editor, 8-15 
dialog control integer numeric editor, 8-11 
dialog control latitude/logitude editor, 8-18 
dialog control LODGER class, 8-1 
dialog control long numeric editor, 8-10 
dialog control numeric editors, 8-10 
dialog control pack selector, 8-19, 8-21 


dialog control push button, 8-7 

dialog control range numeric editor, 8-13 
dialog control text window, 8-2 

dialog control word numeric editor, 8-12 
dialog controls, 7-1, 8-1 

dialog dynamic items, 7-5, 7-7 

dialog item adding, 7-7 

dialog item dim/undim, 7-7 

dialog item focus, 7-2 

dialog item lock/unlock, 7-7 

dialog item replacing, 7-7 

dialog items, 7-1, 8-1 

dialog items changing, 7-5, 7-7 

dialog launching example, 7-4 

dialog maximum items, 7-2 

dialog modal, 7-1 

dialog resource example, 7-3 

dialog resource flags, 7-4 

dialog resource structures, 7-4 

dialog resource TEXTWIN HWIM, 8-2 
dialog results retrieval, 7-9 

dialog simple, 7-5 

dialog subdialog, 7-11 

dialog system, 7-1 

dialog wait - with or without, 7-9 
dialog width controlling, 7-9 

dialogs, 7-1 

dialogs and resource files, 7-3 

ecobj.exe utility program, A-7 

edit like windows, 12-34 

edit like windows Berlitz, 12-34 

edit like windows Spellchecker, 12-34 
edit window features additional, 12-1 
edit window features of, 12-1 

edit window landlord example, 12-6 
edit window special characters, 12-9 
edit windows, 12-1 

edit windows document object interaction, 
12-25 

edit windows document offset concept, 
12-14 

edit windows layout and formatting, 12-16 
edit windows notes example app, 12-2 
edit windows read only, 12-15 

EDWIN class, 12-1 

EDWIN class additional methods, 12-11 
EDWIN class simple use, 12-5 
environment variable Record application, 
16-3 

error detection - spy application, 10-2 
error handling, 10-1 

error handling mechanisms, 10-1 

error handling Record application, 10-2 
error numbers, 10-1 

error recovery, 10-1 

errors AM_CLEAN_UP message, 10-2 
errors cleanup list, 10-5 

errors general, 10-2 

errors initialisation, 10-1 

errors memory, 10-1 

errors object component rollback, 10-3 
errors resource releasing, 10-4 

errors roll back principle, 10-2 

errors system code interactions, 10-5 
event scheduling loop, 1-9 


EWLINKSYV link paste edit windows, 14-11 
example - hello world, 4-1 

file based applications, 11-1 

file based apps initialisation, 11-1 

file based apps opening/creating, 11-2 
file based apps saving, 11-3 

file based apps shut down message, 11-3 
file based apps switch files, 11-3 

file based apps termination, 11-3 

FRC Record example, 16-7 

GATE class Series 3a, 18-1 

handles of objects, 16-2 

hLoadResBuf resource function, 15-2 
hLoadResource resource function, 15-2 
initialisation application specific, 5-12 
inter-process messages ATS data, 18-2 
inter-process messages Series 3a, 18-1 
keyboard filtering example, 16-7 

library, 1-1 

library vs Hwif, 1-2 

library vs OPL, 1-2 

line page break calculation, 13-2 

link paste, 14-1 

link paste client side, 14-8 

link paste client transaction, 14-8 

link paste data availability, 14-8 

link paste data formats, 14-6 

link paste edit windows, 14-10 

link paste edit windows example, 14-11 
link paste example modified ehello, 14-1 
link paste initialising w_am, 14-4 

link paste IPC mechanisms, 14-1 

link paste LINKSV class, 14-1 

link paste LINKSV example, 14-4 

link paste native formats, 14-13 

link paste server comments, 14-5 

link paste server operation, 14-13 

link paste server side, 14-1 

link paste server status declaring, 14-2 
link paste server transaction, 14-4 

link paste text formats revisisted, 14-12 
link paste text types, 14-7 

link paste word wrap, 14-7 

LINKCL example, 14-9 

loading a resource, 15-2 

LPRINTER class, 13-1 

LPRINTER examples of use, 13-7 
LPRINTER examples of use advanced, 
13-15 
LPRINTER font width tables, 13-18 
LPRINTER for standard printing, 13-6 
LPRINTER PAGES active object, 13-19 
LPRINTER print setup, 13-7 
LPRINTER property, 13-16 

LPRINTER vs XPRINTER, 13-24 
LPRINTER WDR class description, 13-21 
LPRINTER WDR objects, 13-23 
LPRINTER widths, 13-7 

LPRINTER widths variable fonts, 13-17 
LPRINTER word-wrap, 13-7 
LPRINTER word-wrapping default, 13-17 
mechanisms categories, C-3 
mechanisms category handle referencing, 
C-5 

mechanisms category handles, C-3 


INDEX 


mechanisms category numbers, C-3 
mechanisms class descriptor, C-1 
mechanisms classes, C-1 

mechanisms dynamic linkage, C-4 
mechanisms message passing, C-5 
mechanisms method calling conventions, 
C-6 

mechanisms method parameters, C-6 
mechanisms object creation, C-2 
mechanisms psion OOP, C-1 

menu accelerator separators, 5-6 

menu accelerators - shifted, 5-5 

menu accelerators adding, 5-2 

menu accelerators dynamic, 5-13 

menu accelerators rules, 5-13 

menu bar, 1-11 

menu bar dynamic, 5-12 

menu bar replacing, 5-12 

menu command handling, 5-1 

menu options adding - example, 5-2 
menu options changing number of, 5-10 
menu options code sharing, 5-6 

menu options disabling, 5-9 

menu options dynamic, 5-7 

menu options multi-lingual, 5-8 

menu options validity checking, 5-9 
menu submenus, 5-14 

message shut down, 5-14 

method function calling, B-1 

method function parameters, B-1 
method function source files, B-1 
method source file generation, B-1 
multi-lingual applications, 1-9 

OLIB dyl category file, A-2 

OLIB library, 16-3 

oop option, 1-8 

PDR class, 13-32 

print buffer lifetime, 13-5 

print context file save/restore, 13-34 
print preview, 13-1 

print preview without XPRINTER, 13-33 
printing - page size and margins, 13-3 
printing - print setup storage, 13-4 
printing font and style by line, 13-5 
printing font style changing, 13-4 
printing options WDR system, 13-1 
printing page break calculation, 13-2 
printing printer units, 13-3 

printing WDR basic model, 13-1 
printing WDR system, 13-1 

printing WDR_PRINT_IDLE flag, 13-5 
printing WDR_PRINT_KEEP flag, 13-5 
PROPERTY category file statement, A-3 
PROPERTY number, A-3 

Record application design, 16-3, 16-5 
Record application specification, 16-3 
REPLACE category file statement, A-3 
REQUIRE sub-category file statement, A-4 
resource files, 15-1 

resource files - application, 1-10 
resource files - system, 1-10 

resource files application, 15-1 

resource files location, 15-1 

resource files system, 15-2 

resource files system source code, 15-2 


OBJECT ORIENTED PROGRAMMING GUIDE 


resource help context identifiers, 15-5 
resource help dialog identifiers, 15-5 
resource help identifiers, 15-4 
resource help using, 15-4 
resource loading - example, 5-4 
resource structures, 15-2 
resource system loading, 15-3 
resource system referencing, 15-3 
resource system using, 15-3 
SCRIMG edit window image, 12-20 
SCRLAY edit window layout, 12-17 
shut down message Record application, 
16-3 
shut down messages, 5-14 
sound digital example, 16-3 
status window displaying, 5-11 
status window size of, 5-11 
sub-category files, A-3 
sub-category files using, A-4 
subdialog, 7-11, 8-3 
submenus, 5-14 
switch files message Record application, 
16-3 
twips printer units, 13-3 
TYPES category file statement, A-3 
WDR classes diagram, 13-32 
WDR miscellany, 13-32 
window classes, 6-1 
window draw/redraw mechanism, 6-2 
window drawing, 6-3 
window drawing a border, 6-3 
window emphasis, 6-5 
window lodger, 6-4 
window main, 6-2 
window resizing, 6-4 
window usage, 6-2 
windows, 6-1 
Word attached application, 17-1 
WS_DO_SUBMENU example of use, 5-14 
WS_SET_MENUBAR example of use, 5-12 
wve files Record application, 16-3 
XADD library, 13-1 
XPRINTER class, 13-1 
XPRINTER print preview, 13-24 
XPRINTER print/preview example, 13-25 
XPRINTER vs LPRINTER, 13-24 
XPRINTER vs LPRINTER comments, 
13-31 
HWIM class 
command manager, 1-10 
hwim.rh 
include file in oop, 15-2 
HWIMMAN class 
application manager, 1-9 
icon 
oop application, 1-17 
icon editor 
Iconed example application, 1-17 
Iconeda example 3a application, 1-17 
icon file 
oop application, 1-17 
Iconed 
icon editor example application, 1-17 
Iconeda 
icon editor example 3a application, 1-17 


viii 


image 
categories in oop, 1-4 
IN_WSERV 
struture in oop, 1-16 
INCLUDE 
category file statement HWIM, A-2 
include file 
.g file HWIM, A-8 
.ing asm include file HWIM, A-10 
ats.h HWIM, 18-1 
inheritance 
in oop, 1-3 
initialisation 
application specific in HWIM, 5-12 
instance 
of class in oop, 1-3 
integer numeric editor 
dialog control HWIM, 8-11 
inter-process messages 
ATS data HWIM, 18-2 
ATS HWIM, 18-1 
Series 3a HWIM, 18-1 
Kats 
ATS macro recorder example, 18-7 
keyboard 
filtering example HWIM, 16-7 
latitude/logitude editor 
dialog control HWIM, 8-18 
If.bat 
linking batch file with example DYL, 3-3 
lfc. bat 
linking batch file with example DYL, 3-3 
libraries 
object oriented, 1-1 
oop - advantages, 1-2 
library 
FORM, 1-2 
HWIM, 1-1 
HWIM vs Hwif, 1-2 
HWIM vs OPL, 1-2 
OLIB, 1-1 
XADD, 1-2 
line break 
calculation printing in HWIM, 13-2 
link errors 
spurious in oop, 2-6 
link paste 
client side HWIM, 14-8 
client transaction HWIM, 14-8 
data availability HWIM, 14-8 
data formats HWIM, 14-6 
edit windows example HWIM, 14-11 
edit windows HWIM, 14-10 
EWLINKSYV edit windows HWIM, 14-11 
example modified ehello HWIM, 14-1 
HWIM, 14-1 
initialising w_am HWIM, 14-4 
IPC mechanisms HWIM, 14-1 
link server comments HWIM, 14-5 
LINKCL example HWIM, 14-9 
LINKSV class HWIM, 14-1 
LINKSV example code HWIM, 14-4 
native formats HWIM, 14-13 
server operation HWIM, 14-13 
server side HWIM, 14-1 


server status declaring HWIM, 14-2 
server transaction HWIM, 14-4 
text formats revisited HWIM, 14-12 
text types HWIM, 14-7 
word wrap HWIM, 14-7 
linking 
If.bat file with example DYL, 3-3 
lfc.bat file with example DYL, 3-3 
LINKSV class 
link paste HWIM, 14-1 
LLEDIT 
dialog resource HWIM, 8-18 
LNCEDIT 
dialog resource HWIM, 8-11 
loading and linking 
DYL example, 3-3 
lodger 
windows in HWIM, 6-4 
LODGER class 
dialog control class HWIM, 8-1 
long numeric editor 
dialog control HWIM, 8-10 
LPRINTER class 
examples of use advanced HWIM, 13-15 
examples of use HWIM, 13-7 
font width tables HWIM, 13-18 
HWIM, 13-1 
PAGES active object HWIM, 13-19 
print setup HWIM, 13-7 
property HWIM, 13-16 
standard printing with HWIM, 13-6 
WDR class description HWIM, 13-21 
WDR objects HWIM, 13-23 
widths HWIM, 13-7 
widths variable fonts HWIM, 13-17 
word-wrap HWIM, 13-7 
word-wrapping default HWIM, 13-17 
XPRINTER contrasted HWIM, 13-24 
macro recorder 
example ATS HWIM, 18-7 
macros 
ATS HWIM, 18-1 
magic static 
DatApp1 to 7, 1-11 
DatDialogPtr, 1-12, 8-1 
DatGate, 18-1 
DatLocked file based apps HWIM, 11-3 
DatUsedPathNamePtr, 11-1 
w_am, 1-9 
w_ws, 1-10, 5-1 
main 
function in oop application, 1-15 
make file 
oop example, 4-8 
make.bat 
oop example batch file, 4-8 
menu 
submenus in HWIM applications, 5-14 
MENU 
dialog resource and choice list HWIM, 8-6 
menu accelerators 
3a in HWIM applications, 5-5 
adding in HWIM applications, 5-2 
dynamic in HWIM applications, 5-13 
grouping in HWIM applications, 5-6 


INDEX 


rules in HWIM applications, 5-13 
separators in HWIM applications, 5-6 
shifted in HWIM applications, 5-5 
menu bar 
dynamic in HWIM applications, 5-12 
HWIM application, 1-11, 5-2 
replacing in HWIM applications, 5-12 
menu commands 
handling in oop, 5-1 
menu options 
adding - example HWIM application, 5-2 
adding in HWIM applications, 5-2 
changing in HWIM applications, 5-7 
changing number of - in HWIM 
applications, 5-10 
code sharing in HWIM applications, 5-6 
disabling in HWIM applications, 5-9 
dynamic in HWIM applications, 5-7 
enabling in HWIM applications, 5-9 
language variants in HWIM applications, 
5-8 
multi-lingual in HWIM applications, 5-8 
number of - changing in HWIM 
applications, 5-10 
validity checking in oop, 5-9 
message 
AM_INIT in oop, 1-16 
ATS types HWIM, 18-2 
numbers in oop, 1-6 
shut down in HWIM applications, 5-14 
message sending 
in oop, 1-5 
method 
function names in oop, 1-6 
functions in oop, 1-14 
names in oop, 1-6 
method function 
calling conventions HWIM, B-1 
calling in oop, 1-5 
parameters HWIM, B-1 
prototypes in oop, 1-7 
source files HWIM, B-1 
method number 
in oop, 1-5 
method sending 
p_send functions, 1-5 
method source 
file generation HWIM, B-1 
method WSERV 
ws_dyn_init, 1-10 
methods 
of objects in oop, 1-3 
multi-lingual 
applications HWIM, 1-9 
menus in HWIM applications, 5-8 
naming source files 
in oop, 2-2 
NCEDIT 
dialog resource HWIM, 8-12 
notes 
edit windows multiple example app HWIM, 
12-2 
numeric editors 
dialog control HWIM, 8-10 


OBJECT ORIENTED PROGRAMMING GUIDE 


object 
component objects, 1-3 
creation in oop, 1-3 
destruction of in oop, 1-4 
handles in oop, 1-3, 1-6 
introduction to, 1-3 
libraries oop option, 1-8 
window server in oop, 1-10 

object oriented programming 
introduction, 1-1 
libraries, 1-1 
see oop, 1-1 

OLIB 
dyl category file HWIM, A-2 
library, 1-1 
library HWIM, 16-3 

OLIB class 
APPMAN, 1-9 

oop 
add files, 1-17 
app file from img file, 2-2 
application category file, 1-12 
application required files, 1-12 
application resource file, 1-16 
application source files, 1-14 
application specific classes - oop option, 1-8 
application start up, 1-16 
applications and PLIB, 2-6 
basic concepts - Psion's system, 1-3 
building an application, 2-1 
building an image illustrated, 2-1 
building application oop example, 4-6 
building applications, 2-5 
C++ contrasted with Psion oop, 1-3 
categories, 1-4 
category DYL, 1-4 
category file convertion via ct.bat, 2-2 
category file oop example, 4-3 
category file oop example, 4-1 
category files, A-1 
category files in oop, 1-3 
category image, 1-4 
category numbers, 1-6 
class constant declaration, 1-14 
class diagrams, 1-7 
class instances, 1-3 
class names, 1-6 
class numbers, 1-6 
class property declaration, 1-14 
class property number declaration, 1-14 
class relationships, 1-7 
class types declaration, 1-14 
classes introduction, 1-3 
client window oop example, 4-6 
command handling, 5-1 
command manager, 5-1 
console based application, 2-5 
ct.bat batch file, A-7 
ctran.exe utility, 1-12 
destruction of objects, 1-4 
DYL building, 3-1 
DYL differences from applications, 3-1 
DYLs - oop option, 1-8 
ecobj.exe utility program HWIM, A-7 
edit window features additional, 12-1 


edit window features of, 12-1 

edit windows, 12-1 

error detection - spy application, 10-2 
error handling, 10-1 

error handling mechanisms, 10-1 
error handling Record application, 10-2 
error numbers, 10-1 

error recovery, 10-1 

errors AM_CLEAN_UP message, 10-2 
errors cleanup list, 10-5 

errors general, 10-2 

errors initialisation, 10-1 

errors memory, 10-1 

errors object component rollback, 10-3 
errors resource releasing, 10-4 

errors roll back principle, 10-2 

errors system code interactions, 10-5 
example application - hello world, 4-1 
file based applications, 11-1 

file based apps initialisation, 11-1 

file based apps opening/creating, 11-2 
file based apps saving, 11-3 

file based apps shut down message, 11-3 
file based apps switch files, 11-3 

file based apps termination, 11-3 
function calls, 1-5 

HWIM - oop option, 1-8 

HWIM application components, 1-9 
icon file, 1-17 

inheritance in, 1-3 

introduction, 1-1 

libraries advantages, 1-2 

link errors spurious, 2-6 

main function, 1-15 

main function oop example, 4-5 
mechanisms categories, C-3 
mechanisms category handle referencing, 
C-5 

mechanisms category handles, C-3 
mechanisms category numbers, C-3 
mechanisms class descriptor, C-1 
mechanisms classes, C-1 

mechanisms dynamic linkage, C-4 
mechanisms message passing, C-5 
mechanisms method calling conventions, 
C-6 

mechanisms method parameters, C-6 
mechanisms object creation, C-2 
mechanisms psion, C-1 

menu command handling, 5-1 
message numbers, 1-6 

method declaration keywords, 1-13 
method function calling, B-1 

method function names, 1-6 

method function parameters, B-1 
method functions, 1-14 

method names, 1-6 

method numbers, 1-5 

methods of objects, 1-3 

miscellaneous files, 1-17 

notation and convensions, 1-6 

object creation, 1-3 

object handles, 1-6 

object libraries - oop option, 1-8 


object oriented programming introduction, 
1-1 
options - techniques, 1-8 
property of objects, 1-3 
property of oop library objects, 1-3 
resource externals file, 1-16 
resource externals file example, 4-3 
resource file example, 4-4 
resource file oop example, 4-1 
shell data file, 1-17 
source code oop example, 4-4 
source file naming in oop, 2-2 
subclasses, 1-3 
superclasses, 1-3 
system resource file, 1-17 
window server object oop example, 4-5 
p_send 
method sending functions, 1-5 
pack selector 
dialog control HWIM, 8-19, 8-21 
page break 
calculation printing in HWIM, 13-2 
PDR class 
HWIM, 13-32 
PLIB 
DYLs and, 3-1 
oop applications and, 2-6 
printer units 
printing in HWIM, 13-3 
printing 
font and style by line HWIM, 13-5 
font style changing HWIM, 13-4 
line break calculation HWIM, 13-2 
LPRINTER class HWIM, 13-1 
LPRINTER examples of use advanced 
HWIM, 13-15 
LPRINTER examples of use HWIM, 13-7 
LPRINTER font width tables HWIM, 13-18 
LPRINTER for standard printing HWIM, 
13-6 
LPRINTER PAGES active object HWIM, 
13-19 
LPRINTER print setup HWIM, 13-7 
LPRINTER property HWIM, 13-16 
LPRINTER vs XPRINTER HWIM, 13-24 
LPRINTER WDR class description HWIM, 
13-21 
LPRINTER WDR objects HWIM, 13-23 
LPRINTER widths HWIM, 13-7 
LPRINTER widths variable fonts HWIM, 
13-17 
LPRINTER word-wrap HWIM, 13-7 
LPRINTER word-wrapping default HWIM, 
13-17 
options WDR system HWIM, 13-1 
page break calculation HWIM, 13-2 
page size and margins HWIM, 13-3 
PDR class HWIM, 13-32 
print buffer lifetime HWIM, 13-5 
print context file save/restore HWIM, 13-34 
print preview HWIM, 13-1 
print preview without XPRINTER HWIM, 
13-33 
print setup storage HWIM, 13-4 
printer units HWIM, 13-3 


INDEX 


twips printer units HWIM, 13-3 
WDR basic model HWIM, 13-1 
WDR classes diagram HWIM, 13-32 
WDR miscellany HWIM, 13-32 
WDR system HWIM, 13-1 
WDR_PRINT flags, 13-2 
WDR_PRINT structure, 13-1 
WDR_PRINT_IDLE flag HWIM, 13-5 
WDR_PRINT_KEEP flag HWIM, 13-5 
XADD library, 13-1 
XPRINTER class HWIM, 13-1 
XPRINTER print preview HWIM, 13-24 
XPRINTER print/preview example HWIM, 
13-25 
XPRINTER vs LPRINTER comments 
HWIM, 13-31 
XPRINTER vs LPRINTER HWIM, 13-24 
priority 
active objects HWIM, 9-2 
PRIORITY_ACTIVE_COMPUTE 
compute intensive tasks HWIM, 9-2 
prndir 
example application in oop, 2-2 
programming options 
in oop, 1-8 
project file 
DYL built in example, 4-7 
DYL example, 3-2 
oop example application, 4-6 
property 
handles of component objects, 1-3 
of objects, 1-3 
of objects in oop, 1-3 
of objects in oop libraries, 1-3 
use of - example HWIM application, 5-4 
PROPERTY 
category file statement HWIM, A-3 
number category file HWIM, A-3 
psion oop 
mechanisms, C-1 
push button 
dialog control HWIM, 8-7 
PUSH_BUT 
dialog resource HWIM, 8-8 
range numeric editor 
dialog control HWIM, 8-13 
rcomp.exe 
resource file compiler HWIM, 15-2 
re.bat file 
oop applications, 1-16 
Record 
application classes HWIM, 16-5 
application design HWIM, 16-1, 16-3, 16-5 
application error handling HWIM, 10-2 
application specification HWIM, 16-3 
REPLACE 
category file statement HWIM, A-3 
oop keyword, 1-14 
REQUIRE 
sub-category file statement HWIM, A-4 
reserved static 
see magic static, 1-9 
resizeable windows 
notes example application HWIM, 12-2 


OBJECT ORIENTED PROGRAMMING GUIDE 


resizing 
windows in HWIM, 6-4 
resource 
application loading HWIM, 15-2 
help using HWIM, 15-4 
HELP_ARRAY help, 15-4 


hLoadResBuf loading function HWIM, 15-2 


hLoadResource loading function HWIM, 
15-2 
identifiers help context HWIM, 15-5 
identifiers help dialog HWIM, 15-5 
identifiers help HWIM, 15-4 
identifiers HWIM, 15-2 
loading - example HWIM application, 5-4 
structures HWIM, 15-2 
system loading HWIM, 15-3 
system referencing HWIM, 15-3 
system using HWIM, 15-3 
TOPIC_ARRAY help, 15-4 
resource externals file 
example in oop, 4-3 
oop application, 1-16 
resource files 
AM_RSCNAME loading method, 15-1 
application HWIM, 15-1 
application oop example, 4-1 
compiler rcomp.exe HWIM, 15-2 
HWIM, 15-1 
HWIM - system, 1-10 
HWIM application, 1-10 
loading a resource HWIM, 15-2 
location HWIM, 15-1 
oop application, 1-16 
oop example, 4-4 
ROM based, 1-10 
system HWIM, 15-2 
system source code HWIM, 15-2 
resource header file 
HWIM application hwim.rh, 4-4 
resources 
releasing on errors HWIM, 10-4 
RGEDIT 
dialog resource HWIM, 8-14 
ROM 
DYLs, 1-4 
ROM based 
resource file, 1-10 
system resource file, 1-17 
root class 
suppling DYL example, 3-4 
RUN_ACTIVE_USED 
active objects HWIM, 9-2 
runsort 
DYL example, 3-3 
SCRIMG 
edit windows screen image HWIM, 12-20 
SCRLAY 
edit windows screen layout HWIM, 12-17 
SE_DTEDIT 
structure HWIM, 8-17, 8-18 
SE_EDWIN 
structure HWIM, 8-10 
SE_FLEDIT 
structure HWIM, 8-15 


SE_LLEDIT 
structure HWIM, 8-19 
SE_LNCEDIT 
structure HWIM, 8-11 
SE_NCEDIT 
structure HWIM, 8-12 
SE_RGEDIT 
structure HWIM, 8-14 
SE_TEXTWIN 
structure HWIM, 8-4 
SE_WNCEDIT 
structure HWIM, 8-13 
self 
handle of objects in oop, 1-3 
sending messages 
in oop, 1-5 
Series 3a 
attached applications HWIM, 17-1 
shell data file 
oop application, 1-17 
shut down 
message file based apps oop, 11-3 
message Record application HWIM, 16-3 
messages in HWIM applications, 5-14 
skeleton method 
.c source file HWIM, A-10 
sort example 
DYL based example, 3-3 
sound 
digital example HWIM, 16-3 
digital Record application HWIM, 16-3 
source code 
application oop example, 4-4 
source files 
method functions HWIM, B-1 
naming in oop, 2-2 
oop application, 1-14 
Spellchecker 
edit like windows example HWIM, 12-34 
start up 
oop application, 1-16 
status window 
displaying in HWIM applications, 5-11 
size in HWIM applications, 5-11 
structures 
ATS HWIM, 18-2 
IN_WSERYV in oop, 1-16 
resources HWIM, 15-2 
SE_DTEDIT HWIM, 8-17, 8-18 
SE_EDWIN HWIM, 8-10 
SE_FLEDIT HWIM, 8-15 
SE_LLEDIT HWIM, 8-19 
SE_LNCEDIT HWIM, 8-11 
SE_NCEDIT HWIM, 8-12 
SE_RGEDIT HWIM, 8-14 
SE_TEXTWIN HWIM, 8-4 
SE_WNCEDIT HWIM, 8-13 
WDR_PRINT, 13-1 
sub-category 
files HWIM, A-3 
files using HWIM, A-4 
subclass 
in oop, 1-3 
subdialog 


HWIM, 7-11, 8-3 


submenus 
in HWIM applications, 5-14 
superclass 
in oop, 1-3 
switch files 
file based apps HWIM, 11-2 
message file based apps oop, 11-3 
message Record application HWIM, 16-3 
system resource file 
oop application, 1-17 
ROM, 1-17 
termination 
file based apps oop, 11-3 
text window 
dialog control HWIM, 8-2 
timer example 
active objects HWIM, 9-3 


TOPIC_ARRAY 

help resource, 15-4 
tsc 

example use in oop, 4-8 
tscx 

example use in oop, 4-8 
twips 

printer units HWIM, 13-3 
TXTMESS 

dialog resource HWIM, 8-3 
TYPES 


category file statement HWIM, A-3 
user interface 

application design HWIM, 16-2 
utility program 

rcomp.exe resource compiler, 15-2 
utility program 

ctran.exe HWIM, A-6 

ecobj.exe in oop, 2-1 

edump.exe, 3-6 

wspcx.exe bitmap processing, 1-17 
w_am 

magic static, 1-9, 5-13 
W_Ws 

magic static, 1-10, 5-1 
WDR 

print preview HWIM, 13-1 

printing basic model HWIM, 13-1 

printing system HWIM, 13-1 

printing system options HWIM, 13-1 
WDR_PRINT 

flags, 13-2 

structure, 13-1 
WDR_PRINT_IDLE 

printing flag HWIM, 13-5 
WDR_PRINT_KEEP 

printing flag HWIM, 13-5 
window 

draw/redraw mechanism in HWIM, 6-2 

drawing borders in HWIM, 6-3 

drawing in HWIM, 6-3 

emphasis in HWIM, 6-5 

HWIM application and, 6-1 

lodgers in HWIM, 6-4 

main client HWIM, 16-3 

main client in oop example, 4-6 

resizeable notes example app HWIM, 12-2 

resizing in HWIM, 6-4 


INDEX 


usage in HWIM application, 6-2 
window classes 

HWIM application and, 6-1 
window main 

HWIM application, 1-10 
window server 

active object WSERV, 6-2 

object in oop, 1-10 

object in oop example, 4-5 

object via w_ws, 1-10 
WNCEDIT 

dialog resource HWIM, 8-13 
word numeric editor 

dialog control HWIM, 8-12 
WS_DO_DIAL 

example of use, 7-2 
WS_DO_SUBMENU 

example of use in oop, 5-14 
ws_dyn_init 

WSERV method, 1-10 
WS_SET_MENUBAR 

example of use in oop, 5-12 
WSERV 

window server active object, 6-2 
Wspcx.exe 

bitmap utility program, 1-17 
wve files 

Record application HWIM, 16-3 
XADD 

library, 1-2 

library HWIM, 13-1 
XPRINTER class 

HWIM, 13-1 
XPRINTER subclass 

LPRINTER contrasted comments HWIM, 

13-31 

LPRINTER contrasted HWIM, 13-24 

print preview HWIM, 13-24 

print/preview example HWIM, 13-25 


xiii 


