12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779 |
- MODULE WMComponents; (** AUTHOR "TF"; PURPOSE "Component Framework based on XML"; *)
- (**
- -- Events: --
- Each VisualComponent can produce keyboard and mouse events which can trigger A2 commands.
- The command string for a given event can by specified by the usage of XML attributes and component properties.
- The following attributes are defined:
- Keyboard: onReturn, onEscape, onKeyPressed, onKeyReleased
- Mouse: onLeftClick, onRightClick, onMiddleClick, onClick
- The command strings are processed (macro substitution) before the actual command is called.
- -- Macro substitution: --
- General form: "^" [namespace ":"] macrostring
- A macro always start with MacroCharacter ("^"). The next occurence of a whitespace character determines the end of the macro.
- Two consequent MacroCharacter's ("^^") will be replaced by the MacroCharacter ("^") not triggering macro substitution at all.
- The user can install MacroHandlerProcedures for a given namespace. At most one handler per namespace can be installed.
- If the namespace is omitted, the default macro handler is triggered.
- The DefaultMacroHandler currently supports the following macro substitutions:
- ^selection is replaced by the last selection of the user
- ^clipboard is replaced by the content of the clipboard
- ^attribute=[component "."] attribute
- ^property=[component "."] property
- is replaced by the value of <attribute> or <property>.
- If the component qualifier is omitted, <attribute> or <property> is supposed to be an attribute or property of the originator of the event.
- If no MacroHandlerProcedure is found for a given macro, no substitution takes place.
- Example:
- onLeftClick = System.Show ^attribute=generator
- onMiddleClick = System.Show ^property=FillColor
- *)
- (*PH 08/14:
- - avoid parallel call of FormWindow.SetContent, Component.AddContent, Form.InvalidateRect by different processes, through use of EXCLUSIVE sections.
- - send an "invalidate content" message to a window after it appears on the display, which is handled after "form" field is ready
- - restructure FormWindow.SetContent() to assure coherent displays and to assure FormWindow.content is consistent
- *)
- IMPORT
- KernelLog, Inputs, Streams, Events, Files, Texts, TextUtilities,
- XML, XMLScanner, XMLParser, XMLObjects, Codecs, Localization, Repositories,
- Messages := WMMessages, Rectangles := WMRectangles,
- WMEvents, WMProperties, WMGraphics, Strings, WM := WMWindowManager, Raster,
- Commands, Modules, Kernel, Locks, Objects, WMDropTarget;
- CONST
- Ok* = 0;
- DuplicateNamespace* = 1;
- AlignNone* = 0; AlignLeft* = 1; AlignTop* = 2; AlignRight* = 3; AlignBottom* = 4; AlignClient* = 5; AlignRelative*=6;
- None=0; Left=1; Right=2; Lower=3; Upper=4; LowerRight=5; UpperRight=6; LowerLeft=7; UpperLeft=8; Inside = 9;
- MaxRel = 16*1024;
- MaxComponentNameSize* = 64; (* including 0X *)
- TraceFocus = 0;
- TraceFinalize = 1;
- Trace = {};
- (* Enable event logging? *)
- Logging = TRUE;
- (* Macro handling *)
- (* General form of macro: MacroCharacter [Namespace + NamespaceCharacter] MacroName *)
- MacroCharacter = "^";
- NamespaceCharacter = ":";
- NoNamespace = "";
- (* Namespace used if no namespace is specified *)
- DefaultNamespace = "system";
- (* Macros names of default macro handler *)
- MacroSelection = "selection";
- MacroClipboard = "clipboard";
- MacroAttributePrefix = "attribute=";
- MacroPropertyPrefix = "property=";
- CanYield = TRUE;
-
- (*temporary - to be removed*)
- FlagDirty=13;
- TYPE
- (** Installable event preview handlers. Are called by the components sequencer thread *)
- PointerHandler* = PROCEDURE {DELEGATE} (x, y : LONGINT; keys : SET; VAR handled : BOOLEAN);
- PointerLeaveHandler* = PROCEDURE {DELEGATE} (VAR handled : BOOLEAN);
- DragDropHandler* = PROCEDURE {DELEGATE} (x, y : LONGINT; dragInfo : WM.DragInfo; VAR handled : BOOLEAN);
- DragResultHandler* = PROCEDURE {DELEGATE} (accepted : BOOLEAN; recipient : ANY; dragInfo : WM.DragInfo; VAR handled : BOOLEAN);
- DragAutoStartHandler* = PROCEDURE {DELEGATE} (VAR handled : BOOLEAN);
- FocusHandler* = PROCEDURE {DELEGATE} (hasFocus : BOOLEAN);
- ContextMenuHandler* = PROCEDURE {DELEGATE} (sender : ANY; x, y: LONGINT);
- KeyEventHandler* = PROCEDURE {DELEGATE} (ucs : LONGINT; flags : SET; VAR keySym : LONGINT; VAR handled : BOOLEAN);
- DrawHandler* = PROCEDURE {DELEGATE} (canvas : WMGraphics.Canvas);
- Recursion*= ENUM None*, FromComponent*, FromBottom* END;
- TYPE
- SetStringProcedure = PROCEDURE {DELEGATE} (CONST string : ARRAY OF CHAR; x,y : LONGINT; VAR res : WORD);
- DropTarget = OBJECT(WMDropTarget.DropTarget)
- VAR
- originator : ANY;
- setString : SetStringProcedure;
- x,y : LONGINT;
- PROCEDURE &Init(originator : ANY; setString : SetStringProcedure; x,y : LONGINT);
- BEGIN
- ASSERT(setString # NIL);
- SELF.originator := originator;
- SELF.setString := setString;
- SELF.x := x;
- SELF.y := y;
- END Init;
- PROCEDURE GetInterface*(type : LONGINT) : WMDropTarget.DropInterface;
- VAR sdi : DropString;
- BEGIN
- IF (type = WMDropTarget.TypeString) THEN
- NEW(sdi, originator, setString, x,y); RETURN sdi;
- ELSE
- RETURN NIL;
- END;
- END GetInterface;
- END DropTarget;
- DropString = OBJECT(WMDropTarget.DropString)
- VAR
- originator : ANY;
- setString : SetStringProcedure;
- x,y : LONGINT;
- PROCEDURE &Init(originator : ANY; setString : SetStringProcedure; x,y : LONGINT);
- BEGIN
- ASSERT(setString # NIL);
- SELF.originator := originator;
- SELF.setString := setString;
- SELF.x := x; SELF.y := y;
- END Init;
- PROCEDURE Set*(CONST string : ARRAY OF CHAR; VAR res : WORD);
- BEGIN
- setString(string, x,y, res);
- END Set;
- END DropString;
- LanguageExtension* = POINTER TO RECORD(Messages.MessageExtension)
- languages* : Localization.Languages;
- END;
- ToggleEditMode* = POINTER TO RECORD
- recursion*: Recursion;
- END;
-
- FindComponentMode* = POINTER TO RECORD END;
- Event* = RECORD
- END;
- KeyPressedEvent* = RECORD(Event)
- ucs- : LONGINT;
- flags- : SET;
- keysym- : LONGINT;
- END;
- PointerEvent* = RECORD(Event)
- x-, y-, z- : LONGINT;
- keys- : SET;
- END;
- EventContext* = OBJECT(Repositories.Context)
- VAR
- originator- : Component; (** {originator # NIL} *)
- command- : Strings.String; (** {command # NIL}, immutable *)
- timestamp- : LONGINT;
- PROCEDURE &New*(originator : Component; command : Strings.String; in, arg : Streams.Reader; out, error : Streams.Writer; caller : OBJECT);
- BEGIN
- ASSERT((originator # NIL) & (command # NIL));
- SELF.originator := originator;
- SELF.command := command;
- Init(in, arg, out, error, caller);
- END New;
- END EventContext;
- PointerContext* = OBJECT(EventContext)
- VAR
- pointer- : PointerEvent;
- END PointerContext;
- KeyContext* = OBJECT(EventContext)
- VAR
- key- : KeyPressedEvent;
- END KeyContext;
- TYPE
- (** Basic component *)
- ComponentStyleChanged = OBJECT
- END ComponentStyleChanged;
- Component* = OBJECT(Repositories.Component)
- VAR
- sequencer- : Messages.MsgSequencer;
- initialized- : BOOLEAN;
- properties- : WMProperties.PropertyList;
- events- : WMEvents.EventSourceList;
- eventListeners- : WMEvents.EventListenerList;
- id-, uid- : WMProperties.StringProperty;
- enabled- : WMProperties.BooleanProperty;
- (* discard property changes that come from a property change within the same component*)
- inPropertyUpdate, inLinkUpdate : BOOLEAN;
- (* If TRUE, this component is supposed to be created and managed by its parent. It is not externalized. *)
- internal- : BOOLEAN;
- (* after Init() , calling Reset() implicitely by insertion into FormWindow or explicitely, thereby triggering Initialize() is required to render component responsive to messages *)
- PROCEDURE &Init*;
- BEGIN
- Init^;
- SetNameAsString(StrComponent);
- sequencer := NIL;
- initialized := FALSE;
- NEW(properties); properties.onPropertyChanged.Add(SELF.InternalPropertyChanged); properties.onLinkChanged.Add(SELF.InternalLinkChanged);
- NEW(events);
- NEW(eventListeners);
- NEW(id, PrototypeID, NIL, NIL); properties.Add(id);
- NEW(uid, PrototypeUID, NIL, NIL); properties.Add(uid);
- NEW(enabled, PrototypeEnabled, NIL, NIL); properties.Add(enabled);
- inPropertyUpdate := FALSE;
- inLinkUpdate := FALSE;
- internal := FALSE;
- SetGenerator("WMComponents.NewComponent");
- END Init;
- PROCEDURE Write*(w : Streams.Writer;context: ANY; level : LONGINT);
- VAR enum: XMLObjects.Enumerator; c: ANY; name : Strings.String; nextLevel : LONGINT;
- BEGIN
- IF IsLocked() THEN (* D.String("Component.Write: islocked"); D.Ln; *) RETURN; END;
- IF ~internal THEN
- name := GetName();
- w.Char('<'); IF name = NIL THEN w.String("_NILNAME_") ELSE w.String(name^) END;
- WriteAttributes(w, context, level);
- w.Char('>');
- properties.WriteXML(w, context, level);
- nextLevel := level + 1;
- ELSE
- (* D.String("Component.Write: isInternal"); D.Ln; *)
- nextLevel := level;
- END;
- enum := GetContents();
- WHILE enum.HasMoreElements() DO
- c := enum.GetNext();
- IF ~(c IS WMProperties.Properties) THEN
- IF ~((c IS Component) & ((c(Component).internal) OR c(Component).IsLocked())) THEN NewLine(w, 0); NewLine(w, nextLevel); END;
- c(XML.Content).Write(w, context, nextLevel);
- (*c(Component).Write(w, context, nextLevel)*)
- END;
- END;
- IF ~internal THEN
- NewLine(w, level);
- w.String("</"); IF name = NIL THEN w.String("_NILNAME_") ELSE w.String(name^); END; w.Char('>')
- END;
- END Write;
- (*
- PROCEDURE ToRepository*(CONST repository: ARRAY OF CHAR; w: Streams.Writer; level: LONGINT);
- VAR enum: XMLObjects.Enumerator; c: ANY; name : Strings.String; nextLevel : LONGINT;
- BEGIN
- IF IsLocked() THEN RETURN; END;
- IF ~internal THEN
- name := GetName();
- w.Char('<'); IF name = NIL THEN w.String("_NILNAME_") ELSE w.String(name^) END;
- WriteAttributes(w, NIL, level);
- w.Char('>');
- properties.ToRepository(repository,w, level);
- nextLevel := level + 1;
- ELSE
- nextLevel := level;
- END;
- enum := GetContents();
- WHILE enum.HasMoreElements() DO
- c := enum.GetNext();
- IF ~(c IS WMProperties.Properties) THEN
- IF ~((c IS Component) & ((c(Component).internal) OR c(Component).IsLocked())) THEN NewLine(w, 0); NewLine(w, nextLevel); END;
- IF (c IS Repositories.Component) THEN
- c(Repositories.Component).ToRepository(repository, w, level);
- ELSE
- c(XML.Content).Write(w, NIL, nextLevel);
- END;
- END;
- END;
- IF ~internal THEN
- NewLine(w, level);
- w.String("</"); IF name = NIL THEN w.String("_NILNAME_") ELSE w.String(name^); END; w.Char('>')
- END;
- END ToRepository;
- *)
- PROCEDURE FromXML*(xml: XML.Element);
- VAR component: Component; enum: XMLObjects.Enumerator; c: ANY; element: XML.Element;
- BEGIN
- element := GetElementByName(xml,"Properties");
- IF (element = NIL) & (xml IS Component) THEN (* trick to get XML description of properties if not already there (new components) *)
- xml(Component).properties.ToXML(element)
- END;
- properties.FromXML(element);
- (* was: supercall to Repositories *)
- enum := xml.GetContents();
- WHILE enum.HasMoreElements() DO
- c := enum.GetNext();
- IF c IS XML.Element THEN
- IF ~(c IS Component) OR ~c(Component).internal THEN
- component := ComponentFromXML(c(XML.Element));
- IF component # NIL THEN
- AddContent(component)
- END;
- END;
- END;
- END;
-
- enum :=xml.GetAttributes();
- WHILE enum.HasMoreElements() DO
- c := enum.GetNext();
- IF c(XML.Attribute).GetName()^ # "generator" THEN
- SetAttributeValue(c(XML.Attribute).GetName()^, c(XML.Attribute).GetValue()^);
- END;
- END;
-
- (*Initialize;*) (* redundant *)
- END FromXML;
- PROCEDURE IsCallFromSequencer*():BOOLEAN;
- BEGIN
- ASSERT (sequencer # NIL);
- RETURN sequencer.IsCallFromSequencer()
- END IsCallFromSequencer;
- PROCEDURE AssertLock*;
- BEGIN
- ASSERT((sequencer = NIL) OR sequencer.IsCallFromSequencer() OR sequencer.lock.HasReadLock())
- END AssertLock;
- (** Atomically set the components sequencer *)
- PROCEDURE SetSequencer*(s : Messages.MsgSequencer);
- VAR old : Messages.MsgSequencer; c : XML.Content;
- BEGIN
- old := sequencer;
- IF old # NIL THEN old.lock.AcquireWrite() END;
- sequencer := s;
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS Component THEN c(Component).SetSequencer(s); END; (*? what happens to old sequencers/active objects ?*)
- c := GetNext(c);
- END;
- IF old # NIL THEN old.lock.ReleaseWrite() END
- END SetSequencer;
- PROCEDURE Acquire*;
- BEGIN
- IF sequencer # NIL THEN sequencer.lock.AcquireWrite END
- END Acquire;
- PROCEDURE Release*;
- BEGIN
- IF sequencer # NIL THEN sequencer.lock.ReleaseWrite END
- END Release;
- PROCEDURE CheckReadLock*;
- BEGIN
- IF (sequencer # NIL) & (~sequencer.lock.HasReadLock()) THEN
- KernelLog.String("WMComponents.Component.CheckReadLock: FAILED!"); KernelLog.Ln;
- sequencer.lock.WriteLock
- END;
- IF sequencer # NIL THEN ASSERT(sequencer.lock.HasReadLock()) END
- END CheckReadLock;
- (** AddContent adds a content (element or subtree) to the element *)
- PROCEDURE AddContent*(c : XML.Content);
- VAR m:Messages.Message; rect:Rectangles.Rectangle;
- BEGIN
- ASSERT(c # NIL);
- Acquire;
- BEGIN (*{EXCLUSIVE}*)(* EXCLUSIVE leads to deadlock ?*)
- IF c IS WMProperties.Properties THEN
- properties.SetXML(c(WMProperties.Properties));
- ELSIF c IS Component THEN
- IF sequencer#NIL THEN
- c(Component).SetSequencer(sequencer);
- c(Component).Reset(SELF,NIL); (* will be scheduled by sequencer. implied RecacheProperties*)
- Initialize; (*? there is also a Initialize() within Reset() above, but that one seems sometimes not be effective because scheduled later; however ,this is partial redundancy *)
- ELSE (* no tree traversal - is less costly *)
- c(Component).initialized:=FALSE;
- c(Component).sequencer:=NIL;
- END;
- ELSIF ~(c IS XML.Comment) THEN
- Release; RETURN
- END;
- END;
- (*Acquire;*)
- AddContent^(c);
- Release;
- END AddContent;
- PROCEDURE RemoveContent*(c : XML.Content);
- BEGIN
- (*ASSERT(c # NIL);*)
- IF c = NIL THEN RETURN END;
- Acquire;
- RemoveContent^(c);
- Release;
- END RemoveContent;
- (** Add internal component. Internal components are supposed to be created and managed by its parent component.
- Internal components and their subcomponents are not externalized *)
- PROCEDURE AddInternalComponent*(component : Component);
- BEGIN
- IF (component # NIL) THEN
- component.internal := TRUE;
- AddContent(component);
- END;
- END AddInternalComponent;
- (** Return the root element of the component hierarchy. This is not necessarily the same as the
- root element of XML since it is possible to have multiple component hierarchies in an XML file *)
- PROCEDURE GetComponentRoot*(): Component;
- VAR p, c : XML.Element;
- BEGIN
- c := SELF;
- LOOP
- p := c.GetParent();
- IF (p # NIL) & (p IS Component) THEN c := p ELSE RETURN c(Component) END
- END
- END GetComponentRoot;
- PROCEDURE Find*(id : ARRAY OF CHAR) : Component;
- VAR
- root, component : Component;
- PROCEDURE IsUID(CONST id : ARRAY OF CHAR) : BOOLEAN;
- BEGIN
- RETURN id[0] = "&";
- END IsUID;
- PROCEDURE RemoveAmpersand(VAR id : ARRAY OF CHAR);
- VAR i : LONGINT;
- BEGIN
- ASSERT(id[0] = "&");
- FOR i := 0 TO LEN(id)-2 DO
- id[i] := id[i + 1];
- END;
- END RemoveAmpersand;
- BEGIN
- component := NIL;
- IF IsUID(id) THEN
- RemoveAmpersand(id);
- root := GetComponentRoot();
- component := root.FindByUID(id);
- ELSE
- component := FindByPath(id, 0);
- END;
- RETURN component;
- END Find;
- (** Find a sub component by its uid *)
- PROCEDURE FindByUID*(CONST uid : ARRAY OF CHAR) : Component;
- VAR c : XML.Content; result : Component; s : Strings.String;
- BEGIN
- IF (uid = "") THEN RETURN NIL END;
- s := SELF.uid.Get();
- IF (s # NIL) & (s^ = uid) THEN
- RETURN SELF
- ELSE
- result := NIL;
- Acquire;
- c := GetFirst();
- WHILE (result = NIL) & (c # NIL) DO
- IF (c IS Component) THEN result := c(Component).FindByUID(uid) END;
- c := GetNext(c);
- END;
- Release;
- RETURN result
- END
- END FindByUID;
- (** find a component by relative path *)
- PROCEDURE FindByPath*(CONST path : ARRAY OF CHAR; pos : LONGINT) : Component;
- VAR component : Component;
- BEGIN
- Acquire;
- component := FindRelativePath(SELF, path, pos);
- Release;
- RETURN component;
- END FindByPath;
- PROCEDURE StringToComponent*(str : Strings.String) : Component;
- VAR
- id : ARRAY 100 OF CHAR;
- isUID : BOOLEAN;
- ch : CHAR;
- sr : Streams.StringReader;
- r, target : Component;
- BEGIN
- NEW(sr, LEN(str)); sr.Set(str^);
- isUID := FALSE; IF sr.Peek() = "%" THEN isUID := TRUE; ch := sr.Get() END;
- sr.Token(id);
- IF isUID THEN r := GetComponentRoot(); target := r.FindByUID(id);
- IF target = NIL THEN KernelLog.String("StringToComponent : UID target not found: "); KernelLog.String(id); KernelLog.Ln; END
- ELSE target := FindByPath(id, 0);
- IF target = NIL THEN KernelLog.String("StringToComponent : Path target not found: "); KernelLog.String(id); KernelLog.Ln; END
- END;
- RETURN target
- END StringToComponent;
- (** Search a CompCommand by string *)
- PROCEDURE StringToCompCommand*(eventstr : Strings.String) : WMEvents.EventListener;
- VAR
- id, name : ARRAY 100 OF CHAR;
- isUID : BOOLEAN;
- ch : CHAR;
- sr : Streams.StringReader;
- r, target : Component;
- BEGIN
- NEW(sr, LEN(eventstr)); sr.Set(eventstr^);
- isUID := FALSE; IF sr.Peek() = "%" THEN isUID := TRUE; ch := sr.Get() END;
- sr.Token(id); sr.SkipWhitespace; sr.Token(name);
- IF isUID THEN r := GetComponentRoot(); target := r.FindByUID(id);
- IF target = NIL THEN KernelLog.String("StringToEvent : UID target not found: "); KernelLog.String(id); KernelLog.Ln; END
- ELSE target := FindByPath(id, 0);
- IF target = NIL THEN KernelLog.String("StringToEvent : Path target not found: "); KernelLog.String(id); KernelLog.Ln; END
- END;
- IF target # NIL THEN RETURN target.eventListeners.GetHandlerByName(NewString(name))
- ELSE RETURN NIL
- END
- END StringToCompCommand;
- (** The Finalize Method is asynchronous since queuing could result in modules being freed before finalize ispropagated..
- Active components should terminate, external resources should be released *)
- PROCEDURE Finalize*; (** PROTECTED *)
- VAR c : XML.Content;
- BEGIN
- IF TraceFinalize IN Trace THEN IF uid # NIL THEN (* KernelLog.String(uid.string) *) KernelLog.String(".Finalize") END END;
- Acquire;
- c := GetFirst();
- WHILE (c # NIL) DO
- IF (c IS Component) THEN c(Component).Finalize END;
- c := GetNext(c);
- END;
- properties.Finalize;
- Release;
- END Finalize;
- (* reset/initialize a hierarchy of components *)
- PROCEDURE Reset*(sender, data : ANY); (** PROTECTED *)
- VAR c : XML.Content;
- BEGIN
- IF ~IsCallFromSequencer() THEN
- sequencer.ScheduleEvent(SELF.Reset, sender, data);
- IF CanYield THEN Objects.Yield END;
- ELSE
- BEGIN (* how about exclusivity ?*)
- RecacheProperties;
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS Component THEN
- c(Component).Reset(sender, data)
- END;
- c := GetNext(c);
- END;
- IF ~initialized THEN Initialize END;
- END;
- END
- END Reset;
- (* Initialize is called by Reset() and is required to render components responsive *)
- PROCEDURE Initialize*; (** PROTECTED *)
- BEGIN
- BEGIN{EXCLUSIVE}
- initialized := TRUE
- END;
- END Initialize;
- (** Internal interface of the message handler. This method may only be called via the Handle method.
- Components that need to handle messages should implement HandleInternal. *)
- PROCEDURE HandleInternal*(VAR msg : Messages.Message); (** PROTECTED *)
- VAR pa : WMProperties.PropertyArray; i : LONGINT;
- BEGIN
- ASSERT(IsCallFromSequencer());
- IF (msg.msgType = Messages.MsgSetLanguage) & (msg.ext # NIL) & (msg.ext IS LanguageExtension) THEN
- pa := properties.Enumerate();
- IF (pa # NIL) THEN
- FOR i := 0 TO LEN(pa) - 1 DO
- IF (pa[i] # NIL) & (pa[i] IS WMProperties.StringProperty) THEN
- pa[i](WMProperties.StringProperty).SetLanguage(msg.ext(LanguageExtension).languages);
- END;
- END;
- END;
- LanguageChanged(msg.ext(LanguageExtension).languages);
- BroadcastSubcomponents(msg);
- ELSE
- BroadcastSubcomponents(msg); (*added PH 0816 - unhandled messages may be meant for children !*)
- END;
- END HandleInternal;
- (** External interface to the message handler. Asynchronous messages are synchronized by
- the sequencer of the Container *)
- PROCEDURE Handle*(VAR msg : Messages.Message); (** FINAL *)
- VAR s : Strings.String;
- BEGIN
- (* if asynchronous call --> synchronize *)
- IF sequencer=NIL THEN RETURN
- ELSIF ~IsCallFromSequencer() THEN
- IF ~sequencer.Add(msg) THEN
- s := uid.Get();
- KernelLog.String("A message sent to ");
- IF s # NIL THEN KernelLog.String(s^) ELSE KernelLog.String(" <uid = NIL>") END;
- KernelLog.String(" was discarded")
- END;
- IF CanYield THEN Objects.Yield END (* give the sequencer an immediate chance to react -- important on single-processor machines *)
- ELSE HandleInternal(msg) END
- END Handle;
- (** Broadcast a message to all direct subcomponents. The subcomponent can then decide
- whether to further propagate the message to its children or not *)
- PROCEDURE BroadcastSubcomponents*(VAR msg : Messages.Message); (** FINAL *)
- VAR c : XML.Content;
- BEGIN
- Acquire;
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS Component THEN c(Component).Handle(msg) END;
- c := GetNext(c);
- END;
- Release
- END BroadcastSubcomponents;
- (* not to be called from user *)
- PROCEDURE LanguageChanged*(languages : Localization.Languages);
- BEGIN
- ASSERT(languages # NIL);
- ASSERT(IsCallFromSequencer());
- END LanguageChanged;
- (* LinkChanged can be called to inform about changes of the state of links (i.e. objects in reference properties)
- Unlike PropertyChanged which informs about an actual replacement of the link *)
- PROCEDURE LinkChanged*(sender, link: ANY);
- BEGIN ASSERT(IsCallFromSequencer());
- END LinkChanged;
- (* will be called synchronously if a property of the component changes. May not be called directly.
- Call Invalidate in this procedure whenever a property changed that impacts the visualization.
- No such messages are sent until the component is initialized *)
- PROCEDURE PropertyChanged*(sender, property : ANY);(** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- END PropertyChanged;
- (** called by the internal property changed handler via the sequencer, either if multiple properties have
- changed or a Reset occured. The PropertyChanged method is called, too in case of multi-property changes
- The component should call the inherited RecacheProperties method.
- Do not call Invalidate in RecacheProperties, but rather in PropertyChanged(). *)
- PROCEDURE RecacheProperties*;
- BEGIN
- END RecacheProperties;
- PROCEDURE InternalPropertyChanged(sender, property : ANY);
- BEGIN
- IF ~initialized THEN RETURN END;
- IF ~IsCallFromSequencer() THEN
- sequencer.ScheduleEvent(SELF.InternalPropertyChanged, sender, property);
- IF CanYield THEN Objects.Yield END;
- ELSE
- IF ~inPropertyUpdate THEN
- inPropertyUpdate := TRUE;
- IF property = properties THEN RecacheProperties END;
- PropertyChanged(sender, property);
- inPropertyUpdate := FALSE
- END;
- END
- END InternalPropertyChanged;
- PROCEDURE InternalLinkChanged(sender, link : ANY);
- BEGIN
- IF ~initialized THEN RETURN END;
- IF ~IsCallFromSequencer() THEN
- sequencer.ScheduleEvent(SELF.InternalLinkChanged, sender, link);
- IF CanYield THEN Objects.Yield END;
- ELSE
- IF ~inLinkUpdate THEN
- inLinkUpdate := TRUE;
- LinkChanged(sender, link);
- inLinkUpdate := FALSE
- END;
- END
- END InternalLinkChanged;
- END Component;
- TYPE
- Macro* = ARRAY 128 OF CHAR;
- (** Installable macro handler procedure. {(originator # NIL) & (w # NIL)} *)
- MacroHandlerProcedure* = PROCEDURE {DELEGATE} (CONST macro : Macro; originator : Component; w : Streams.Writer; VAR handled : BOOLEAN);
- Namespace = ARRAY 16 OF CHAR;
- MacroHandler = POINTER TO RECORD
- handler : MacroHandlerProcedure;
- namespace : Namespace;
- next : MacroHandler;
- END;
- TYPE
- (** Basic visual component *)
- VisualComponent* = OBJECT(Component)
- VAR
- bounds-, bearing-, relativeBounds-: WMProperties.RectangleProperty;
- alignment- : WMProperties.Int32Property;
- fillColor- : WMProperties.ColorProperty;
- font- : WMProperties.FontProperty;
- scaleFont-: WMProperties.Int32Property;
- visible-, takesFocus-, needsTab-, editMode- : WMProperties.BooleanProperty;
- focusPrevious-, focusNext- : WMProperties.StringProperty;
- model- : WMProperties.ReferenceProperty;
- onStartDrag- : WMEvents.EventSource;
- canvasState- : WMGraphics.CanvasState; (** PROTECTED *)
- fPointerOwner : VisualComponent;
- hasFocus- : BOOLEAN;
- focusComponent : VisualComponent; (** Subcomponent that has the keyboard focus, if any *)
- extPointerDown, extPointerUp, extPointerMove : PointerHandler;
- extPointerLeave : PointerLeaveHandler;
- extDragOver, extDragDropped : DragDropHandler;
- extDragResult : DragResultHandler;
- extKeyEvent : KeyEventHandler;
- extDraw : DrawHandler;
- extFocus : FocusHandler;
- extContextMenu : ContextMenuHandler;
- extGetPositionOwner : GetPositionOwnerHandler;
- layoutManager : LayoutManager;
- aligning* : BOOLEAN;
- pointerInfo : WM.PointerInfo;
- editRegion: LONGINT;
- editX, editY: LONGINT;
- keyFlags: SET; (*! remove *)
- oldPointerInfo : WM.PointerInfo;
- PROCEDURE &Init*;
- BEGIN
- Init^;
- SetGenerator("WMComponents.NewVisualComponent");
- SetNameAsString(StrVisualComponent);
- NEW(bounds, PrototypeBounds, NIL, NIL); properties.Add(bounds);
- NEW(relativeBounds, PrototypeBoundsRelative, NIL, NIL); properties.Add(relativeBounds);
- NEW(bearing, PrototypeBearing, NIL, NIL); properties.Add(bearing);
- NEW(alignment, PrototypeAlignment, NIL, NIL); properties.Add(alignment);
- NEW(fillColor, PrototypeFillColor, NIL, NIL); properties.Add(fillColor);
- NEW(visible, PrototypeVisible, NIL, NIL); properties.Add(visible);
- NEW(takesFocus, PrototypeTakesFocus, NIL, NIL); properties.Add(takesFocus);
- NEW(needsTab, PrototypeNeedsTab, NIL, NIL); properties.Add(needsTab);
- NEW(focusPrevious, PrototypeFocusPrevious, NIL, NIL); properties.Add(focusPrevious);
- NEW(focusNext, PrototypeFocusNext, NIL, NIL); properties.Add(focusNext);
- NEW(editMode, PrototypeEditMode, NIL,NIL); properties.Add(editMode); editMode.Set(FALSE);
- NEW(model, ModelPrototype, NIL, NIL); properties.Add(model);
- NEW(font, PrototypeFont, NIL, NIL); properties.Add(font);
- NEW(scaleFont, PrototypeScaleFont, NIL,NIL); properties.Add(scaleFont);
- NEW(onStartDrag, SELF, GSonStartDrag,GSonStartDragInfo, SELF.StringToCompCommand);
- events.Add(onStartDrag);
- extGetPositionOwner := NIL;
- aligning := FALSE; fPointerOwner := SELF; focusComponent := SELF;
- END Init;
- (** Focus handling *)
- PROCEDURE TraceFocusChain*;
- BEGIN
- KernelLog.String(" -> ");
- ShowComponent(SELF);
- IF focusComponent = SELF THEN
- KernelLog.String(" <END>"); KernelLog.Ln;
- ELSIF focusComponent = NIL THEN
- KernelLog.String("ERROR focusComponent is NIL"); KernelLog.Ln;
- ELSE
- focusComponent.TraceFocusChain;
- END;
- END TraceFocusChain;
- (** Set the keyboard focus chain to this component its takesFocus field is set and unset the old chain *)
- PROCEDURE SetFocus*;
- VAR root, vc : VisualComponent; p : XML.Element;
- BEGIN
- Acquire;
- IF (takesFocus.Get() OR editMode.Get()) & visible.Get() THEN
- IF TraceFocus IN Trace THEN KernelLog.String("Set focus to: "); ShowComponent(SELF); KernelLog.Ln; END;
- root := GetVisualComponentRoot();
- IF (root IS Form) THEN root(Form).lastFocusComponent := SELF; END;
- (* unset the old focus chain *)
- (* find the leaf component that has the focus *)
- vc := root;
- WHILE (vc # NIL) & (vc.focusComponent # NIL) & (vc.focusComponent # vc) DO vc := vc.focusComponent; END;
- (* clear the focus chain until the root or this component *)
- p := vc;
- WHILE (p # SELF) & (p # NIL) & (p IS VisualComponent) DO
- vc := p(VisualComponent);
- vc.focusComponent := vc;
- vc.FocusLost;
- IF (vc.extFocus # NIL) THEN vc.extFocus(FALSE); END;
- p := p.GetParent();
- END;
- (* set the new chain *)
- vc := SELF; vc.focusComponent := SELF;
- WHILE (vc # NIL) DO
- IF ~vc.hasFocus THEN
- vc.FocusReceived;
- IF vc.extFocus # NIL THEN vc.extFocus(TRUE) END;
- END;
- p := vc.GetParent();
- IF (p # NIL) & (p IS VisualComponent) THEN
- p(VisualComponent).focusComponent := vc; vc := p(VisualComponent);
- ELSE
- vc := NIL;
- END;
- END;
- ELSE (* component does not take focus or is not visible *)
- IF TraceFocus IN Trace THEN ShowComponent(SELF); KernelLog.String("does not take focus."); KernelLog.Ln END;
- END;
- Release;
- END SetFocus;
- PROCEDURE FocusReceived*;
- BEGIN
- hasFocus := TRUE
- END FocusReceived;
- PROCEDURE FocusLost*;
- BEGIN
- hasFocus := FALSE
- END FocusLost;
- PROCEDURE SetFocusTo(CONST id : ARRAY OF CHAR);
- VAR vc : Component;
- BEGIN
- vc := Find(id);
- IF (vc # NIL) & (vc IS VisualComponent) THEN
- vc(VisualComponent).SetFocus;
- ELSE
- KernelLog.String("Warning: WMComponents.VisualComponent.SetFocusTo: Component ");
- KernelLog.String(id); KernelLog.String(" not found."); KernelLog.Ln;
- END;
- END SetFocusTo;
- PROCEDURE FocusNext*;
- VAR string : Strings.String;
- BEGIN
- string := focusNext.Get();
- IF (string # NIL) THEN
- SetFocusTo(string^);
- END;
- END FocusNext;
- PROCEDURE FocusPrev*;
- VAR string : Strings.String;
- BEGIN
- string := focusPrevious.Get();
- IF (string # NIL) THEN
- SetFocusTo(string^);
- END;
- END FocusPrev;
- (* LinkChanged can be called to inform about changes of the state of links (i.e. objects in reference properties)
- Unlike PropertyChanged which informs about an actual replacement of the link *)
- PROCEDURE LinkChanged*(sender, link: ANY);
- BEGIN
- IF sender = model THEN
- Invalidate
- END;
- END LinkChanged;
- PROCEDURE PropertyChanged*(sender, property : ANY);
- BEGIN
- IF property = bounds THEN
- (*ScaleFont(bounds.GetHeight(), scaleFont.Get());*)
- Resized (*implicit Invalidate*)
- ELSIF property = bearing THEN Resized;
- (* ELSIF bounds=relativeBounds THEN ? *)
- ELSIF property = alignment THEN AlignmentChanged; Invalidate (*moved here from implicit Invalidate*)
- ELSIF property = fillColor THEN Invalidate;
- ELSIF property = font THEN
- IF scaleFont.Get() # 0 THEN
- ScaleFont(bounds.GetHeight(), scaleFont.Get()); (* implicit Invalidate*)
- END;
- Invalidate;
- ELSIF (property = scaleFont) THEN ScaleFont(bounds.GetHeight(),scaleFont.Get()); (*implicit Invalidate*)
- ELSIF property = visible THEN Resized (*Implicit Invalidate*)
- (* ELSIF takesFocus, needsTab...*)
- ELSIF property = editMode THEN Invalidate;
- ELSIF property = model THEN LinkChanged(model, model.Get());
- ELSE PropertyChanged^(sender, property)
- END;
- END PropertyChanged;
- PROCEDURE RecacheProperties*;
- BEGIN
- RecacheProperties^;
- IF scaleFont.Get() # 0 THEN ScaleFont(bounds.GetHeight(), scaleFont.Get()) END;
- IF (model # NIL) & (model.Get() # NIL) THEN LinkChanged(model,model.Get()) END;
- END RecacheProperties;
- (** Get the root of visible components. Not neccessarily the same as GetComponentRoot() OR GetRoot() *)
- PROCEDURE GetVisualComponentRoot*(): VisualComponent;
- VAR p, c : XML.Element;
- BEGIN
- c := SELF;
- LOOP
- p := c.GetParent();
- IF (p # NIL) & (p IS VisualComponent) THEN c := p
- ELSE RETURN c(VisualComponent)
- END
- END
- END GetVisualComponentRoot;
- PROCEDURE AdaptRelativeBounds(inner: Rectangles.Rectangle; parent: XML.Element);
- VAR outer: Rectangles.Rectangle;
- BEGIN
- Acquire;
- IF (parent # NIL) & (parent IS VisualComponent) THEN
- (* inner := bounds.Get();*)
- outer := parent(VisualComponent).bounds.Get();
- IF (outer.b - outer.t > 0) & (outer.r - outer.l > 0) THEN
- relativeBounds.Set(Rectangles.MakeRect( (inner.l * MaxRel) DIV (outer.r-outer.l), (inner.t * MaxRel) DIV (outer.b-outer.t),
- (inner.r * MaxRel) DIV (outer.r - outer.l), (inner.b * MaxRel) DIV (outer.b - outer.t)));
- END;
- END;
- Release
- END AdaptRelativeBounds;
- (** Position handling *)
- PROCEDURE AlignmentChanged;
- VAR p : XML.Element; inner, outer: Rectangles.Rectangle;
- BEGIN
- Acquire;
- IF alignment.Get()= AlignRelative THEN
- AdaptRelativeBounds(bounds.Get(), GetParent());
- END;
- p := SELF.GetParent();
- IF (p # NIL) & (p IS VisualComponent) THEN
- p(VisualComponent).AlignSubComponents
- END;
- (*Invalidate;*)
- Release
- END AlignmentChanged;
- (** Get the bounds of the component *)
- PROCEDURE GetClientRect*() : Rectangles.Rectangle;
- VAR r, t : Rectangles.Rectangle;
- BEGIN
- r := bounds.Get();
- t := Rectangles.MakeRect(0, 0, r.r - r.l, r.b - r.t);
- RETURN t
- END GetClientRect;
- PROCEDURE SetLayoutManager*(layoutManager : LayoutManager);
- BEGIN
- Acquire;
- SELF.layoutManager := layoutManager;
- Release
- END SetLayoutManager;
- PROCEDURE AlignEvent(sender, data: ANY);
- BEGIN
- AlignSubComponents;
- END AlignEvent;
- PROCEDURE AlignSubComponents*;
- VAR c : XML.Content; vc : VisualComponent;
- r, b, rel : Rectangles.Rectangle;
- BEGIN
- Acquire;
- IF (sequencer # NIL) & ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.AlignEvent, NIL,NIL); Release; RETURN
- ELSIF sequencer = NIL THEN Release; RETURN
- END;
- IF aligning THEN Release; RETURN END;
- DisableUpdate;
- aligning := TRUE;
- IF layoutManager # NIL THEN layoutManager(SELF)
- ELSE
- r := GetClientRect();
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS VisualComponent THEN
- vc := c(VisualComponent);
- IF vc.visible.Get() THEN
- b := vc.bearing.Get();
- CASE vc.alignment.Get() OF
- | AlignTop : vc.bounds.Set(Rectangles.MakeRect(r.l + b.l , r.t + b.t, r.r - b.r, r.t + b.t + vc.bounds.GetHeight())); INC(r.t, vc.bounds.GetHeight() + b.t + b.b)
- | AlignLeft : vc.bounds.Set(Rectangles.MakeRect(r.l + b.l, r.t + b.t, r.l + b.l + vc.bounds.GetWidth(), r.b - b.b)); INC(r.l, vc.bounds.GetWidth() + b.l + b.r)
- | AlignBottom : vc.bounds.Set(Rectangles.MakeRect(r.l + b.l, r.b - vc.bounds.GetHeight() - b.b, r.r - b.r, r.b - b.b)); DEC(r.b, vc.bounds.GetHeight() + b.t + b.b)
- | AlignRight : vc.bounds.Set(Rectangles.MakeRect(r.r - vc.bounds.GetWidth() - b.r , r.t + b.t, r.r - b.r, r.b - b.b)); DEC(r.r, vc.bounds.GetWidth() + b.l + b.r);
- | AlignClient : IF ~Rectangles.RectEmpty(r) THEN vc.bounds.Set(Rectangles.MakeRect(r.l + b.l , r.t + b.t, r.r - b.r, r.b - b.b)) END
- | AlignRelative:
- IF ~editMode.Get() THEN
- rel := vc.relativeBounds.Get();
- vc.bounds.Set(Rectangles.MakeRect(r.l + ((r.r-r.l)*rel.l+MaxRel DIV 2) DIV MaxRel, r.t + ((r.b-r.t)*rel.t+MaxRel DIV 2) DIV MaxRel,
- r.l + ((r.r-r.l)*rel.r +MaxRel DIV 2) DIV MaxRel, r.t+((r.b-r.t)*rel.b + MaxRel DIV 2) DIV MaxRel));
- ELSE
- vc.AdaptRelativeBounds(vc.bounds.Get(),SELF);
- END;
- ELSE (* nothing *)
- END;
- END;
- END;
- c := GetNext(c);
- END;
- END;
- EnableUpdate;
- aligning := FALSE;
- Release;
- END AlignSubComponents;
- PROCEDURE Initialize*;
- BEGIN
- Initialize^;
- AlignSubComponents;
- IF sequencer#NIL THEN Invalidate END;
- END Initialize;
- (** Locating *)
- (** transform the local component coordinates into global window manager coordinates *)
- PROCEDURE ToWMCoordinates*(x, y : LONGINT; VAR gx, gy : LONGINT);
- VAR cr : Component; tc : XML.Element; r : Rectangles.Rectangle;
- BEGIN
- gx := x; gy := y; tc := SELF;
- REPEAT
- IF (tc # NIL) & (tc IS VisualComponent) THEN
- r := tc(VisualComponent).bounds.Get();
- INC(gx, r.l); INC(gy, r.t)
- END;
- tc := tc.GetParent()
- UNTIL (tc = NIL) OR ~(tc IS VisualComponent);
- cr := GetComponentRoot();
- IF (cr # NIL) & (cr IS Form) THEN
- INC(gx, cr(Form).window.bounds.l);
- INC(gy, cr(Form).window.bounds.t)
- END
- END ToWMCoordinates;
- (** Return if the component is hit at (x, y) in component coordinates *)
- PROCEDURE IsHit*(x, y: LONGINT): BOOLEAN;
- BEGIN
- RETURN visible.Get() & enabled.Get() & Rectangles.PointInRect(x, y, GetClientRect())
- END IsHit;
- (** Return the topmost first child component at (x, y) *)
- PROCEDURE GetPositionOwner*(x, y: LONGINT): VisualComponent;
- VAR c: XML.Content; result, vc : VisualComponent; r : Rectangles.Rectangle;
- BEGIN
- Acquire;
- result := SELF;
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS VisualComponent THEN
- vc := c(VisualComponent);
- r := vc.bounds.Get();
- IF Rectangles.PointInRect(x, y, r) & vc.IsHit(x - r.l, y - r.t) THEN
- result := vc
- END;
- END;
- c := GetNext(c);
- END;
- Release;
- RETURN result
- END GetPositionOwner;
- (** DragOver is called via the message handler. The should call manager.SetDragAccept(SELF, .... *)
- PROCEDURE DragOver*(x, y: LONGINT; dragInfo : WM.DragInfo);
- END DragOver;
- (** Dropped is called via the message handler to indicate an item has been dropped. *)
- PROCEDURE DragDropped*(x, y: LONGINT; dragInfo : WM.DragInfo);
- BEGIN
- IF dragInfo.onReject # NIL THEN dragInfo.onReject(SELF,dragInfo) END;
- END DragDropped;
- (*
- PROCEDURE EditDragOver(x,y: LONGINT; dragInfo: WMWindowManager.DragInfo);
- BEGIN
- END EditDragOver;
- *)
- PROCEDURE FromXML*(xml: XML.Element);
- BEGIN
- FromXML^(xml);
- END FromXML;
- (*
- PROCEDURE AddContent*(c : XML.Content);
- VAR m:Messages.Message;
- BEGIN
- AddContent^(c);
- IF c IS VisualComponent THEN
- m.sender:=SELF (*c*); (*move to VisualComponent ?*)
- m.msgType := Messages.MsgInvalidate;
- m.msgSubType := Messages.MsgSubAll;
- (* SELF(VisualComponent).Invalidate ...*)
- IF sequencer.Add(m) THEN END;
- END;
- END AddContent;
- *)
- PROCEDURE AddVisualComponent(c :VisualComponent; x, y : LONGINT);
- VAR bounds : Rectangles.Rectangle;canvas: WMGraphics.BufferCanvas; relativeAlignment: BOOLEAN;
- BEGIN
- ASSERT(c # NIL);
-
- IF (c.bounds.GetWidth() < 10) OR (c.bounds.GetHeight() < 10) THEN
- c.bounds.SetExtents(40, 20);
- END;
- bounds := c.bounds.Get();
- Rectangles.MoveRel(bounds, x, y);
- c.bounds.Set(bounds);
- c.AdaptRelativeBounds(c.bounds.Get(), SELF);
- (*
- IF c.sequencer # sequencer THEN c.SetSequencer(sequencer) END; (* redundant - implicit in AddContent *)
- c.Reset(NIL, NIL); (*currently redundant - already in happens Component.AddContent() *)
- c.RecacheProperties; (*currently redundant - already in happens Reset() *)
- *)
- Acquire;
- AddContent(c);
- Release;
- END AddVisualComponent;
-
- PROCEDURE EditDragDropped(x,y: LONGINT; dragInfo: WM.DragInfo): BOOLEAN;
- VAR data: ANY; e: ComponentListEntry; parent: XML.Element; dt: DropTarget; pos: LONGINT;
- BEGIN
- data := dragInfo.data;
- IF (data # NIL) & (data IS VisualComponent) THEN
- IF dragInfo.sender # SELF THEN
- IF dragInfo.onAccept # NIL THEN dragInfo.onAccept(SELF,dragInfo) END;
- data(VisualComponent).bounds.Set(Rectangles.MakeRect(0, 0, data(VisualComponent).bounds.GetWidth(), data(VisualComponent).bounds.GetHeight()));
- AddVisualComponent(data(VisualComponent),x+dragInfo.offsetX,y+dragInfo.offsetY);
- Invalidate;
- ELSE
- parent := GetParent();
- IF parent = NIL THEN RETURN FALSE END;
- x := x + bounds.GetLeft();
- y := y + bounds.GetTop();
- RETURN parent(VisualComponent).EditDragDropped(x,y,dragInfo);
- END;
- RETURN TRUE
- ELSIF (data # NIL) & (data IS Repositories.Component) THEN
- IF dragInfo.onAccept # NIL THEN dragInfo.onAccept(SELF,dragInfo) END;
- model.Set(data(Repositories.Component));
- RETURN TRUE
- ELSIF (data # NIL) & (data IS SelectionList) THEN
- IF (dragInfo.sender # SELF) & ~data(SelectionList).Has(SELF) THEN
- IF dragInfo.onAccept # NIL THEN dragInfo.onAccept(SELF,dragInfo) END;
- e := data(SelectionList).first;
- WHILE e # NIL DO
- e.component.bounds.Set(Rectangles.MakeRect(0, 0, e.component.bounds.GetWidth(), e.component.bounds.GetHeight()));
- ASSERT(e.component IS VisualComponent);
- AddVisualComponent(e.component,x+e.dx+dragInfo.offsetX, y+e.dy + dragInfo.offsetY);
- e := e.next;
- END;
- Invalidate;
- ELSE
- parent := GetParent();
- IF parent = NIL THEN RETURN FALSE END;
- x := x + bounds.GetLeft();
- y := y + bounds.GetTop();
- RETURN parent(VisualComponent).EditDragDropped(x,y,dragInfo);
- END;
- RETURN TRUE
- ELSE
- NEW(dt, SELF, SetDroppedString, x,y);
- dragInfo.data := dt;
- ConfirmDrag(TRUE, dragInfo);
- RETURN FALSE
- END;
- END EditDragDropped;
- PROCEDURE SetDroppedString( CONST string : ARRAY OF CHAR; x,y : LONGINT; VAR res : WORD);
- VAR gen: XML.GeneratorProcedure; moduleName, procedureName ,msg: Modules.Name; element: XML.Element;
- context: Repositories.Context; repositoryName, componentName: ARRAY 265 OF CHAR; componentID: LONGINT; object: Repositories.Component;
- BEGIN
- Commands.Split(string, moduleName, procedureName, res, msg);
- IF (res = Commands.Ok) THEN
- GETPROCEDURE(moduleName, procedureName, gen);
- END;
- IF gen # NIL THEN
- element := gen();
- ELSIF Repositories.IsCommandString(string) THEN
- Repositories.CallCommand(string, context, res);
- IF (res = Repositories.Ok) & (context.object # NIL) & (context.object IS Repositories.Component) THEN
- element := context.object(Repositories.Component);
- END;
- ELSIF Repositories.SplitName(string, repositoryName, componentName, componentID) THEN
- Repositories.GetComponent(repositoryName, componentName, componentID, object, res);
- element := object;
- END;
- IF (element # NIL) & (element IS VisualComponent) THEN
- AddVisualComponent(element(VisualComponent),x,y);
- Invalidate;
- ELSIF (element # NIL) & (element IS Repositories.Component) THEN
- model.Set(element(Repositories.Component))
- END;
- res := 1; (* to avoid removal of source *)
- END SetDroppedString;
- (** Is called via the message handler to inform about the result of a recent drag operation *)
- PROCEDURE DragResult*(accepted : BOOLEAN; recipient : ANY; dragInfo : WM.DragInfo);
- END DragResult;
- (** Start a drag operation. *)
- PROCEDURE StartDrag*(data : ANY; img : WMGraphics.Image; offsetX, offsetY: LONGINT; onAccept, onReject : Messages.CompCommand) : BOOLEAN;
- VAR rc : Component;
- BEGIN
- rc := GetVisualComponentRoot();
- IF (rc # NIL) & (rc IS Form) THEN
- RETURN rc(Form).window.StartDrag(SELF, data, img, offsetX, offsetY, onAccept, onReject)
- ELSE
- RETURN FALSE
- END
- END StartDrag;
- (** confirm a drag operation. *)
- PROCEDURE ConfirmDrag*(accept : BOOLEAN; dragInfo : WM.DragInfo);
- VAR rc : Component;
- BEGIN
- rc := GetVisualComponentRoot();
- IF (rc # NIL) & (rc IS Form) THEN rc(Form).window.ConfirmDrag(accept, dragInfo)
- END
- END ConfirmDrag;
- (** Is called by the component if it detects a default drag action. The subclass should then call StartDrag with
- the respective coordinates. If it wants to start the drag operation *)
- PROCEDURE AutoStartDrag*;
- BEGIN
- onStartDrag.Call(NIL)
- END AutoStartDrag;
- (** Is called by the component if it detects a request for a context menu. The subclass should open the
- context menu if applicable *)
- PROCEDURE ShowContextMenu*(x, y : LONGINT);
- BEGIN
- IF extContextMenu # NIL THEN extContextMenu(SELF, x, y) END;
- END ShowContextMenu;
- (** Special methods *)
- PROCEDURE Resized*;
- VAR p : XML.Element;
- BEGIN
- (*
- AdaptRelativeBounds(GetParent());
- *)
- IF sequencer # NIL THEN ASSERT(sequencer.lock.HasWriteLock()) END;
- DisableUpdate;
- p := SELF.GetParent();
- IF (p # NIL) & (p IS VisualComponent) & (alignment.Get() # AlignNone) THEN p(VisualComponent).AlignSubComponents END;
- IF visible.Get() THEN
- AlignSubComponents;
- IF scaleFont.Get() # 0 THEN ScaleFont(bounds.GetHeight(), scaleFont.Get()) END;
- END;
- EnableUpdate;
- IF (p # NIL) & (p IS VisualComponent) THEN p(VisualComponent).Invalidate
- ELSE Invalidate()
- END
- END Resized;
- (** Is called before any sub-components are drawn *)
- PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
- VAR color: WMGraphics.Color; i : LONGINT; name:Strings.String;
- (* DebugUpdates can be used in order to visualize updates via some color cycling
- Moreover, it slows down display extremely such that updates can be seen
- *)
- CONST DebugUpdates = FALSE;
- BEGIN
- (* message tracing
- IF sequencer = Messages.debug THEN
- D.Enter;
- D.Ln;
- D.String("##############"); D.Ln;
- name := GetName();
- IF name # NIL THEN D.String(name^); D.Ln; END;
- name := id.Get();
- IF name # NIL THEN D.String(name^); D.Ln; END;
- D.Int(Kernel.GetTicks(),1); D.Ln;
- (*D.TraceBack;*)
- D.Exit;
- END;
- *)
- CheckReadLock;
-
- IF DebugUpdates THEN
- canvas.Fill(GetClientRect(), Kernel.GetTicks()*100H +0FFH, WMGraphics.ModeSrcOverDst);
- FOR i := 0 TO 10000000 DO END;
- ELSE
- color := fillColor.Get();
- IF color # 0 THEN canvas.Fill(GetClientRect(), color, WMGraphics.ModeSrcOverDst) END;
- END;
- END DrawBackground;
- (** Is called after all sub-components are drawn *)
- PROCEDURE DrawForeground*(canvas : WMGraphics.Canvas);
- END DrawForeground;
- PROCEDURE DrawSelection(canvas : WMGraphics.Canvas);
- VAR r,r0: Rectangles.Rectangle; x,y,x0,y0: LONGINT; color: WMGraphics.Color;
- PROCEDURE MarkSelected(r: Rectangles.Rectangle; w: LONGINT; color: WMGraphics.Color);
- VAR r0: Rectangles.Rectangle;
- BEGIN
- r0 :=r; r0.r := r.l+w; r0.b := r.t+w;
- canvas.Fill(r0, color, WMGraphics.ModeSrcOverDst);
- r0 :=r; r0.r := r.l+w; r0.t := r.b-w;
- canvas.Fill(r0, color, WMGraphics.ModeSrcOverDst);
- r0 :=r; r0.l := r.r-w; r0.b := r.t+w;
- canvas.Fill(r0, color, WMGraphics.ModeSrcOverDst);
- r0 :=r; r0.l := r.r-w; r0.t := r.b-w;
- canvas.Fill(r0, color, WMGraphics.ModeSrcOverDst);
- r0 := r; r0.l := r.r-1; canvas.Fill(r0,color, WMGraphics.ModeSrcOverDst);
- r0 := r; r0.r := r.l+1; canvas.Fill(r0,color, WMGraphics.ModeSrcOverDst);
- r0 := r; r0.b := r.t+1; canvas.Fill(r0,color, WMGraphics.ModeSrcOverDst);
- r0 := r; r0.t := r.b-1; canvas.Fill(r0,color, WMGraphics.ModeSrcOverDst);
- END MarkSelected;
- BEGIN
- CheckReadLock;
- r := GetClientRect();
- IF editMode.Get() THEN
- y := r.t + (-r.t) MOD 8;
- y0 := 0;
- WHILE y < r.b DO
- r0.t := y; r0.b := y+2;
- x := r.l + (-r.l) MOD 8; x0 := 0;
- WHILE x < r.r DO
- r0.l := x; r0.r := x+2;
- IF ODD(x DIV 8+y DIV 8) THEN color := 060H;
- ELSE color := WMGraphics.Color(0FFFFFF60H);
- END;
- canvas.Fill(r0,color, WMGraphics.ModeSrcOverDst);
- INC(x,8); INC(x0);
- END;
- INC(y,8);INC(y0);
- END;
- IF selection.rectOwner = SELF THEN
- r0 := selection.rect;
- color := WMGraphics.Color(0FF000080H);
- canvas.Fill(r0, color, WMGraphics.ModeCopy);
- END;
- END;
- IF selection.Has(SELF) THEN
- IF selection.state = 0 THEN
- MarkSelected(r,8,WMGraphics.Color(080H));
- ELSE
- MarkSelected(r,8,WMGraphics.Color(0FFFFFFFF80H));
- END;
- END;
- END DrawSelection;
- PROCEDURE DrawSubComponents*(canvas : WMGraphics.Canvas);
- VAR c : XML.Content; vc : VisualComponent; cr, r : Rectangles.Rectangle;
- BEGIN
- CheckReadLock;
- canvas.GetClipRect(cr);
- canvas.SaveState(canvasState);
- (* draw all sub-components *)
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS VisualComponent THEN
- vc := c(VisualComponent); r := vc.bounds.Get();
- IF Rectangles.Intersect(r, cr) THEN (* only draw if the component has a chance to be visible *)
- canvas.SetClipRect(r); canvas.SetClipMode({WMGraphics.ClipRect});
- canvas.ClipRectAsNewLimits(r.l, r.t);
- vc.Draw(canvas);
- canvas.RestoreState(canvasState);
- END;
- END;
- c := GetNext(c);
- END;
- END DrawSubComponents;
- PROCEDURE GetFont*() : WMGraphics.Font;
- BEGIN
- IF font.Get() = NIL THEN RETURN WMGraphics.GetDefaultFont()
- ELSE RETURN font.Get()
- END
- END GetFont;
- PROCEDURE SetFont*(font : WMGraphics.Font);
- BEGIN
- Acquire;
- IF SELF.font.Get() # font THEN
- SELF.font.Set(font);
- (*?Invalidate()*) (* Invalidate already in PropertyChanged() *)
- END;
- Release
- END SetFont;
- PROCEDURE ScaleFont*(height: LONGINT; percent: LONGINT);
- VAR fh,newSize: LONGINT; f: WMGraphics.Font;
- BEGIN
- IF height < 4 THEN height := 4 END;
- IF percent <= 0 THEN RETURN END;
- Acquire;
- f := GetFont();
- f := WMGraphics.GetFont(f.name, 100, f.style); (* expensive ? *)
- fh := f.GetAscent() + f.GetDescent();
- fh := height * percent DIV fh;
- IF fh > 100 THEN fh := fh - fh MOD 8
- ELSIF fh > 32 THEN fh := fh - fh MOD 4
- ELSIF fh > 12 THEN fh := fh - fh MOD 2
- END;
- IF font.GetSize() # fh THEN
- font.SetSize(fh);
- Invalidate;
- END;
- Release;
- END ScaleFont;
- (** Called by the component owner whenever a redraw to a canvas is needed. Caller must hold hierarchy lock *)
- PROCEDURE Draw*(canvas : WMGraphics.Canvas);
- VAR command: Strings.String; event: Event;
- BEGIN
- (*
- can lead to deadlock:
- we hold the lock "lock"
- onDraw tries to get the Objects lock, but this may be held by other component (should better not, but did, dead: WMPartitionsComponents.OperationEventHandler
- command := GetAttributeValue("onDraw");
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- *)
- CheckReadLock;
- IF ~visible.Get() THEN RETURN END;
- canvas.SaveState(canvasState);
- IF font # NIL THEN canvas.SetFont(font.Get()) END;
- DrawBackground(canvas);
- IF extDraw # NIL THEN extDraw(canvas) END;
- DrawSelection(canvas);
- DrawSubComponents(canvas);
- DrawForeground(canvas);
- canvas.RestoreState(canvasState)
- END Draw;
- (** declare a rectangle area as dirty *)
- PROCEDURE InvalidateRect*(r: Rectangles.Rectangle);
- VAR parent : XML.Element;
- m : Messages.Message; b : Rectangles.Rectangle;
- BEGIN
- IF ~initialized THEN RETURN END;
- IF ~visible.Get() THEN RETURN END;
- IF ~IsCallFromSequencer() THEN
- m.msgType := Messages.MsgInvalidate;
- m.msgSubType := Messages.MsgSubRectangle;
- (*
- m.msgType := Messages.MsgExt;
- m.ext := invalidateRectMsg;
- *)
- m.x := r.l; m.y := r.t; m.dx := r.r; m.dy := r.b; m.sender := SELF;
- IF sequencer.Add(m) THEN IF CanYield THEN Objects.Yield END END;
- ELSE
- parent := GetParent();
- IF (parent # NIL) & (parent IS VisualComponent) THEN
- b := bounds.Get();
- Rectangles.MoveRel(r, b.l, b.t);
- parent(VisualComponent).InvalidateRect(r)
- END
- END
- END InvalidateRect;
-
- PROCEDURE PostInvalidateCommand*(sender, par : ANY);
- VAR m: Messages.Message; r, b: Rectangles.Rectangle; client: VisualComponent; parent: XML.Element;
- BEGIN
- IF ~initialized OR ~visible.Get() THEN RETURN END; (*? double call to visible.Get here and below. Which one is better ?*)
- r := GetClientRect();
- client := SELF;
- parent := GetParent();
- WHILE (parent # NIL) & (parent IS VisualComponent) DO
- IF ~parent(VisualComponent).visible.Get() THEN RETURN END;
- b := client.bounds.Get();
- Rectangles.MoveRel(r, b.l, b.t);
- client := parent(VisualComponent);
- parent := client.GetParent();
- END;
- m.msgType := Messages.MsgInvalidate;
- m.msgSubType := Messages.MsgSubRectangle;
- m.x := r.l; m.y := r.t; m.dx := r.r; m.dy := r.b;
- m.sender := client;
- IF sequencer.Add(m) THEN IF CanYield THEN Objects.Yield END END;
- END PostInvalidateCommand;
- PROCEDURE InvalidateCommand*(sender, par : ANY);
- VAR m: Messages.Message; r, b: Rectangles.Rectangle; client: VisualComponent; parent: XML.Element;
- BEGIN
- IF ~initialized OR ~visible.Get() THEN RETURN END; (*? double call to visible.Get here and below. Which one is better ?*)
- IF ~IsCallFromSequencer() OR ~visible.Get() THEN
- PostInvalidateCommand(sender, par);
- ELSE
- InvalidateRect(GetClientRect());
- END;
- END InvalidateCommand;
- PROCEDURE Invalidate*; (* For convenience in component internal use *)
- BEGIN
- PostInvalidateCommand(SELF, NIL)
- END Invalidate;
- (** recursively disable the redrawing of any components in the hierarchy *)
- (** dont forget to re-enable it ;-). Use with care to optimize sub-component operations *)
- PROCEDURE DisableUpdate*;
- VAR vc: VisualComponent;
- BEGIN
- ASSERT(IsCallFromSequencer());
- vc := GetVisualComponentRoot();
- IF (vc # NIL) & (vc IS Form) THEN vc(Form).DisableUpdate() END
- END DisableUpdate;
- (** recursively enable the redrawing of any components in the hierarchy *)
- (** Only enable drawing if it was disabled before, but dont forget it, then ! *)
- PROCEDURE EnableUpdate*;
- VAR vc: VisualComponent;
- BEGIN
- ASSERT(IsCallFromSequencer());
- vc := GetVisualComponentRoot();
- IF (vc # NIL) & (vc IS Form) THEN vc(Form).EnableUpdate() END
- END EnableUpdate;
- PROCEDURE GetInternalPointerInfo*() : WM.PointerInfo;
- VAR vc: VisualComponent;
- BEGIN
- ASSERT(IsCallFromSequencer());
- vc := GetVisualComponentRoot();
- IF (vc # NIL) & (vc IS Form) THEN
- RETURN vc(Form).GetPointerInfo()
- ELSE
- RETURN NIL
- END
- END GetInternalPointerInfo;
- PROCEDURE SetInternalPointerInfo*(pi : WM.PointerInfo);
- VAR vc: VisualComponent;
- BEGIN
- AssertLock;
- vc := GetVisualComponentRoot();
- IF (vc # NIL) & (vc IS Form) THEN vc(Form).SetPointerInfo(pi) END
- END SetInternalPointerInfo;
- PROCEDURE SetPointerInfo*(pi : WM.PointerInfo);
- BEGIN
- Acquire;
- SetInternalPointerInfo(pi);
- pointerInfo := pi;
- Release
- END SetPointerInfo;
- PROCEDURE GetPointerInfo*() : WM.PointerInfo;
- BEGIN
- RETURN pointerInfo
- END GetPointerInfo;
- (** User interaction messages *)
- PROCEDURE SetExtPointerLeaveHandler*(handler : PointerLeaveHandler);
- BEGIN
- Acquire; extPointerLeave := handler; Release
- END SetExtPointerLeaveHandler;
- PROCEDURE SetExtPointerDownHandler*(handler : PointerHandler);
- BEGIN
- Acquire; extPointerDown := handler; Release
- END SetExtPointerDownHandler;
- PROCEDURE SetExtPointerMoveHandler*(handler : PointerHandler);
- BEGIN
- Acquire; extPointerMove := handler; Release
- END SetExtPointerMoveHandler;
- PROCEDURE SetExtPointerUpHandler*(handler : PointerHandler);
- BEGIN
- Acquire; extPointerUp := handler; Release
- END SetExtPointerUpHandler;
- PROCEDURE SetExtDragOverHandler*(handler : DragDropHandler);
- BEGIN
- Acquire; extDragOver := handler; Release
- END SetExtDragOverHandler;
- PROCEDURE SetExtDragDroppedHandler*(handler : DragDropHandler);
- BEGIN
- Acquire; extDragDropped := handler; Release
- END SetExtDragDroppedHandler;
- PROCEDURE SetExtDragResultHandler*(handler : DragResultHandler);
- BEGIN
- Acquire; extDragResult := handler; Release
- END SetExtDragResultHandler;
- PROCEDURE SetExtKeyEventHandler*(handler : KeyEventHandler);
- BEGIN
- Acquire; extKeyEvent := handler; Release
- END SetExtKeyEventHandler;
- PROCEDURE SetExtDrawHandler*(handler : DrawHandler);
- BEGIN
- Acquire; extDraw := handler; Release
- END SetExtDrawHandler;
- PROCEDURE SetExtFocusHandler*(handler : FocusHandler);
- BEGIN
- Acquire; extFocus := handler; Release
- END SetExtFocusHandler;
- PROCEDURE SetExtContextMenuHandler*(handler : ContextMenuHandler);
- BEGIN
- Acquire; extContextMenu := handler; Release
- END SetExtContextMenuHandler;
- PROCEDURE SetExtGetPositionOwnerHandler*(handler : GetPositionOwnerHandler);
- BEGIN
- Acquire; extGetPositionOwner := handler; Release;
- END SetExtGetPositionOwnerHandler;
- (** Indicates the pointing device has left the component without a key pressed down.
- May only be called from the sequencer thread.
- Components interested in this message can override this method instead of searching for the message in HandleInternal. *)
- PROCEDURE PointerLeave*; (** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- END PointerLeave;
- (** Indicates one of the pointer keys went down. keys is the set of buttons currently pressed. x, y is the position in component
- coordinates.
- May only be called from the sequencer thread.
- Components interested in this message can override this method instead of searching for the message in HandleInternal. *)
- PROCEDURE PointerDown*(x, y: LONGINT; keys: SET); (** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- IF keys = {2} THEN ShowContextMenu(x, y)
- END;
- END PointerDown;
- (** Indicates the pointer was moved. keys is the set of buttons currently pressed. x, y is the position in component
- coordinates.
- May only be called from the sequencer thread.
- Components interested in this message can override this method instead of searching for the message in HandleInternal.
- When using PointerMove to move the component itself within a context (window or parent component),
- remember that Component.PointerMove are given in component coordinates (thus, a moving coordinate origin ...), but you want to move the component in context coordinates !
- *)
- PROCEDURE PointerMove*(x, y: LONGINT; keys: SET); (** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- END PointerMove;
- PROCEDURE WheelMove*(dz: LONGINT); (** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- END WheelMove;
- (** Indicates one of the pointer keys went up. keys is the set of buttons currently pressed. x, y is the position in component
- coordinates.
- May only be called from the sequencer thread.
- Components interested in this message can override this method instead of searching for the message in HandleInternal. *)
- PROCEDURE PointerUp*(x, y: LONGINT; keys: SET); (** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- END PointerUp;
- (** The component can determine wheter the key was pressed or released by examining the
- Inputs.Release flag in flags. ucs contains the unicode equivalent of the key. Special input editors
- send the generated unicode characters via KeyEvent.
- May only be called from the sequencer thread.
- Components interested in this message can override this method instead of searching for the message in HandleInternal. *)
- PROCEDURE KeyEvent*(ucs : LONGINT; flags: SET; VAR keySym: LONGINT); (** PROTECTED *)
- BEGIN ASSERT(IsCallFromSequencer());
- END KeyEvent;
- PROCEDURE EditKeyEvents(ucs : LONGINT; flags: SET; VAR keySym: LONGINT): BOOLEAN; (** FINAL *)
- VAR event : KeyPressedEvent; command : Strings.String; scale: LONGINT;
- clone: Repositories.Component; parent: XML.Content; parentEditMode: BOOLEAN;
- enum: XMLObjects.Enumerator; obj: ANY;
- po: VisualComponent;
- entry: ComponentListEntry;
- c: VisualComponent;
- BEGIN
- ASSERT(IsCallFromSequencer());
- IF fPointerOwner # SELF THEN
- RETURN fPointerOwner.EditKeyEvents(ucs,flags,keySym);
- END;
- event.ucs := ucs; event.flags := flags; event.keysym := keySym;
- parent := GetParent();
- IF (parent # NIL) & (parent IS VisualComponent) & parent(VisualComponent).editMode.Get() THEN
- parentEditMode := TRUE
- ELSE
- parentEditMode := FALSE
- END;
- IF ({Inputs.Release} * flags = {}) THEN
- IF (keySym = Inputs.KsF1) & (Inputs.Shift * flags # {}) THEN
- SetEditMode(~editMode.Get(), FALSE);
- RETURN TRUE
- ELSIF (keySym = Inputs.KsEscape) THEN
- selection.Reset(NIL);
- RETURN FALSE
- ELSIF parentEditMode OR editMode.Get() THEN
- IF Inputs.Shift * flags # {} THEN scale := 1 ELSE scale := 4 END;
- IF keySym = Inputs.KsLeft THEN selection.Shift(-scale,0); RETURN TRUE
- ELSIF keySym = Inputs.KsRight THEN selection.Shift(scale,0); RETURN TRUE
- ELSIF keySym = Inputs.KsDown THEN selection.Shift(0,scale); RETURN TRUE
- ELSIF keySym = Inputs.KsUp THEN selection.Shift(0,-scale); RETURN TRUE
- ELSIF keySym=4 (* CTRL-D *) THEN
- entry := selection.first;
- WHILE entry # NIL DO
- clone := Clone(entry.component);
- parent := selection.first.component.GetParent();
- c := clone(VisualComponent);
- IF c.sequencer # parent(Component).sequencer THEN c.SetSequencer(parent(Component).sequencer) END;
- c.Reset(NIL, NIL);
- c.RecacheProperties;
- parent(Component).AddContent(c);
- entry.component := clone(VisualComponent);
- entry := entry.next;
- END;
- selection.Shift(20,20);
- RETURN TRUE
- ELSIF keySym=1 THEN (* CTRL-A *)
- enum := GetContents();
- WHILE enum.HasMoreElements() DO
- obj := enum.GetNext();
- IF obj IS VisualComponent THEN
- selection.Add(obj(VisualComponent))
- END;
- END;
- ELSIF keySym = Inputs.KsDelete THEN
- RemoveSelection();
- RETURN TRUE
- END;
- END
- END;
- RETURN FALSE;
- END EditKeyEvents;
- PROCEDURE CheckKeyEvents(ucs : LONGINT; flags: SET; VAR keySym: LONGINT); (** FINAL *)
- VAR event : KeyPressedEvent; command : Strings.String; scale: LONGINT; clone: Repositories.Component; parent: XML.Content;
- BEGIN
- ASSERT(IsCallFromSequencer());
- event.ucs := ucs; event.flags := flags; event.keysym := keySym;
- IF ({Inputs.Release} * flags = {}) THEN
- IF (keySym = Inputs.KsReturn) THEN
- command := GetAttributeValue("onReturn");
- ELSIF (keySym = Inputs.KsEscape) THEN
- command := GetAttributeValue("onEscape");
- selection.Reset(NIL);
- END;
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- command := GetAttributeValue("onKeyPressed");
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- ELSE
- command := GetAttributeValue("onKeyReleased");
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- END;
- END CheckKeyEvents;
- PROCEDURE CheckPointerEvent(x, y, z : LONGINT; keys : SET);
- VAR event : PointerEvent; command : Strings.String;
- BEGIN
- ASSERT(IsCallFromSequencer());
- event.x := x; event.y := y; event.z := z; event.keys := keys;
- IF ({0} * keys = {0}) THEN
- command := GetAttributeValue("onLeftClick");
- ELSIF ({2} * keys = {2}) THEN
- command := GetAttributeValue("onRightClick");
- ELSIF ({1} * keys = {1}) THEN
- command := GetAttributeValue("onMiddleClick");
- END;
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- command := GetAttributeValue("onClick");
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- END CheckPointerEvent;
- PROCEDURE CheckPointerUpEvent(x, y, z : LONGINT; keys : SET);
- VAR event : PointerEvent; command : Strings.String;
- BEGIN
- ASSERT(IsCallFromSequencer());
- event.x := x; event.y := y; event.z := z; event.keys := keys;
- command := GetAttributeValue("onRelease");
- IF (command # NIL) THEN HandleEvent(event, SELF, command); END;
- END CheckPointerUpEvent;
- PROCEDURE InEditBounds(x,y: LONGINT): LONGINT;
- CONST Border = 8;
- VAR left, right, top, bottom: LONGINT;
- BEGIN
- left := bounds.GetLeft();
- right := bounds.GetRight();
- top := bounds.GetTop();
- bottom := bounds.GetBottom();
- INC(x,left); INC(y,top); (* relative -> absolute *)
- IF (ABS(left-x) <= Border) THEN
- IF (ABS(top-y) <= Border) THEN
- RETURN UpperLeft
- ELSIF (ABS(bottom-y) <= Border) THEN
- RETURN LowerLeft
- ELSE
- RETURN Left
- END
- ELSIF (ABS(right-x) <= Border) THEN
- IF (ABS(top-y) <= Border) THEN
- RETURN UpperRight
- ELSIF (ABS(bottom-y) <= Border) THEN
- RETURN LowerRight
- ELSE
- RETURN Right
- END
- ELSIF (ABS(y-top) <= Border) THEN
- RETURN Upper
- ELSIF (ABS(bottom-y) <= Border) THEN
- RETURN Lower
- ELSIF (x > left+Border) & (x < right-Border) & (y > top+Border) & (y< bottom-Border) THEN
- RETURN Inside
- ELSE
- RETURN None
- END;
- END InEditBounds;
- PROCEDURE Edit(VAR msg: Messages.Message);
- VAR region: LONGINT; dx,dy: LONGINT; b: Rectangles.Rectangle; manager: WM.WindowManager;
- w,h: LONGINT; img: WMGraphics.Image; canvas: WMGraphics.BufferCanvas; e: ComponentListEntry;
- tr,or : Rectangles.Rectangle; enum: XMLObjects.Enumerator; obj: ANY;
- alignRelative : BOOLEAN;
- BEGIN
- IF msg.msgSubType = Messages.MsgSubPointerUp THEN
- editRegion := None;
- SetPointerInfo(oldPointerInfo);
- RETURN
- END;
- dx := msg.x-editX; dy := msg.y-editY;
- b := bounds.Get();
- IF editRegion = Right THEN
- b.r := b.r + dx
- ELSIF editRegion = Left THEN
- b.l := b.l + dx; dx := 0;
- ELSIF editRegion = Lower THEN
- b.b := b.b + dy
- ELSIF editRegion = Upper THEN
- b.t := b.t + dy; dy := 0;
- ELSIF editRegion = LowerLeft THEN
- b.b := b.b + dy;
- b.l := b.l + dx; dx := 0;
- ELSIF editRegion = LowerRight THEN
- b.b := b.b + dy;
- b.r := b.r + dx
- ELSIF editRegion = UpperLeft THEN
- b.t := b.t + dy; dy := 0;
- b.l := b.l + dx; dx := 0;
- ELSIF editRegion = UpperRight THEN
- b.t := b.t + dy; dy := 0;
- b.r := b.r + dx
- ELSIF (editRegion = Inside) & ((dx # 0) OR (dy # 0)) THEN
- img := selection.ToImg(SELF,e);
- IF e # NIL THEN
- IF StartDrag(selection,img,-msg.x-e.dx,-msg.y-e.dy, EditMoved,NIL) THEN END;
- END;
- RETURN
- ELSIF (editRegion = None) & (msg.flags * {0,1,2} # {}) THEN
- tr.l :=MIN (editX,msg.x); ;
- tr.t := MIN(editY, msg.y);
- tr.r := MAX(editX, msg.x);
- tr.b := MAX(editY, msg.y);
- selection.rectOwner := SELF;
- selection.rect := tr;
- Invalidate;
- enum := GetContents();
- WHILE enum.HasMoreElements() DO
- obj := enum.GetNext();
- IF (obj IS VisualComponent) THEN
- or := obj(VisualComponent).bounds.Get();
- IF Rectangles.Intersect(or, tr) THEN
- selection.Add(obj(VisualComponent))
- END;
- END;
- END;
- RETURN;
- END;
- AdaptRelativeBounds(b, GetParent());
- bounds.Set(b);
- editX := editX + dx; editY := editY + dy;
- END Edit;
- PROCEDURE SetEditMode*(mode: BOOLEAN; recurse: BOOLEAN);
- VAR vc: VisualComponent; c: XML.Content;
- BEGIN
- Acquire;
- editMode.Set(mode);
- IF recurse THEN
- c := GetFirst();
- WHILE (c # NIL) DO
- IF c IS VisualComponent THEN
- vc := c(VisualComponent);
- vc.SetEditMode(mode, TRUE);
- END;
- c := GetNext(c);
- END;
- END;
- Release;
- END SetEditMode;
- PROCEDURE EditMoved(sender, data: ANY);
- VAR parent: XML.Element; ldata: ANY; e: ComponentListEntry;
- BEGIN
- IF (sender # SELF) THEN
- IF (data # NIL) & (data IS WM.DragInfo) THEN
- ldata := data(WM.DragInfo).data;
- IF (ldata # NIL) & (ldata IS XML.Element) THEN
- parent := ldata(XML.Element).GetParent();
- parent.RemoveContent(ldata(XML.Element));
- parent(VisualComponent).Invalidate;
- ELSIF (ldata # NIL) & (ldata IS SelectionList) THEN
- e := ldata(SelectionList).first;
- WHILE e # NIL DO
- parent := e.component.GetParent();
- ldata := e.component;
- parent.RemoveContent(ldata(XML.Element));
- parent(VisualComponent).Invalidate;
- e := e.next;
- END;
- END;
- END;
- END;
- END EditMoved;
- PROCEDURE HandleInternal*(VAR msg : Messages.Message); (** PROTECTED *)
- VAR
- po : VisualComponent; nm : Messages.Message; handled : BOOLEAN; b : Rectangles.Rectangle;
- r, v : VisualComponent;
- p : XML.Element;
- keyFlags: SET; manager : WM.WindowManager;
- currentEditRegion: LONGINT;
- parent: XML.Element;
- parentEditMode: BOOLEAN;
- BEGIN
- ASSERT(IsCallFromSequencer());
- handled := FALSE;
- IF msg.msgType = Messages.MsgPointer THEN
- parent := GetParent();
- IF (parent # NIL) & (parent IS VisualComponent) & parent(VisualComponent).editMode.Get() THEN
- parentEditMode := TRUE
- ELSE
- parentEditMode := FALSE
- END;
- IF msg.msgSubType = Messages.MsgSubPointerMove THEN
- IF (msg.flags * {0, 1, 2} = {}) OR (fPointerOwner = NIL) THEN
- IF parentEditMode & ~editMode.Get() THEN fPointerOwner := SELF; handled := TRUE
- ELSIF ~parentEditMode & (extGetPositionOwner # NIL) THEN extGetPositionOwner(msg.x, msg.y, fPointerOwner, handled);
- END;
- IF ~handled THEN
- po := GetPositionOwner(msg.x, msg.y);
- IF po # fPointerOwner THEN
- nm.msgType := Messages.MsgPointer;
- nm.msgSubType := Messages.MsgSubPointerLeave;
- HandleInternal(nm)
- END;
- fPointerOwner := po
- ELSE
- handled := FALSE;
- END;
- END
- END;
- IF (fPointerOwner = SELF) THEN
- IF (msg.originator # NIL) & (msg.originator IS WM.ViewPort) THEN
- manager := msg.originator(WM.ViewPort).manager;
- msg.originator(WM.ViewPort).GetKeyState(keyFlags);
- END;
- IF parentEditMode & (editRegion # None) THEN
- Edit(msg)
- ELSE
- IF msg.msgSubType = Messages.MsgSubPointerMove THEN
- IF (parentEditMode) & (msg.originator # NIL) & (msg.originator IS WM.ViewPort) THEN
- currentEditRegion := InEditBounds(msg.x, msg.y);
- CASE currentEditRegion OF
- | Lower, Upper: SetPointerInfo(manager.pointerUpDown)
- | Left, Right:SetPointerInfo(manager.pointerLeftRight)
- | LowerLeft, UpperRight:SetPointerInfo(manager.pointerURDL)
- | UpperLeft, LowerRight: SetPointerInfo(manager.pointerULDR)
- | Inside: SetPointerInfo(manager.pointerMove)
- ELSE
- IF oldPointerInfo # NIL THEN
- SetPointerInfo(oldPointerInfo); oldPointerInfo := NIL;
- ELSE oldPointerInfo := GetPointerInfo();
- END;
- END;
- ELSIF editMode.Get() & (msg.flags * {0, 1, 2} # {}) & ~parentEditMode THEN
- IF (keyFlags # {}) & (keyFlags <= Inputs.Shift) THEN
- ELSE
- selection.Reset(SELF);
- END;
- Edit(msg);
- END;
- IF extPointerMove # NIL THEN extPointerMove(msg.x, msg.y, msg.flags, handled) END;
- SetInternalPointerInfo(pointerInfo);
- IF ~handled THEN PointerMove(msg.x, msg.y, msg.flags) END;
- IF msg.dz # 0 THEN WheelMove(msg.dz) END
- ELSIF msg.msgSubType = Messages.MsgSubPointerDown THEN
- IF parentEditMode THEN
- editRegion := InEditBounds(msg.x, msg.y);
- ELSIF editMode.Get() THEN
- editRegion := None;
- editX := msg.x; editY := msg.y;
- END;
- (*
- IF (msg.originator # NIL) & (msg.originator IS WM.ViewPort) THEN
- msg.originator(WM.ViewPort).GetKeyState(keyFlags);
- IF (keyFlags # {}) & (keyFlags <= Inputs.Ctrl) THEN editRegion := InEditBounds(msg.x, msg.y) ELSE editRegion := None END;
- ELSE
- editRegion := None
- END;
- *)
- IF (editRegion # None) & parentEditMode THEN
- IF (keyFlags # {}) & (keyFlags <= Inputs.Shift) THEN
- selection.Toggle(SELF)
- ELSIF ~selection.Has(SELF) THEN
- selection.Reset(SELF);
- selection.Add(SELF);
- END;
- manager := msg.originator(WM.ViewPort).manager;
- editX := msg.x; editY := msg.y;
- ELSE
- IF extPointerDown # NIL THEN extPointerDown(msg.x, msg.y, msg.flags, handled) END;
- IF ~handled THEN PointerDown(msg.x, msg.y, msg.flags) END;
- END;
- SetFocus
- ELSIF msg.msgSubType = Messages.MsgSubPointerUp THEN
- IF selection.rectOwner = SELF THEN
- Invalidate;
- selection.rectOwner := NIL;
- END;
- IF extPointerUp # NIL THEN extPointerUp(msg.x, msg.y, msg.flags, handled) END;
- IF ~handled THEN PointerUp(msg.x, msg.y, msg.flags) END
- ELSIF msg.msgSubType = Messages.MsgSubPointerLeave THEN
- IF extPointerLeave # NIL THEN extPointerLeave(handled) END;
- IF ~handled THEN PointerLeave END
- END;
- IF ~parentEditMode & (msg.flags * {0, 1, 2} # {}) THEN
- IF (msg.msgSubType = Messages.MsgSubPointerDown) THEN
- CheckPointerEvent(msg.x, msg.y, msg.z, msg.flags);
- ELSIF msg.msgSubType = Messages.MsgSubPointerUp THEN
- CheckPointerUpEvent(msg.x, msg.y, msg.z, msg.flags);
- END;
- END;
- END;
- ELSE
- b := fPointerOwner.bounds.Get();
- msg.x := msg.x - b.l; msg.y := msg.y - b.t;
- fPointerOwner.Handle(msg)
- END
- ELSIF msg.msgType = Messages.MsgKey THEN
- IF fPointerOwner.EditKeyEvents(msg.x, msg.flags, msg.y) THEN
- handled := TRUE
- ELSIF focusComponent # SELF THEN focusComponent.Handle(msg)
- ELSIF (visible.Get()) THEN
- IF ~needsTab.Get() & (msg.y = 0FF09H) THEN
- IF (Inputs.Shift * msg.flags # {}) THEN FocusPrev ELSE FocusNext END
- ELSIF msg.y = 0FF67H THEN ShowContextMenu(0, 0)
- ELSE
- IF extKeyEvent # NIL THEN extKeyEvent(msg.x, msg.flags, msg.y, handled) END;
- IF ~handled THEN KeyEvent(msg.x, msg.flags, msg.y) END;
- CheckKeyEvents(msg.x, msg.flags, msg.y);
- END
- END;
- ELSIF msg.msgType = Messages.MsgDrag THEN
- IF extGetPositionOwner # NIL THEN extGetPositionOwner(msg.x, msg.y, po, handled); END;
- IF ~handled THEN
- po := GetPositionOwner(msg.x, msg.y);
- ELSE
- handled := FALSE;
- END;
- IF (po # SELF) & editMode.Get() & (~po.editMode.Get() OR (msg.ext # NIL) & (msg.ext(WM.DragInfo).data=po)) THEN
- po := SELF
- ELSIF (msg.ext # NIL) & (msg.ext(WM.DragInfo).data # NIL) & (msg.ext(WM.DragInfo).data IS ToggleEditMode) & (msg.ext(WM.DragInfo).data(ToggleEditMode).recursion = Recursion.FromBottom) THEN
- po := SELF
- END;
- IF (po # SELF) THEN (* Let child handle the drag and drop message *)
- b := po.bounds.Get();
- msg.x := msg.x - b.l; msg.y := msg.y - b.t;
- po.Handle(msg)
- ELSE (* handle the drag and drop message *)
- IF msg.msgSubType = Messages.MsgDragOver THEN
- IF (msg.ext # NIL) THEN
- IF extDragOver # NIL THEN extDragOver(msg.x, msg.y, msg.ext(WM.DragInfo), handled) END;
- IF ~handled THEN po.DragOver(msg.x, msg.y, msg.ext(WM.DragInfo)) END
- END
- ELSIF msg.msgSubType = Messages.MsgDragDropped THEN
- IF (msg.ext # NIL) THEN
- IF (msg.ext(WM.DragInfo).data # NIL) & (msg.ext(WM.DragInfo).data IS FindComponentMode) THEN
- IF msg.ext(WM.DragInfo).onAccept # NIL THEN
- msg.ext(WM.DragInfo).onAccept(po, msg.ext(WM.DragInfo));
- END;
- ELSIF (msg.ext(WM.DragInfo).data # NIL) & (msg.ext(WM.DragInfo).data IS ToggleEditMode) THEN
- SetEditMode(~editMode.Get(), msg.ext(WM.DragInfo).data(ToggleEditMode).recursion # Recursion.None);
- Invalidate;
- ELSIF editMode.Get() THEN
- handled := EditDragDropped(msg.x,msg.y,msg.ext(WM.DragInfo));
- ELSIF extDragDropped # NIL THEN
- extDragDropped(msg.x, msg.y, msg.ext(WM.DragInfo), handled)
- END;
- IF ~handled THEN
- po.DragDropped(msg.x, msg.y, msg.ext(WM.DragInfo))
- END
- END
- END
- END
- ELSIF (msg.msgType = Messages.MsgFocus) & (msg.msgSubType = Messages.MsgSubFocusLost) THEN
- (* unset the old focus chain *)
- r := GetVisualComponentRoot(); (* find the leaf component that has the focus *)
- WHILE (r # NIL) & (r.focusComponent # NIL) & (r.focusComponent # r) DO r := r.focusComponent END;
- p := r; (* clear the focus chain until the root or this component *)
- WHILE (p # SELF) & (p # NIL) & (p IS VisualComponent) DO
- v := p(VisualComponent);
- v.focusComponent := v;
- v.FocusLost; IF v.extFocus # NIL THEN v.extFocus(FALSE) END; p := p.GetParent()
- END;
- ELSIF msg.msgType = Messages.MsgInvalidate THEN
- IF msg.msgSubType = Messages.MsgSubAll THEN
- msg.sender(VisualComponent).InvalidateRect(GetClientRect());
- ELSIF msg.msgSubType = Messages.MsgSubRectangle THEN
- msg.sender(VisualComponent).InvalidateRect(Rectangles.MakeRect(msg.x, msg.y, msg.dx, msg.dy));
- ELSE (* nothing to do *)
- END;
- ELSIF msg.msgType = Messages.MsgExt THEN
- IF msg.ext = invalidateRectMsg THEN
- TRACE("WARNING: OLD MESSAGE FORM");
- msg.sender(VisualComponent).InvalidateRect(Rectangles.MakeRect(msg.x, msg.y, msg.dx, msg.dy))
- ELSE
- BroadcastSubcomponents(msg);
- END
- ELSE HandleInternal^(msg)
- END;
- END HandleInternal;
- END VisualComponent;
- GetPositionOwnerHandler* = PROCEDURE {DELEGATE} (x, y : LONGINT; VAR positionOwner : VisualComponent; VAR handled : BOOLEAN);
- TYPE
- (* Layout Manager *)
- LayoutManager* = PROCEDURE {DELEGATE} (vc : VisualComponent);
- FormWindow* = OBJECT(WM.DoubleBufferWindow)
- VAR
- form- : Form;
- cs : WMGraphics.CanvasState;
- disableUpdate : LONGINT;
- content : VisualComponent;
- scaling* : BOOLEAN;
- PROCEDURE ToXML*():XML.Content;
- VAR winx: XML.Element; a: XML.Attribute; string: ARRAY 128 OF CHAR; title:Strings.String;
- BEGIN {EXCLUSIVE}
- NEW(winx); winx.SetName("FormWindow");
- NEW(a); a.SetName("name");
- title:=GetTitle(); IF title=NIL THEN a.SetValue("componentWindow") ELSE a.SetValue(title^) END;
- winx.AddAttribute(a);
- NEW(a); a.SetName("loader"); a.SetValue("WMComponents.FormWindowGen"); winx.AddAttribute(a);
- NEW(a); a.SetName("l"); Strings.IntToStr(bounds.l, string); a.SetValue(string); winx.AddAttribute(a);
- NEW(a); a.SetName("t"); Strings.IntToStr(bounds.t, string); a.SetValue(string); winx.AddAttribute(a);
- NEW(a); a.SetName("r"); Strings.IntToStr(bounds.r, string); a.SetValue(string); winx.AddAttribute(a);
- NEW(a); a.SetName("b"); Strings.IntToStr(bounds.b, string); a.SetValue(string); winx.AddAttribute(a);
- NEW(a); a.SetName("flags"); Strings.SetToStr(flags, string); a.SetValue(string); winx.AddAttribute(a);
- NEW(a); a.SetName("canvasGenerator"); a.SetValue(canvas.generator^); winx.AddAttribute(a);
- winx.AddContent(form);
- RETURN winx
- END ToXML;
-
- PROCEDURE LoadComponents*(xml: XML.Element);
- VAR component: Repositories.Component;
- BEGIN
- IF xml # NIL THEN
- component := Repositories.ComponentFromXML(xml);
- IF (component # NIL) & (component IS VisualComponent) THEN
- SetContent(component);
- ELSE
- KernelLog.String("formwindow could not load content"); KernelLog.Ln;
- END;
- END;
- END LoadComponents;
- PROCEDURE StoreComponents*(): XML.Element;
- BEGIN RETURN content (* do not store form separately *)
- END StoreComponents;
- PROCEDURE SetContent*(x : XML.Content);
- VAR c: XML.Content;
- m:Messages.Message;
- BEGIN
- IF sequencer # NIL THEN sequencer.WaitFree() END;
- BEGIN{EXCLUSIVE}
- INC(disableUpdate);
- INCL(flags, 13); (* render windows background non-displayed*)
- IF form # NIL THEN form.Finalize; form.sequencer.Stop; content:=NIL END;
- IF x IS Form THEN
- form := x(Form);
- form.initialized:=FALSE;
- form.SetWindow(SELF); (* includes new sequencer *)
- c:=form.GetFirst(); (* get first VisualComponent content of form*)
- WHILE (c#NIL) & (c IS XML.Container) & ~(c IS VisualComponent) DO
- c:=c(XML.Container).GetNext(c);
- END;
- IF c#NIL THEN form.RemoveContent(c) END; (* avoid duplicates. will be added in a systematic way below in AddContent *)
- ELSE
- NEW(form, SELF); (* includes new sequencer; initialized=FALSE *)
- form.uid.Set(NewString("form"));
- c:=x;
- END;
- IF (c#NIL) & (c IS VisualComponent) THEN
- content := c(VisualComponent);
- form.initialized:=TRUE;
- form.AddContent(content);
- form.focusComponent := content;
- form.fPointerOwner := content;
- END;
- DEC(disableUpdate);
- END;
- (*form.Initialize;*)(*implied above*)
- (*form.Invalidate;*)(*implied above*)
- END SetContent;
- PROCEDURE DisableUpdate*;
- BEGIN {EXCLUSIVE}
- INC(disableUpdate);
- ASSERT(disableUpdate # -1); (* overflow *)
- END DisableUpdate;
- PROCEDURE EnableUpdate*;
- BEGIN {EXCLUSIVE}
- DEC(disableUpdate);
- ASSERT(disableUpdate # -1); (* underflow *)
- END EnableUpdate;
- PROCEDURE Resized*( width, height: LONGINT);
- BEGIN
- IF ~scaling THEN
- DisableUpdate;
- form.Acquire;
- ReInit(width, height);
- form.Release;
- form.bounds.Set(Rectangles.MakeRect(0, 0, GetWidth(), GetHeight()));
- content.bounds.Set(Rectangles.MakeRect(0, 0, GetWidth(), GetHeight()));
- EnableUpdate;
- form.Invalidate()
- END
- END Resized;
- PROCEDURE Trap():BOOLEAN;
- BEGIN
- KernelLog.String("WMComponents.FormWindow.Trap !!! --> Resetting Locks "); KernelLog.Ln;
- form.sequencer.lock.Reset;
- RETURN TRUE
- END Trap;
- PROCEDURE Update(rect : Rectangles.Rectangle);
- BEGIN
- (*KernelLog.String("Update "); KernelLog.Int(disableUpdate,0); KernelLog.Ln;*)
- IF disableUpdate > 0 THEN RETURN END;
- form.Acquire;
- canvas.SaveState(cs);
- canvas.SetClipRect(rect);
- canvas.ClipRectAsNewLimits(0, 0);
- IF Raster.alpha IN img.fmt.components THEN
- canvas.Fill(rect, 0H, WMGraphics.ModeCopy)
- ELSE
- canvas.Fill(rect, 0H (*0FFH*), Raster.clear(*WMGraphics.ModeCopy*))
- END;
- form.Draw(canvas);
- canvas.RestoreState(cs);
- form.Release;
- CopyRect(rect);
- Invalidate(rect)
- END Update;
-
- PROCEDURE Handle*(VAR m : Messages.Message);
- VAR pendingM: Messages.Message;
- BEGIN
- Handle^(m);
-
- IF (m.msgType = Messages.MsgExt) & (m.ext # NIL) THEN
- IF (m.ext = componentStyleMsg) THEN CSChanged
- END;
- ELSIF (m.msgType = Messages.MsgFocus) & (m.msgSubType = Messages.MsgSubFocusGot) THEN
- IF (form # NIL) & (form.lastFocusComponent # NIL) THEN
- form.lastFocusComponent.SetFocus;
- END;
- ELSIF (m.msgType = Messages.MsgSetLanguage) & (m.ext # NIL) & (m.ext IS LanguageExtension) THEN
- LanguageChanged(m.ext(LanguageExtension).languages);
- ELSIF (m.msgType=Messages.MsgInvalidate) THEN (* sent by WindowManager when a window is added to display space to assure it is up-to-date*)
- IF form=NIL THEN RETURN
- ELSE m.sender:=form; (* will be passed to form below, which will call sender.InvalidateRect *)
- END;
- END;
- IF (TraceFocus IN Trace) THEN
- IF (m.msgType = Messages.MsgFocus) THEN
- IF (m.msgSubType = Messages.MsgSubFocusGot) THEN
- KernelLog.String("Got Focus: "); form.TraceFocusChain;
- ELSIF (m.msgSubType = Messages.MsgSubMasterFocusGot) THEN
- KernelLog.String("Got Master Focus: "); form.TraceFocusChain;
- END;
- ELSIF (m.msgType = Messages.MsgKey) & (m.x = ORD("f")) THEN
- KernelLog.String("Focus chain: "); form.TraceFocusChain;
- END;
- END;
-
- IF (form # NIL) THEN form.Handle(m); END;
- END Handle;
- PROCEDURE LanguageChanged*(languages : Localization.Languages);
- BEGIN
- ASSERT(languages # NIL);
- END LanguageChanged;
- PROCEDURE CSChanged*;
- BEGIN
- DisableUpdate; (* the components are going to redraw like crazy *)
- form.Acquire;
- form.Reset(SELF, NIL);
- form.Release;
- EnableUpdate;
- END CSChanged;
- PROCEDURE Close*;
- BEGIN
- Close^; (* remove the form to avoid further messages *)
- IF form # NIL THEN
- form.Acquire;
- form.Finalize; form.sequencer.Stop;
- form.Release
- END;
- END Close;
- END FormWindow;
- Form* = OBJECT(VisualComponent)
- VAR
- window- : FormWindow;
- lastFocusComponent : VisualComponent;
- PROCEDURE &New*(window : FormWindow);
- BEGIN
- Init;
- SetGenerator("WMComponents.NewForm");
- lastFocusComponent := NIL;
- SetNameAsString(StrForm);
- SetWindow(window);
- END New;
-
- PROCEDURE SetWindow*(window: FormWindow);
- VAR seq: Messages.MsgSequencer;
- BEGIN {EXCLUSIVE}
- IF window # NIL THEN
- SELF.window := window;
- window.form := SELF;
- bounds.Set(Rectangles.MakeRect(0, 0, window.GetWidth(), window.GetHeight()));
- NEW(seq, Handle); seq.SetTrapHandler(window.Trap); SetSequencer(seq);
- END;
- END SetWindow;
- PROCEDURE GetPointerInfo*() : WM.PointerInfo;
- BEGIN
- ASSERT(IsCallFromSequencer());
- IF window # NIL THEN RETURN window.pointerInfo ELSE RETURN NIL END
- END GetPointerInfo;
- PROCEDURE SetPointerInfo*(pi : WM.PointerInfo);
- BEGIN
- ASSERT(IsCallFromSequencer());
- IF window # NIL THEN window.SetPointerInfo(pi) END;
- END SetPointerInfo;
- PROCEDURE DisableUpdate*;
- BEGIN
- ASSERT(IsCallFromSequencer());
- IF window # NIL THEN window.DisableUpdate END
- END DisableUpdate;
- PROCEDURE EnableUpdate*;
- BEGIN
- ASSERT(IsCallFromSequencer());
- IF window # NIL THEN window.EnableUpdate END
- END EnableUpdate;
- PROCEDURE InvalidateRect*(rect : Rectangles.Rectangle);
- BEGIN
- IF window # NIL THEN
- BEGIN{EXCLUSIVE} AWAIT(initialized) END;
- window.Update(rect)
- END;
- END InvalidateRect;
- PROCEDURE PropertyChanged*(sender, property : ANY);
- VAR w,h: LONGINT;
- BEGIN
- IF property = bounds THEN
- IF ~ Rectangles.IsEqual(window.bounds, bounds.Get()) THEN
- bounds.GetExtents(w,h);
- IF window # NIL THEN
- window.manager.SetWindowSize(window,w,h);
- END;
- ELSE
- (*ScaleFont(bounds.GetHeight(), scaleFont.Get());*)
- Resized
- END;
- END
- END PropertyChanged;
- END Form;
- TYPE
- (** PropertyLists for style support *)
- PropertyListEntry = POINTER TO RECORD
- next : PropertyListEntry;
- name : Strings.String;
- list : WMProperties.PropertyList;
- END;
- ListArray* = POINTER TO ARRAY OF WMProperties.PropertyList;
- PropertyListList* = OBJECT
- VAR
- first : PropertyListEntry;
- PROCEDURE Find*(CONST name : ARRAY OF CHAR) : WMProperties.PropertyList;
- VAR cur : PropertyListEntry;
- BEGIN {EXCLUSIVE}
- cur := first;
- WHILE (cur # NIL) & (cur.name^ # name) DO cur := cur.next END;
- IF cur # NIL THEN RETURN cur.list
- ELSE RETURN NIL
- END
- END Find;
- PROCEDURE RemoveInternal(CONST name : ARRAY OF CHAR);
- VAR cur : PropertyListEntry;
- BEGIN
- IF first = NIL THEN RETURN END;
- IF (first # NIL) & (first.name^ = name) THEN first := first.next
- ELSE
- cur := first;
- WHILE (cur.next # NIL) DO
- IF (cur.next.name^ = name) THEN cur.next := cur.next.next END;
- cur := cur.next
- END
- END
- END RemoveInternal;
- PROCEDURE Remove*(CONST name : ARRAY OF CHAR);
- BEGIN {EXCLUSIVE}
- RemoveInternal(name)
- END Remove;
- PROCEDURE Add*(CONST name : ARRAY OF CHAR; pl : WMProperties.PropertyList);
- VAR new : PropertyListEntry;
- BEGIN {EXCLUSIVE}
- RemoveInternal(name);
- NEW(new); new.name := NewString(name); new.list := pl; new.next := first; first := new
- END Add;
- PROCEDURE Enumerate*() : ListArray;
- VAR array : ListArray; current : PropertyListEntry; i : LONGINT;
- BEGIN {EXCLUSIVE}
- i := 0;
- current := first;
- WHILE current # NIL DO INC(i); current := current.next END;
- NEW(array, i );
- current := first;
- i := 0;
- WHILE current # NIL DO
- array[i] := current.list;
- INC(i);
- current := current.next
- END;
- RETURN array
- END Enumerate;
- PROCEDURE UpdateStyle*;
- VAR
- en : XMLObjects.Enumerator;
- p : ANY; s : Strings.String;
- pl : WMProperties.PropertyList;
- BEGIN
- IF currentStyle = NIL THEN RETURN END;
- en := currentStyle.GetContents();
- WHILE en.HasMoreElements() DO
- p := en.GetNext();
- IF p IS XML.Element THEN
- s := p(XML.Element).GetName();
- pl := propertyListList.Find(s^);
- IF pl # NIL THEN pl.SetXML(p(XML.Element)) END
- END
- END
- END UpdateStyle;
- END PropertyListList;
- ComponentListEntry= POINTER TO RECORD
- component: VisualComponent;
- dx,dy: LONGINT;
- next: ComponentListEntry
- END;
- SelectionArray* = POINTER TO ARRAY OF VisualComponent;
- SelectionList*= OBJECT
- VAR first, last: ComponentListEntry; number: LONGINT; state: LONGINT; timer: Kernel.Timer;
- onChanged-: WMEvents.EventSource;
- lock: Locks.RecursiveLock;
- rectOwner: ANY;
- rect : Rectangles.Rectangle;
- PROCEDURE &Init;
- BEGIN
- NEW(lock);
- first := NIL; last := NIL; number := 0; state := 0; NEW(onChanged, NIL, NIL, NIL, NIL);
- END Init;
- PROCEDURE Reset(this: VisualComponent);
- VAR entry: ComponentListEntry;
- BEGIN
- lock.Acquire;
- entry := first;
- first := NIL; last := NIL; number := 0;
- WHILE entry # NIL DO entry.component.Invalidate; entry := entry.next END;
- lock.Release;
- (*Add(this);*)
- onChanged.Call(SELF);
- END Reset;
- PROCEDURE Has*(this: ANY): BOOLEAN;
- VAR entry: ComponentListEntry;
- BEGIN
- IF first = NIL THEN RETURN FALSE END; (* no lock for usual case *)
- lock.Acquire;
- entry := first;
- WHILE (entry # NIL) & (entry.component # this) DO entry := entry.next END;
- lock.Release;
- RETURN entry # NIL
- END Has;
- PROCEDURE Add*(this: VisualComponent);
- VAR entry: ComponentListEntry;
- BEGIN
- IF (this = NIL) OR Has(this) THEN RETURN END;
- lock.Acquire;
- NEW(entry); entry.component := this; entry.next := NIL;
- IF last = NIL THEN
- ASSERT(first = NIL);
- first := entry; last := entry;
- ELSE
- last.next := entry; last := entry
- END;
- INC(number);
- lock.Release;
- this.Invalidate;
- onChanged.Call(SELF);
- END Add;
- PROCEDURE Remove*(this: VisualComponent);
- VAR entry, prev: ComponentListEntry;
- BEGIN
- lock.Acquire;
- entry := first; prev := NIL;
- WHILE (entry # NIL) & (entry.component # this) DO
- prev := entry;
- entry := entry.next;
- END;
- IF entry = NIL THEN lock.Release; RETURN END;
- IF prev # NIL THEN prev.next := entry.next END;
- IF entry = first THEN first := first.next END;
- IF entry = last THEN last := prev END;
- DEC(number);
- lock.Release;
- this.Invalidate;
- onChanged.Call(SELF);
- END Remove;
- PROCEDURE GetSelection*(): SelectionArray;
- VAR array: SelectionArray; i: LONGINT; e: ComponentListEntry;
- BEGIN
- lock.Acquire;
- NEW(array, number);
- e := first; i := 0;
- WHILE e # NIL DO
- array[i] := e.component;
- INC(i);
- e := e.next;
- END;
- lock.Release;
- RETURN array;
- END GetSelection;
- PROCEDURE Toggle*(this: VisualComponent);
- BEGIN
- IF Has(this) THEN Remove(this) ELSE Add(this) END;
- END Toggle;
- PROCEDURE Update;
- VAR e: ComponentListEntry;
- BEGIN
- e := first;
- WHILE e # NIL DO
- e.component.Invalidate;
- e := e.next;
- END;
- END Update;
- PROCEDURE Shift(dx, dy: LONGINT);
- VAR e: ComponentListEntry; rect: Rectangles.Rectangle;
- BEGIN
- e := first;
- WHILE e # NIL DO
- rect := e.component.bounds.Get();
- INC(rect.l,dx); INC(rect.r,dx);
- INC(rect.t,dy); INC(rect.b,dy);
- e.component.AdaptRelativeBounds(rect,e.component.GetParent());
- e.component.bounds.Set(rect);
- e := e.next
- END;
- END Shift;
- PROCEDURE ToImg(start: VisualComponent; VAR this: ComponentListEntry): WMGraphics.Image;
- VAR l,t,r,b: LONGINT; e: ComponentListEntry; rect: Rectangles.Rectangle; img, image: WMGraphics.Image; w,h: LONGINT;
- canvas: WMGraphics.BufferCanvas; srcCopy: Raster.Mode;
- BEGIN
- l := MAX(LONGINT); r := MIN(LONGINT);
- t := MAX(LONGINT); b := MIN(LONGINT);
- e := first;
- WHILE e # NIL DO
- rect := e.component(VisualComponent).bounds.Get();
- IF rect.l < l THEN l := rect.l END;
- IF rect.r > r THEN r := rect.r END;
- IF rect.t < t THEN t := rect.t END;
- IF rect.b > b THEN b := rect.b END;
- e := e.next;
- END;
- Raster.InitMode(srcCopy, Raster.srcCopy);
- NEW(image);
- w := r-l+1; h := b-t+1;
- Raster.Create(image, w,h, Raster.BGRA8888);
- e := first;
- WHILE e # NIL DO
- rect := e.component.bounds.Get();
- NEW(img);
- Raster.Create(img,rect.r-rect.l+1, rect.b-rect.t+1, Raster.BGRA8888);
- NEW(canvas,img);
- e.component.Draw(canvas);
- Raster.Copy(img,image,0,0,img.width-1, img.height-1,rect.l-l, rect.t-t, srcCopy);
- e.dx := rect.l-l; e.dy := rect.t-t;
- IF e.component = start THEN this := e END;
- e := e.next
- END;
- RETURN image
- END ToImg;
- BEGIN {ACTIVE}
- NEW(timer);
- LOOP
- timer.Sleep(400);
- state := (state + 1) MOD 2;
- Update;
- END
- END SelectionList;
- WindowGenerator*= PROCEDURE(xml: XML.Content): WM.Window;
- VAR
- hasErrors : BOOLEAN; (* accessed only from (EXCLUSIVE) *)
- invalidateRectMsg- : Messages.MessageExtension; (* used as unique ID *)
- PrototypeID, PrototypeUID : WMProperties.StringProperty;
- PrototypeBounds-, PrototypeBoundsRelative-, PrototypeBearing : WMProperties.RectangleProperty;
- PrototypeEnabled : WMProperties.BooleanProperty;
- PrototypeFillColor : WMProperties.ColorProperty;
- PrototypeAlignment : WMProperties.Int32Property;
- PrototypeVisible, PrototypeTakesFocus, PrototypeNeedsTab, PrototypeEditMode: WMProperties.BooleanProperty;
- PrototypeScaleFont: WMProperties.Int32Property;
- PrototypeFocusPrevious, PrototypeFocusNext : WMProperties.StringProperty;
- PrototypeFont- : WMProperties.FontProperty;
- StrComponent, StrVisualComponent, StrForm, StrFormWindow, StrModel, StrModelInfo : Strings.String;
- GSonStartDrag, GSonStartDragInfo : Strings.String;
- ModelPrototype-: WMProperties.ReferenceProperty;
- propertyListList- : PropertyListList;
- currentStyle- : XML.Element;
- componentStyleMsg- : ComponentStyleChanged;
- timestamp : LONGINT;
- macroHandlers : MacroHandler; (* the head of the list is always the DefaultMacroHandler *)
- selection-: SelectionList;
- PROCEDURE IsWhiteSpace(ch : CHAR) : BOOLEAN;
- BEGIN
- RETURN ch <= " ";
- END IsWhiteSpace;
- PROCEDURE SkipWhiteSpace(CONST string : ARRAY OF CHAR; VAR index : LONGINT);
- VAR length : LONGINT;
- BEGIN
- length := LEN(string);
- WHILE (index < length) & (string[index] # 0X) & IsWhiteSpace(string[index]) DO INC(index); END;
- ASSERT(index < LEN(string));
- END SkipWhiteSpace;
- PROCEDURE ReadWord*(CONST string : ARRAY OF CHAR; VAR word : ARRAY OF CHAR; VAR index : LONGINT) : BOOLEAN;
- VAR length, wordLength, i : LONGINT;
- BEGIN
- SkipWhiteSpace(string, index);
- length := LEN(string);
- wordLength := LEN(word);
- i := 0;
- WHILE (index < length) & (string[index] # 0X) & ~IsWhiteSpace(string[index]) & (i < wordLength) DO
- word[i] := string[index];
- INC(i);
- INC(index);
- END;
- IF (i < wordLength) THEN word[i] := 0X; END;
- ASSERT(index < LEN(string));
- RETURN (i > 0) & (index < length) & (i < wordLength);
- END ReadWord;
- (* Split <string> into two strings separated by <separator> *)
- PROCEDURE SplitMacroString(CONST string : ARRAY OF CHAR; VAR namespace, name : ARRAY OF CHAR; separator : CHAR);
- VAR i, j : LONGINT;
- BEGIN
- ASSERT((LEN(namespace) >= LEN(string)) & (LEN(name) >= LEN(string)));
- i := 0;
- WHILE (i < LEN(string)) & (string[i] # 0X) & (string[i] # separator) DO
- namespace[i] := string[i];
- INC(i);
- END;
- namespace[i] := 0X;
- INC(i); (* skip separator *)
- j := 0;
- WHILE (i < LEN(string)) & (string[i] # 0X) DO
- name[j] := string[i];
- INC(i); INC(j);
- END;
- name[j] := 0X;
- IF (name = "") THEN COPY(namespace, name); COPY(NoNamespace, namespace); END; (* no namespace *)
- END SplitMacroString;
- PROCEDURE ReportError(CONST text, argument1, argument2 : ARRAY OF CHAR);
- VAR
- message : Events.Message;
- textIdx, messageIdx : LONGINT;
- secondArgument : BOOLEAN;
- PROCEDURE Append(VAR message : ARRAY OF CHAR; CONST argument : ARRAY OF CHAR; VAR index : LONGINT);
- VAR i : LONGINT;
- BEGIN
- i := 0;
- WHILE (i < LEN(argument)) & (argument[i] # 0X) & (index < LEN(message) - 1) DO
- message[index] := argument[i];
- INC(i);
- INC(index);
- END;
- END Append;
- BEGIN
- secondArgument := FALSE;
- textIdx := 0;
- messageIdx := 0;
- WHILE (textIdx < LEN(text)) & (text[textIdx] # 0X) & (messageIdx < LEN(message) - 1) DO
- IF (text[textIdx] # "%") THEN
- message[messageIdx] := text[textIdx];
- INC(messageIdx);
- ELSE
- IF ~secondArgument THEN
- secondArgument := TRUE;
- Append(message, argument1, messageIdx);
- ELSE
- Append(message, argument2, messageIdx);
- END;
- END;
- INC(textIdx);
- END;
- message[messageIdx] := 0X;
- Events.AddEvent("Components", Events.Error, 0, 0, 0, message, FALSE);
- END ReportError;
- PROCEDURE GetArgumentStream*(command: Strings.String; offset: LONGINT; VAR arguments: Streams.StringReader);
- VAR i: LONGINT;
- BEGIN
- IF command = NIL THEN arguments := NIL; RETURN END;
- i := offset;
- WHILE (i < LEN(command)) & (command[i] # 0X) DO INC(i); END;
- IF (i # offset) THEN
- NEW(arguments, i - offset + 1);
- arguments.SetRaw(command^, offset, i - offset + 1);
- ELSE
- arguments := NIL;
- END;
- END GetArgumentStream;
- PROCEDURE GenerateContext*(oldCommand, command : Strings.String; index : LONGINT; originator : Component; CONST event : Event) : EventContext;
- VAR
- context : EventContext; pointerContext : PointerContext; keyContext : KeyContext;
- arguments : Streams.StringReader;
- i : LONGINT;
- BEGIN
- ASSERT((command # NIL) & (0 <= index) & (index < LEN(command)));
- ASSERT(originator # NIL);
- GetArgumentStream(command,index,arguments);
- IF (event IS PointerEvent) THEN
- NEW(pointerContext, originator, oldCommand, NIL, arguments, NIL, NIL, NIL); pointerContext.pointer := event(PointerEvent);
- context := pointerContext;
- ELSIF (event IS KeyPressedEvent) THEN
- NEW(keyContext, originator, oldCommand, NIL, arguments, NIL, NIL, NIL); keyContext.key := event(KeyPressedEvent);
- context := keyContext;
- ELSE
- NEW(context, originator, oldCommand, NIL, arguments, NIL, NIL, NIL);
- END;
- BEGIN {EXCLUSIVE}
- context.timestamp := timestamp;
- INC(timestamp);
- END;
- ASSERT(context # NIL);
- RETURN context;
- END GenerateContext;
- PROCEDURE HandleEvent*(CONST event : Event; originator : Component; command : Strings.String);
- VAR
- commandString : ARRAY 128 OF CHAR;
- newCommand : Strings.String;
- context : EventContext;
- msg : Events.Message;
- index : LONGINT;
- BEGIN
- ASSERT((originator # NIL) & (command # NIL));
- index := 0;
- IF Logging THEN
- COPY(command^, msg);
- Events.AddEvent("Components", Events.Information, 0, 0, 0, msg, FALSE);
- END;
- SubstituteMacros(command, newCommand, originator);
- IF ReadWord(newCommand^, commandString, index) THEN
- context := GenerateContext(command, newCommand, index, originator, event);
- Commands.Activate(commandString, context, {}, context.result, msg); (* asynchronous call since holding the originators lock! *)
- IF (context.result # Commands.Ok) THEN
- Events.AddEvent("Components", Events.Error, 0, 0, 0, msg, FALSE);
- END;
- ELSE
- Events.AddEvent("Components", Events.Error, 0, 0, 0, "Expected command", FALSE);
- END;
- END HandleEvent;
- PROCEDURE ContainsMacros(CONST string : ARRAY OF CHAR) : BOOLEAN;
- VAR result : BOOLEAN; length, i : LONGINT;
- BEGIN
- result := FALSE;
- i := 0; length := LEN(string);
- WHILE (i < length) & (string[i] # 0X) & ~result DO
- IF (string[i] = MacroCharacter) THEN
- result := (i + 1 < length) & (string[i+1] # MacroCharacter);
- IF ~result THEN (* two consequent MacroCharacter's are used to escape *)
- INC(i); (*skip string[i+1] *)
- END;
- END;
- INC(i);
- END;
- RETURN result;
- END ContainsMacros;
- PROCEDURE WriteSelectionToStream(w : Streams.Writer);
- VAR text : Texts.Text; from, to : Texts.TextPosition; a, b : LONGINT;
- BEGIN
- ASSERT(w # NIL);
- IF Texts.GetLastSelection(text, from, to) THEN
- text.AcquireRead;
- a := MIN(from.GetPosition(), to.GetPosition());
- b := MAX(from.GetPosition(), to.GetPosition());
- IF (text.GetLength() > 0) THEN
- TextUtilities.SubTextToStream(text, a, b - a + 1, w);
- END;
- text.ReleaseRead;
- END;
- END WriteSelectionToStream;
- PROCEDURE SubstituteMacro(CONST command : Strings.String; VAR index : LONGINT; originator : Component; w : Streams.Writer);
- VAR oldIndex : LONGINT; macro, namespace, name : Macro; handler : MacroHandlerProcedure; handled : BOOLEAN;
- BEGIN
- ASSERT((command # NIL) & (0 <= index) & (index < LEN(command)) & (command[index] = MacroCharacter));
- ASSERT(originator # NIL);
- ASSERT(w # NIL);
- oldIndex := index;
- INC(index); (* skip MacroCharacter *)
- IF ReadWord(command^, macro, index) THEN (*? TBD error handling *)
- SplitMacroString(macro, namespace, name, NamespaceCharacter);
- IF (namespace = NoNamespace) OR (namespace = DefaultNamespace) THEN
- handler := DefaultMacroHandler;
- ELSE
- BEGIN {EXCLUSIVE}
- handler := FindMacroHandler(namespace);
- END;
- END;
- handled := FALSE;
- IF (handler # NIL) THEN handler(name, originator, w, handled); END;
- IF ~handled THEN
- w.Char(MacroCharacter); w.String(macro); (* don't substitute *)
- END;
- END;
- ASSERT(index > oldIndex);
- END SubstituteMacro;
- PROCEDURE SubstituteMacros*(CONST command : Strings.String; VAR newCommand : Strings.String; originator : Component);
- VAR index, oldIndex, length : LONGINT; w : Streams.Writer; buffer : Strings.Buffer;
- BEGIN
- ASSERT((command # NIL) & (originator # NIL));
- IF ContainsMacros(command^) THEN
- NEW(buffer, 256);
- w := buffer.GetWriter();
- index := 0; length := LEN(command^);
- WHILE (index < length) & (command[index] # 0X) DO
- oldIndex := index;
- IF (command[index] = MacroCharacter) THEN
- IF (index + 1 < length) & (command[index + 1] = MacroCharacter) THEN (* escape *)
- w.Char(MacroCharacter);
- index := index + 2; (* skip both MacroCharacter's *)
- ELSE
- (* substitute macro *)
- SubstituteMacro(command, index, originator, w);
- END;
- ELSE
- w.Char(command[index]);
- INC(index);
- END;
- ASSERT(index > oldIndex);
- END;
- newCommand := buffer.GetString();
- ELSE
- newCommand := command;
- END;
- ASSERT(newCommand # NIL);
- END SubstituteMacros;
- PROCEDURE GetAttributeValue(originator : Component; CONST fullname : ARRAY OF CHAR) : Strings.String;
- VAR value : Strings.String; c : Component; component, attribute : ARRAY 64 OF CHAR;
- BEGIN
- ASSERT(originator # NIL);
- value := NIL;
- Strings.GetExtension(fullname, component, attribute);
- IF (attribute = "") THEN
- COPY(component, attribute);
- COPY("", component);
- END;
- IF (component[0] = "@") THEN component[0] := "&"; END; (*? TBD: Hack to avoid ampersand in XML *)
- IF (component = "") THEN
- c := originator;
- ELSE
- c := originator.Find(component);
- END;
- IF (c # NIL) THEN
- IF c.HasAttribute(attribute) THEN
- RETURN c.GetAttributeValue(attribute);
- ELSE
- ReportError("Attribute % of component % not found", attribute, component);
- END;
- ELSE
- ReportError("Component % not found", component, "");
- END;
- RETURN value;
- END GetAttributeValue;
- PROCEDURE GetPropertyValue(originator : Component; CONST fullname : ARRAY OF CHAR) : Strings.String;
- VAR value : ARRAY 256 OF CHAR; string:Strings.String; c : Component; component, property : ARRAY 64 OF CHAR;
- BEGIN
- ASSERT(originator # NIL);
- Strings.GetExtension(fullname, component, property);
- IF (property = "") THEN COPY(component, property); COPY("", component);
- END;
- IF (component[0] = "@") THEN component[0] := "&"; END; (*? TBD: Hack to avoid ampersand in XML *)
- IF (component = "") THEN c := originator;
- ELSE c := originator.Find(component);
- END;
- IF (c # NIL) THEN
- IF c.properties.GetPropertyValue(property,value) THEN RETURN Strings.NewString(value)
- ELSE ReportError("Property % of component % not found", property, component);
- END;
- ELSE ReportError("Component % not found", component, "");
- END;
- RETURN NIL;
- END GetPropertyValue;
- PROCEDURE DefaultMacroHandler(CONST macro : Macro; originator : Component; w : Streams.Writer; VAR handled : BOOLEAN);
- VAR string, value : Strings.String;
- BEGIN
- ASSERT((originator # NIL) & (w # NIL));
- handled := TRUE;
- IF (macro = MacroSelection) THEN
- WriteSelectionToStream(w);
- ELSIF (macro = MacroClipboard) THEN
- TextUtilities.TextToStream(Texts.clipboard, w);
- ELSIF Strings.StartsWith(MacroAttributePrefix, 0, macro) THEN
- string := Strings.Substring(Strings.Length(MacroAttributePrefix), Strings.Length(macro), macro);
- value := GetAttributeValue(originator, string^);
- IF (value # NIL) THEN
- w.String(value^);
- ELSE
- handled := FALSE;
- END;
- ELSIF Strings.StartsWith(MacroPropertyPrefix, 0, macro) THEN
- string := Strings.Substring(Strings.Length(MacroPropertyPrefix), Strings.Length(macro), macro);
- value := GetPropertyValue(originator,string^);
- IF (value # NIL) THEN
- w.String(value^);
- ELSE
- handled := FALSE;
- END;
- ELSE
- handled := FALSE;
- END;
- END DefaultMacroHandler;
- PROCEDURE FindMacroHandler(CONST namespace : ARRAY OF CHAR) : MacroHandlerProcedure;
- VAR node : MacroHandler; handler : MacroHandlerProcedure;
- BEGIN (* caller must hold module lock! *)
- node := macroHandlers;
- WHILE (node # NIL) & (node.namespace # namespace) DO node := node.next; END;
- IF (node # NIL) THEN
- handler := node.handler;
- ELSE
- handler := NIL;
- END;
- RETURN handler;
- END FindMacroHandler;
- PROCEDURE AddMacroHandler*(CONST namespace : Namespace; handler : MacroHandlerProcedure; VAR res : WORD);
- VAR new, node : MacroHandler; h : MacroHandlerProcedure;
- BEGIN {EXCLUSIVE}
- ASSERT((namespace # NoNamespace) & (handler # NIL));
- ASSERT(macroHandlers # NIL);
- h := FindMacroHandler(namespace);
- IF (h = NIL) THEN (* append new handler to list *)
- NEW(new);
- new.handler := handler;
- new.namespace := namespace;
- new.next := NIL;
- node := macroHandlers;
- WHILE (node.next # NIL) DO node := node.next; END;
- node.next := new;
- res := Ok;
- ELSE
- res := DuplicateNamespace;
- END;
- END AddMacroHandler;
- PROCEDURE RemoveMacroHandler*(handler : MacroHandlerProcedure);
- VAR node : MacroHandler;
- BEGIN {EXCLUSIVE}
- ASSERT((handler # NIL) & (handler # DefaultMacroHandler));
- ASSERT(macroHandlers # NIL);
- node := macroHandlers;
- WHILE (node.next # NIL) & (node.next.handler # handler) DO node := node.next; END;
- ASSERT((node.next # NIL) & (node.next.handler = handler));
- node.next := node.next.next;
- END RemoveMacroHandler;
- PROCEDURE SetAttribute*(context : Commands.Context); (** component attribute value ~ *)
- VAR originator, target : Component; name, attribute, value : ARRAY 128 OF CHAR; (*? TBD array size *)
- BEGIN
- IF (context IS EventContext) THEN
- originator := context(EventContext).originator;
- IF context.arg.GetString(name) & context.arg.GetString(attribute) & context.arg.GetString(value) THEN
- target := originator.Find(name);
- IF (target # NIL) THEN
- IF target.HasAttribute(attribute) THEN
- target.SetAttributeValue(attribute, value);
- ELSE
- context.result := Commands.CommandError;
- END;
- ELSE
- context.result := Commands.CommandError;
- END;
- ELSE
- context.error.String("Expected component name, attribute and value parameters"); context.error.Ln;
- context.result := Commands.CommandParseError;
- END;
- ELSE
- context.error.String("Command requires EventContext."); context.error.Ln;
- context.result := Commands.CommandParseError;
- END;
- END SetAttribute;
- (** Activate a string of commands, including their parameters.
- The string is parsed from left to right and Activate is called for every command.
- Parsing stops at the end of the string, or when Activate returns an error.
- The flags are applied to every command, i.e., for sequential execution,
- use the Wait flag (the caller waits until all commands return).
- Syntax:
- cmds = [mode " " ] cmd {";" cmd} .
- mode = "PAR" | "SEQ" .
- cmd = mod ["." proc] [" " params] .
- params = {<any character except ";">} .
- REMARK: For now, this is almost the same as Commands.Call. This procedure will either be enhanced to
- support some component-related macro substitution or be replaced by Commands.Call
- *)
- PROCEDURE Call*(cmds : ARRAY OF CHAR; caller : Component; flags : SET; VAR res : WORD; VAR msg : ARRAY OF CHAR);
- VAR
- context : Commands.Context; arg : Streams.StringReader;
- buffer : Strings.Buffer; w : Streams.Writer; par : Strings.String;
- length, i, k : LONGINT;
- PROCEDURE Expand(CONST string : ARRAY OF CHAR; w : Streams.Writer; start : LONGINT; VAR end : LONGINT);
- VAR
- component : Component;
- componentStr, attributeStr : ARRAY 256 OF CHAR;
- property : WMProperties.Property; attribute : XML.Attribute;
- value : Strings.String;
- lastDotIdx, i, j : LONGINT; error : BOOLEAN;
- BEGIN
- ASSERT((string[start] = "&") & (start + 1 < LEN(string)) & (w # NIL));
- end := start; WHILE (end < LEN(string)) & (string[end] # 0X) & (string[end] # ";") & (string[end] > " ") DO INC(end); END;
- DEC(end);
- lastDotIdx := end;
- WHILE (lastDotIdx > start) & (string[lastDotIdx] # ".") DO DEC(lastDotIdx); END;
- error := (lastDotIdx <= start); (* missing dot *)
- IF ~error THEN
- i := start + 1; (* skip ampersand *)
- IF (string[i] = "&") OR (string[i] = "/") THEN
- j := 0;
- WHILE (i < lastDotIdx) & (j < LEN(componentStr) - 1) DO
- componentStr[j] := string[i];
- INC(i); INC(j);
- END;
- componentStr[j] := 0X;
- component := caller.Find(componentStr);
- ELSE
- componentStr := "";
- component := caller;
- END;
- ASSERT(string[i] = ".");
- INC(i); (* skip dot *)
- attributeStr := "";
- j := 0;
- WHILE (j < LEN(attributeStr)) & (i <= end) DO
- attributeStr[j] := string[i];
- INC(i); INC(j);
- END;
- error := (attributeStr = "");
- IF ~error THEN
- IF (component # NIL) THEN
- property := component.properties.Get(attributeStr);
- IF (property # NIL) THEN
- property.ToStream(w);
- ELSE
- attribute := component.GetAttribute(attributeStr);
- IF (attribute # NIL) THEN
- value := attribute.GetValue();
- IF (value # NIL) THEN w.String(value^); ELSE w.String("NIL"); END;
- ELSE
- error := TRUE;
- END;
- END;
- ELSE
- error := TRUE;
- END;
- END;
- END;
- IF error THEN (* don't expand macro *)
- FOR i := start TO end DO w.Char(string[i]); END;
- END;
- ASSERT(end >= start);
- END Expand;
- BEGIN
- ASSERT(caller # NIL);
- NEW(buffer, LEN(cmds)); w := buffer.GetWriter();
- IF Strings.StartsWith2(Repositories.CommandPrefix, cmds) THEN i := Strings.Length(Repositories.CommandPrefix); ELSE i := 0; END;
- LOOP
- buffer.Clear;
- w.Reset;
- k := 0;
- WHILE (i < LEN(cmds)) & (cmds[i] # " ") & (cmds[i] # 09X) & (cmds[i] # 0DX) & (cmds[i] # 0AX) & (cmds[i] # 0X) & (cmds[i] # ";") DO cmds[k] := cmds[i]; INC(k); INC(i); END;
- IF k = 0 THEN EXIT; END; (* end of string *)
- IF (i < LEN(cmds)) & (cmds[i] # ";") & (cmds[i] # 0X) THEN (* parameters *)
- INC(i); (* skip delimiter *)
- WHILE (i < LEN(cmds)) & (cmds[i] # 0X) & (cmds[i] # ";") DO
- IF (cmds[i] = "&") & (i + 1 < LEN(cmds)) & ((cmds[i+1] = "&") OR (cmds[i+1] = ".") OR (cmds[i+1] = "/")) THEN
- Expand(cmds, w, i, i);
- ELSE
- w.Char(cmds[i]);
- END;
- INC(i);
- END;
- END;
- IF (i < LEN(cmds)) & (cmds[i] = ";") THEN (* skip command delimiter *) INC(i); END;
- cmds[k] := 0X;
- length := buffer.GetLength();
- IF (length > 0) THEN
- par := buffer.GetString();
- NEW(arg, length + 1); arg.SetRaw(par^, 0, length + 1);
- ELSE
- arg := NIL;
- END;
- NEW(context, NIL, arg, NIL, NIL, caller);
- Commands.Activate(cmds, context, flags, res, msg);
- IF (res # Commands.Ok) THEN KernelLog.String("WMComponents.Call error, res = "); KernelLog.Int(res, 0); KernelLog.Ln; EXIT; END;
- END;
- END Call;
- PROCEDURE GetComponent*(CONST name : ARRAY OF CHAR) : Component;
- VAR component : Component; c : Repositories.Component; res : WORD;
- BEGIN
- component := NIL;
- Repositories.GetComponentByString(name, c, res);
- IF (res = Repositories.Ok) THEN
- IF (c # NIL) & (c IS Component) THEN
- component := c (Component);
- ELSE
- KernelLog.String("WMComponents.GetComponent: Could not generate component ");
- KernelLog.String(name); KernelLog.String(": Wrong type"); KernelLog.Ln;
- END;
- ELSE
- KernelLog.String("WMComponents.GetComponent: Could not generate component ");
- KernelLog.String(name); KernelLog.String(", res: "); KernelLog.Int(res, 0); KernelLog.Ln;
- END;
- RETURN component;
- END GetComponent;
- PROCEDURE GetVisualComponent*(CONST name : ARRAY OF CHAR) : VisualComponent;
- VAR component : VisualComponent; c : Repositories.Component; res : WORD;
- BEGIN
- component := NIL;
- Repositories.GetComponentByString(name, c, res);
- IF (res = Repositories.Ok) THEN
- IF (c # NIL) & (c IS VisualComponent) THEN
- component := c (VisualComponent);
- ELSE
- KernelLog.String("WMComponents.GetVisualComponent: Could not generate component ");
- KernelLog.String(name); KernelLog.String(": Wrong type"); KernelLog.Ln;
- END;
- ELSE
- KernelLog.String("WMComponents.GetVisualComponent: Could not generate component ");
- KernelLog.String(name); KernelLog.String(", res: "); KernelLog.Int(res, 0); KernelLog.Ln;
- END;
- RETURN component;
- END GetVisualComponent;
- PROCEDURE SetStyle*(style : XML.Element);
- BEGIN
- SetStyleInternal(style)
- END SetStyle;
- PROCEDURE SetStyleInternal(style : XML.Element);
- VAR msg : Messages.Message; m : WM.WindowManager;
- BEGIN
- currentStyle := style;
- IF propertyListList # NIL THEN propertyListList.UpdateStyle END;
- msg.msgType := Messages.MsgExt; msg.ext := componentStyleMsg;
- m := WM.GetDefaultManager();
- m.Broadcast(msg)
- END SetStyleInternal;
- PROCEDURE FindRelativePath(x : Component; CONST path : ARRAY OF CHAR; pos : LONGINT) : Component;
- VAR c : XML.Content;
- sn : ARRAY MaxComponentNameSize OF CHAR;
- i : LONGINT; id : Strings.String;
- BEGIN
- IF x = NIL THEN RETURN NIL
- ELSIF path[pos] = 0X THEN RETURN x
- ELSIF (pos = 0) & (path[0] = "/") THEN RETURN FindRelativePath(x.GetComponentRoot(), path, pos + 1)
- ELSIF (path[pos] = ".") & (path[pos + 1] = ".") THEN
- INC(pos, 2); IF path[pos]="/" THEN INC(pos) END;
- c := x.GetParent();
- IF (c # NIL) & (c IS Component) THEN
- RETURN FindRelativePath(c(Component), path, pos)
- ELSE
- RETURN NIL
- END
- ELSE
- i := 0; WHILE (i < MaxComponentNameSize - 1) & (path[pos] # 0X) & (path[pos] # "/") DO
- sn[i] := path[pos]; INC(i); INC(pos)
- END;
- IF (path[pos] = "/") THEN INC(pos) END;
- sn[i] := 0X;
- c := x.GetFirst();
- WHILE (c # NIL) DO
- IF (c IS Component) THEN
- id := c(Component).id.Get();
- IF (id # NIL) & (id^ = sn) THEN
- RETURN FindRelativePath(c(Component), path, pos);
- END;
- END;
- c := x.GetNext(c);
- END;
- RETURN NIL
- END
- END FindRelativePath;
- (* Report errors while parsing *)
- PROCEDURE Error(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
- BEGIN
- KernelLog.String("Parse error at pos "); KernelLog.Int(pos, 5); KernelLog.String(" in line "); KernelLog.Int(line, 5);
- KernelLog.String(" row "); KernelLog.Int(row, 5); KernelLog.String(" - "); KernelLog.String(msg); KernelLog.Ln;
- hasErrors := TRUE
- END Error;
- (** Load an XML file. Return NIL if errors occured *)
- PROCEDURE Load*(CONST filename : ARRAY OF CHAR) : XML.Content;
- VAR scanner : XMLScanner.Scanner;
- parser : XMLParser.Parser;
- doc : XML.Document;
- in : Streams.Reader;
- BEGIN {EXCLUSIVE}
- hasErrors := FALSE;
- in := Codecs.OpenInputStream(filename);
- IF in # NIL THEN
- NEW(scanner, in); scanner.reportError := Error;
- NEW(parser, scanner); parser.reportError := Error;
- parser.elemReg := Repositories.registry; doc := parser.Parse();
- IF hasErrors THEN RETURN NIL END;
- RETURN doc.GetRoot()
- END;
- RETURN NIL
- END Load;
- PROCEDURE FormWindowGen*(xml:XML.Content): WM.Window;
- VAR winx: XML.Element; formx: XML.Content; window: FormWindow; name, string:Strings.String; canvas:WMGraphics.BufferCanvas;
- canvasGenerator: WMGraphics.CanvasGenerator;
- moduleName, procedureName : Modules.Name;
- msg : ARRAY 128 OF CHAR;
- res: WORD;
- l,t,r,b: LONGINT;
- BEGIN
- IF xml IS XML.Element THEN
- winx:=xml(XML.Element);
- string:=winx.GetName();
- IF string^="FormWindow" THEN
- string:=winx.GetAttributeValue("l"); Strings.StrToInt(string^,l);
- string:=winx.GetAttributeValue("t"); Strings.StrToInt(string^,t);
- string:=winx.GetAttributeValue("r"); Strings.StrToInt(string^,r);
- string:=winx.GetAttributeValue("b"); Strings.StrToInt(string^,b);
- NEW(window, r-l, b-t, TRUE);
- name:=winx.GetAttributeValue("name"); window.SetTitle(name);
- window.bounds.r:=r; window.bounds.l:=l; window.bounds.t:=t; window.bounds.b:=b;
- string:=winx.GetAttributeValue("flags"); Strings.StrToSet(string^,window.flags);
- string:=winx.GetAttributeValue("canvasGenerator"); (* allow to plug in alternative canvas versions,e.g. WMGraphicsGfx.Canvas *)
- IF (string#NIL) THEN
- Commands.Split(string^, moduleName, procedureName, res, msg);
- IF (res = Commands.Ok) THEN
- GETPROCEDURE(moduleName, procedureName, canvasGenerator);
- IF (canvasGenerator # NIL) THEN
- window.SetCanvasGenerator(canvasGenerator);
- END;
- END;
- END;
-
- formx:=winx.GetFirst(); (* this typically has name="Form" *)
- IF (formx#NIL)&(formx IS XML.Element) THEN
- window.LoadComponents(formx(XML.Element)); (* at the price of duplication of component tree construction ...*)
- window.form.Reset(NIL,NIL);
- ELSE window:=NIL;
- END;
- END;
- END;
- RETURN window
- END FormWindowGen;
- (* generic loading of any form window using the generator procedure supplied in the XML as 'loader' attribute *)
- PROCEDURE LoadFormWindow*(xml:XML.Content): WM.Window;
- VAR winx: XML.Element; window: WM.Window; formWindow:FormWindow;
- formx, c:Component;
- name, string, load:Strings.String;
- moduleName, procedureName : Modules.Name;
- msg : ARRAY 128 OF CHAR;
- res: WORD;
- gen:WindowGenerator;
- bounds:Rectangles.Rectangle;
- BEGIN
- IF xml IS XML.Element THEN
- winx:=xml(XML.Element);
- name:=winx.GetName();
- IF name^="FormWindow" THEN
- string:=winx.GetAttributeValue("loader");
- Commands.Split(string^, moduleName, procedureName, res, msg);
- IF (res = Commands.Ok) THEN
- GETPROCEDURE(moduleName, procedureName, gen);
- IF (gen # NIL) THEN
- window:=gen(xml);
- END;
- END;
- ELSE (*generate FormWindow from generic Visual Component*)
- c:=ComponentFromXML(xml(XML.Element));
- IF (c#NIL) & (c IS VisualComponent) THEN
- bounds:=c(VisualComponent).bounds.Get();
- NEW(formWindow, bounds.r-bounds.l, bounds.b-bounds.t, TRUE);
- formWindow.SetContent(c);
- (*formWindow.SetTitle(c.GetName());*)
- window:=formWindow;
- END;
- END;
- END;
- RETURN window
- END LoadFormWindow;
- (** Open form window and build its component tree *)
- PROCEDURE Open*(context : Commands.Context);
- VAR filename: Files.FileName; window: WM.Window; xml:XML.Content;
- BEGIN
- IF context.arg.GetString(filename) & (Strings.Length(filename)>0) THEN
- xml:=Load(filename); (* here, the xml tree is already constructed, however not in the right sequence for component contruction ( [Init()..loadProperties()..Initialize()] )*)
- window:=LoadFormWindow(xml);
- IF window#NIL THEN
- WM.AddWindow(window,window.bounds.l,window.bounds.t);
- END;
- END;
- END Open;
- PROCEDURE LoadStyleInternal(CONST filename : ARRAY OF CHAR);
- VAR f : Files.File;
- scanner : XMLScanner.Scanner;
- parser : XMLParser.Parser;
- reader : Files.Reader;
- doc : XML.Document;
- BEGIN
- hasErrors := FALSE;
- f := Files.Old(filename);
- IF f # NIL THEN
- NEW(reader, f, 0);
- NEW(scanner, reader); scanner.reportError := Error;
- NEW(parser, scanner); parser.reportError := Error;
- parser.elemReg := Repositories.registry; doc := parser.Parse();
- IF hasErrors THEN KernelLog.String("Stylefile not ok"); KernelLog.Ln
- ELSE
- SetStyleInternal(doc.GetRoot())
- END
- END
- END LoadStyleInternal;
- (** Load Component registry file. Return NIL if errors occured *)
- PROCEDURE LoadStyle*(context : Commands.Context);
- VAR filename : ARRAY 64 OF CHAR;
- BEGIN {EXCLUSIVE}
- IF context.arg.GetString(filename) THEN
- LoadStyleInternal(filename);
- ELSE
- context.result := Commands.CommandParseError;
- END;
- END LoadStyle;
- PROCEDURE NewString*(CONST x : ARRAY OF CHAR) : Strings.String;
- VAR t : Strings.String;
- BEGIN
- NEW(t, LEN(x)); COPY(x, t^); RETURN t
- END NewString;
- PROCEDURE InitStrings;
- BEGIN
- StrComponent := NewString("Component");
- StrVisualComponent := NewString("VisualComponent");
- StrForm := NewString("Form");
- StrFormWindow := NewString("FormWindow");
- GSonStartDrag := NewString("onStartDrag");
- GSonStartDragInfo := NewString("Event generated whenever a drag is started");
- StrModel := NewString("Model");
- StrModelInfo := NewString("Model used by component");
- END InitStrings;
- PROCEDURE InitPrototypes;
- BEGIN
- (* General component properties *)
- NEW(PrototypeID, NIL, NewString("ID"),
- NewString("identifier of the component"));
- NEW(PrototypeUID, NIL, NewString("UID"),
- NewString("unique identifier of the component"));
- NEW(PrototypeEnabled, NIL, NewString("Enabled"),
- NewString("defines if the component is enabled"));
- PrototypeEnabled.Set(TRUE);
- (* Visual component properties *)
- NEW(PrototypeBounds, NIL, NewString("Bounds"),
- NewString("the bounding box of the component in parent coordinates"));
- NEW(PrototypeBoundsRelative, NIL, NewString("RelBounds"),
- NewString("the bounding box of the component in relative parent coordinates"));
- NEW(PrototypeBearing, NIL, NewString("Bearing"),
- NewString("the bearing (empty space) aroung the component if auto aligned"));
- NEW(PrototypeFillColor, NIL, NewString("FillColor"),
- NewString("the main fill color of the component. i.e. background"));
- NEW(PrototypeAlignment, NIL, NewString("Alignment"),
- NewString("defines the alignment none, left, right, top, bottom or client"));
- PrototypeAlignment.Set(0);
- NEW(PrototypeVisible, NIL, NewString("Visible"),
- NewString("defines if the component is visible"));
- PrototypeVisible.Set(TRUE);
- NEW(PrototypeTakesFocus, NIL, NewString("TakesFocus"),
- NewString("defines if the component takes the keyboard focus"));
- NEW(PrototypeNeedsTab, NIL, NewString("NeedsTab"),
- NewString("defines if the component handles the tabulator key"));
- NEW(PrototypeFocusPrevious, NIL, NewString("FocusPrevious"), NewString("Previous focus component ID"));
- PrototypeFocusPrevious.Set(NIL);
- NEW(PrototypeFocusNext, NIL, NewString("FocusNext"), NewString("Next focus component ID"));
- PrototypeFocusNext.Set(NIL);
- NEW(PrototypeEditMode, NIL, NewString("EditMode"), NewString("defines if the contents of the component can be edited"));
- PrototypeEditMode.Set(FALSE);
- NEW(PrototypeFont, NIL, NewString("Font"), NewString("Font"));
- PrototypeFont.Set(WMGraphics.GetDefaultFont());
- NEW(PrototypeScaleFont, NIL, Strings.NewString("ScaleFont"), Strings.NewString("percentage that fonts scales with height (0=none)"));
- NEW(ModelPrototype, NIL, StrModel, StrModelInfo);
- END InitPrototypes;
- PROCEDURE ShowComponent(component : Component);
- VAR string : Strings.String;
- BEGIN
- IF (component # NIL) THEN
- string := component.GetName();
- IF (string # NIL) THEN KernelLog.String(string^); ELSE KernelLog.String("NoName"); END;
- KernelLog.String(" [");
- string := component.uid.Get();
- IF (string # NIL) THEN KernelLog.String(string^); ELSE KernelLog.String("NIL"); END;
- IF (component IS VisualComponent) THEN
- KernelLog.String(", "); KernelLog.Boolean(component(VisualComponent).takesFocus.Get());
- END;
- KernelLog.String("]");
- ELSE
- KernelLog.String("NIL?");
- END;
- END ShowComponent;
- PROCEDURE NewLine(w : Streams.Writer; level : LONGINT);
- BEGIN
- w.Ln; WHILE level > 0 DO w.Char(09X); DEC(level) END
- END NewLine;
- PROCEDURE InstallDefaultMacroHandler;
- BEGIN
- NEW(macroHandlers);
- macroHandlers.handler := DefaultMacroHandler;
- macroHandlers.namespace := DefaultNamespace;
- macroHandlers.next := NIL;
- END InstallDefaultMacroHandler;
- (*! ---- xml tool --- move to where appropriate *)
- PROCEDURE GetElementByName(parent : XML.Element; CONST name : ARRAY OF CHAR) : XML.Element;
- VAR elem : XML.Element; enum : XMLObjects.Enumerator; ptr : ANY; string : Strings.String;
- BEGIN
- IF parent # NIL THEN
- enum := parent.GetContents(); enum.Reset();
- WHILE enum.HasMoreElements() DO
- ptr := enum.GetNext();
- IF ptr IS XML.Element THEN
- elem := ptr (XML.Element);
- string := elem.GetName();
- IF (string # NIL) & (string^ = name) THEN
- RETURN elem;
- END;
- END;
- END;
- END;
- RETURN NIL;
- END GetElementByName;
- PROCEDURE NewComponent*(): XML.Element;
- VAR component: Component;
- BEGIN NEW(component); RETURN component;
- END NewComponent;
- PROCEDURE NewVisualComponent*(): XML.Element;
- VAR component: VisualComponent;
- BEGIN NEW(component); RETURN component;
- END NewVisualComponent;
- (* does not work like this its own because a form is statically bound to a window, but for completeness.. *)
- PROCEDURE NewForm*(): XML.Element;
- VAR component: Form;
- BEGIN NEW(component, NIL); RETURN component
- END NewForm;
- PROCEDURE Align*(context: Commands.Context);
- VAR width,height,bwidth,bheight: LONGINT; entry: ComponentListEntry; b,rect: Rectangles.Rectangle; string: ARRAY 32 OF CHAR; l,t: LONGINT; done: BOOLEAN;
- BEGIN
- entry := selection.first;
- rect.l := MAX(LONGINT); rect.r := MIN(LONGINT);
- rect.t := MAX(LONGINT); rect.b := MIN(LONGINT);
- width := 0; height := 0;
- WHILE entry # NIL DO
- b := entry.component.bounds.Get();
- bwidth := b.r-b.l; bheight := b.b-b.t;
- IF b.l < rect.l THEN rect.l := b.l END;
- IF b.r > rect.r THEN rect.r := b.r END;
- IF b.t < rect.t THEN rect.t := b.t END;
- IF b.b > rect.b THEN rect.b := b.b END;
- IF width < bwidth THEN width := bwidth END;
- IF height < bheight THEN height := bheight END;
- entry := entry.next
- END;
- done := FALSE;
- WHILE ~done & context.arg.GetString(string) DO
- l := rect.l; t := rect.t;
- entry := selection.first;
- WHILE ~done & (entry # NIL) DO
- b := entry.component.bounds.Get(); bwidth := b.r-b.l; bheight := b.b-b.t;
- entry.component.AdaptRelativeBounds(b,entry.component.GetParent());
- IF string = "left" THEN b.l := rect.l; b.r := rect.l + bwidth;
- ELSIF string = "right" THEN b.r := rect.r; b.l := rect.r-bwidth
- ELSIF string = "top" THEN b.t := rect.t; b.b := rect.t + bheight;
- ELSIF string = "bottom" THEN b.b := rect.b; b.t := rect.b-bheight
- ELSIF string = "width" THEN b.r := b.l + width;
- ELSIF string = "height" THEN b.b := b.t + height;
- ELSIF string = "size" THEN b.r := b.l + width; b.b := b.t + height;
- ELSIF string = "hcenter" THEN b.l := (rect.l+rect.r) DIV 2 - bwidth DIV 2; b.r := b.l + bwidth;
- ELSIF string = "vcenter" THEN b.t := (rect.t + rect.b) DIV 2 - bheight DIV 2; b.b := b.t + bheight;
- ELSIF string = "horizontal" THEN b.l := l; b.r := b.l + bwidth; l := b.r+1
- ELSIF string = "vertical" THEN b.t := t; b.b := b.t + bheight; t := b.b + 1;
- ELSIF string = "none" THEN entry.component.alignment.Set(AlignNone)
- ELSIF string = "relative" THEN entry.component.alignment.Set(AlignRelative)
- ELSE done := TRUE
- END;
- entry.component.AdaptRelativeBounds(b,entry.component.GetParent());
- entry.component.bounds.Set(b);
- entry := entry.next
- END;
- END;
- END Align;
- PROCEDURE SetProperty*(context: Commands.Context);
- VAR name, value: ARRAY 256 OF CHAR; entry: ComponentListEntry;
- BEGIN
- IF context.arg.GetString(name) & context.arg.GetString(value) THEN
- entry := selection.first;
- WHILE entry # NIL DO
- IF entry.component.properties.SetPropertyValue(name, value) THEN END;
- entry := entry.next;
- END;
- END;
- END SetProperty;
- PROCEDURE RemoveSelection*;
- VAR entry: ComponentListEntry; parent: XML.Element;
- BEGIN
- entry := selection.first;
- WHILE entry # NIL DO
- parent := entry.component.GetParent();
- IF parent # NIL THEN parent(VisualComponent).RemoveContent(entry.component); parent(VisualComponent).Invalidate END;
- entry := entry.next
- END;
- END RemoveSelection;
- PROCEDURE ComponentFromXML*(xml: XML.Element): Component;
- VAR generator: PROCEDURE(): XML.Element;
- VAR
- l,name: Strings.String;
- moduleName, procedureName: Modules.Name;
- res: WORD; msg: ARRAY 32 OF CHAR;
- component: Component;
- element: XML.Element;
- BEGIN
- component := NIL;
- IF xml # NIL THEN
- name := xml.GetName();
- l := xml.GetAttributeValue("generator");
- IF l # NIL THEN
- Commands.Split(l^, moduleName, procedureName, res, msg);
- IF (res = Commands.Ok) THEN
- GETPROCEDURE(moduleName, procedureName, generator);
- IF (generator # NIL) THEN
- element := generator();
- IF (element # NIL) & (element IS Component) THEN
- component := element(Component);
- component.SetName(name^);
- component.FromXML(xml);
- END;
- ELSE KernelLog.String("WMComponents error: invalid generator "); KernelLog.String(l^); KernelLog.Ln;
- END;
- ELSE KernelLog.String("WMComponents error: could not generate component "); KernelLog.String(l^); KernelLog.Ln;
- END;
- END;
- END;
- RETURN component
- END ComponentFromXML;
- PROCEDURE Clone*(x: Component): Repositories.Component;
- BEGIN
- RETURN ComponentFromXML(x)
- END Clone;
- BEGIN
- timestamp := 0;
- NEW(componentStyleMsg);
- NEW(propertyListList);
- InitStrings;
- InitPrototypes;
- NEW(invalidateRectMsg);
- InstallDefaultMacroHandler;
- NEW(selection);
- END WMComponents.
- WMComponents.Open FigureExample.Cwd ~
- WMComponents.Open DictEntry.wm ~
- The message sequencer contains a reader writer lock that can be used to block the hierarchy.
- Each message-call from the sequencer posesses the writer lock.
- WMComponents.LoadStyle ComponentStyle.XML ~
- If a focusComponent is set in an non-focus container-component, the focus can not escape the "isolated" component group
|