WMTextView.Mod 144 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410
  1. MODULE WMTextView; (** AUTHOR "TF"; PURPOSE "Generic unicode text viewer"; *)
  2. IMPORT
  3. Kernel, Modules, Inputs, KernelLog, XML, Texts, TextUtilities, SyntaxHighlighter,
  4. WMGraphics, WMGraphicUtilities, WMMessages, WMComponents,
  5. WMStandardComponents, Strings, WMDropTarget, Raster,
  6. WMRectangles, WMWindowManager, WMProperties,
  7. Commands, FileHandlers, Streams, WMPopups, FP1616,
  8. WMPieMenu, WMEvents, UnicodeBidirectionality, PositionDebugging, ContextualDependency, D := Debugging;
  9. CONST
  10. TraceRenderOptimize = 0;
  11. TraceLayout = 1;
  12. TraceBaseLine = 2;
  13. TraceInvalidate = 3;
  14. TraceCopy = 4;
  15. TraceCommands = 5;
  16. Trace = {};
  17. (* When pressing the middle mouse button and holding down ALT, execute this command with
  18. the actual command and its parameters as parameter *)
  19. AltMMCommand = "WMUtilities.Call";
  20. CallURLPointer = 0; (* mousebutton which will invoke the URL *)
  21. (** Text wrapping modes (default = WrapWord) *)
  22. NoWrap* = 0; Wrap* = 1; WrapWord* = 2;
  23. AlignLeft = 0; AlignCenter = 1; AlignRight = 2;
  24. DragDist = 5;
  25. MaxCallParameterBuf = 1 * 1024 * 1024;
  26. MaxCommandLength = 256; (* without parameters *)
  27. (* If TRUE, a mouse right click will open a pie menu *)
  28. UsePieMenu = TRUE;
  29. InterclickNone = 0;
  30. Interclick01 = 1; (* mouse button 0 & 1 *)
  31. Interclick02 = 2; (* mouse button 0 & 2 *)
  32. InterclickCancelled = 99;
  33. SelectionColor = 0000FF60H;
  34. SelectionColorInterclick01 = LONGINT(0FFFF0060H);
  35. SelectionColorInterclick02 = LONGINT(0FF000060H);
  36. TYPE
  37. Char32 = Texts.Char32;
  38. ClickInfo = OBJECT
  39. VAR
  40. cmd, cmdPar : Strings.String;
  41. END ClickInfo;
  42. TabStops* = OBJECT
  43. VAR tabDist : LONGINT;
  44. (* return the next TabStop from the x position *)
  45. PROCEDURE GetNextTabStop*(x : LONGINT) : LONGINT;
  46. BEGIN
  47. RETURN ((x DIV tabDist) + 1) * tabDist
  48. END GetNextTabStop;
  49. END TabStops;
  50. TabPositions* = POINTER TO ARRAY OF LONGINT;
  51. CustomTabStops* = OBJECT (TabStops)
  52. VAR
  53. positions : TabPositions;
  54. PROCEDURE GetNextTabStop*(x : LONGINT) : LONGINT;
  55. VAR
  56. idx : LONGINT;
  57. BEGIN
  58. idx := 0;
  59. ASSERT(positions # NIL);
  60. IF x >= positions[LEN(positions) - 1] THEN RETURN GetNextTabStop^(x) END; (* return default tab stop *)
  61. WHILE x >= positions[idx] DO INC(idx) END;
  62. RETURN positions[idx]
  63. END GetNextTabStop;
  64. PROCEDURE &New *(tp : TabPositions);
  65. VAR
  66. idx : LONGINT;
  67. BEGIN
  68. idx := 0; tabDist := 150;
  69. WHILE idx < LEN(tp)-1 DO ASSERT(tp[idx] <= tp[idx+1]); INC(idx) END;
  70. positions := tp
  71. END New;
  72. END CustomTabStops;
  73. LineInfo = RECORD
  74. leftIndent, rightIndent, firstIndent, spaceBefore, spaceAfter : LONGINT;
  75. firstInParagraph, lastInParagraph : BOOLEAN;
  76. height, width, ascent : LONGINT;
  77. pos : LONGINT; (* the position in the text, where this line starts *)
  78. align : LONGINT;
  79. tabStops : TabStops;
  80. END;
  81. LineInfoArray = POINTER TO ARRAY OF LineInfo;
  82. TYPE
  83. Layout = RECORD
  84. nofLines : LONGINT;
  85. lines : LineInfoArray;
  86. text : Texts.Text;
  87. paperWidth : LONGINT;
  88. textWidth : LONGINT; (* maximal width of the text <= textWidth *)
  89. textHeight : LONGINT;
  90. layoutLineProc : PROCEDURE {DELEGATE} (VAR pos : LONGINT; VAR ch : Char32; VAR lineInfo : LineInfo; wrapWidth, stopPos, stopXPos : LONGINT);
  91. bidiFormatter : UnicodeBidirectionality.BidiFormatter;
  92. initialized : BOOLEAN;
  93. PROCEDURE &New*;
  94. BEGIN
  95. NEW(lines, 4);
  96. (* this helps saving some bidi computations *)
  97. initialized := FALSE;
  98. END New;
  99. (** Replace the text *)
  100. PROCEDURE SetText(text : Texts.Text);
  101. BEGIN
  102. ASSERT(text # NIL);
  103. SELF.text := text;
  104. END SetText;
  105. PROCEDURE GrowLines;
  106. VAR i : LONGINT; newLines : LineInfoArray;
  107. BEGIN
  108. NEW(newLines, LEN(lines) * 2);
  109. FOR i := 0 TO LEN(lines) - 1 DO newLines[i] := lines[i] END;
  110. lines := newLines
  111. END GrowLines;
  112. (** find the linenumber by the position *)
  113. PROCEDURE FindLineNrByPos(pos : LONGINT) : LONGINT;
  114. VAR a, b, m : LONGINT;
  115. BEGIN
  116. a := 0; b := nofLines - 1;
  117. WHILE (a < b) DO m := (a + b) DIV 2;
  118. IF lines[m].pos <= pos THEN a := m + 1
  119. ELSE b := m
  120. END
  121. END;
  122. (* last line hack *)
  123. IF lines[a].pos <= pos THEN INC(a) END;
  124. RETURN a - 1
  125. END FindLineNrByPos;
  126. PROCEDURE GetLineStartPos(lineNr : LONGINT) : LONGINT;
  127. BEGIN
  128. IF (lineNr >= 0) & (lineNr < nofLines) THEN RETURN lines[lineNr].pos ELSE RETURN 0 END
  129. END GetLineStartPos;
  130. (** return the length in characters of this line *)
  131. PROCEDURE GetLineLength(lineNr : LONGINT) : LONGINT;
  132. BEGIN
  133. IF (lineNr >= 0) & (lineNr < nofLines - 1) THEN RETURN lines[lineNr + 1].pos - lines[lineNr].pos
  134. ELSE
  135. IF (lineNr >= 0) & (lineNr < nofLines) THEN RETURN text.GetLength() - lines[lineNr].pos + 1
  136. ELSE RETURN 0
  137. END
  138. END
  139. END GetLineLength;
  140. PROCEDURE GetNofLines() : LONGINT;
  141. BEGIN
  142. RETURN nofLines
  143. END GetNofLines;
  144. PROCEDURE LayoutLine(VAR pos : LONGINT; VAR lineInfo : LineInfo);
  145. VAR
  146. dummyCh : Char32;
  147. BEGIN
  148. IF layoutLineProc # NIL THEN layoutLineProc(pos, dummyCh, lineInfo, paperWidth, -1, -1) END
  149. END LayoutLine;
  150. (* generate a new layout from scratch. if the text has not actually changed, no bidi-reformatting needs to be done *)
  151. PROCEDURE FullLayout(textChanged : BOOLEAN);
  152. VAR i, pos, oldpos : LONGINT;
  153. BEGIN
  154. ASSERT((text # NIL) & (lines#NIL));
  155. text.AcquireRead;
  156. textWidth := 0;
  157. IF TraceLayout IN Trace THEN KernelLog.String("FullLayout"); KernelLog.Ln END;
  158. (* create a new bidiformatter and reformat the whole text if necessary *)
  159. IF textChanged & initialized & text.isUTF THEN
  160. NEW(bidiFormatter,text);
  161. bidiFormatter.ReformatText;
  162. END;
  163. i := 0;
  164. pos := 0; nofLines := 0; textHeight := 0;
  165. WHILE pos < text.GetLength() DO
  166. oldpos := pos;
  167. LayoutLine(pos, lines[nofLines]); INC(textHeight, lines[nofLines].height);
  168. textWidth := MAX(textWidth, lines[nofLines].width);
  169. ASSERT(pos > oldpos);
  170. IF TraceLayout IN Trace THEN KernelLog.String("Line from : "); KernelLog.Int(lines[nofLines].pos, 5); KernelLog.Ln END;
  171. INC(nofLines); IF nofLines >= LEN(lines) THEN GrowLines END
  172. END;
  173. IF TraceLayout IN Trace THEN KernelLog.String("FullLayout found "); KernelLog.Int(nofLines, 4); KernelLog.String(" lines"); KernelLog.Ln END;
  174. text.ReleaseRead
  175. END FullLayout;
  176. (** Fix the layouting of the text starting at pos where delta characters have been inserted (delta negativ if deleted) *)
  177. PROCEDURE FixLayoutFrom(pos, delta : LONGINT; VAR first, last : LONGINT; VAR linesChanged : BOOLEAN);
  178. VAR l, dl, oldh : LONGINT;
  179. BEGIN
  180. ASSERT(text # NIL);
  181. text.AcquireRead;
  182. linesChanged := FALSE;
  183. l := FindLineNrByPos(pos);
  184. IF (l < 0) THEN FullLayout(TRUE); first := 0; last := nofLines; text.ReleaseRead; RETURN END;
  185. pos := lines[l].pos;
  186. oldh := lines[l].height;
  187. LayoutLine(pos, lines[l]);
  188. IF oldh # lines[l].height THEN linesChanged := TRUE END;
  189. first := l;
  190. INC(l); dl := 0;
  191. IF (delta < 0) THEN
  192. IF (l >= nofLines) OR (lines[l].pos + delta = pos) THEN
  193. last := l;
  194. WHILE (l < nofLines) DO lines[l].pos := lines[l].pos + delta; INC(l) END
  195. ELSE
  196. linesChanged := TRUE;
  197. WHILE (pos < text.GetLength()) DO
  198. DEC(textHeight, lines[l].height);
  199. LayoutLine(pos, lines[l]);
  200. textWidth := MAX(textWidth, lines[l].width);
  201. INC(textHeight, lines[nofLines].height);
  202. INC(dl);
  203. IF TraceLayout IN Trace THEN KernelLog.String("Line from : "); KernelLog.Int(lines[nofLines].pos, 5); KernelLog.Ln END;
  204. INC(l);
  205. IF l >= LEN(lines) THEN GrowLines END
  206. END;
  207. nofLines := l ;
  208. last := nofLines - 1
  209. END
  210. ELSE
  211. WHILE (pos < text.GetLength()) & (lines[l].pos + delta # pos) DO
  212. linesChanged := TRUE;
  213. DEC(textHeight, (lines[l].height));
  214. LayoutLine(pos, lines[l]);
  215. textWidth := MAX(textWidth, lines[l].width);
  216. INC(textHeight, (lines[nofLines].height));
  217. INC(dl);
  218. IF TraceLayout IN Trace THEN KernelLog.String("Line from : "); KernelLog.Int(lines[nofLines].pos, 5); KernelLog.Ln END;
  219. INC(l); IF l >= LEN(lines) THEN GrowLines END
  220. END;
  221. last := l;
  222. IF TraceLayout IN Trace THEN
  223. KernelLog.String("Delta Lines : "); KernelLog.Int(dl, 4); KernelLog.Ln;
  224. KernelLog.String("Lines to redraw : "); KernelLog.Int(first, 5); KernelLog.String(" to "); KernelLog.Int(last, 5); KernelLog.Ln
  225. END;
  226. (* fix up the positions *)
  227. IF l < nofLines THEN WHILE (l < nofLines) DO lines[l].pos := lines[l].pos + delta; INC(l) END
  228. ELSE nofLines := l
  229. END
  230. END;
  231. text.ReleaseRead
  232. END FixLayoutFrom;
  233. END (*Layout*);
  234. CONST HLOver* = 0; HLUnder* = 1; HLWave* = 2;
  235. TYPE
  236. Highlight* = OBJECT
  237. VAR
  238. kind : LONGINT;
  239. from*, to* : Texts.TextPosition;
  240. a*, b* : LONGINT; (* only valid after sort, while holding the lock *)
  241. active* : BOOLEAN; (* only valid after sort, while holding the lock *)
  242. oldFrom, oldTo : LONGINT;
  243. oldColor, color : WMGraphics.Color;
  244. text : Texts.UnicodeText;
  245. onChanged : WMMessages.CompCommand;
  246. PROCEDURE &New*;
  247. BEGIN
  248. color := SelectionColor;
  249. oldColor := color;
  250. END New;
  251. PROCEDURE SetKind*(kind : LONGINT);
  252. BEGIN
  253. IF SELF.kind # kind THEN
  254. SELF.kind := kind;
  255. onChanged(SELF, NIL)
  256. END
  257. END SetKind;
  258. PROCEDURE SetColor*(color : WMGraphics.Color);
  259. BEGIN
  260. oldColor := SELF.color;
  261. IF SELF.color # color THEN
  262. SELF.color := color;
  263. onChanged(SELF, NIL)
  264. END
  265. END SetColor;
  266. PROCEDURE SetFrom*(from : LONGINT);
  267. BEGIN
  268. IF text = NIL THEN RETURN END; (* if no text is set, the position within is undef *)
  269. text.AcquireRead;
  270. oldFrom := SELF.from.GetPosition();
  271. IF oldFrom # from THEN
  272. SELF.from.SetPosition(from);
  273. onChanged(SELF, NIL)
  274. END;
  275. text.ReleaseRead
  276. END SetFrom;
  277. PROCEDURE SetTo*(to : LONGINT);
  278. BEGIN
  279. IF text = NIL THEN RETURN END; (* if no text is set, the position within is undef *)
  280. text.AcquireRead;
  281. oldTo := SELF.to.GetPosition();
  282. IF oldTo # to THEN
  283. SELF.to.SetPosition(to);
  284. onChanged(SELF, NIL)
  285. END;
  286. text.ReleaseRead
  287. END SetTo;
  288. PROCEDURE SetFromTo*(from, to : LONGINT);
  289. BEGIN
  290. IF text = NIL THEN RETURN END; (* if no text is set, the position within is undef *)
  291. text.AcquireRead;
  292. oldTo := SELF.to.GetPosition();
  293. oldFrom := SELF.from.GetPosition();
  294. IF (oldTo # to) OR (oldFrom # from) THEN
  295. IF ((oldTo = oldFrom) & (to = from)) THEN
  296. SELF.to.SetPosition(to);
  297. SELF.from.SetPosition(from)
  298. ELSE
  299. SELF.to.SetPosition(to);
  300. SELF.from.SetPosition(from);
  301. onChanged(SELF, NIL)
  302. END
  303. END;
  304. text.ReleaseRead
  305. END SetFromTo;
  306. PROCEDURE Sort*;
  307. VAR t : LONGINT;
  308. BEGIN
  309. a := from.GetPosition();
  310. b := to.GetPosition();
  311. IF a > b THEN t := a; a := b; b := t END;
  312. active := a # b
  313. END Sort;
  314. PROCEDURE SetText(text : Texts.UnicodeText);
  315. BEGIN
  316. IF text # NIL THEN SELF.text := text; NEW(from, text); NEW(to, text) END
  317. END SetText;
  318. END Highlight;
  319. HighlightArray = POINTER TO ARRAY OF Highlight;
  320. TYPE
  321. PositionMarker* = OBJECT
  322. VAR
  323. pos : Texts.TextPosition;
  324. img : WMGraphics.Image;
  325. color : WMGraphics.Color;
  326. hotX, hotY : LONGINT;
  327. currentArea : WMRectangles.Rectangle;
  328. text : Texts.UnicodeText;
  329. onChanged : WMMessages.CompCommand;
  330. visible : BOOLEAN;
  331. PROCEDURE &Init*;
  332. BEGIN
  333. color := LONGINT(0FF0000CCH); visible := TRUE
  334. END Init;
  335. PROCEDURE Draw(canvas : WMGraphics.Canvas; x, y, ascent : LONGINT);
  336. BEGIN
  337. IF ~visible THEN RETURN END;
  338. IF img # NIL THEN canvas.DrawImage(x - hotX, y - hotY, img, WMGraphics.ModeSrcOverDst)
  339. ELSE
  340. currentArea := GetArea(x, y, ascent);
  341. canvas.Fill(currentArea, LONGINT(0FF0000CCH), WMGraphics.ModeSrcOverDst)
  342. END
  343. END Draw;
  344. PROCEDURE GetArea(x, y, ascent : LONGINT) : WMRectangles.Rectangle;
  345. BEGIN
  346. IF img # NIL THEN RETURN WMRectangles.MakeRect(x - hotX, y - hotY, x - hotX + img.width, y - hotY + img.height)
  347. ELSE RETURN WMRectangles.MakeRect(x , y - ascent, x + 2, y)
  348. END
  349. END GetArea;
  350. PROCEDURE Load*(CONST filename : ARRAY OF CHAR);
  351. BEGIN
  352. img := WMGraphics.LoadImage(filename, TRUE);
  353. IF img # NIL THEN hotX := img.width DIV 2; hotY := img.height DIV 2 END;
  354. onChanged(SELF, NIL)
  355. END Load;
  356. PROCEDURE SetVisible*(visible : BOOLEAN);
  357. BEGIN
  358. IF SELF.visible # visible THEN
  359. SELF.visible := visible;
  360. onChanged(SELF, NIL)
  361. END
  362. END SetVisible;
  363. PROCEDURE SetPosition*(pos : LONGINT);
  364. BEGIN
  365. IF text = NIL THEN RETURN END; (* if no text is set, the position within is undef *)
  366. text.AcquireRead;
  367. IF pos # SELF.pos.GetPosition() THEN
  368. SELF.pos.SetPosition(pos);
  369. onChanged(SELF, NIL)
  370. END;
  371. text.ReleaseRead
  372. END SetPosition;
  373. PROCEDURE GetPosition*() : LONGINT;
  374. BEGIN
  375. RETURN pos.GetPosition()
  376. END GetPosition;
  377. PROCEDURE SetColor*(color : WMGraphics.Color);
  378. BEGIN
  379. IF SELF.color # color THEN
  380. SELF.color := color;
  381. onChanged(SELF, NIL)
  382. END
  383. END SetColor;
  384. PROCEDURE SetText(text : Texts.UnicodeText);
  385. BEGIN
  386. IF text # NIL THEN
  387. SELF.text := text;
  388. NEW(pos, text);
  389. END
  390. END SetText;
  391. PROCEDURE SetNextInternalPosition*(next : LONGINT);
  392. BEGIN
  393. pos.nextInternalPos := next;
  394. END SetNextInternalPosition;
  395. PROCEDURE GetNextInternalPosition*() : LONGINT;
  396. BEGIN
  397. RETURN pos.nextInternalPos;
  398. END GetNextInternalPosition;
  399. END PositionMarker;
  400. PositionMarkerArray = POINTER TO ARRAY OF PositionMarker;
  401. TYPE
  402. Cursor = OBJECT(PositionMarker)
  403. VAR
  404. isVisible : BOOLEAN;
  405. PROCEDURE &Init*;
  406. BEGIN
  407. isVisible := TRUE;
  408. END Init;
  409. PROCEDURE SetCurrentVisibility(isVisible : BOOLEAN);
  410. BEGIN
  411. IF (SELF.isVisible # isVisible) THEN
  412. SELF.isVisible := isVisible;
  413. onChanged(SELF, NIL);
  414. END;
  415. END SetCurrentVisibility;
  416. PROCEDURE GetArea(x, y, ascent : LONGINT) : WMRectangles.Rectangle;
  417. BEGIN
  418. IF img # NIL THEN RETURN WMRectangles.MakeRect(x - hotX, y - hotY, x - hotX + img.width, y - hotY + img.height)
  419. ELSE RETURN WMRectangles.MakeRect(x , y - ascent - 2, x + 1, y + 2)
  420. END
  421. END GetArea;
  422. PROCEDURE Draw(canvas : WMGraphics.Canvas; x, y, ascent : LONGINT);
  423. BEGIN
  424. IF ~visible OR ~isVisible THEN RETURN END;
  425. IF img # NIL THEN canvas.DrawImage(x - hotX, y - hotY, img, WMGraphics.ModeSrcOverDst)
  426. ELSE
  427. currentArea := GetArea(x, y, ascent);
  428. canvas.Fill(currentArea, WMGraphics.Black, WMGraphics.ModeSrcOverDst)
  429. END
  430. END Draw;
  431. END Cursor;
  432. TYPE
  433. CursorBlinkerCallback = PROCEDURE {DELEGATE} (isVisible : BOOLEAN);
  434. (** Global thread that periodically toggles the visibility of the currently active cursor *)
  435. CursorBlinker* = OBJECT
  436. VAR
  437. cursor : ANY;
  438. callback : CursorBlinkerCallback;
  439. interval : LONGINT;
  440. isVisible : BOOLEAN;
  441. alive, dead : BOOLEAN;
  442. timer : Kernel.Timer;
  443. PROCEDURE &Init;
  444. BEGIN
  445. cursor := NIL; callback := NIL;
  446. interval := 500;
  447. isVisible := TRUE;
  448. alive := TRUE; dead := FALSE;
  449. NEW(timer);
  450. END Init;
  451. (** Set the currently active cursor and a callback that will be periodically called *)
  452. PROCEDURE Set*(cursor : ANY; callback : CursorBlinkerCallback);
  453. BEGIN {EXCLUSIVE}
  454. ASSERT((cursor # NIL) & (callback # NIL));
  455. IF (SELF.cursor # NIL) THEN callback(TRUE); END;
  456. SELF.cursor := cursor;
  457. SELF.callback := callback;
  458. isVisible := TRUE;
  459. timer.Wakeup;
  460. END Set;
  461. (** Set the cursor blinking interval in milliseconds. An interval of MAX(LONGINT) means don't blink *)
  462. PROCEDURE SetInterval*(ms : LONGINT);
  463. BEGIN {EXCLUSIVE}
  464. ASSERT(ms > 0);
  465. interval := ms;
  466. timer.Wakeup;
  467. IF (interval = MAX(LONGINT)) & (cursor # NIL) THEN
  468. isVisible := TRUE;
  469. callback(isVisible);
  470. END;
  471. END SetInterval;
  472. (** If 'cursor' is the currently active cursor, set the currently active cursor to NIL *)
  473. PROCEDURE Remove*(cursor : ANY);
  474. BEGIN {EXCLUSIVE}
  475. ASSERT(cursor # NIL);
  476. IF (SELF.cursor = cursor) THEN
  477. SELF.cursor := NIL;
  478. SELF.callback := NIL;
  479. END;
  480. END Remove;
  481. (** If 'cursor' is the currently active cursor, show it for one period *)
  482. PROCEDURE Show*(cursor : ANY);
  483. BEGIN {EXCLUSIVE}
  484. ASSERT(cursor # NIL);
  485. IF (SELF.cursor = cursor) THEN
  486. isVisible := TRUE;
  487. timer.Wakeup;
  488. END;
  489. END Show;
  490. PROCEDURE Finalize;
  491. BEGIN
  492. BEGIN {EXCLUSIVE} alive := FALSE; END;
  493. timer.Wakeup;
  494. BEGIN {EXCLUSIVE} AWAIT(dead); END;
  495. END Finalize;
  496. BEGIN {ACTIVE}
  497. WHILE alive DO
  498. BEGIN {EXCLUSIVE}
  499. AWAIT(~alive OR ((cursor # NIL) & (interval # MAX(LONGINT))));
  500. IF alive THEN
  501. callback(isVisible);
  502. isVisible := ~isVisible;
  503. END;
  504. END;
  505. timer.Sleep(interval);
  506. END;
  507. BEGIN {EXCLUSIVE} dead := TRUE; END;
  508. END CursorBlinker;
  509. TYPE
  510. TextDropTarget* = OBJECT(WMDropTarget.DropTarget);
  511. VAR text : Texts.Text;
  512. pos : Texts.TextPosition;
  513. PROCEDURE &New*(text : Texts.Text; pos : Texts.TextPosition);
  514. BEGIN
  515. SELF.text := text; SELF.pos := pos
  516. END New;
  517. PROCEDURE GetInterface*(type : LONGINT) : WMDropTarget.DropInterface;
  518. VAR di : WMDropTarget.DropText;
  519. BEGIN
  520. IF type = WMDropTarget.TypeText THEN
  521. NEW(di); di.text := text; di.pos := pos;
  522. RETURN di
  523. ELSE RETURN NIL
  524. END
  525. END GetInterface;
  526. END TextDropTarget;
  527. TYPE
  528. LinkWrapper* = POINTER TO RECORD
  529. link* : Texts.Link;
  530. END;
  531. TYPE
  532. TextView* = OBJECT(WMComponents.VisualComponent)
  533. VAR
  534. defaultTextColor-, defaultTextBgColor- : WMProperties.ColorProperty;
  535. defaultTextColorI, defaultTextBgColorI : WMGraphics.Color;
  536. isMultiLine- : WMProperties.BooleanProperty;
  537. isMultiLineI : BOOLEAN;
  538. (** Text wrapping mode: NoWrap, Wrap or WrapWord (default : WrapWord) *)
  539. wrapMode- : WMProperties.Int32Property;
  540. wrapModeI : LONGINT;
  541. firstLine- : WMProperties.Int32Property;
  542. firstLineI : LONGINT;
  543. leftShift- : WMProperties.Int32Property;
  544. leftShiftI : LONGINT; (* number of units, the view is shifted to left -> line scrolling *)
  545. showBorder- : WMProperties.BooleanProperty;
  546. showBorderI : BOOLEAN;
  547. borders- : WMProperties.RectangleProperty;
  548. bordersI, borderClip : WMRectangles.Rectangle;
  549. x0 : LONGINT; (* text starts at x = x0. Used to get column for line numbers in subclass CodeView *)
  550. alwaysShowCursor- : WMProperties.BooleanProperty;
  551. alwaysShowCursorI : BOOLEAN;
  552. showLabels- : WMProperties.BooleanProperty;
  553. (** Is set to TRUE, the characters will be replaced by passwordChar *)
  554. isPassword- : WMProperties.BooleanProperty;
  555. isPasswordI : BOOLEAN; (* cache of the property value to avoid per-character-locks *)
  556. passwordChar- : WMProperties.Int32Property; (* not cached *)
  557. (** Mouse wheel scrolling speed multiplier? (default: 3, 0: disable mouse wheel scrolling) *)
  558. mouseWheelScrollSpeed- : WMProperties.Int32Property;
  559. mouseWheelScrollSpeedI : LONGINT;
  560. (** Allow middle-click command execution? (default: TRUE) *)
  561. allowCommandExecution- : WMProperties.BooleanProperty;
  562. (** Allow text selection using the mouse? (default: TRUE) *)
  563. allowTextSelection- : WMProperties.BooleanProperty;
  564. (** Should a mouse right-click open the pie menu? (default : TRUE) *)
  565. allowPiemenu- : WMProperties.BooleanProperty;
  566. (** Syntax highlighting *)
  567. highlighting- : WMProperties.StringProperty;
  568. highlighter : SyntaxHighlighter.Highlighter;
  569. state : SyntaxHighlighter.State;
  570. fontCache : FontCache;
  571. (** vertical centering -- momentarily only working for a single line *)
  572. textAlignV-: WMProperties.Int32Property;
  573. showLineNumbers- : WMProperties.BooleanProperty;
  574. showLineNumbersI : BOOLEAN;
  575. lineNumberColor-, lineNumberBgColor- : WMProperties.ColorProperty;
  576. lineNumberColorI, lineNumberBgColorI : WMGraphics.Color;
  577. lineNumberFont, lineNumberFont10 : WMGraphics.Font;
  578. indicateTabs- : WMProperties.BooleanProperty;
  579. indicateTabsI : BOOLEAN;
  580. clBgCurrentLine- : WMProperties.ColorProperty;
  581. clBgCurrentLineI : WMGraphics.Color;
  582. selection- : Highlight;
  583. cursor- : Cursor;
  584. onLinkClicked- : WMEvents.EventSource;
  585. onCtrlClicked- : WMEvents.EventSource;
  586. (** Commands.Context.caller will be set to this object when executing a command *)
  587. commandCaller*: OBJECT;
  588. commandWriter*, errorWriter*: Streams.Writer;
  589. (** Called whenever the cursor position changes *)
  590. onCursorChanged* : PROCEDURE {DELEGATE};
  591. optimize* : BOOLEAN;
  592. piemenu : WMPieMenu.Menu;
  593. text : Texts.Text;
  594. layout : Layout;
  595. utilreader : Texts.TextReader; (* single process ! *)
  596. clipState : WMGraphics.CanvasState;
  597. defaultTabStops : TabStops;
  598. vScrollbar : WMStandardComponents.Scrollbar;
  599. hScrollbar : WMStandardComponents.Scrollbar;
  600. (* highlighting *)
  601. nofHighlights : LONGINT;
  602. highlights : HighlightArray;
  603. (* marked positions *)
  604. nofPositionMarkers : LONGINT;
  605. positionMarkers : PositionMarkerArray;
  606. lastCursorPos: LONGINT;
  607. selecting : BOOLEAN;
  608. doubleclickedWord : BOOLEAN;
  609. dragPossible : BOOLEAN;
  610. dragSelA, dragSelB : Texts.TextPosition;
  611. dragCopy : BOOLEAN;
  612. canStart, openFile : BOOLEAN; (* set for command selection mode *)
  613. commandMarker : Highlight;
  614. downX, downY : LONGINT;
  615. selectWords : BOOLEAN;
  616. wordSelOrdered : BOOLEAN;
  617. lineEnter : LONGINT;
  618. modifierFlags : SET;
  619. oldFlags : SET; (* old pointer flags *)
  620. interclick : LONGINT;
  621. lastTimeStamp : LONGINT;
  622. oldObject, focusObject : ANY;
  623. oldPos, focusPos : LONGINT;
  624. objHasFocus : BOOLEAN;
  625. PROCEDURE &Init*;
  626. BEGIN
  627. Init^;
  628. SetGenerator("WMTextView.GenTextView");
  629. SetNameAsString(StrTextView);
  630. (* properties *)
  631. NEW(defaultTextColor, PTVdefaultTextColor, NIL, NIL); properties.Add(defaultTextColor);
  632. NEW(defaultTextBgColor, PTVdefaultTextBgColor, NIL, NIL); properties.Add(defaultTextBgColor);
  633. NEW(isMultiLine, PTVIsMultiLine, NIL, NIL); properties.Add(isMultiLine);
  634. NEW(wrapMode, PTVWrapMode, NIL, NIL); properties.Add(wrapMode);
  635. NEW(firstLine, PTVfirstLine, NIL, NIL); properties.Add(firstLine);
  636. NEW(leftShift, PTVleftShift, NIL, NIL); properties.Add(leftShift);
  637. NEW(showBorder, PTVShowBorder, NIL, NIL); properties.Add(showBorder);
  638. NEW(borders, PTVborders, NIL, NIL); properties.Add(borders);
  639. NEW(alwaysShowCursor, PTValwaysShowCursor, NIL, NIL); properties.Add(alwaysShowCursor);
  640. NEW(showLabels, PTVShowLabels, NIL, NIL); properties.Add(showLabels);
  641. NEW(isPassword, PTVIsPassword, NIL, NIL); properties.Add(isPassword);
  642. NEW(passwordChar, PTVPasswordChar, NIL, NIL); properties.Add(passwordChar);
  643. NEW(mouseWheelScrollSpeed, PTVMouseWheelScrollSpeed, NIL, NIL); properties.Add(mouseWheelScrollSpeed);
  644. NEW(allowCommandExecution, PTVAllowCommandExecution, NIL, NIL); properties.Add(allowCommandExecution);
  645. NEW(allowTextSelection, PTVAllowTextSelection, NIL, NIL); properties.Add(allowTextSelection);
  646. NEW(allowPiemenu, PTVAllowPiemenu, NIL, NIL); properties.Add(allowPiemenu);
  647. NEW(highlighting, PTVHighlighting, NIL, NIL); properties.Add(highlighting);
  648. highlighter := NIL; state := NIL; fontCache := NIL;
  649. NEW(showLineNumbers, PTVShowLineNumbers, NIL, NIL); properties.Add(showLineNumbers);
  650. NEW(lineNumberColor, PTVLineNumberColor, NIL, NIL); properties.Add(lineNumberColor);
  651. NEW(lineNumberBgColor, PTVLineNumberBgColor, NIL, NIL); properties.Add(lineNumberBgColor);
  652. lineNumberFont := NIL; lineNumberFont10 := NIL;
  653. NEW(indicateTabs, PTVIndicateTabs, NIL, NIL); properties.Add(indicateTabs);
  654. NEW(clBgCurrentLine, PTVclBgCurrentLine, NIL, NIL); properties.Add(clBgCurrentLine);
  655. NEW(textAlignV, PVTtextAlignV, NIL, NIL); properties.Add(textAlignV);
  656. (* events *)
  657. NEW(onLinkClicked, SELF, PTVonLinkClick, PTVonLinkClickInfo, SELF.StringToCompCommand); events.Add(onLinkClicked);
  658. onLinkClicked.Add(LinkClicked);
  659. NEW(onCtrlClicked, SELF, PTVonCtrlLinkClick, PTVonCtrlLinkClickInfo, SELF.StringToCompCommand); events.Add(onCtrlClicked);
  660. (* selection and cursor *)
  661. (*! NEW(layout);*)
  662. layout.New();
  663. layout.layoutLineProc := LayoutLine;
  664. nofHighlights := 0;
  665. NEW(highlights, 4);
  666. nofPositionMarkers := 0;
  667. NEW(positionMarkers, 4); nofPositionMarkers := 0;
  668. selection := CreateHighlight();
  669. selection.kind := HLOver;
  670. selection.color := SelectionColor;
  671. cursor := CreateCursor();
  672. commandCaller := NIL;
  673. commandWriter := NIL;
  674. onCursorChanged := NIL;
  675. (* Initialization of internal fields *)
  676. optimize := FALSE;
  677. piemenu := NIL;
  678. text := NIL;
  679. utilreader := NIL;
  680. NEW(defaultTabStops); defaultTabStops.tabDist := 20;
  681. vScrollbar := NIL; hScrollbar := NIL;
  682. lastCursorPos := -1;
  683. selecting := FALSE;
  684. doubleclickedWord := FALSE;
  685. dragPossible := FALSE;
  686. dragSelA := NIL; dragSelB := NIL;
  687. canStart := FALSE; openFile := FALSE;
  688. downX := 0; downY := 0;
  689. selectWords := FALSE;
  690. wordSelOrdered := FALSE;
  691. lineEnter := 0;
  692. modifierFlags := {}; oldFlags := {};
  693. interclick := InterclickNone;
  694. lastTimeStamp := 0;
  695. oldObject := NIL; focusObject := NIL;
  696. oldPos := 0; focusPos := 0;
  697. objHasFocus := FALSE;
  698. takesFocus.Set(TRUE);
  699. needsTab.Set(TRUE);
  700. SetPointerInfo(manager.pointerText);
  701. END Init;
  702. PROCEDURE Initialize*;
  703. BEGIN
  704. ASSERT(IsCallFromSequencer());
  705. (*Initialize^; RecacheProperties;*)
  706. IF text#NIL THEN Resized END; (*implicit redundant invalidate in Resized *)(*! Resized is probably redundant*)
  707. (* from now on, bidi-formatting can be done *)
  708. layout.initialized := TRUE;
  709. Initialize^;
  710. cursor.SetVisible(FALSE);
  711. END Initialize;
  712. PROCEDURE Finalize*;
  713. BEGIN
  714. Finalize^;
  715. IF text # NIL THEN text.onTextChanged.Remove(TextChanged); END;
  716. cursorBlinker.Remove(cursor);
  717. END Finalize;
  718. PROCEDURE FocusReceived*;
  719. BEGIN
  720. FocusReceived^;
  721. cursor.SetVisible(TRUE);
  722. cursorBlinker.Set(cursor, cursor.SetCurrentVisibility);
  723. (* let the module know that this is the currently visible TextView *)
  724. currentTextView := SELF;
  725. END FocusReceived;
  726. PROCEDURE FocusLost*;
  727. BEGIN
  728. FocusLost^;
  729. modifierFlags := {};
  730. cursorBlinker.Remove(cursor);
  731. SetInterclick(InterclickNone);
  732. IF ~alwaysShowCursorI THEN cursor.SetVisible(FALSE); END;
  733. END FocusLost;
  734. (* Inserts a character directly into the text. This should be used by external tools that insert character
  735. without the usage of the keyboard, e.g. WMUnicodeMarkerTool) *)
  736. PROCEDURE InsertChar(char : Char32) : INTEGER;
  737. VAR oneCharString : ARRAY 2 OF Texts.Char32;
  738. BEGIN
  739. (* Only insert a character into a valid text, that is either utf-formatted or gets a simple ASCII-character
  740. as input. *)
  741. IF text # NIL THEN
  742. IF text.isUTF OR (char < 256) THEN
  743. oneCharString[0] := char;
  744. oneCharString[1] := 0H;
  745. text.AcquireWrite;
  746. text.InsertUCS32(GetInternalPos(cursor.GetPosition()),oneCharString);
  747. text.ReleaseWrite;
  748. RETURN 0;
  749. ELSE
  750. RETURN -1;
  751. END;
  752. ELSE
  753. RETURN -2;
  754. END;
  755. END InsertChar;
  756. PROCEDURE RecacheProperties*;
  757. VAR
  758. highlighter : SyntaxHighlighter.Highlighter;
  759. oldBorders : WMRectangles.Rectangle;
  760. string : Strings.String;
  761. BEGIN
  762. ASSERT(IsCallFromSequencer());
  763. RecacheProperties^;
  764. defaultTextColorI := defaultTextColor.Get();
  765. defaultTextBgColorI := defaultTextBgColor.Get();
  766. isMultiLineI := isMultiLine.Get();
  767. wrapModeI := wrapMode.Get();
  768. firstLineI := firstLine.Get();
  769. leftShiftI := leftShift.Get();
  770. showBorderI := showBorder.Get();
  771. oldBorders := bordersI;
  772. bordersI := borders.Get();
  773. alwaysShowCursorI := alwaysShowCursor.Get();
  774. mouseWheelScrollSpeedI := mouseWheelScrollSpeed.Get();
  775. isPasswordI := isPassword.Get();
  776. showLineNumbersI := showLineNumbers.Get();
  777. ShowLineNumbers(showLineNumbersI);
  778. lineNumberColorI := lineNumberColor.Get();
  779. lineNumberBgColorI := lineNumberBgColor.Get();
  780. indicateTabsI := indicateTabs.Get();
  781. clBgCurrentLineI := clBgCurrentLine.Get();
  782. string := highlighting.Get();
  783. IF (string # NIL) THEN
  784. highlighter := SyntaxHighlighter.GetHighlighter(string^);
  785. ELSE
  786. highlighter := NIL;
  787. END;
  788. IF text#NIL THEN
  789. SetSyntaxHighlighter(highlighter);
  790. UpdateScrollbars;
  791. IF ~WMRectangles.IsEqual(oldBorders, bordersI) THEN BordersChanged END;
  792. END;
  793. (*Invalidate;*)
  794. END RecacheProperties;
  795. PROCEDURE SetScrollbars*(hScrollbar, vScrollbar : WMStandardComponents.Scrollbar);
  796. BEGIN
  797. Acquire;
  798. IF hScrollbar # NIL THEN hScrollbar.onPositionChanged.Remove(ScrollbarsChanged) END;
  799. IF vScrollbar # NIL THEN vScrollbar.onPositionChanged.Remove(ScrollbarsChanged) END;
  800. SELF.hScrollbar := hScrollbar; SELF.vScrollbar := vScrollbar;
  801. IF hScrollbar # NIL THEN hScrollbar.onPositionChanged.Add(ScrollbarsChanged) END;
  802. IF vScrollbar # NIL THEN vScrollbar.onPositionChanged.Add(ScrollbarsChanged) END;
  803. UpdateScrollbars;
  804. Release
  805. END SetScrollbars;
  806. PROCEDURE ScrollbarsChanged(sender, data : ANY);
  807. BEGIN
  808. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.ScrollbarsChanged, sender, data)
  809. ELSE
  810. IF sender = vScrollbar THEN firstLine.Set(vScrollbar.pos.Get())
  811. ELSIF sender = hScrollbar THEN leftShift.Set(hScrollbar.pos.Get())
  812. END
  813. END
  814. END ScrollbarsChanged;
  815. PROCEDURE UpdateScrollbars;
  816. BEGIN
  817. IF vScrollbar # NIL THEN
  818. vScrollbar.max.Set(layout.GetNofLines());
  819. vScrollbar.pos.Set(firstLineI);
  820. END;
  821. IF hScrollbar # NIL THEN
  822. IF (wrapModeI # NoWrap) THEN
  823. hScrollbar.visible.Set(FALSE);
  824. ELSE
  825. hScrollbar.visible.Set(TRUE);
  826. (* hScrollbar.visible.Set(layout.textWidth > bounds.GetWidth()); *)
  827. hScrollbar.max.Set(layout.textWidth);
  828. hScrollbar.pageSize.Set(bounds.GetWidth());
  829. hScrollbar.pos.Set(leftShiftI);
  830. END;
  831. END;
  832. END UpdateScrollbars;
  833. PROCEDURE BordersChanged;
  834. VAR vScroll : LONGINT;
  835. BEGIN
  836. ASSERT(IsCallFromSequencer());
  837. IF (vScrollbar # NIL) & (vScrollbar.visible.Get()) THEN vScroll := vScrollbar.bounds.GetWidth() ELSE vScroll := 0 END;
  838. borderClip := WMRectangles.MakeRect(bordersI.l, bordersI.t, bounds.GetWidth() - bordersI.r, bounds.GetHeight() - bordersI.b);
  839. layout.paperWidth := bounds.GetWidth() - (bordersI.l + bordersI.r) - vScroll;
  840. layout.FullLayout(FALSE); CheckNumberOfLines;
  841. END BordersChanged;
  842. PROCEDURE WrapModeChanged;
  843. BEGIN
  844. ASSERT(IsCallFromSequencer());
  845. wrapModeI := wrapMode.Get();
  846. IF (wrapModeI # NoWrap) THEN
  847. leftShift.Set(0); leftShiftI := 0; (* no scrollbars -> don't shift *)
  848. END;
  849. optimize := TRUE;
  850. layout.FullLayout(optimize);
  851. optimize := FALSE;
  852. UpdateScrollbars;
  853. (*Invalidate;*)
  854. END WrapModeChanged;
  855. PROCEDURE PropertyChanged*(sender, property : ANY);
  856. VAR
  857. highlighter : SyntaxHighlighter.Highlighter;
  858. oldBorders : WMRectangles.Rectangle;
  859. string : Strings.String;
  860. BEGIN
  861. IF property = defaultTextColor THEN
  862. defaultTextColorI := defaultTextColor.Get(); Invalidate;
  863. ELSIF property = defaultTextBgColor THEN
  864. defaultTextBgColorI := defaultTextBgColor.Get(); Invalidate;
  865. ELSIF property = isMultiLine THEN
  866. isMultiLineI := isMultiLine.Get(); Invalidate;
  867. ELSIF property = wrapMode THEN
  868. wrapModeI := wrapMode.Get(); WrapModeChanged; Invalidate;
  869. ELSIF property = firstLine THEN
  870. firstLineI := firstLine.Get(); UpdateScrollbars; Invalidate;
  871. ELSIF property = leftShift THEN
  872. leftShiftI := leftShift.Get(); UpdateScrollbars; Invalidate;
  873. ELSIF property = showBorder THEN
  874. showBorderI := showBorder.Get(); Invalidate;
  875. ELSIF property = borders THEN
  876. oldBorders := bordersI; bordersI := borders.Get(); BordersChanged; Invalidate;
  877. ELSIF property = alwaysShowCursor THEN
  878. alwaysShowCursorI := alwaysShowCursor.Get();
  879. IF (alwaysShowCursorI = TRUE) THEN cursor.SetVisible(TRUE);
  880. ELSIF ~hasFocus THEN cursor.SetVisible(FALSE);
  881. END;
  882. Invalidate;
  883. ELSIF property = mouseWheelScrollSpeed THEN
  884. mouseWheelScrollSpeedI := mouseWheelScrollSpeed.Get();
  885. ELSIF property = isPassword THEN
  886. isPasswordI := isPassword.Get(); Invalidate;
  887. ELSIF (property = highlighting) THEN
  888. string := highlighting.Get();
  889. IF (string # NIL) THEN
  890. highlighter := SyntaxHighlighter.GetHighlighter(string^);
  891. ELSE
  892. highlighter := NIL;
  893. END;
  894. SetSyntaxHighlighter(highlighter);
  895. ELSIF (property = showLineNumbers) THEN
  896. showLineNumbersI := showLineNumbers.Get();
  897. ShowLineNumbers(showLineNumbersI);
  898. Invalidate;
  899. ELSIF (property = indicateTabs) THEN
  900. indicateTabsI := indicateTabs.Get(); Invalidate;
  901. ELSIF (property = clBgCurrentLine) THEN
  902. clBgCurrentLineI := clBgCurrentLine.Get(); Invalidate;
  903. ELSIF (property = textAlignV) THEN
  904. Invalidate;
  905. ELSIF (property = lineNumberColor) OR (property = lineNumberBgColor) THEN
  906. lineNumberColorI := lineNumberColor.Get();
  907. lineNumberBgColorI := lineNumberBgColor.Get();
  908. Invalidate;
  909. ELSE
  910. PropertyChanged^(sender, property)
  911. END
  912. END PropertyChanged;
  913. PROCEDURE Resized*;
  914. VAR prevWidth: LONGINT;
  915. BEGIN
  916. ASSERT(IsCallFromSequencer());
  917. Resized^; (*? here, an implicit Invalidate() is triggered - this is probably redundant *)
  918. prevWidth := layout.paperWidth;
  919. layout.paperWidth := bounds.GetWidth() - (bordersI.l + bordersI.r);
  920. borderClip.r := bounds.GetWidth() - bordersI.r; borderClip.b := bounds.GetHeight() - bordersI.b;
  921. IF (prevWidth # layout.paperWidth) & (wrapMode.Get()#NoWrap) THEN
  922. layout.FullLayout(optimize);
  923. END;
  924. CheckNumberOfLines;
  925. END Resized;
  926. (** Replace the text *)
  927. PROCEDURE SetText*(text : Texts.Text);
  928. VAR i : LONGINT;
  929. BEGIN
  930. ASSERT(text # NIL);
  931. Acquire;
  932. IF SELF.text # NIL THEN SELF.text.onTextChanged.Remove(TextChanged) END; (* unregister the TextChanged listener from the old text *)
  933. SELF.text := text;
  934. text.onTextChanged.Add(TextChanged); (* register the TextChanged listener with the new text*)
  935. NEW(utilreader, text);
  936. (* update all highlights *)
  937. FOR i := 0 TO nofHighlights - 1 DO highlights[i].SetText(text) END;
  938. FOR i := 0 TO nofPositionMarkers - 1 DO
  939. positionMarkers[i].SetText(text);
  940. (* Let the cursor know about the local position-translation procedures *)
  941. IF text.isUTF THEN
  942. positionMarkers[i].pos.SetInternalPositionTranslator(GetInternalPos);
  943. positionMarkers[i].pos.SetDisplayPositionTranslator(GetDisplayPos);
  944. END;
  945. END;
  946. text.AcquireRead; (* also protect SELF.highlighter and SELF.state here *)
  947. IF (highlighter # NIL) THEN
  948. ASSERT(state # NIL);
  949. highlighter.RebuildRegions(utilreader, state);
  950. END;
  951. layout.SetText(text);
  952. layout.FullLayout(TRUE);
  953. CheckNumberOfLines;
  954. ASSERT(((highlighter = NIL) & (state = NIL)) OR ((highlighter # NIL) & (state # NIL)));
  955. text.ReleaseRead;
  956. (*Invalidate;(*! Redundant ?*)*)
  957. Release;
  958. END SetText;
  959. PROCEDURE SetSyntaxHighlighter*(highlighter : SyntaxHighlighter.Highlighter);
  960. BEGIN
  961. ASSERT(text # NIL);
  962. Acquire;
  963. IF (SELF.highlighter # highlighter) & ((SELF.highlighter # NIL) OR (highlighter # NIL)) THEN
  964. text.AcquireRead; (* also protect SELF.highlighter and SELF.state here *)
  965. SELF.highlighter := highlighter;
  966. IF (highlighter # NIL) THEN
  967. IF (state = NIL) THEN
  968. state := highlighter.GetState();
  969. ASSERT(state # NIL);
  970. END;
  971. highlighter.RebuildRegions(utilreader, state);
  972. ELSE
  973. state := NIL;
  974. END;
  975. layout.FullLayout(TRUE);
  976. CheckNumberOfLines;
  977. ASSERT(((highlighter = NIL) & (state = NIL)) OR ((highlighter # NIL) & (state # NIL)));
  978. text.ReleaseRead;
  979. Invalidate;
  980. END;
  981. Release;
  982. END SetSyntaxHighlighter;
  983. PROCEDURE ShowLineNumbers(enabled : BOOLEAN);
  984. VAR font : WMGraphics.Font;
  985. BEGIN
  986. font := GetFont( );
  987. IF enabled THEN
  988. x0 := 55;
  989. lineNumberFont := WMGraphics.GetFont(font.name, font.size, {});
  990. lineNumberFont10 := WMGraphics.GetFont(font.name, font.size, {WMGraphics.FontBold});
  991. ELSE
  992. x0 := 0;
  993. lineNumberFont := NIL;
  994. lineNumberFont10 := NIL;
  995. END;
  996. END ShowLineNumbers;
  997. PROCEDURE SetTabStops*(ts : TabStops);
  998. BEGIN
  999. Acquire;
  1000. defaultTabStops := ts;
  1001. layout.FullLayout(TRUE);
  1002. CheckNumberOfLines;
  1003. Release;
  1004. END SetTabStops;
  1005. (* BEGIN highlighting *)
  1006. PROCEDURE AddHighlight(highlight : Highlight);
  1007. VAR newHighlights : HighlightArray; i : LONGINT;
  1008. BEGIN
  1009. INC(nofHighlights);
  1010. IF nofHighlights > LEN(highlights) THEN
  1011. NEW(newHighlights, LEN(highlights) * 2);
  1012. FOR i := 0 TO LEN(highlights) - 1 DO newHighlights[i] := highlights[i] END;
  1013. highlights := newHighlights;
  1014. END;
  1015. highlights[nofHighlights - 1] := highlight;
  1016. HighlightChanged(highlight, NIL);
  1017. END AddHighlight;
  1018. PROCEDURE CreateHighlight*() : Highlight;
  1019. VAR h : Highlight;
  1020. BEGIN
  1021. Acquire;
  1022. NEW(h); h.SetText(text);
  1023. h.onChanged := HighlightChanged;
  1024. AddHighlight(h);
  1025. Release;
  1026. RETURN h
  1027. END CreateHighlight;
  1028. PROCEDURE RemoveHighlight*(x : Highlight);
  1029. VAR i : LONGINT;
  1030. BEGIN
  1031. Acquire;
  1032. i := 0; WHILE (i < nofHighlights) & (highlights[i] # x) DO INC(i) END;
  1033. IF i < nofHighlights THEN
  1034. WHILE (i < nofHighlights - 1) DO highlights[i] := highlights[i+1]; INC(i) END;
  1035. DEC(nofHighlights);
  1036. highlights[nofHighlights] := NIL
  1037. END;
  1038. HighlightChanged(NIL, NIL);
  1039. Release
  1040. END RemoveHighlight;
  1041. PROCEDURE InvalidateRange(a, b : LONGINT);
  1042. VAR
  1043. t, l0, l1 : LONGINT;
  1044. x0, y0, x1, y1, d : LONGINT;
  1045. ia, ib : LONGINT;
  1046. BEGIN
  1047. ia := GetDisplayPos(a);
  1048. ib := GetDisplayPos(b);
  1049. (* Sort the display positions, not the internal positions so as not to get weird results! *)
  1050. IF ia > ib THEN t := ia; ia := ib; ib := t END;
  1051. l0 := layout.FindLineNrByPos(ia);
  1052. l1 := layout.FindLineNrByPos(ib);
  1053. IF l0 = l1 THEN (* only one line... optimize *)
  1054. LineYPos(l0, y0, y1);
  1055. (* if text is UTF-formatted (and thus might content RTL-text) the whole line is invalidated.
  1056. this might - in some rare cases - be a bit slower than invalidating the minimum rectangle
  1057. but is guaranteed to always be correct. *)
  1058. IF text.isUTF OR (~(FindScreenPos(ia, x0, d) & FindScreenPos(ib, x1, d))) THEN
  1059. x0 := 0; x1 := bounds.GetWidth();
  1060. END;
  1061. InvalidateRect(WMRectangles.MakeRect(x0, y0, x1, y1));
  1062. ELSE
  1063. LineYPos(l0, y0, d); LineYPos(l1, d, y1);
  1064. InvalidateRect(WMRectangles.MakeRect(0, y0, bounds.GetWidth(), y1));
  1065. END;
  1066. IF TraceInvalidate IN Trace THEN KernelLog.String("ir ") END;
  1067. END InvalidateRange;
  1068. PROCEDURE HighlightChanged(sender, data : ANY);
  1069. VAR hl : Highlight; min, max : LONGINT;
  1070. BEGIN
  1071. IF ~initialized THEN RETURN END;
  1072. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.HighlightChanged, sender, data)
  1073. ELSE
  1074. text.AcquireRead;
  1075. IF (sender # NIL) & (sender IS Highlight) THEN
  1076. hl := sender(Highlight);
  1077. IF ((hl.oldFrom # hl.from.GetPosition()) & (hl.oldTo # hl.to.GetPosition())) OR (hl.oldColor # hl.color) THEN (* both changed *)
  1078. min := MIN(
  1079. MIN(hl.oldFrom, hl.from.GetPosition()),
  1080. MIN(hl.oldTo, hl.to.GetPosition()));
  1081. max := MAX(
  1082. MAX(hl.oldFrom, hl.from.GetPosition()),
  1083. MAX(hl.oldTo, hl.to.GetPosition()));
  1084. InvalidateRange(min, max)
  1085. ELSIF hl.oldTo # hl.to.GetPosition() THEN (* to changed *)
  1086. InvalidateRange(hl.oldTo, hl.to.GetPosition())
  1087. ELSIF hl.oldFrom # hl.from.GetPosition() THEN (* from changed *)
  1088. InvalidateRange(hl.oldFrom, hl.from.GetPosition())
  1089. ELSE (* position noch changed... probably color, style or visibility changed, invalidate range *)
  1090. InvalidateRange(hl.from.GetPosition(),hl.to.GetPosition())
  1091. END
  1092. ELSE
  1093. IF TraceInvalidate IN Trace THEN KernelLog.String("H") END;
  1094. Invalidate
  1095. END;
  1096. text.ReleaseRead
  1097. END
  1098. END HighlightChanged;
  1099. (* END highlighting *)
  1100. (* BEGIN PositionMarkers *)
  1101. PROCEDURE AddPositionMarker(pm : PositionMarker);
  1102. VAR newPositionMarkers : PositionMarkerArray; i : LONGINT;
  1103. BEGIN
  1104. INC(nofPositionMarkers);
  1105. IF nofPositionMarkers > LEN(positionMarkers) THEN
  1106. NEW(newPositionMarkers, LEN(positionMarkers) * 2);
  1107. FOR i := 0 TO LEN(positionMarkers) - 1 DO newPositionMarkers[i] := positionMarkers[i] END;
  1108. positionMarkers := newPositionMarkers
  1109. END;
  1110. positionMarkers[nofPositionMarkers - 1] := pm
  1111. END AddPositionMarker;
  1112. PROCEDURE CreatePositionMarker*() : PositionMarker;
  1113. VAR p : PositionMarker;
  1114. BEGIN
  1115. Acquire;
  1116. NEW(p); p.SetText(text);
  1117. p.onChanged := PositionMarkerChanged;
  1118. AddPositionMarker(p);
  1119. Release;
  1120. RETURN p
  1121. END CreatePositionMarker;
  1122. PROCEDURE CreateCursor*() : Cursor;
  1123. VAR p : Cursor;
  1124. BEGIN
  1125. Acquire;
  1126. NEW(p); p.SetText(text);
  1127. p.onChanged := PositionMarkerChanged;
  1128. AddPositionMarker(p);
  1129. Release;
  1130. RETURN p
  1131. END CreateCursor;
  1132. PROCEDURE RemovePositionMarker*(x : PositionMarker);
  1133. VAR i, xp, yp, l, ascent : LONGINT; newRect : WMRectangles.Rectangle;
  1134. BEGIN
  1135. Acquire;
  1136. i := 0; WHILE (i < nofPositionMarkers) & (positionMarkers[i] # x) DO INC(i) END;
  1137. IF i < nofPositionMarkers THEN
  1138. WHILE (i < nofPositionMarkers - 1) DO positionMarkers[i] := positionMarkers[i+1]; INC(i) END;
  1139. DEC(nofPositionMarkers);
  1140. positionMarkers[nofPositionMarkers] := NIL
  1141. END;
  1142. IF FindScreenPos(x.pos.GetPosition(), xp, yp) THEN
  1143. l := layout.FindLineNrByPos(x.pos.GetPosition());
  1144. IF (l < LEN(layout.lines^)) & (l >= 0) THEN
  1145. ascent := layout.lines[l].ascent;
  1146. (* IF ascent = 0 THEN ascent := layout.lines[l].height END;
  1147. IF ascent = 0 THEN ascent := 10 END; *)
  1148. newRect := x.GetArea(xp, yp, ascent);
  1149. InvalidateRect(newRect)
  1150. END
  1151. END;
  1152. Release
  1153. END RemovePositionMarker;
  1154. PROCEDURE PositionMarkerChanged(sender, data : ANY);
  1155. VAR newRect, combinedRect : WMRectangles.Rectangle; x, y, l, ascent : LONGINT;
  1156. BEGIN
  1157. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.PositionMarkerChanged, sender, data)
  1158. ELSE
  1159. data := sender;
  1160. IF (data # NIL) & (data IS PositionMarker) THEN
  1161. text.AcquireRead;
  1162. IF data = cursor THEN CheckCursor; END;
  1163. IF (data = cursor) & (clBgCurrentLineI # 0) THEN
  1164. Invalidate; (* HACK to handle clBgCurrentLine correcty. Should be replaced by a more efficient solution *)
  1165. ELSE
  1166. IF FindScreenPos(data(PositionMarker).pos.GetPosition(), x, y) THEN
  1167. l := layout.FindLineNrByPos(data(PositionMarker).pos.GetPosition());
  1168. IF (l < LEN(layout.lines^)) & (l >= 0) THEN
  1169. ascent := layout.lines[l].ascent;
  1170. (* IF ascent = 0 THEN ascent := layout.lines[l].height END;
  1171. IF ascent = 0 THEN ascent := 10 END;*)
  1172. newRect := data(PositionMarker).GetArea(x, y, ascent)
  1173. END
  1174. END;
  1175. combinedRect := data(PositionMarker).currentArea;
  1176. IF WMRectangles.RectEmpty(combinedRect) THEN combinedRect := newRect
  1177. ELSE WMRectangles.ExtendRect(combinedRect, newRect)
  1178. END;
  1179. IF ~WMRectangles.RectEmpty(combinedRect) THEN
  1180. IF (WMRectangles.Area(data(PositionMarker).currentArea) + WMRectangles.Area(newRect)) * 5 < WMRectangles.Area(combinedRect) THEN
  1181. InvalidateRect(data(PositionMarker).currentArea);
  1182. InvalidateRect(newRect)
  1183. ELSE
  1184. InvalidateRect(combinedRect)
  1185. END
  1186. END;
  1187. END;
  1188. text.ReleaseRead;
  1189. ELSE
  1190. Invalidate;
  1191. END;
  1192. END
  1193. END PositionMarkerChanged;
  1194. (* END PositionMarkers *)
  1195. PROCEDURE CheckNumberOfLines;
  1196. BEGIN
  1197. UpdateScrollbars;
  1198. firstLine.SetBounds(0, layout.GetNofLines() - 1)
  1199. END CheckNumberOfLines;
  1200. PROCEDURE CheckCursor;
  1201. VAR
  1202. cp, l, i : LONGINT;
  1203. ty : LONGINT;
  1204. lineStartPosition, lineLength: LONGINT;
  1205. li: LineInfo;
  1206. dummyCh : Char32;
  1207. x, dummyY, xend, paperWidth, newShift: LONGINT;
  1208. dummyBool : BOOLEAN;
  1209. BEGIN
  1210. ASSERT(IsCallFromSequencer() & text.HasReadLock());
  1211. (* Scroll up, down to make cursor visible *)
  1212. cp := cursor.GetPosition();
  1213. IF cp = lastCursorPos THEN
  1214. RETURN
  1215. ELSE
  1216. lastCursorPos := cp
  1217. END;
  1218. IF (cp < 0) THEN
  1219. cursor.SetPosition(GetDisplayPos(0));
  1220. ELSIF (cp > text.GetLength()) THEN
  1221. cursor.SetPosition(text.GetLength());
  1222. END;
  1223. l := layout.FindLineNrByPos(cursor.GetPosition());
  1224. IF (l < firstLineI) THEN
  1225. (* move the cursor down by 3 lines to get more context *)
  1226. l := MAX(0, l - 3);
  1227. firstLine.Set(l);
  1228. ELSIF (l < layout.GetNofLines()) THEN
  1229. ty := bordersI.t; i := firstLineI;
  1230. WHILE i < l DO
  1231. ty := ty + layout.lines[i].height;
  1232. CheckParagraphBegin(i, ty);
  1233. CheckParagraphEnd(i, ty);
  1234. INC(i);
  1235. END;
  1236. ty := ty + layout.lines[i].height;
  1237. IF ty >= bounds.GetHeight() - bordersI.b THEN
  1238. l := MAX(0, l - 3);
  1239. firstLine.Set(l)
  1240. END
  1241. END;
  1242. (* fof071127: Scroll left right to make cursor visible *)
  1243. lineStartPosition := layout.GetLineStartPos(l);
  1244. lineLength := layout.GetLineLength(l);
  1245. (* compute x position of the cursor on the line *)
  1246. IF optimize OR ~text.isUTF THEN
  1247. LayoutLine(lineStartPosition,dummyCh,li,layout.paperWidth,cp,-1);
  1248. x := li.width + GetLineLeftIndent(l);
  1249. ELSE
  1250. dummyBool := FindScreenPos(cp,x,dummyY);
  1251. IF x < 0 THEN
  1252. x := 0;
  1253. END;
  1254. INC(x,GetLineLeftIndent(l));
  1255. END;
  1256. (* compute x position of the end of the cursor's line *)
  1257. lineStartPosition := layout.GetLineStartPos(l);
  1258. lineLength := layout.GetLineLength(l);
  1259. LayoutLine(lineStartPosition, dummyCh, li, layout.paperWidth, lineStartPosition+lineLength-1, -1);
  1260. xend := li.width + GetLineLeftIndent(l);
  1261. newShift := leftShiftI;
  1262. (* align shift such that the cursor is visible *)
  1263. paperWidth := layout.paperWidth - bordersI.l - x0;
  1264. IF paperWidth > 0 THEN
  1265. IF x-leftShiftI > paperWidth THEN (* cursor right of displayed area *)
  1266. newShift := x-paperWidth; (* move content such that cursor is barely visible to the right *)
  1267. ELSIF x-leftShiftI < 0 THEN (* cursor is left of displayed area *)
  1268. newShift := x; (* move content such that cursor is barely visible to the left *)
  1269. END;
  1270. (* now check some possibly more optimal ways of displaying *)
  1271. IF xend-newShift < paperWidth THEN
  1272. (* line can be shown more fully to the left, we don't want to waste space to the right *)
  1273. newShift := xend-paperWidth;
  1274. IF newShift < 0 THEN newShift := 0 END;
  1275. END;
  1276. (* do the shift *)
  1277. IF newShift # leftShiftI THEN
  1278. leftShift.Set(newShift);
  1279. END;
  1280. END;
  1281. END CheckCursor;
  1282. PROCEDURE CheckParagraphBegin(lineNr : LONGINT; VAR height: LONGINT);
  1283. BEGIN
  1284. IF layout.lines[lineNr].firstInParagraph THEN height := height + layout.lines[lineNr].spaceBefore END
  1285. END CheckParagraphBegin;
  1286. PROCEDURE CheckParagraphEnd(lineNr : LONGINT; VAR height: LONGINT);
  1287. BEGIN
  1288. IF layout.lines[lineNr].lastInParagraph THEN height := height + layout.lines[lineNr].spaceAfter; END;
  1289. END CheckParagraphEnd;
  1290. PROCEDURE TextChanged(sender, data : ANY);
  1291. VAR
  1292. f, l, t, b, i, p, pa, pb, h: LONGINT; linesChanged, fullLayout : BOOLEAN;
  1293. info : Texts.TextChangeInfo;
  1294. BEGIN
  1295. IF ~initialized THEN RETURN END;
  1296. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.TextChanged, sender, data)
  1297. ELSE
  1298. IF (data # NIL) & (data IS Texts.TextChangeInfo) & (data(Texts.TextChangeInfo).op # Texts.OpMulti) THEN
  1299. text.AcquireRead;
  1300. info := data(Texts.TextChangeInfo);
  1301. IF text.GetTimestamp() = info.timestamp THEN
  1302. info := data(Texts.TextChangeInfo);
  1303. IF (highlighter # NIL) THEN
  1304. ASSERT(state # NIL);
  1305. fullLayout := FALSE;
  1306. IF ((info.op = Texts.OpInsert) OR (info.op = Texts.OpDelete)) THEN
  1307. highlighter.PatchRegions(info, utilreader, state, fullLayout);
  1308. ELSIF (info.op = Texts.OpAttributes) THEN
  1309. (* do nothing here *)
  1310. ELSE
  1311. highlighter.RebuildRegions(utilreader, state);
  1312. fullLayout := TRUE;
  1313. END;
  1314. IF fullLayout THEN
  1315. layout.FullLayout(TRUE);
  1316. lastTimeStamp := text.GetTimestamp();
  1317. CheckCursor;
  1318. CheckNumberOfLines;
  1319. text.ReleaseRead;
  1320. InvalidateRect(GetClientRect());
  1321. cursorBlinker.Show(cursor);
  1322. RETURN;
  1323. END;
  1324. END;
  1325. (* Upon an insertion or deletion in the text, parts of the text may need reformatting *)
  1326. IF info.op = Texts.OpInsert THEN
  1327. (* If necessary, reformat the affected text *)
  1328. IF layout.initialized & text.isUTF & (layout.bidiFormatter # NIL) THEN
  1329. layout.bidiFormatter.ReformatTextFrom(info.pos,info.len);
  1330. END;
  1331. layout.FixLayoutFrom(info.pos, info.len, f, l, linesChanged);
  1332. ELSE
  1333. (* If necessary, reformat the affected text *)
  1334. IF layout.initialized & text.isUTF & (layout.bidiFormatter # NIL) THEN
  1335. layout.bidiFormatter.ReformatTextFrom(info.pos,-info.len)
  1336. END;
  1337. layout.FixLayoutFrom(info.pos, -info.len, f, l, linesChanged);
  1338. END;
  1339. t := bordersI.t;
  1340. FOR i := firstLineI TO f - 1 DO
  1341. t := t + (layout.lines[i].height);
  1342. CheckParagraphBegin(i, t);
  1343. CheckParagraphEnd(i, t);
  1344. END;
  1345. h := bounds.GetHeight();
  1346. IF linesChanged THEN b := h ELSE
  1347. b := t; i := f;
  1348. WHILE (i <= l) & (b < h) DO
  1349. b := b + (layout.lines[i].height);
  1350. CheckParagraphBegin(i, b);
  1351. CheckParagraphEnd(i, b);
  1352. INC(i);
  1353. END
  1354. END;
  1355. pa := layout.lines[f].pos;
  1356. IF l + 1 < layout.nofLines THEN pb := layout.lines[l + 1].pos ELSE pb := text.GetLength() END;
  1357. FOR i := 0 TO nofPositionMarkers - 1 DO
  1358. p := positionMarkers[i].pos.GetPosition();
  1359. IF (p >= pa) & (p < pb) THEN
  1360. (* very conservative *)
  1361. h := positionMarkers[i].currentArea.b - positionMarkers[i].currentArea.t;
  1362. t := t - h;
  1363. b := b + h
  1364. END
  1365. END;
  1366. CheckCursor;
  1367. UpdateScrollbars;
  1368. InvalidateRect(WMRectangles.MakeRect(0, t, bounds.GetWidth(), b));
  1369. ELSIF (lastTimeStamp - info.timestamp) > 0 THEN
  1370. (* Don't update lastTimeStamp since we didn't update the layout *)
  1371. ELSE
  1372. IF (highlighter # NIL) THEN
  1373. ASSERT(state # NIL);
  1374. highlighter.RebuildRegions(utilreader, state);
  1375. END;
  1376. layout.FullLayout(TRUE);
  1377. lastTimeStamp := text.GetTimestamp();
  1378. CheckCursor;
  1379. InvalidateRect(GetClientRect())
  1380. END;
  1381. CheckNumberOfLines;
  1382. text.ReleaseRead
  1383. ELSE
  1384. text.AcquireRead;
  1385. IF (highlighter # NIL) THEN
  1386. ASSERT(state # NIL);
  1387. highlighter.RebuildRegions(utilreader, state);
  1388. END;
  1389. layout.FullLayout(TRUE);
  1390. lastTimeStamp := text.GetTimestamp();
  1391. CheckCursor;
  1392. CheckNumberOfLines;
  1393. text.ReleaseRead;
  1394. InvalidateRect(GetClientRect())
  1395. END;
  1396. cursorBlinker.Show(cursor);
  1397. END;
  1398. END TextChanged;
  1399. (* BEGIN view dependant layout functions *)
  1400. (** Return the left indent of a line - depending on alignment *)
  1401. (* returns left border, in case of errors *)
  1402. PROCEDURE GetLineLeftIndent(linenr : LONGINT): LONGINT;
  1403. VAR indent : LONGINT;
  1404. BEGIN
  1405. IF (linenr < 0) OR (linenr >= layout.nofLines) THEN RETURN 0 END;
  1406. IF layout.lines[linenr].firstInParagraph THEN indent := layout.lines[linenr].firstIndent ELSE indent := layout.lines[linenr].leftIndent END;
  1407. CASE layout.lines[linenr].align OF
  1408. AlignLeft : RETURN indent;
  1409. |AlignCenter : RETURN ((layout.paperWidth - (layout.lines[linenr].width)) DIV 2 - indent DIV 2);
  1410. |AlignRight : RETURN (layout.paperWidth - layout.lines[linenr].width - layout.lines[linenr].rightIndent);
  1411. ELSE
  1412. RETURN 0;
  1413. END;
  1414. END GetLineLeftIndent;
  1415. (** Find the line number that currently contains the y value (y relative to 0 in component)*)
  1416. PROCEDURE FindLineByY*(firstLine, y : LONGINT) : LONGINT;
  1417. VAR i : LONGINT; ypos : LONGINT;
  1418. BEGIN
  1419. ASSERT(text.HasReadLock());
  1420. ypos := bordersI.t; i := firstLine;
  1421. IF y < 0 THEN RETURN 0 END;
  1422. WHILE (i < layout.nofLines) & (ypos <= y) DO
  1423. ypos := ypos + layout.lines[i].height;
  1424. CheckParagraphBegin(i, ypos);
  1425. CheckParagraphEnd(i, ypos);
  1426. INC(i);
  1427. END;
  1428. RETURN MAX(i -1, 0)
  1429. END FindLineByY;
  1430. PROCEDURE ViewToTextPos*(x, y: LONGINT; VAR pos : LONGINT);
  1431. VAR
  1432. l : LONGINT;
  1433. dummy : LineInfo;
  1434. dummyCh : Char32;
  1435. indent : LONGINT;
  1436. BEGIN
  1437. text.AcquireRead;
  1438. pos := -1;
  1439. x := MAX(0, MIN(x, bounds.GetWidth()));
  1440. y := MAX(0, MIN(y, bounds.GetHeight()));
  1441. l := FindLineByY(firstLineI, MIN(MAX(y, bordersI.t), bounds.GetHeight() - bordersI.b));
  1442. x := x - bordersI.l - x0 + leftShiftI;
  1443. IF x < 0 THEN x := 0 END;
  1444. IF l >= 0 THEN
  1445. dummy := layout.lines[l]; (* this line belongs in here! *)
  1446. pos := layout.GetLineStartPos(l);
  1447. IF dummy.firstInParagraph THEN indent := dummy.firstIndent
  1448. ELSE indent := dummy.leftIndent END;
  1449. IF dummy.align = 0 THEN (* Left *)
  1450. LayoutLine(pos, dummyCh, dummy, layout.paperWidth, -1, x-indent)
  1451. ELSIF dummy.align = 1 THEN (* Center *)
  1452. LayoutLine(pos, dummyCh, dummy, layout.paperWidth, -1, x-((layout.paperWidth - dummy.width - indent) DIV 2))
  1453. ELSIF dummy.align = 2 THEN (* Right *)
  1454. LayoutLine(pos, dummyCh, dummy, layout.paperWidth, -1, x-(layout.paperWidth - dummy.width - dummy.rightIndent))
  1455. END;
  1456. (* Adjust the position if necessary *)
  1457. IF IsRightToLeft(pos) THEN
  1458. DEC(pos);
  1459. END;
  1460. END;
  1461. text.ReleaseRead
  1462. END ViewToTextPos;
  1463. (* Returns the height for the given width *)
  1464. PROCEDURE GetHeight*(width: LONGINT): LONGINT;
  1465. VAR oldWidth, height : LONGINT;
  1466. BEGIN
  1467. oldWidth := layout.paperWidth;
  1468. layout.paperWidth := width;
  1469. layout.FullLayout(FALSE);
  1470. height := layout.textHeight;
  1471. (* reset old state *)
  1472. layout.paperWidth := oldWidth;
  1473. layout.FullLayout(FALSE);
  1474. RETURN height
  1475. END GetHeight;
  1476. (* Returns the size of the largest word and line in pixels *)
  1477. PROCEDURE GetMinMaxWidth*(VAR word, line : LONGINT);
  1478. VAR dx, pos : LONGINT;
  1479. cws, cls : LONGINT;
  1480. f,cf : WMGraphics.Font;
  1481. ch : Char32;
  1482. tabstring : ARRAY 256 OF CHAR; tabs : CustomTabStops; tp : TabPositions;
  1483. sr : Streams.StringReader; tabCounter, tabPos : LONGINT; token : ARRAY 16 OF CHAR;
  1484. pStyle : Texts.ParagraphStyle;
  1485. cStyle : Texts.CharacterStyle;
  1486. PROCEDURE GetWidth(ch : Char32; VAR dx : LONGINT);
  1487. VAR gs : WMGraphics.GlyphSpacings; vc : WMComponents.VisualComponent;
  1488. BEGIN
  1489. IF ch = Texts.ObjectChar THEN
  1490. IF (utilreader.object # NIL) & (utilreader.object IS WMGraphics.Image) THEN
  1491. dx := utilreader.object(WMGraphics.Image).width
  1492. ELSIF (utilreader.object # NIL) & (utilreader.object IS WMComponents.VisualComponent) THEN
  1493. vc := utilreader.object(WMComponents.VisualComponent);
  1494. dx := vc.bounds.GetWidth();
  1495. END
  1496. ELSIF ch = Texts.TabChar THEN
  1497. IF tabs # NIL THEN dx := tabs.GetNextTabStop(cls) - cls
  1498. ELSE dx := defaultTabStops.GetNextTabStop(cls) - cls
  1499. END;
  1500. ELSE
  1501. IF isPasswordI THEN ch := passwordChar.Get() END;
  1502. IF f.HasChar(ch) THEN
  1503. f.GetGlyphSpacings(ch, gs);
  1504. ELSE
  1505. WMGraphics.FBGetGlyphSpacings(ch, gs);
  1506. END;
  1507. dx := gs.bearing.l + gs.width + gs.bearing.r
  1508. END
  1509. END GetWidth;
  1510. BEGIN
  1511. cf := GetFont(); (* set the default component font *)
  1512. f := cf;
  1513. pos := 0; cws := 0; cls := 0; word := 0; line := 0;
  1514. text.AcquireRead;
  1515. utilreader.SetDirection(1); utilreader.SetPosition(pos);
  1516. REPEAT
  1517. utilreader.ReadCh(ch);
  1518. (* Get the Paragraph Style *)
  1519. IF utilreader.pstyle # NIL THEN
  1520. pStyle := utilreader.pstyle;
  1521. (* parse tabstops *)
  1522. COPY(pStyle.tabStops, tabstring);
  1523. IF (tabstring # "default") & (tabstring # "0") & (tabstring # "") THEN
  1524. NEW(sr, LEN(tabstring)); sr.Set(tabstring); tabCounter := 0;
  1525. WHILE (sr.res = Streams.Ok) DO
  1526. sr.SkipWhitespace; sr.String(token);
  1527. INC(tabCounter);
  1528. END;
  1529. NEW(tp, tabCounter);
  1530. sr.Reset; tabCounter := 0;
  1531. WHILE (sr.res = Streams.Ok) DO
  1532. sr.SkipWhitespace; sr.String(token);
  1533. Strings.StrToInt(token, tabPos);
  1534. tp[tabCounter] := tabPos;
  1535. INC(tabCounter);
  1536. END;
  1537. NEW(tabs, tp)
  1538. END
  1539. END;
  1540. (* Get the Character Styles / Attributes *)
  1541. IF utilreader.cstyle # NIL THEN
  1542. cStyle := utilreader.cstyle;
  1543. IF (cStyle.fontcache #NIL) & (cStyle.fontcache IS WMGraphics.Font) THEN
  1544. f := cStyle.fontcache(WMGraphics.Font);
  1545. ELSE
  1546. f := WMGraphics.GetFont(cStyle.family, ENTIER(FP1616.FixpToFloat(cStyle.size)), cStyle.style);
  1547. utilreader.cstyle.fontcache := f
  1548. END;
  1549. ELSIF utilreader.pstyle # NIL THEN
  1550. IF pStyle.charStyle # NIL THEN
  1551. cStyle := pStyle.charStyle;
  1552. IF (cStyle.fontcache #NIL) &
  1553. (cStyle.fontcache IS WMGraphics.Font) THEN
  1554. f := cStyle.fontcache(WMGraphics.Font);
  1555. ELSE
  1556. f := WMGraphics.GetFont(cStyle.family, ENTIER(FP1616.FixpToFloat(cStyle.size)), cStyle.style);
  1557. utilreader.pstyle.charStyle.fontcache := f
  1558. END
  1559. END;
  1560. ELSIF utilreader.attributes # NIL THEN
  1561. IF utilreader.attributes.fontInfo # NIL THEN
  1562. IF (utilreader.attributes.fontInfo.fontcache # NIL)
  1563. & (utilreader.attributes.fontInfo.fontcache IS WMGraphics.Font) THEN
  1564. f := utilreader.attributes.fontInfo.fontcache(WMGraphics.Font);
  1565. ELSE
  1566. f := GetFontFromAttr(utilreader.attributes.fontInfo);
  1567. utilreader.attributes.fontInfo.fontcache := f
  1568. END
  1569. ELSE f := cf
  1570. END
  1571. ELSE f := cf;
  1572. END;
  1573. INC(pos);
  1574. GetWidth(ch, dx);
  1575. IF (ch = Texts.ObjectChar) THEN
  1576. word := MAX(word, dx);
  1577. cls := cls + dx;
  1578. cws := 0
  1579. ELSIF (ch = Texts.NewLineChar) THEN
  1580. line := MAX(line, cls);
  1581. cls := 0
  1582. ELSIF (ch = 32) THEN
  1583. word := MAX(word, cws);
  1584. cws := 0
  1585. ELSE
  1586. cws := cws + dx;
  1587. cls := cls + dx;
  1588. END;
  1589. UNTIL utilreader.eot;
  1590. line := MAX(line, cls);
  1591. word := MAX(word, cws);
  1592. text.ReleaseRead;
  1593. END GetMinMaxWidth;
  1594. (* END view dependant layout functions *)
  1595. PROCEDURE LineYPos(lineNr : LONGINT; VAR y0, y1 : LONGINT);
  1596. VAR i : LONGINT;
  1597. BEGIN
  1598. IF (lineNr >= firstLineI) & (lineNr < layout.GetNofLines()) THEN
  1599. y0 := bordersI.t; i := firstLineI;
  1600. WHILE i < lineNr DO
  1601. y0 := y0 + layout.lines[i].height;
  1602. CheckParagraphBegin(i, y0);
  1603. CheckParagraphEnd(i, y0);
  1604. INC(i);
  1605. END;
  1606. y1 := y0 + layout.lines[i].height;
  1607. CheckParagraphBegin(i, y1);
  1608. ELSE y0 := 0; y1 := 0
  1609. END
  1610. END LineYPos;
  1611. PROCEDURE FindScreenPos*(pos : LONGINT; VAR x, y : LONGINT) : BOOLEAN;
  1612. VAR
  1613. l, i, startPos, intPos: LONGINT;
  1614. ty : LONGINT;
  1615. li : LineInfo;
  1616. thisCh, lastCh : Char32;
  1617. lastLine : BOOLEAN;
  1618. f : WMGraphics.Font;
  1619. gs: WMGraphics.GlyphSpacings;
  1620. BEGIN
  1621. text.AcquireRead;
  1622. lastLine := FALSE;
  1623. IF (pos = text.GetLength()) THEN
  1624. utilreader.SetDirection(1); utilreader.SetPosition(text.GetLength() - 1);
  1625. utilreader.ReadCh(thisCh);
  1626. IF thisCh = Texts.NewLineChar THEN lastLine := TRUE END
  1627. END;
  1628. IF lastLine THEN
  1629. ty := bordersI.t; i := firstLineI;
  1630. WHILE i < layout.nofLines DO
  1631. ty := ty + layout.lines[i].height;
  1632. CheckParagraphBegin(i, ty);
  1633. CheckParagraphEnd(i, ty);
  1634. INC(i);
  1635. END;
  1636. IF i > 0 THEN
  1637. y := (ty + layout.lines[i - 1].ascent)
  1638. ELSE
  1639. f := GetFont();
  1640. y := (ty + f.GetAscent());
  1641. END;
  1642. x := bordersI.l + x0 - leftShiftI;
  1643. text.ReleaseRead;
  1644. RETURN TRUE
  1645. ELSIF (pos = 0) & (firstLineI = 0) THEN
  1646. ty := bordersI.t;
  1647. IF layout.GetNofLines() > 0 THEN
  1648. y := (ty + layout.lines[0].ascent);
  1649. ELSE
  1650. f := GetFont();
  1651. y := ty+f.GetAscent();
  1652. END;
  1653. CheckParagraphBegin(0, y);
  1654. x := bordersI.l + x0 - leftShiftI;
  1655. text.ReleaseRead;
  1656. RETURN TRUE
  1657. ELSE
  1658. l := layout.FindLineNrByPos(pos);
  1659. IF (l >= firstLineI) & (l < layout.GetNofLines()) THEN
  1660. ty := bordersI.t; i := firstLineI;
  1661. WHILE i < l DO
  1662. ty := ty + layout.lines[i].height;
  1663. CheckParagraphBegin(i, ty);
  1664. CheckParagraphEnd(i, ty);
  1665. INC(i);
  1666. END;
  1667. y := (ty + layout.lines[i].ascent);
  1668. CheckParagraphBegin(i, y);
  1669. startPos := layout.GetLineStartPos(i);
  1670. f := GetFont();
  1671. intPos := GetInternalPos(pos);
  1672. utilreader.SetPosition(intPos-1);
  1673. utilreader.ReadCh(lastCh);
  1674. utilreader.ReadCh(thisCh);
  1675. (* if this character is rtl and its predecessor is ltr, move the position to the right of the previous character *)
  1676. IF (intPos # 0) & (IsRightToLeft(intPos) & ~IsRightToLeft(intPos-1) & (intPos # startPos)) OR
  1677. ((~IsRightToLeft(intPos) OR (thisCh = 0AH)) & ~IsRightToLeft(intPos-1) & ODD(GetParagraphEmbeddingLevel(pos))) THEN
  1678. LayoutLine(startPos, lastCh, li, layout.paperWidth, GetDisplayPos(intPos-1), -1);
  1679. IF f.HasChar(lastCh) THEN
  1680. f.GetGlyphSpacings(lastCh, gs);
  1681. ELSE
  1682. WMGraphics.FBGetGlyphSpacings(lastCh, gs);
  1683. END;
  1684. x := li.width + GetLineLeftIndent(l) + bordersI.l + x0 - leftShiftI + gs.bearing.l + gs.width + gs.bearing.r;
  1685. ELSIF (intPos # 0) & ((thisCh = 0AH) OR (thisCh = 0H)) & IsRightToLeft(intPos-1) THEN
  1686. LayoutLine(startPos, thisCh, li, layout.paperWidth, GetDisplayPos(intPos-1), -1);
  1687. x := (li.width + GetLineLeftIndent(l) + bordersI.l + x0 - leftShiftI);
  1688. (* if this and its predecessor are rtl, move the position to the right of this character *)
  1689. ELSIF IsRightToLeft(intPos) THEN
  1690. LayoutLine(startPos, thisCh, li, layout.paperWidth, pos, -1);
  1691. IF f.HasChar(thisCh) THEN
  1692. f.GetGlyphSpacings(thisCh, gs);
  1693. ELSE
  1694. WMGraphics.FBGetGlyphSpacings(thisCh, gs);
  1695. END;
  1696. x := li.width + GetLineLeftIndent(l) + bordersI.l + x0 - leftShiftI + gs.bearing.l + gs.width + gs.bearing.r;
  1697. (* if this character is ltr and its predecessor is rtl move the position to the left of the predecessor *)
  1698. ELSIF (intPos # 0) & (~IsRightToLeft(intPos) OR (thisCh = 0AH)) & IsRightToLeft(intPos-1) THEN
  1699. LayoutLine(startPos, thisCh, li, layout.paperWidth, GetDisplayPos(intPos-1), -1);
  1700. x := (li.width + GetLineLeftIndent(l) + bordersI.l + x0 - leftShiftI);
  1701. (* if this and the previous character are ltr, leave the position at the left of this character *)
  1702. ELSE
  1703. LayoutLine(startPos, thisCh, li, layout.paperWidth, pos, -1);
  1704. x := (li.width + GetLineLeftIndent(l) + bordersI.l + x0 - leftShiftI);
  1705. END;
  1706. text.ReleaseRead;
  1707. RETURN TRUE
  1708. ELSE
  1709. text.ReleaseRead;
  1710. RETURN FALSE
  1711. END
  1712. END
  1713. END FindScreenPos;
  1714. (* Get the internal position for a given display position. *)
  1715. PROCEDURE GetInternalPos*(pos : LONGINT) : LONGINT;
  1716. VAR
  1717. lineNr, startPos, lineLength : LONGINT;
  1718. dummyTextReader : Texts.TextReader;
  1719. BEGIN
  1720. (* if the text is non-utf formatted, the internal position and the display position are the same *)
  1721. IF ~text.isUTF OR (layout.bidiFormatter = NIL) THEN
  1722. RETURN pos;
  1723. END;
  1724. text.AcquireRead;
  1725. lineNr := layout.FindLineNrByPos(pos);
  1726. startPos := layout.GetLineStartPos(lineNr);
  1727. lineLength := layout.GetLineLength(lineNr);
  1728. dummyTextReader := layout.bidiFormatter.ReorderLine(startPos,lineLength);
  1729. text.ReleaseRead;
  1730. RETURN layout.bidiFormatter.GetInternalPosition(pos,startPos);
  1731. END GetInternalPos;
  1732. (* Get the display position for a given display position. *)
  1733. PROCEDURE GetDisplayPos*(pos : LONGINT) : LONGINT;
  1734. VAR
  1735. lineNr, startPos, lineLength : LONGINT;
  1736. dummyTextReader : Texts.TextReader;
  1737. BEGIN
  1738. (* if the text is non-utf formatted, the internal position and the display position are the same *)
  1739. IF ~text.isUTF OR (layout.bidiFormatter = NIL) THEN
  1740. RETURN pos;
  1741. END;
  1742. lineNr := layout.FindLineNrByPos(pos);
  1743. startPos := layout.GetLineStartPos(lineNr);
  1744. lineLength := layout.GetLineLength(lineNr);
  1745. dummyTextReader := layout.bidiFormatter.ReorderLine(startPos,lineLength);
  1746. RETURN layout.bidiFormatter.GetDisplayPosition(pos,startPos);
  1747. END GetDisplayPos;
  1748. (* Checks if the current position is in an rtl context *)
  1749. PROCEDURE IsRightToLeft*(pos : LONGINT) : BOOLEAN;
  1750. VAR
  1751. lineNr, startPos, lineLength : LONGINT;
  1752. dummyTextReader : Texts.TextReader;
  1753. BEGIN
  1754. IF ~text.isUTF OR (layout.bidiFormatter = NIL) THEN
  1755. RETURN FALSE;
  1756. END;
  1757. lineNr := layout.FindLineNrByPos(pos);
  1758. startPos := layout.GetLineStartPos(lineNr);
  1759. lineLength := layout.GetLineLength(lineNr);
  1760. IF layout.initialized THEN
  1761. dummyTextReader := layout.bidiFormatter.ReorderLine(startPos,lineLength);
  1762. END;
  1763. RETURN ODD(layout.bidiFormatter.GetImplicitLevel(pos));
  1764. END IsRightToLeft;
  1765. (* Gets the paragraph embedding level of the current position's line *)
  1766. PROCEDURE GetParagraphEmbeddingLevel*(pos : LONGINT) : LONGINT;
  1767. BEGIN
  1768. IF ~text.isUTF OR (layout.bidiFormatter = NIL) THEN
  1769. RETURN 0;
  1770. END;
  1771. RETURN layout.bidiFormatter.GetParagraphEmbeddingLevel(pos);
  1772. END GetParagraphEmbeddingLevel;
  1773. PROCEDURE LayoutLine(VAR pos : LONGINT; VAR ch : Char32; VAR l : LineInfo; wrapwidth, stopPos, stopXPos : LONGINT);
  1774. VAR
  1775. i, wrapPos : LONGINT;
  1776. eol, first : BOOLEAN;
  1777. ascent, descent, leading, ld, a, d, dx, x : LONGINT;
  1778. align, firstIndent, leftIndent, rightIndent, spaceBefore, spaceAfter : LONGINT;
  1779. tabstring : ARRAY 256 OF CHAR; tabs : CustomTabStops; tp : TabPositions;
  1780. sr : Streams.StringReader; tabCounter, tabPos : LONGINT; token : ARRAY 16 OF CHAR;
  1781. pStyle : Texts.ParagraphStyle;
  1782. start, stop, isFirst : BOOLEAN;
  1783. bidiTextReader, localTextReader : Texts.TextReader;
  1784. regionStart, regionEnd,lastEnd : LONGINT;
  1785. readerPosition : LONGINT;
  1786. highlighterStyle, lastHighlighterStyle : SyntaxHighlighter.Style;
  1787. currentStyle, lastStyle : ANY;
  1788. cf: WMGraphics.Font;
  1789. style : RECORD
  1790. voff : LONGINT;
  1791. font : WMGraphics.Font;
  1792. END;
  1793. PROCEDURE GetExtents(ch : Char32; VAR dx, ascent, descent: LONGINT);
  1794. VAR gs : WMGraphics.GlyphSpacings; vc : WMComponents.VisualComponent; font : WMGraphics.Font;
  1795. BEGIN
  1796. IF ch = Texts.ObjectChar THEN
  1797. IF (localTextReader.object # NIL) & (localTextReader.object IS WMGraphics.Image) THEN
  1798. ascent := localTextReader.object(WMGraphics.Image).height - style.voff;
  1799. descent := style.voff;
  1800. dx := localTextReader.object(WMGraphics.Image).width
  1801. ELSIF (localTextReader.object # NIL) & (localTextReader.object IS WMComponents.VisualComponent) THEN
  1802. vc := localTextReader.object(WMComponents.VisualComponent);
  1803. dx := vc.bounds.GetWidth();
  1804. ascent := vc.bounds.GetHeight() - style.voff;
  1805. descent := style.voff;
  1806. (* Add a Sequencer to the object if none exists *)
  1807. IF (vc.sequencer = NIL) OR (vc.sequencer # sequencer) THEN
  1808. vc.SetSequencer(sequencer);
  1809. IF sequencer#NIL THEN vc.Reset(NIL, NIL); END;
  1810. END;
  1811. END
  1812. ELSIF ch = Texts.TabChar THEN
  1813. IF l.tabStops # NIL THEN dx := l.tabStops.GetNextTabStop(x) - x
  1814. ELSE dx := defaultTabStops.GetNextTabStop(x) - x
  1815. END;
  1816. ascent := style.font.GetAscent() - style.voff;
  1817. descent := style.font.GetDescent() + style.voff
  1818. ELSIF ch = Texts.LabelChar THEN
  1819. IF showLabels.Get() THEN
  1820. font := cf;
  1821. font.GetStringSize(localTextReader.object(Texts.LabelPiece).label^, dx, ascent);
  1822. INC(dx, 4);
  1823. ELSE
  1824. ascent := 0; descent := 0;
  1825. dx := 0;
  1826. END;
  1827. ELSE
  1828. IF isPasswordI THEN ch := passwordChar.Get() END;
  1829. IF style.font.HasChar(ch) THEN
  1830. style.font.GetGlyphSpacings(ch, gs);
  1831. ELSE
  1832. WMGraphics.FBGetGlyphSpacings(ch, gs);
  1833. END;
  1834. ascent := gs.ascent - style.voff;
  1835. descent := gs.descent + style.voff;
  1836. dx := gs.bearing.l + gs.width + gs.bearing.r
  1837. END
  1838. END GetExtents;
  1839. BEGIN
  1840. style.voff := 0;
  1841. cf := GetFont();
  1842. style.font := cf;
  1843. x := 0; l.pos := pos; l.height := style.font.GetHeight();
  1844. (* For layouting a reordered line, the reordered text is needed, to correctly measure
  1845. the extends of each character. *)
  1846. IF text.isUTF & (layout.bidiFormatter # NIL) THEN
  1847. isFirst := FALSE;
  1848. bidiTextReader := layout.bidiFormatter.ReadyTextReader(pos,isFirst);
  1849. END;
  1850. (* if a reformatted text is available initialize it correpsondingly *)
  1851. IF (bidiTextReader # NIL) THEN
  1852. (* if a reordered line is available, the contextual dependency rules are applied *)
  1853. bidiTextReader.CloneProperties(utilreader);
  1854. localTextReader := bidiTextReader;
  1855. localTextReader.text.AcquireRead;
  1856. localTextReader.SetPosition(0);
  1857. (* or initialize to default otherwise *)
  1858. ELSE
  1859. localTextReader := utilreader;
  1860. localTextReader.SetPosition(pos);
  1861. END;
  1862. localTextReader.SetDirection(1); first := TRUE;
  1863. (* the bidi formatter needs special treatment when finding out about the first line of the paragraph *)
  1864. start := FALSE; stop := FALSE;
  1865. IF (pos = 0) THEN start := TRUE;
  1866. ELSIF (bidiTextReader = NIL) THEN
  1867. localTextReader.SetPosition(pos-1);
  1868. localTextReader.ReadCh(ch);
  1869. IF (ch = Texts.NewLineChar) THEN start := TRUE;
  1870. ELSE start := FALSE;
  1871. END;
  1872. ELSE (* bidiTextReader # NIL *)
  1873. IF isFirst THEN
  1874. start := TRUE;
  1875. ELSE
  1876. start := FALSE;
  1877. END;
  1878. END;
  1879. i := 0; leading := 0; ascent := style.font.GetAscent(); descent := style.font.GetDescent();
  1880. align := AlignLeft; l.tabStops := NIL; COPY("", tabstring);
  1881. firstIndent := 0; leftIndent := 0; rightIndent := 0; spaceBefore := 0; spaceAfter := 0;
  1882. lastEnd := -1;
  1883. highlighterStyle := NIL; lastHighlighterStyle := NIL;
  1884. currentStyle := NIL; lastStyle := NIL;
  1885. eol := FALSE;
  1886. REPEAT
  1887. readerPosition := localTextReader.GetPosition();
  1888. localTextReader.ReadCh(ch);
  1889. IF (highlighter # NIL) THEN
  1890. ASSERT(state # NIL);
  1891. IF (lastEnd < readerPosition) THEN
  1892. highlighterStyle := highlighter.GetRegionStyle(readerPosition, state, regionStart, regionEnd);
  1893. IF (highlighterStyle # NIL) THEN
  1894. lastEnd := regionEnd;
  1895. ELSE
  1896. IF (ch > 32) THEN
  1897. highlighterStyle := highlighter.GetWordStyle(localTextReader, readerPosition, lastEnd);
  1898. END;
  1899. END;
  1900. localTextReader.SetPosition(readerPosition);
  1901. localTextReader.ReadCh(ch); (* restore text reader state *)
  1902. END;
  1903. IF (highlighterStyle = NIL) THEN
  1904. highlighterStyle := highlighter.GetDefaultStyle();
  1905. END;
  1906. END;
  1907. (* Get the Paragraph Style *)
  1908. IF localTextReader.pstyle # NIL THEN
  1909. pStyle := localTextReader.pstyle;
  1910. (* pStyle := Texts.GetParagraphStyleByName(pStyle.name); *)
  1911. spaceBefore := ENTIER(FP1616.FixpToFloat(pStyle.spaceBefore));
  1912. spaceAfter := ENTIER(FP1616.FixpToFloat(pStyle.spaceAfter));
  1913. firstIndent := ENTIER(FP1616.FixpToFloat(pStyle.firstIndent));
  1914. leftIndent := ENTIER(FP1616.FixpToFloat(pStyle.leftIndent));
  1915. rightIndent := ENTIER(FP1616.FixpToFloat(pStyle.rightIndent));
  1916. align := pStyle.alignment;
  1917. (* parse tabstops *)
  1918. COPY(pStyle.tabStops, tabstring);
  1919. IF (tabstring # "default") & (tabstring # "0") & (tabstring # "") THEN
  1920. NEW(sr, LEN(tabstring)); sr.Set(tabstring); tabCounter := 0;
  1921. WHILE (sr.res = Streams.Ok) DO
  1922. sr.SkipWhitespace; sr.String(token);
  1923. INC(tabCounter);
  1924. END;
  1925. NEW(tp, tabCounter);
  1926. sr.Reset; tabCounter := 0;
  1927. WHILE (sr.res = Streams.Ok) DO
  1928. sr.SkipWhitespace; sr.String(token);
  1929. Strings.StrToInt(token, tabPos);
  1930. tp[tabCounter] := tabPos;
  1931. INC(tabCounter);
  1932. END;
  1933. NEW(tabs, tp);
  1934. IF l.tabStops = NIL THEN l.tabStops := tabs END
  1935. END;
  1936. END;
  1937. IF (highlighterStyle = NIL) OR (highlighterStyle.defined * SyntaxHighlighter.DefineMask # SyntaxHighlighter.DefineMask) THEN
  1938. IF localTextReader.cstyle # NIL THEN
  1939. IF (currentStyle # localTextReader.cstyle) THEN
  1940. currentStyle := localTextReader.cstyle;
  1941. style.voff := ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.baselineShift));
  1942. ld := ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.leading));
  1943. IF (localTextReader.cstyle.fontcache #NIL) & (localTextReader.cstyle.fontcache IS WMGraphics.Font) THEN
  1944. style.font := localTextReader.cstyle.fontcache(WMGraphics.Font);
  1945. ELSE
  1946. style.font := WMGraphics.GetFont(localTextReader.cstyle.family, ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.size)), localTextReader.cstyle.style);
  1947. localTextReader.cstyle.fontcache := style.font;
  1948. END;
  1949. END;
  1950. ELSIF localTextReader.pstyle # NIL THEN
  1951. IF pStyle.charStyle # NIL THEN
  1952. style.voff := ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.baselineShift));
  1953. ld := ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.leading));
  1954. IF (localTextReader.cstyle.fontcache #NIL) & (localTextReader.cstyle.fontcache IS WMGraphics.Font) THEN
  1955. style.font := localTextReader.cstyle.fontcache(WMGraphics.Font);
  1956. ELSE
  1957. style.font := WMGraphics.GetFont(localTextReader.cstyle.family, ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.size)), localTextReader.cstyle.style);
  1958. localTextReader.pstyle.charStyle.fontcache := style.font
  1959. END
  1960. END;
  1961. ELSIF localTextReader.attributes # NIL THEN
  1962. IF (currentStyle # localTextReader.attributes) THEN
  1963. currentStyle := localTextReader.attributes;
  1964. style.voff := localTextReader.attributes.voff;
  1965. ld := 0;
  1966. IF localTextReader.attributes.fontInfo # NIL THEN
  1967. IF (localTextReader.attributes.fontInfo.fontcache # NIL) & (localTextReader.attributes.fontInfo.fontcache IS WMGraphics.Font) THEN
  1968. style.font := localTextReader.attributes.fontInfo.fontcache(WMGraphics.Font);
  1969. ELSE
  1970. style.font := GetFontFromAttr(localTextReader.attributes.fontInfo);
  1971. localTextReader.attributes.fontInfo.fontcache := style.font;
  1972. END
  1973. ELSE
  1974. style.font := cf
  1975. END
  1976. END;
  1977. ELSE
  1978. IF (currentStyle # DefaultStyle) THEN
  1979. currentStyle := DefaultStyle;
  1980. style.voff := 0;
  1981. style.font := cf;
  1982. ld := 0;
  1983. END;
  1984. END;
  1985. ASSERT(style.font # NIL);
  1986. END;
  1987. IF (highlighterStyle # NIL) THEN
  1988. IF (highlighterStyle # lastHighlighterStyle) OR (currentStyle # lastStyle) THEN
  1989. IF SyntaxHighlighter.Voff IN highlighterStyle.defined THEN style.voff := highlighterStyle.attributes.voff; END;
  1990. IF (SyntaxHighlighter.FontMask * highlighterStyle.defined # {}) THEN
  1991. CheckFont(highlighterStyle, style.font, fontCache);
  1992. style.font := highlighterStyle.attributes.fontInfo.fontcache (WMGraphics.Font);
  1993. END;
  1994. END;
  1995. currentStyle := NIL;
  1996. END;
  1997. lastStyle := currentStyle;
  1998. lastHighlighterStyle := highlighterStyle;
  1999. IF first THEN
  2000. IF (ch = Texts.NewLineChar) OR (ch = 0) THEN
  2001. ascent := style.font.GetAscent(); descent := style.font.GetDescent();
  2002. ELSE
  2003. descent := 0; ascent := 0;
  2004. END;
  2005. IF start THEN wrapwidth := wrapwidth - firstIndent - rightIndent;
  2006. ELSE wrapwidth := wrapwidth - leftIndent - rightIndent;
  2007. END;
  2008. first := FALSE;
  2009. END;
  2010. INC(pos);
  2011. IF (stopPos < 0) OR (pos <= stopPos) THEN
  2012. IF (ch # Texts.NewLineChar) & (ch # 0) THEN
  2013. GetExtents(ch, dx, a, d); ascent := MAX(ascent, a); descent := MAX(descent, d);
  2014. IF ld = 0 THEN ld := ascent + descent; ELSE ld := MAX(ld, ascent + descent); END; leading := MAX(leading, ld);
  2015. IF isMultiLineI & (wrapModeI # NoWrap) & (i > 0) & (x0 + x + dx > wrapwidth) THEN
  2016. eol := TRUE; DEC(pos); wrapPos := pos;
  2017. (* Go left for last space *)
  2018. IF wrapModeI = WrapWord THEN
  2019. pos := TextUtilities.FindPosWordLeft(localTextReader, pos);
  2020. IF pos <= l.pos THEN pos := wrapPos (* no word break found. wrap at latest possible pos *)
  2021. ELSE (* decrease width to actual size.. *)
  2022. (* localTextReader.SetPosition(pos);
  2023. WHILE pos < wrapPos DO
  2024. localTextReader.ReadCh(ch); GetExtents(ch, dx, a, d); x := x - dx; INC(pos)
  2025. END
  2026. *) END
  2027. END
  2028. ELSE
  2029. IF (stopXPos >= 0) & (x + dx DIV 2 > stopXPos) THEN
  2030. DEC(pos);
  2031. (* the bidi formatted text's lock needs to be released explicitly *)
  2032. IF (bidiTextReader # NIL) THEN
  2033. localTextReader.text.ReleaseRead;
  2034. END;
  2035. RETURN
  2036. END;
  2037. INC(x, dx)
  2038. END;
  2039. ELSE
  2040. eol := TRUE;
  2041. stop := TRUE;
  2042. IF (stopXPos >= 0) THEN DEC(pos) END;
  2043. END;
  2044. ELSE
  2045. eol := TRUE
  2046. END;
  2047. INC(i);
  2048. UNTIL eol OR localTextReader.eot;
  2049. l.width := x;
  2050. l.ascent := ascent; l.height := leading; (* ascent + descent; *)
  2051. l.align := align; l.leftIndent := leftIndent; l.rightIndent := rightIndent;
  2052. IF l.height = 0 THEN l.height := style.font.GetHeight() END;
  2053. IF start THEN l.firstInParagraph := TRUE; l.firstIndent := firstIndent; l.spaceBefore := spaceBefore;
  2054. ELSE l.firstInParagraph := FALSE; END;
  2055. IF stop THEN l.lastInParagraph := TRUE; l.spaceAfter := spaceAfter;
  2056. ELSE l.lastInParagraph := FALSE END;
  2057. (* the bidi formatted text's lock needs to be released explicitly *)
  2058. IF (bidiTextReader # NIL) THEN
  2059. localTextReader.text.ReleaseRead;
  2060. END;
  2061. END LayoutLine;
  2062. (* llen = -1 to render until the end of line > 0 to render llen elements in the line *)
  2063. PROCEDURE RenderLine*(canvas : WMGraphics.Canvas; VAR l : LineInfo; linenr, top, llen : LONGINT);
  2064. VAR sx, dx, dy, x, sp, i, j, k, t, tx, linelength, w, p : LONGINT; char : Char32; gs: WMGraphics.GlyphSpacings;
  2065. font : WMGraphics.Font;
  2066. vc : WMComponents.VisualComponent;
  2067. hc : BOOLEAN;
  2068. bidiTextReader, localTextReader : Texts.TextReader;
  2069. cursorPosition : LONGINT;
  2070. regionStart, regionEnd, lastEnd: LONGINT;
  2071. readerPosition : LONGINT;
  2072. lineNumberString : ARRAY 16 OF CHAR;
  2073. canvasState : WMGraphics.CanvasState;
  2074. cliprect, temp : WMRectangles.Rectangle;
  2075. highlighterStyle, lastHighlighterStyle : SyntaxHighlighter.Style;
  2076. currentStyle, lastStyle : ANY;
  2077. lastColor : WMGraphics.Color;
  2078. cf: WMGraphics.Font;
  2079. style : RECORD
  2080. color, bgColor : WMGraphics.Color;
  2081. voff : LONGINT;
  2082. font : WMGraphics.Font;
  2083. END;
  2084. BEGIN
  2085. IF TraceRenderOptimize IN Trace THEN
  2086. KernelLog.String("RenderLine : "); KernelLog.Int(linenr, 5); KernelLog.String(" from position : ");
  2087. KernelLog.Int(layout.GetLineStartPos(linenr), 5); KernelLog.Ln;
  2088. END;
  2089. sp := l.pos;
  2090. IF sp >= text.GetLength() THEN RETURN END;
  2091. style.color := defaultTextColorI;
  2092. canvas.SetColor(style.color); lastColor := style.color;
  2093. style.bgColor := defaultTextBgColorI;
  2094. style.voff := 0;
  2095. cf := GetFont();
  2096. style.font := cf;
  2097. IF llen < 0 THEN
  2098. linelength := layout.GetLineLength(linenr);
  2099. (* hack for the bidi formatter *)
  2100. IF linenr = layout.GetNofLines() - 1 THEN
  2101. DEC(linelength);
  2102. END;
  2103. ELSE
  2104. linelength := llen
  2105. END;
  2106. (* if there is a bidi formatter, reorder the current line *)
  2107. IF text.isUTF & (layout.bidiFormatter # NIL) THEN
  2108. bidiTextReader := layout.bidiFormatter.ReorderLine(sp,linelength);
  2109. END;
  2110. (* the bidi text reader needs special treatment for the initialization *)
  2111. IF (bidiTextReader # NIL) THEN
  2112. (* after reordering the line, contextual dependency rules are applied *)
  2113. bidiTextReader := ContextualDependency.AnalyzeLine(bidiTextReader,-1,-1);
  2114. layout.bidiFormatter.SetReadyTextReader(sp,bidiTextReader);
  2115. bidiTextReader.CloneProperties(utilreader);
  2116. localTextReader := bidiTextReader;
  2117. localTextReader.text.AcquireRead;
  2118. localTextReader.SetPosition(0);
  2119. ELSE
  2120. (* revert the hack for the bidi formatter *)
  2121. IF (llen < 0) & (linenr = layout.GetNofLines() - 1) THEN
  2122. INC(linelength);
  2123. END;
  2124. localTextReader := utilreader;
  2125. localTextReader.text.AcquireRead;
  2126. localTextReader.SetPosition(sp);
  2127. END;
  2128. i := 0;
  2129. x := GetLineLeftIndent(linenr);
  2130. sx := - leftShiftI + bordersI.l + x0;
  2131. IF TraceBaseLine IN Trace THEN
  2132. canvas.Line(0, top + (l.ascent), bounds.GetWidth(), top + (l.ascent), 01F0000FFH, WMGraphics.ModeCopy)
  2133. END;
  2134. selection.Sort;
  2135. IF (cursor.visible) & (selection.b - selection.a <= 0) & (clBgCurrentLineI # 0) THEN
  2136. cursorPosition := cursor.GetPosition();
  2137. IF (l.pos <= cursorPosition) & (cursorPosition < l.pos + linelength) THEN
  2138. canvas.Fill(WMRectangles.MakeRect(0, top, bounds.GetWidth() - bordersI.r, top + l.height), clBgCurrentLineI, WMGraphics.ModeSrcOverDst);
  2139. END;
  2140. END;
  2141. IF showLineNumbersI THEN
  2142. canvas.SaveState(canvasState);
  2143. Strings.IntToStr(linenr + 1, lineNumberString);
  2144. temp := WMRectangles.MakeRect(bordersI.l, top, x0 - 1, top + l.height);
  2145. IF (lineNumberBgColorI # 0) THEN
  2146. canvas.Fill(temp, lineNumberBgColorI, WMGraphics.ModeSrcOverDst);
  2147. END;
  2148. temp.r := temp.r - 4;
  2149. IF ((linenr + 1) MOD 10 = 0) THEN
  2150. canvas.SetFont(lineNumberFont10);
  2151. ELSE
  2152. canvas.SetFont(lineNumberFont);
  2153. END;
  2154. canvas.SetColor(lineNumberColorI);
  2155. WMGraphics.DrawStringInRect(canvas, temp, FALSE, WMGraphics.AlignRight, WMGraphics.AlignCenter, lineNumberString);
  2156. canvas.RestoreState(canvasState); (* restore font and font color *)
  2157. canvas.SaveState(canvasState);
  2158. canvas.GetClipRect(cliprect);
  2159. cliprect.l := x0;
  2160. canvas.SetClipRect(cliprect);
  2161. END;
  2162. w := bounds.GetWidth() - bordersI.r;
  2163. localTextReader.SetDirection(1);
  2164. lastEnd := -1;
  2165. highlighterStyle := NIL; lastHighlighterStyle := NIL;
  2166. currentStyle := DefaultStyle; lastStyle := NIL;
  2167. REPEAT
  2168. readerPosition := localTextReader.GetPosition();
  2169. localTextReader.ReadCh(char);
  2170. IF (highlighter # NIL) THEN
  2171. ASSERT(state # NIL);
  2172. IF (lastEnd < readerPosition) THEN
  2173. highlighterStyle := highlighter.GetRegionStyle(readerPosition, state, regionStart, regionEnd);
  2174. IF (highlighterStyle # NIL) THEN
  2175. lastEnd := regionEnd;
  2176. ELSE
  2177. IF (char > 32) THEN
  2178. highlighterStyle := highlighter.GetWordStyle(localTextReader, readerPosition, lastEnd);
  2179. END;
  2180. END;
  2181. localTextReader.SetPosition(readerPosition);
  2182. localTextReader.ReadCh(char); (* restore text reader state *)
  2183. END;
  2184. IF (highlighterStyle = NIL) THEN
  2185. highlighterStyle := highlighter.GetDefaultStyle();
  2186. END;
  2187. END;
  2188. IF (highlighterStyle = NIL) OR (highlighterStyle.defined * SyntaxHighlighter.DefineMask # SyntaxHighlighter.DefineMask) THEN
  2189. IF (localTextReader.cstyle # NIL) THEN
  2190. IF (currentStyle # localTextReader.cstyle) THEN
  2191. currentStyle := localTextReader.cstyle;
  2192. style.color := localTextReader.cstyle.color;
  2193. style.bgColor := localTextReader.cstyle.bgColor;
  2194. style.voff := localTextReader.cstyle.baselineShift;
  2195. IF (localTextReader.cstyle.fontcache # NIL) & (localTextReader.cstyle.fontcache IS WMGraphics.Font) THEN
  2196. style.font := localTextReader.cstyle.fontcache(WMGraphics.Font);
  2197. ELSE
  2198. style.font := WMGraphics.GetFont(localTextReader.cstyle.family, ENTIER(FP1616.FixpToFloat(localTextReader.cstyle.size)), localTextReader.cstyle.style);
  2199. localTextReader.cstyle.fontcache := style.font;
  2200. END;
  2201. END;
  2202. ELSIF (localTextReader.attributes # NIL) THEN
  2203. IF (currentStyle # localTextReader.attributes) THEN
  2204. currentStyle := localTextReader.attributes;
  2205. style.color := localTextReader.attributes.color;
  2206. style.bgColor := localTextReader.attributes.bgcolor;
  2207. style.voff := localTextReader.attributes.voff;
  2208. IF (localTextReader.attributes.fontInfo # NIL) THEN
  2209. IF (localTextReader.attributes.fontInfo.fontcache # NIL) & (localTextReader.attributes.fontInfo.fontcache IS WMGraphics.Font) THEN
  2210. style.font := localTextReader.attributes.fontInfo.fontcache (WMGraphics.Font);
  2211. ELSE
  2212. style.font := GetFontFromAttr(localTextReader.attributes.fontInfo);
  2213. localTextReader.attributes.fontInfo.fontcache := style.font;
  2214. END;
  2215. ELSE
  2216. style.font := cf;
  2217. END;
  2218. END;
  2219. ELSE
  2220. IF (currentStyle # DefaultStyle) THEN
  2221. currentStyle := DefaultStyle;
  2222. style.color := defaultTextColorI;
  2223. style.bgColor := defaultTextBgColorI;
  2224. style.voff := 0;
  2225. style.font := cf;
  2226. END;
  2227. END;
  2228. ASSERT(style.font # NIL);
  2229. END;
  2230. IF (highlighterStyle # NIL) THEN
  2231. IF (highlighterStyle # lastHighlighterStyle) OR (currentStyle # lastStyle) THEN
  2232. IF SyntaxHighlighter.Voff IN highlighterStyle.defined THEN style.voff := highlighterStyle.attributes.voff; END;
  2233. IF SyntaxHighlighter.Color IN highlighterStyle.defined THEN style.color := highlighterStyle.attributes.color; END;
  2234. IF SyntaxHighlighter.BgColor IN highlighterStyle.defined THEN style.bgColor := highlighterStyle.attributes.bgcolor; END;
  2235. IF (SyntaxHighlighter.FontMask * highlighterStyle.defined # {}) THEN
  2236. CheckFont(highlighterStyle, style.font, fontCache);
  2237. style.font := highlighterStyle.attributes.fontInfo.fontcache (WMGraphics.Font);
  2238. END;
  2239. END;
  2240. currentStyle := NIL; (* force reevaluation of localTextReader style *)
  2241. END;
  2242. lastStyle := currentStyle;
  2243. lastHighlighterStyle := highlighterStyle;
  2244. IF (style.color # lastColor) THEN canvas.SetColor(style.color); lastColor := style.color; END;
  2245. IF char = Texts.ObjectChar THEN
  2246. IF (localTextReader.object # NIL) & (localTextReader.object IS WMGraphics.Image) THEN
  2247. canvas.DrawImage(x, top + (l.ascent) + style.voff - localTextReader.object(WMGraphics.Image).height, localTextReader.object(WMGraphics.Image),
  2248. WMGraphics.ModeSrcOverDst);
  2249. dx := localTextReader.object(WMGraphics.Image).width
  2250. ELSIF (localTextReader.object # NIL) & (localTextReader.object IS WMComponents.VisualComponent) THEN
  2251. vc := localTextReader.object(WMComponents.VisualComponent);
  2252. dx := vc.bounds.GetWidth();
  2253. dy := vc.bounds.GetHeight();
  2254. canvas.SaveState(clipState); (* save the current clip-state *)
  2255. canvas.SetClipRect(WMRectangles.MakeRect(x + sx, top + (l.ascent - dy), x + dx + sx, top + (l.height)));
  2256. canvas.ClipRectAsNewLimits(x + sx, top + (l.ascent - dy));
  2257. (* assuming the component will not delay --> otherwise a buffer is needed *)
  2258. vc.Acquire; vc.Draw(canvas); vc.Release;
  2259. canvas.RestoreState(clipState)
  2260. END
  2261. ELSIF char = 0 THEN (* EOT *)
  2262. ELSIF char = Texts.TabChar THEN
  2263. tx := x;
  2264. IF l.firstInParagraph THEN tx := tx - l.firstIndent
  2265. ELSE tx := tx - l.leftIndent END;
  2266. IF l.tabStops # NIL THEN dx := l.tabStops.GetNextTabStop(tx) - tx
  2267. ELSE dx := defaultTabStops.GetNextTabStop(tx) - tx
  2268. END;
  2269. IF style.bgColor # 0 THEN
  2270. canvas.Fill(WMRectangles.MakeRect(x + sx, top, x + dx + sx, top + (l.height)), style.bgColor, WMGraphics.ModeSrcOverDst)
  2271. END;
  2272. IF indicateTabsI THEN canvas.SetPixel(x + sx + ((dx + 1) DIV 2), top + ((l.ascent + 1) DIV 2), WMGraphics.Blue, WMGraphics.ModeCopy); END;
  2273. ELSIF char = Texts.LabelChar THEN
  2274. IF showLabels.Get() THEN
  2275. font := cf;
  2276. font.GetStringSize(localTextReader.object(Texts.LabelPiece).label^, dx, dy);
  2277. font.RenderString(canvas, x + sx+2, top + (l.ascent), localTextReader.object(Texts.LabelPiece).label^);
  2278. INC(dx, 4);
  2279. canvas.Fill(WMRectangles.MakeRect(x + sx, top, x + dx + sx, top + (l.height)), LONGINT(0FF880050H), WMGraphics.ModeSrcOverDst);
  2280. WMGraphicUtilities.RectGlassShade(canvas, WMRectangles.MakeRect(x + sx, top, x + dx + sx, top + (l.height)), 1, FALSE)
  2281. ELSE dx := 0; END;
  2282. ELSE
  2283. IF char = Texts.NewLineChar THEN
  2284. localTextReader.text.ReleaseRead;
  2285. IF showLineNumbersI THEN canvas.RestoreState(canvasState); END;
  2286. RETURN
  2287. END;
  2288. IF isPasswordI THEN
  2289. char := passwordChar.Get()
  2290. END;
  2291. (* If the text is utf-formatted get the display version of the character.
  2292. Note, that only some special invisible characters differ from their actual representation. *)
  2293. IF text.isUTF THEN
  2294. UnicodeBidirectionality.GetDisplayCharacter(char);
  2295. END;
  2296. hc := style.font.HasChar(char);
  2297. IF hc THEN style.font.GetGlyphSpacings(char, gs)
  2298. ELSE WMGraphics.FBGetGlyphSpacings(char, gs)
  2299. END;
  2300. dx := gs.bearing.l + gs.width + gs.bearing.r;
  2301. IF style.bgColor MOD 256 # 0 THEN
  2302. canvas.Fill(WMRectangles.MakeRect(x + sx, top, x + dx + sx, top + (l.height)), style.bgColor, WMGraphics.ModeCopy)
  2303. END;
  2304. IF hc THEN style.font.RenderChar(canvas, x + sx, top + (l.ascent) + style.voff, char)
  2305. ELSE WMGraphics.FBRenderChar(canvas, x + sx, top + (l.ascent) + style.voff, char)
  2306. END
  2307. END;
  2308. (* link *)
  2309. IF localTextReader.link # NIL THEN
  2310. canvas.Line(x + sx, top + (l.ascent)+1, x + dx + sx, top + (l.ascent)+1, canvas.color, WMGraphics.ModeSrcOverDst);
  2311. END;
  2312. (* highlight - since highlights store the global text position, the line's starting position needs to be added,
  2313. when operating on the local, bidirectional text reader. *)
  2314. IF bidiTextReader # NIL THEN
  2315. p := GetInternalPos(localTextReader.GetPosition()+sp-1);
  2316. ELSE
  2317. p := localTextReader.GetPosition() - 1;
  2318. END;
  2319. FOR j := 0 TO nofHighlights - 1 DO
  2320. IF (p >= highlights[j].a) & (p < highlights[j].b) THEN
  2321. CASE highlights[j].kind OF
  2322. |HLOver: canvas.Fill(WMRectangles.MakeRect(x + sx, top, x + dx + sx, top + (l.height)), highlights[j].color, WMGraphics.ModeSrcOverDst)
  2323. |HLUnder: canvas.Line(x + sx, top + (l.ascent), x + dx + sx, top + (l.ascent), highlights[j].color, WMGraphics.ModeSrcOverDst);
  2324. |HLWave:
  2325. FOR k := 0 TO dx - 1 DO
  2326. t := 1 - ABS((x + k) MOD 4 - 2); (* because of compiler bug on intel *)
  2327. canvas.SetPixel(x + k + sx, top + l.ascent + t, highlights[j].color, WMGraphics.ModeSrcOverDst);
  2328. END;
  2329. ELSE
  2330. END
  2331. END
  2332. END;
  2333. x := x + dx;
  2334. INC(i)
  2335. UNTIL (i >= linelength) OR localTextReader.eot OR (x + sx > w);
  2336. localTextReader.text.ReleaseRead;
  2337. IF showLineNumbersI THEN canvas.RestoreState(canvasState); END;
  2338. END RenderLine;
  2339. PROCEDURE RenderAboveTextMarkers*(canvas : WMGraphics.Canvas);
  2340. VAR x, y, l, pos, i, ascent : LONGINT;
  2341. BEGIN
  2342. AssertLock;
  2343. IF text = NIL THEN RETURN END;
  2344. IF optimize THEN RETURN END;
  2345. text.AcquireRead;
  2346. FOR i := nofPositionMarkers - 1 TO 0 BY -1 DO
  2347. pos := positionMarkers[i].pos.GetPosition();
  2348. l := layout.FindLineNrByPos(pos);
  2349. IF FindScreenPos(pos, x, y) THEN
  2350. IF (l >= 0) & (l < layout.GetNofLines()) THEN
  2351. ascent := layout.lines[l].ascent;
  2352. (* IF ascent = 0 THEN ascent := layout.lines[l].height END;
  2353. IF ascent = 0 THEN ascent := 10 END; *)
  2354. ELSE ascent := 10 END;
  2355. positionMarkers[i].Draw(canvas, x, y, ascent)
  2356. END
  2357. END;
  2358. text.ReleaseRead;
  2359. END RenderAboveTextMarkers;
  2360. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  2361. VAR la, lb, i, top, t, b : LONGINT; rect, clip : WMRectangles.Rectangle; cstate : WMGraphics.CanvasState;
  2362. BEGIN
  2363. canvas.GetClipRect(clip);
  2364. IF WMRectangles.RectEmpty(clip) THEN RETURN END;
  2365. rect := GetClientRect();
  2366. canvas.SaveState(cstate);
  2367. IF WMRectangles.Intersect(rect, clip) THEN
  2368. DrawBackground^(canvas);
  2369. IF showBorderI THEN
  2370. WMGraphicUtilities.DrawBevel(canvas, rect,
  2371. 1, TRUE, LONGINT(0808080FFH), WMGraphics.ModeCopy)
  2372. END;
  2373. END;
  2374. (* allow clean clipping in at inner border *)
  2375. WMRectangles.ClipRect(rect, borderClip);
  2376. WMRectangles.ClipRect(clip, borderClip);
  2377. canvas.SetClipRect(clip);
  2378. (* draw gutter *)
  2379. rect.r := x0 - 1;
  2380. IF showLineNumbersI & (lineNumberBgColorI # 0) & WMRectangles.Intersect(rect, clip) THEN
  2381. canvas.Fill(rect, lineNumberBgColorI, WMGraphics.ModeSrcOverDst);
  2382. END;
  2383. text.AcquireRead;
  2384. la := FindLineByY(firstLineI, clip.t);
  2385. lb := FindLineByY(firstLineI, clip.b);
  2386. (* prepare selections *)
  2387. FOR i := 0 TO nofHighlights - 1 DO
  2388. highlights[i].a := highlights[i].from.GetPosition();
  2389. highlights[i].b := highlights[i].to.GetPosition();
  2390. IF highlights[i].a > highlights[i].b THEN t := highlights[i].a; highlights[i].a := highlights[i].b; highlights[i].b := t END
  2391. END;
  2392. top := borderClip.t;
  2393. IF (la = lb) & (textAlignV.Get() = WMGraphics.AlignCenter) THEN
  2394. top := (borderClip.t+borderClip.b-layout.lines[la].height) DIV 2;
  2395. (* something is wrong with ascent and height here, does not comply with the notions in fonts *)
  2396. END;
  2397. FOR i := firstLineI TO la - 1 DO
  2398. top := top + (layout.lines[i].height);
  2399. CheckParagraphBegin(i, top);
  2400. CheckParagraphEnd(i, top);
  2401. END;
  2402. IF la >= 0 THEN
  2403. (* draw the lines that intersect the clipping rectangle *)
  2404. FOR i := la TO lb DO
  2405. CheckParagraphBegin(i, top);
  2406. RenderLine(canvas, layout.lines[i], i, top, -1);
  2407. top := top + (layout.lines[i].height);
  2408. CheckParagraphEnd(i, top);
  2409. END
  2410. END;
  2411. RenderAboveTextMarkers(canvas);
  2412. text.ReleaseRead;
  2413. canvas.RestoreState(cstate);
  2414. END DrawBackground;
  2415. PROCEDURE StoreLineEnter;
  2416. VAR pos, cl : LONGINT;
  2417. BEGIN
  2418. pos := cursor.GetPosition();
  2419. cl := layout.FindLineNrByPos(pos);
  2420. lineEnter := pos - layout.GetLineStartPos(cl)
  2421. END StoreLineEnter;
  2422. (* navigation *)
  2423. PROCEDURE WheelMove*(dz: LONGINT); (** PROTECTED *)
  2424. VAR ddz: DZ;
  2425. BEGIN
  2426. IF modifierFlags * Inputs.Ctrl # {} THEN (* CTRL pressed -> Resize Font*)
  2427. text.AcquireWrite;
  2428. IF dz > 0 THEN dz := 1 ELSIF dz<0 THEN dz := -1 END;
  2429. NEW(ddz, dz);
  2430. text.UpdateAttributes(0, text.GetLength(), ChangeAttribute, ddz);
  2431. text.ReleaseWrite;
  2432. ELSIF mouseWheelScrollSpeedI # 0 THEN
  2433. firstLine.Set(firstLine.Get() + mouseWheelScrollSpeedI * dz)
  2434. END;
  2435. END WheelMove;
  2436. (* abort a possible start of a command. Clear the command start indicator, if it was set *)
  2437. PROCEDURE AbortStart;
  2438. BEGIN
  2439. ASSERT(IsCallFromSequencer());
  2440. IF commandMarker # NIL THEN
  2441. RemoveHighlight(commandMarker);
  2442. commandMarker := NIL
  2443. END;
  2444. canStart := FALSE
  2445. END AbortStart;
  2446. (*
  2447. Handle double-click at text position <pos>.
  2448. Select the double-clicked word, whitespace or line.
  2449. Some explanations:
  2450. Why utilreader.GetPosition()+2 when searching to the left?
  2451. After we read the last character that should be included, the position of the reader is decremented.
  2452. When we now read the next character and see that it should not be included, the reader is decremented again.
  2453. -> The last character to be included was found at position utilreader.GetPosition()+2 (except when we reach EOT)
  2454. The same applies when search to the right. But to highlight the character at, for example, position 4, we need a highlight from 4-5.
  2455. That's why utilreader.GetPosition()-1 is used instead of utilreader.GetPosition()-2.
  2456. *)
  2457. PROCEDURE DoubleClickSelect(pos : LONGINT);
  2458. CONST
  2459. LineFeed = 0AH;
  2460. Underscore = 05FH;
  2461. VAR
  2462. char : Texts.Char32;
  2463. from, to : LONGINT;
  2464. BEGIN
  2465. ASSERT(text.HasReadLock());
  2466. utilreader.SetPosition(pos);
  2467. utilreader.SetDirection(1);
  2468. utilreader.ReadCh(char);
  2469. IF (char = LineFeed) OR utilreader.eot THEN (* select line *)
  2470. IF utilreader.eot THEN to := pos;
  2471. ELSE to := pos+1;
  2472. END;
  2473. from := TextUtilities.FindPosLineStart(utilreader, pos);
  2474. ELSIF TextUtilities.IsWhiteSpace(char,text.isUTF) THEN
  2475. WHILE ~utilreader.eot & TextUtilities.IsWhiteSpace(char,text.isUTF) & (char # LineFeed) DO utilreader.ReadCh(char); END;
  2476. IF utilreader.eot THEN to := utilreader.text.GetLength();
  2477. ELSE to := utilreader.GetPosition()-1;
  2478. END;
  2479. utilreader.SetPosition(pos);
  2480. utilreader.SetDirection(-1);
  2481. utilreader.ReadCh(char);
  2482. WHILE ~utilreader.eot & TextUtilities.IsWhiteSpace(char,text.isUTF) & (char # LineFeed) DO utilreader.ReadCh(char); END;
  2483. IF utilreader.eot THEN from := 0;
  2484. ELSE from := utilreader.GetPosition()+2;
  2485. END;
  2486. ELSIF TextUtilities.IsAlphaNum(char) OR (char = Underscore) THEN (* select word *)
  2487. WHILE ~utilreader.eot & (TextUtilities.IsAlphaNum(char) OR (char = Underscore)) DO utilreader.ReadCh(char); END;
  2488. IF utilreader.eot THEN to := utilreader.text.GetLength();
  2489. ELSE to := utilreader.GetPosition()-1;
  2490. END;
  2491. utilreader.SetPosition(pos);
  2492. utilreader.SetDirection(-1);
  2493. utilreader.ReadCh(char);
  2494. WHILE ~utilreader.eot & (TextUtilities.IsAlphaNum(char) OR (char = Underscore)) DO utilreader.ReadCh(char); END;
  2495. IF utilreader.eot THEN from := 0;
  2496. ELSE from := utilreader.GetPosition()+2;
  2497. END;
  2498. ELSE (* select the character at text position pos *)
  2499. from := pos; to := pos+1;
  2500. END;
  2501. selection.SetFromTo(from, to);
  2502. cursor.SetVisible(to - from > 0);
  2503. END DoubleClickSelect;
  2504. PROCEDURE SetInterclick(new : LONGINT);
  2505. VAR old : LONGINT;
  2506. BEGIN
  2507. old := interclick;
  2508. IF (old # new) THEN
  2509. interclick := new;
  2510. CASE new OF
  2511. | Interclick01: selection.SetColor(SelectionColorInterclick01);
  2512. | Interclick02: selection.SetColor(SelectionColorInterclick02);
  2513. ELSE
  2514. selection.SetColor(SelectionColor);
  2515. END;
  2516. END;
  2517. END SetInterclick;
  2518. PROCEDURE PointerDown*(x, y : LONGINT; keys : SET);
  2519. VAR pos, a, b, internalPos : LONGINT; oldInterclick : LONGINT;
  2520. BEGIN
  2521. ViewToTextPos(x,y,pos);
  2522. internalPos := GetInternalPos(pos);
  2523. oldInterclick := interclick;
  2524. IF (keys * {0, 1} = {0,1}) THEN SetInterclick(Interclick01);
  2525. ELSIF (keys * {0,2} = {0,2}) THEN SetInterclick(Interclick02);
  2526. ELSE SetInterclick(InterclickNone);
  2527. END;
  2528. (* Determine whether to cancel an interclick if any *)
  2529. IF (oldInterclick = InterclickCancelled) OR
  2530. ((oldInterclick # InterclickNone) & (interclick # InterclickNone)) THEN
  2531. SetInterclick(InterclickCancelled);
  2532. END;
  2533. IF allowCommandExecution.Get() & (keys * {0, 1, 2} = {1}) THEN
  2534. canStart := TRUE; openFile := FALSE;
  2535. IF commandMarker = NIL THEN
  2536. commandMarker := CreateHighlight();
  2537. commandMarker.SetKind(HLUnder);
  2538. commandMarker.SetColor(LONGINT(0FF0000FFH));
  2539. text.AcquireRead;
  2540. FindCommand(internalPos, a, b);
  2541. commandMarker.SetFromTo(a, b);
  2542. cursor.SetPosition(pos);
  2543. text.ReleaseRead
  2544. END;
  2545. END;
  2546. IF canStart & (2 IN keys) THEN openFile := TRUE; SetInterclick(InterclickCancelled); END;
  2547. IF keys * {0, 1, 2} = {0, 1, 2} THEN AbortStart END;
  2548. IF allowPiemenu.Get() & (keys * {0, 1, 2} = {2}) THEN
  2549. text.AcquireRead;
  2550. ViewToTextPos(x, y, pos);
  2551. cursor.SetPosition(pos);
  2552. text.ReleaseRead;
  2553. ShowContextMenu(x, y)
  2554. END;
  2555. IF allowTextSelection.Get() &
  2556. ( (keys * {0, 1, 2} = {0}) (* left mouse for select *)
  2557. OR (keys * {0, 1, 2} = {1}) & doubleclickedWord (* remove selection when double clicking *)
  2558. OR (keys * {0,1,2} = {2}) & (~allowPiemenu.Get())) (* right mouse for selection if pie menu is not enabled *)
  2559. THEN
  2560. AbortStart;
  2561. text.AcquireRead;
  2562. ViewToTextPos(x, y, pos);
  2563. dragPossible := FALSE; selectWords := FALSE;
  2564. IF internalPos >= 0 THEN
  2565. selection.Sort;
  2566. IF (internalPos >= selection.a) & (internalPos < selection.b) & (interclick = InterclickNone) THEN
  2567. dragPossible := TRUE; downX := x; downY := y
  2568. ELSIF (interclick = InterclickNone) THEN
  2569. (* clicking the same position twice --> Word Selection Mode *)
  2570. IF (internalPos = GetInternalPos(cursor.GetPosition())) OR ((internalPos - 1 = GetInternalPos(cursor.GetPosition())) & (internalPos - 1 = text.GetLength())) THEN
  2571. (* Workaround: The 2nd check is for the very last line of a text. LayoutLine gives pos = text.GetLength()+1 *)
  2572. selectWords := TRUE; wordSelOrdered := TRUE;
  2573. doubleclickedWord := TRUE;
  2574. DoubleClickSelect(internalPos);
  2575. ELSE
  2576. selection.SetFromTo(internalPos, internalPos); (* reset selection *)
  2577. cursor.SetVisible(TRUE);
  2578. END;
  2579. selecting := TRUE;
  2580. END
  2581. END;
  2582. cursor.SetPosition(pos);
  2583. text.ReleaseRead;
  2584. CursorChanged
  2585. END;
  2586. END PointerDown;
  2587. PROCEDURE PointerMove*(x, y : LONGINT; keys : SET);
  2588. VAR pos, a, b, internalPos : LONGINT;
  2589. BEGIN
  2590. IF ~canStart & dragPossible THEN
  2591. IF (ABS(x - downX) > DragDist) OR (ABS(y - downY) > DragDist) THEN dragPossible := FALSE; AutoStartDrag END
  2592. ELSE
  2593. IF (selecting OR canStart) & (interclick = InterclickNone) THEN
  2594. text.AcquireRead;
  2595. ViewToTextPos(x, y, pos);
  2596. internalPos := GetInternalPos(pos);
  2597. IF selecting & ~doubleclickedWord THEN
  2598. selection.Sort;
  2599. IF selectWords THEN
  2600. IF internalPos < selection.from.GetPosition() THEN
  2601. pos := TextUtilities.FindPosWordLeft(utilreader, internalPos - 1);
  2602. ELSE
  2603. pos := TextUtilities.FindPosWordRight(utilreader, internalPos + 1);
  2604. END;
  2605. selection.SetTo(internalPos)
  2606. ELSE
  2607. selection.SetTo(internalPos);
  2608. END;
  2609. selection.Sort;
  2610. cursor.SetVisible(selection.b - selection.a <= 0);
  2611. Texts.SetLastSelection(text, selection.from, selection.to);
  2612. cursor.SetPosition(pos);
  2613. StoreLineEnter;
  2614. ELSIF canStart THEN
  2615. IF commandMarker # NIL THEN
  2616. FindCommand(internalPos, a, b);
  2617. commandMarker.SetFromTo(a, b)
  2618. END
  2619. END;
  2620. IF doubleclickedWord THEN doubleclickedWord := FALSE; END; (* allow selecting again *)
  2621. text.ReleaseRead;
  2622. CursorChanged
  2623. END
  2624. END
  2625. END PointerMove;
  2626. PROCEDURE PointerUp*(x, y : LONGINT; keys : SET);
  2627. BEGIN
  2628. IF canStart & (commandMarker # NIL) THEN
  2629. commandMarker.Sort;
  2630. StartCommand((commandMarker.a + commandMarker.b) DIV 2, openFile);
  2631. AbortStart
  2632. END;
  2633. IF modifierFlags * Inputs.Ctrl # {} THEN
  2634. onCtrlClicked.Call(NIL)
  2635. END;
  2636. selecting := FALSE;
  2637. doubleclickedWord := FALSE;
  2638. IF (keys * {0,1,2} = {}) THEN
  2639. IF (interclick = Interclick02) THEN
  2640. DeleteSelection;
  2641. END;
  2642. SetInterclick(InterclickNone);
  2643. END;
  2644. IF dragPossible THEN selection.SetFromTo(0, 0); cursor.SetVisible(TRUE); Texts.ClearLastSelection (* reset selection *) END;
  2645. dragPossible := FALSE
  2646. END PointerUp;
  2647. (* Transforms the TextView Coordinates into TextObject obj Coordinates *)
  2648. PROCEDURE TransformCoordinates(VAR x, y : LONGINT; obj : WMComponents.VisualComponent);
  2649. VAR line, pos, x0, y0, y1 : LONGINT;
  2650. BEGIN
  2651. ViewToTextPos(x, y, pos);
  2652. IF FindScreenPos(pos, x0, y0) THEN
  2653. IF x0 > x THEN pos := pos - 1;
  2654. IF FindScreenPos(pos, x0, y0) THEN END;
  2655. END;
  2656. line := layout.FindLineNrByPos(GetInternalPos(pos));
  2657. LineYPos(line, y0, y1);
  2658. x := x - x0;
  2659. y := y - y0;
  2660. IF line >= 0 THEN y := y - (layout.lines[line].ascent - obj.bounds.GetHeight()); END
  2661. END
  2662. END TransformCoordinates;
  2663. (* Change the pointer according to the underlaying component *)
  2664. PROCEDURE ChangePointer(pointerInfo : WMWindowManager.PointerInfo);
  2665. BEGIN
  2666. IF GetPointerInfo() # pointerInfo THEN
  2667. SetPointerInfo(pointerInfo)
  2668. END
  2669. END ChangePointer;
  2670. (* Returns TRUE if an Object is Hit, FALSE otherwise *)
  2671. PROCEDURE HitObject(x, y : LONGINT; (* keys : SET;*) VAR pos : LONGINT; VAR obj : ANY): BOOLEAN;
  2672. VAR ch, tx, ty : LONGINT;
  2673. BEGIN
  2674. text.AcquireRead;
  2675. ViewToTextPos(x, y, pos);
  2676. IF FindScreenPos(pos, tx, ty) THEN
  2677. IF tx > x THEN pos := pos - 1 END
  2678. END;
  2679. utilreader.SetPosition(GetInternalPos(pos));
  2680. utilreader.ReadCh(ch);
  2681. text.ReleaseRead;
  2682. IF ch = Texts.ObjectChar THEN obj := utilreader.object;
  2683. RETURN TRUE
  2684. ELSE
  2685. RETURN FALSE
  2686. END
  2687. END HitObject;
  2688. (* Returns TRUE if a Link is Hit, FALSE otherwise *)
  2689. PROCEDURE HitLink(x, y : LONGINT; VAR pos : LONGINT; VAR link : Texts.Link): BOOLEAN;
  2690. VAR ch, tx, ty : LONGINT;
  2691. BEGIN
  2692. text.AcquireRead;
  2693. ViewToTextPos(x, y, pos);
  2694. IF FindScreenPos(pos, tx, ty) THEN
  2695. IF tx > x THEN pos := pos - 1 END
  2696. END;
  2697. utilreader.SetPosition(GetInternalPos(pos));
  2698. utilreader.ReadCh(ch);
  2699. text.ReleaseRead;
  2700. IF utilreader.link # NIL THEN
  2701. link := utilreader.link;
  2702. RETURN TRUE
  2703. ELSE
  2704. RETURN FALSE
  2705. END
  2706. END HitLink;
  2707. PROCEDURE LinkClick(link : Texts.Link);
  2708. VAR w : LinkWrapper;
  2709. BEGIN
  2710. NEW(w); w.link := link;
  2711. onLinkClicked.Call(w)
  2712. END LinkClick;
  2713. (* builtin behaviour *)
  2714. PROCEDURE LinkClicked*(sender, data : ANY);
  2715. VAR tempLink : ARRAY 2048 OF CHAR;
  2716. tempLabel : ARRAY 256 OF CHAR;
  2717. pos, i : LONGINT;
  2718. BEGIN
  2719. IF data IS LinkWrapper THEN
  2720. COPY(data(LinkWrapper).link^, tempLink);
  2721. IF tempLink[0] = "#" THEN (* internal link *)
  2722. i := 0;
  2723. WHILE tempLink[i] # 0X DO
  2724. tempLabel[i] := tempLink[i+1];
  2725. INC(i);
  2726. END;
  2727. tempLink[i] := 0X;
  2728. (* find label in tv *)
  2729. IF FindLabel(tempLabel, pos) THEN
  2730. i := layout.nofLines-1;
  2731. WHILE (i >= 0) DO
  2732. IF layout.GetLineStartPos(i) < pos THEN firstLine.Set(i); RETURN END;
  2733. DEC(i);
  2734. END;
  2735. END;
  2736. ELSE (* other links *)
  2737. END
  2738. END
  2739. END LinkClicked;
  2740. (* Returns the position of the label in text *)
  2741. PROCEDURE FindLabel*(CONST label : ARRAY OF CHAR; VAR pos : LONGINT): BOOLEAN;
  2742. VAR ch : LONGINT;
  2743. found : BOOLEAN;
  2744. BEGIN
  2745. found := FALSE; pos := 0;
  2746. text.AcquireRead;
  2747. utilreader.SetDirection(1); utilreader.SetPosition(pos);
  2748. REPEAT
  2749. utilreader.ReadCh(ch);
  2750. IF ch = Texts.LabelChar THEN
  2751. IF utilreader.object(Texts.LabelPiece).label^ = label THEN
  2752. found := TRUE;
  2753. END;
  2754. END;
  2755. INC(pos);
  2756. UNTIL utilreader.eot OR found;
  2757. text.ReleaseRead;
  2758. RETURN found;
  2759. END FindLabel;
  2760. (* Drag away operations *)
  2761. PROCEDURE AutoStartDrag*;
  2762. VAR img : WMGraphics.Image;
  2763. c : WMGraphics.BufferCanvas;
  2764. w, h, i, la, lb, top : LONGINT;
  2765. l : LineInfo;
  2766. BEGIN
  2767. text.AcquireRead;
  2768. selection.Sort;
  2769. NEW(dragSelA, text);NEW(dragSelB, text);
  2770. dragSelA.SetPosition(selection.a); dragSelB.SetPosition(selection.b);
  2771. la := Limit(layout.FindLineNrByPos(selection.a), 0, layout.GetNofLines() - 1);
  2772. lb := Limit(layout.FindLineNrByPos(selection.b), 0, layout.GetNofLines() - 1);
  2773. (* estimate the size of the selection *)
  2774. h := 0; w := 0;
  2775. FOR i := la TO lb DO
  2776. h := h + (layout.lines[i].height);
  2777. w := MAX(w, layout.lines[i].width);
  2778. END;
  2779. h := Limit(h, 20, 200);
  2780. w := Limit(w, 20, 400);
  2781. (* render to bitmap *)
  2782. NEW(img); Raster.Create(img, w, h, Raster.BGRA8888);
  2783. NEW(c, img);
  2784. top := 0;
  2785. (* hack the startpos of the first line *)
  2786. l := layout.lines[la]; l.pos := selection.a;
  2787. IF la = lb THEN RenderLine(c, l, la, top, selection.b - selection.a)
  2788. ELSE
  2789. RenderLine(c, l, la, top, -1);
  2790. top := top + l.height
  2791. END;
  2792. FOR i := la + 1 TO lb DO
  2793. IF i = lb THEN
  2794. RenderLine(c, layout.lines[i], i, top, selection.b - layout.lines[i].pos)
  2795. ELSE
  2796. RenderLine(c, layout.lines[i], i, top, -1);
  2797. top := top + (l.height)
  2798. END
  2799. END;
  2800. text.ReleaseRead;
  2801. IF StartDrag(NIL, img, 0,0,DragWasAccepted, NIL) THEN
  2802. ELSE KernelLog.String("WMTextView : Drag could not be started")
  2803. END;
  2804. END AutoStartDrag;
  2805. PROCEDURE DragWasAccepted(sender, data : ANY);
  2806. VAR di : WMWindowManager.DragInfo;
  2807. dt : WMDropTarget.DropTarget;
  2808. itf : WMDropTarget.DropInterface;
  2809. targetText, temp : Texts.Text;
  2810. string : Strings.String;
  2811. pos, a, b: LONGINT; res: WORD;
  2812. BEGIN
  2813. IF (dragSelA = NIL) OR (dragSelB = NIL) THEN RETURN END;
  2814. IF (data # NIL) & (data IS WMWindowManager.DragInfo) THEN
  2815. di := data(WMWindowManager.DragInfo);
  2816. IF (di.data # NIL) & (di.data IS WMDropTarget.DropTarget) THEN
  2817. dt := di.data(WMDropTarget.DropTarget)
  2818. ELSE RETURN
  2819. END
  2820. ELSE RETURN
  2821. END;
  2822. itf := dt.GetInterface(WMDropTarget.TypeText);
  2823. IF itf # NIL THEN
  2824. targetText := itf(WMDropTarget.DropText).text;
  2825. IF targetText # NIL THEN
  2826. targetText.AcquireWrite;
  2827. IF ~dragCopy THEN
  2828. IF TraceCopy IN Trace THEN KernelLog.String("WMTextView: Not copy"); KernelLog.Ln; END;
  2829. text.AcquireWrite;
  2830. a := dragSelA.GetPosition(); b := dragSelB.GetPosition();
  2831. pos := itf(WMDropTarget.DropText).pos.GetPosition();
  2832. IF (targetText # text) OR (pos < a) OR (pos > b) THEN
  2833. NEW(temp); temp.AcquireWrite; temp.CopyFromText(text, a, b-a, 0); temp.ReleaseWrite;
  2834. text.Delete(a, b- a);
  2835. pos := itf(WMDropTarget.DropText).pos.GetPosition();
  2836. temp.AcquireRead;
  2837. targetText.CopyFromText(temp, 0, temp.GetLength(), pos);
  2838. temp.ReleaseRead;
  2839. END;
  2840. text.ReleaseWrite
  2841. ELSE
  2842. IF TraceCopy IN Trace THEN KernelLog.String("WMTextView: Copy"); KernelLog.Ln; END;
  2843. text.AcquireRead;
  2844. pos := itf(WMDropTarget.DropText).pos.GetPosition();
  2845. a := dragSelA.GetPosition(); b := dragSelB.GetPosition();
  2846. targetText.CopyFromText(text, a, b-a, pos);
  2847. text.ReleaseRead;
  2848. END;
  2849. targetText.ReleaseWrite
  2850. END;
  2851. RETURN
  2852. END;
  2853. itf := dt.GetInterface(WMDropTarget.TypeString);
  2854. IF (itf # NIL) THEN
  2855. IF ~dragCopy THEN
  2856. text.AcquireWrite;
  2857. a := dragSelA.GetPosition(); b := dragSelB.GetPosition();
  2858. NEW(temp);
  2859. temp.AcquireWrite;
  2860. temp.CopyFromText(text, a, b-a, 0);
  2861. IF (temp.GetLength() > 0) THEN NEW(string, temp.GetLength() * 5); ELSE NEW(string, 1); string[0] := 0X; END;
  2862. temp.ReleaseWrite;
  2863. text.ReleaseWrite;
  2864. TextUtilities.TextToStr(temp, string^);
  2865. itf(WMDropTarget.DropString).Set(string^, res);
  2866. IF res = 0 THEN
  2867. text.AcquireWrite;
  2868. text.Delete(a, b- a);
  2869. text.ReleaseWrite;
  2870. END;
  2871. ELSE
  2872. text.AcquireRead;
  2873. a := dragSelA.GetPosition(); b := dragSelB.GetPosition();
  2874. NEW(temp);
  2875. temp.AcquireWrite;
  2876. temp.CopyFromText(text, a, b-a, 0);
  2877. IF (temp.GetLength() > 0) THEN NEW(string, temp.GetLength() * 5); ELSE NEW(string, 1); string[0] := 0X; END;
  2878. temp.ReleaseWrite;
  2879. text.ReleaseRead;
  2880. TextUtilities.TextToStr(temp, string^);
  2881. itf(WMDropTarget.DropString).Set(string^, res);
  2882. END;
  2883. END;
  2884. END DragWasAccepted;
  2885. (* Drag onto operations *)
  2886. PROCEDURE DragOver*(x, y : LONGINT; dragInfo : WMWindowManager.DragInfo);
  2887. VAR pos : LONGINT;
  2888. BEGIN
  2889. IF takesFocus.Get() THEN
  2890. text.AcquireRead;
  2891. ViewToTextPos(x, y, pos);
  2892. cursor.SetVisible(TRUE);
  2893. cursor.SetPosition(pos);
  2894. StoreLineEnter;
  2895. text.ReleaseRead
  2896. END;
  2897. END DragOver;
  2898. PROCEDURE ConfirmDrag*(accept: BOOLEAN; dragInfo: WMWindowManager.DragInfo);
  2899. BEGIN
  2900. IF dragInfo.onAccept # NIL THEN
  2901. dragInfo.onAccept(SELF, dragInfo)
  2902. END;
  2903. END ConfirmDrag;
  2904. PROCEDURE DragDropped*(x, y : LONGINT; dragInfo : WMWindowManager.DragInfo);
  2905. VAR dropTarget : TextDropTarget;
  2906. pos, internalPos : LONGINT;
  2907. p : Texts.TextPosition;
  2908. BEGIN
  2909. IF takesFocus.Get() THEN
  2910. text.AcquireRead;
  2911. ViewToTextPos(x, y, pos) ;
  2912. (* prevent a selection from being dropped behind the paragraph separator *)
  2913. internalPos := GetInternalPos(pos);
  2914. IF text.isUTF & (layout.bidiFormatter # NIL) THEN
  2915. IF layout.bidiFormatter.IsLastCharacterInLine(internalPos) THEN
  2916. DEC(internalPos);
  2917. END;
  2918. END;
  2919. NEW(p, text); p.SetPosition(internalPos);
  2920. NEW(dropTarget, text, p);
  2921. text.ReleaseRead;
  2922. IF ~hasFocus & ~alwaysShowCursorI THEN cursor.SetVisible(FALSE) END;
  2923. dragInfo.data := dropTarget;
  2924. ConfirmDrag(TRUE, dragInfo);
  2925. ELSE
  2926. ConfirmDrag(FALSE, dragInfo);
  2927. END;
  2928. END DragDropped;
  2929. PROCEDURE CopySelection*;
  2930. BEGIN
  2931. IF isPassword.Get() THEN RETURN END;
  2932. text.AcquireRead;
  2933. Texts.clipboard.AcquireWrite;
  2934. selection.Sort;
  2935. IF selection.b - selection.a > 0 THEN
  2936. (* clear the clipboard *)
  2937. IF Texts.clipboard.GetLength() > 0 THEN Texts.clipboard.Delete(0, Texts.clipboard.GetLength()) END;
  2938. Texts.clipboard.CopyFromText(text, selection.a, selection.b - selection.a, 0);
  2939. END;
  2940. Texts.clipboard.ReleaseWrite;
  2941. text.ReleaseRead
  2942. END CopySelection;
  2943. PROCEDURE DeleteSelection*;
  2944. BEGIN
  2945. Acquire; (* protect cursor *)
  2946. text.AcquireWrite;
  2947. selection.Sort;
  2948. text.Delete(selection.a, selection.b - selection.a);
  2949. cursor.SetVisible(TRUE);
  2950. text.ReleaseWrite;
  2951. Release;
  2952. END DeleteSelection;
  2953. PROCEDURE Paste*;
  2954. BEGIN
  2955. text.AcquireWrite;
  2956. Texts.clipboard.AcquireRead;
  2957. IF Texts.clipboard.GetLength() > 0 THEN
  2958. IF selection.b - selection.a # 0 THEN DeleteSelection() END;
  2959. text.CopyFromText(Texts.clipboard, 0, Texts.clipboard.GetLength(), cursor.GetPosition())
  2960. END;
  2961. Texts.clipboard.ReleaseRead;
  2962. text.ReleaseWrite
  2963. END Paste;
  2964. PROCEDURE SelectAll*;
  2965. BEGIN
  2966. Acquire; (* protect cursor *)
  2967. text.AcquireRead;
  2968. selection.SetFromTo(0, text.GetLength());
  2969. cursor.SetVisible(text.GetLength() <= 0);
  2970. Texts.SetLastSelection(text, selection.from, selection.to);
  2971. text.ReleaseRead;
  2972. Release;
  2973. END SelectAll;
  2974. (* Prepare to start the selection by keyboard. Clear the selection, if it is not contigous *)
  2975. PROCEDURE KeyStartSelection(pos : LONGINT);
  2976. BEGIN
  2977. IF selection.to.GetPosition() # pos THEN selection.SetFromTo(pos, pos); cursor.SetVisible(TRUE); Texts.ClearLastSelection END;
  2978. END KeyStartSelection;
  2979. (* update the keyboard selection with the new position, redraw from the last StartSelection *)
  2980. PROCEDURE KeyUpdateSelection(pos : LONGINT);
  2981. BEGIN
  2982. selection.SetTo(pos);
  2983. selection.Sort;
  2984. cursor.SetVisible(selection.b - selection.a <= 0);
  2985. Texts.SetLastSelection(text, selection.from, selection.to)
  2986. END KeyUpdateSelection;
  2987. PROCEDURE CursorChanged;
  2988. BEGIN
  2989. cursorBlinker.Show(cursor);
  2990. IF (onCursorChanged # NIL) THEN onCursorChanged END
  2991. END CursorChanged;
  2992. PROCEDURE CursorUp*(select : BOOLEAN);
  2993. VAR
  2994. pos, cPos, cl, lineStart : LONGINT;
  2995. BEGIN
  2996. Acquire;
  2997. text.AcquireRead;
  2998. pos := GetInternalPos(cursor.GetPosition());
  2999. IF select THEN
  3000. KeyStartSelection(pos)
  3001. ELSE
  3002. selection.SetFromTo(pos, pos);
  3003. cursor.SetVisible(TRUE);
  3004. Texts.ClearLastSelection
  3005. END;
  3006. cl := layout.FindLineNrByPos(pos);
  3007. IF cl > 0 THEN
  3008. DEC(cl);
  3009. lineStart := layout.GetLineStartPos(cl);
  3010. cPos := lineStart + MIN(layout.GetLineLength(cl) - 1, lineEnter);
  3011. cursor.SetPosition(cPos);
  3012. IF cl < firstLineI THEN firstLine.Set(cl) END
  3013. END;
  3014. IF select THEN
  3015. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()));
  3016. END;
  3017. text.ReleaseRead;
  3018. Release;
  3019. CursorChanged
  3020. END CursorUp;
  3021. PROCEDURE CursorDown*(select : BOOLEAN);
  3022. VAR pos, cPos, cl, lineStart : LONGINT;
  3023. BEGIN
  3024. Acquire;
  3025. text.AcquireRead;
  3026. pos := GetInternalPos(cursor.GetPosition());
  3027. IF select THEN
  3028. KeyStartSelection(pos)
  3029. ELSE
  3030. selection.SetFromTo(pos, pos);
  3031. cursor.SetVisible(TRUE);
  3032. Texts.ClearLastSelection
  3033. END;
  3034. cl := layout.FindLineNrByPos(pos);
  3035. IF cl < layout.GetNofLines() - 1 THEN
  3036. INC(cl);
  3037. lineStart := layout.GetLineStartPos(cl);
  3038. cPos := lineStart + MIN(layout.GetLineLength(cl) - 1, lineEnter);
  3039. cursor.SetPosition(cPos);
  3040. IF cl > FindLineByY(firstLineI, bounds.GetHeight() - bordersI.b) THEN firstLine.Set(firstLineI + 1 ) END
  3041. END;
  3042. IF select THEN
  3043. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3044. END;
  3045. text.ReleaseRead;
  3046. Release;
  3047. CursorChanged
  3048. END CursorDown;
  3049. (* Move the cursor one character/word to the left *)
  3050. PROCEDURE CursorLeft*(word, select : BOOLEAN);
  3051. VAR
  3052. pos, cPos, wPos : LONGINT;
  3053. BEGIN
  3054. Acquire;
  3055. text.AcquireRead;
  3056. PositionDebugging.SetPos(GetInternalPos(cursor.GetPosition()),cursor.GetPosition());
  3057. pos := GetInternalPos(cursor.GetPosition());
  3058. IF select THEN
  3059. KeyStartSelection(pos)
  3060. ELSE
  3061. selection.SetFromTo(pos, pos);
  3062. cursor.SetVisible(TRUE);
  3063. Texts.ClearLastSelection
  3064. END;
  3065. cPos := GetInternalPos(cursor.GetPosition()) - 1;
  3066. IF ~word THEN
  3067. cursor.SetPosition(GetDisplayPos(cPos));
  3068. ELSE
  3069. wPos := TextUtilities.FindPosWordLeft(utilreader, cPos);
  3070. cursor.SetPosition(GetDisplayPos(wPos));
  3071. END;
  3072. IF select THEN
  3073. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3074. END;
  3075. StoreLineEnter;
  3076. text.ReleaseRead;
  3077. Release;
  3078. CursorChanged
  3079. END CursorLeft;
  3080. (* Move the cursor one character/word to the right *)
  3081. PROCEDURE CursorRight*(word, select : BOOLEAN);
  3082. VAR
  3083. pos, cPos, wPos : LONGINT;
  3084. BEGIN
  3085. Acquire;
  3086. text.AcquireRead;
  3087. PositionDebugging.SetPos(GetInternalPos(cursor.GetPosition()),cursor.GetPosition());
  3088. pos := GetInternalPos(cursor.GetPosition());
  3089. IF select THEN
  3090. KeyStartSelection(pos)
  3091. ELSE
  3092. selection.SetFromTo(pos, pos);
  3093. cursor.SetVisible(TRUE);
  3094. Texts.ClearLastSelection
  3095. END;
  3096. cPos := GetInternalPos(cursor.GetPosition()) + 1;
  3097. IF ~word THEN
  3098. cursor.SetPosition(GetDisplayPos(cPos));
  3099. ELSE
  3100. wPos := TextUtilities.FindPosWordRight(utilreader, cPos);
  3101. cursor.SetPosition(GetDisplayPos(wPos));
  3102. END;
  3103. IF select THEN
  3104. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3105. END;
  3106. StoreLineEnter;
  3107. text.ReleaseRead;
  3108. Release;
  3109. CursorChanged
  3110. END CursorRight;
  3111. PROCEDURE PageDown*(select : BOOLEAN);
  3112. VAR dy : LONGINT; i, pos, iPos : LONGINT;
  3113. cx, cy : LONGINT;
  3114. BEGIN
  3115. Acquire;
  3116. text.AcquireRead;
  3117. iPos := GetInternalPos(cursor.GetPosition());
  3118. IF select THEN
  3119. KeyStartSelection(iPos)
  3120. ELSE
  3121. selection.SetFromTo(iPos, iPos);
  3122. cursor.SetVisible(TRUE);
  3123. Texts.ClearLastSelection
  3124. END;
  3125. IF firstLineI = layout.GetNofLines() - 1 THEN
  3126. cursor.SetPosition(text.GetLength());
  3127. ELSE
  3128. (* save cursor screen pos for repositioning *)
  3129. IF ~FindScreenPos(cursor.GetPosition(), cx, cy) THEN cx := 0; cy := 0 END;
  3130. i := firstLineI; dy := 0;
  3131. WHILE (i < layout.GetNofLines() - 1) & (dy < bounds.GetHeight() - bordersI.t - bordersI.b) DO
  3132. INC(i); dy := dy + (layout.lines[i].height)
  3133. END;
  3134. firstLine.Set(i);
  3135. (* set cursor to nearest pos on new page *)
  3136. ViewToTextPos(cx, cy, pos);
  3137. IF pos >= 0 THEN
  3138. cursor.SetPosition(pos);
  3139. END;
  3140. END;
  3141. IF select THEN
  3142. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3143. END;
  3144. text.ReleaseRead;
  3145. Release;
  3146. CursorChanged
  3147. END PageDown;
  3148. PROCEDURE PageUp*(select : BOOLEAN);
  3149. VAR dy : LONGINT; i, pos, iPos : LONGINT;
  3150. cx, cy : LONGINT;
  3151. BEGIN
  3152. Acquire;
  3153. text.AcquireRead;
  3154. iPos := GetInternalPos(cursor.GetPosition());
  3155. IF select THEN
  3156. KeyStartSelection(iPos)
  3157. ELSE
  3158. selection.SetFromTo(iPos, iPos);
  3159. cursor.SetVisible(TRUE);
  3160. Texts.ClearLastSelection
  3161. END;
  3162. IF firstLineI = 0 THEN
  3163. cursor.SetPosition(0);
  3164. ELSE
  3165. (* save cursor screen pos for repositioning *)
  3166. IF ~FindScreenPos(cursor.GetPosition(), cx, cy) THEN cx := 0; cy := 0 END;
  3167. (* go up one page but at least one page *)
  3168. i := firstLineI; dy := 0;
  3169. WHILE (i > 0) & (dy < bounds.GetHeight() - bordersI.t - bordersI.b) DO
  3170. DEC(i); dy := dy + (layout.lines[i].height)
  3171. END;
  3172. IF (i > 0) & (i = firstLineI) THEN DEC(i) END;
  3173. firstLine.Set(i);
  3174. (* set cursor to nearest pos on new page *)
  3175. ViewToTextPos(cx, cy, pos);
  3176. IF pos >= 0 THEN
  3177. cursor.SetPosition(pos);
  3178. END
  3179. END;
  3180. IF select THEN
  3181. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3182. END;
  3183. text.ReleaseRead;
  3184. Release;
  3185. CursorChanged
  3186. END PageUp;
  3187. PROCEDURE Home*(ctrl, select : BOOLEAN);
  3188. VAR
  3189. lineStart, cl, pos : LONGINT;
  3190. BEGIN
  3191. Acquire;
  3192. text.AcquireRead;
  3193. pos := GetInternalPos(cursor.GetPosition());
  3194. IF select THEN
  3195. KeyStartSelection(pos)
  3196. ELSE
  3197. selection.SetFromTo(pos, pos);
  3198. cursor.SetVisible(TRUE);
  3199. Texts.ClearLastSelection
  3200. END;
  3201. IF ctrl THEN
  3202. cursor.SetPosition(GetDisplayPos(0));
  3203. firstLine.Set(0)
  3204. ELSE
  3205. cl := layout.FindLineNrByPos(cursor.GetPosition());
  3206. lineStart := layout.GetLineStartPos(cl);
  3207. cursor.SetPosition(GetDisplayPos(lineStart));
  3208. END;
  3209. StoreLineEnter;
  3210. IF select THEN
  3211. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3212. END;
  3213. text.ReleaseRead;
  3214. Release;
  3215. CursorChanged
  3216. END Home;
  3217. PROCEDURE End*(ctrl, select : BOOLEAN);
  3218. VAR lineEnd, textLength, cl, pos, dispPos: LONGINT;
  3219. BEGIN
  3220. Acquire;
  3221. text.AcquireRead;
  3222. pos := GetInternalPos(cursor.GetPosition());
  3223. IF select THEN
  3224. KeyStartSelection(pos)
  3225. ELSE
  3226. selection.SetFromTo(pos, pos);
  3227. cursor.SetVisible(TRUE);
  3228. Texts.ClearLastSelection
  3229. END;
  3230. IF ctrl THEN
  3231. textLength := text.GetLength();
  3232. cursor.SetPosition(GetDisplayPos(textLength));
  3233. firstLine.Set(layout.FindLineNrByPos(text.GetLength()))
  3234. ELSE
  3235. cl := layout.FindLineNrByPos(cursor.GetPosition());
  3236. lineEnd := layout.GetLineStartPos(cl) + layout.GetLineLength(cl) - 1;
  3237. dispPos := GetDisplayPos(lineEnd);
  3238. cursor.SetPosition(dispPos);
  3239. END;
  3240. StoreLineEnter;
  3241. IF select THEN
  3242. KeyUpdateSelection(GetInternalPos(cursor.GetPosition()))
  3243. END;
  3244. text.ReleaseRead;
  3245. Release;
  3246. CursorChanged
  3247. END End;
  3248. PROCEDURE KeyEvent*(ucs :LONGINT; flags : SET; VAR keysym : LONGINT);
  3249. BEGIN
  3250. modifierFlags := flags;
  3251. IF Inputs.Release IN flags THEN RETURN END;
  3252. dragCopy := modifierFlags * Inputs.Ctrl # {};
  3253. IF keysym = 01H THEN (* Ctrl-A *)
  3254. SelectAll
  3255. ELSIF keysym = 03H THEN (* Ctrl-C *)
  3256. CopySelection
  3257. ELSIF (keysym = 0FF63H) & (flags * Inputs.Ctrl # {}) THEN (*Ctrl Insert *)
  3258. CopySelection
  3259. ELSIF keysym = 12H THEN (* Ctrl-R *)
  3260. layout.FullLayout(TRUE); Invalidate;CheckNumberOfLines;
  3261. KernelLog.String("Refreshed"); KernelLog.Ln;
  3262. ELSIF keysym = 0FF51H THEN (* Cursor Left *)
  3263. CursorLeft(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
  3264. ELSIF keysym = 0FF53H THEN (* Cursor Right *)
  3265. CursorRight(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
  3266. ELSIF keysym = 0FF54H THEN (* Cursor Down *)
  3267. CursorDown(flags * Inputs.Shift # {})
  3268. ELSIF keysym = 0FF52H THEN (* Cursor Up *)
  3269. CursorUp(flags * Inputs.Shift # {})
  3270. ELSIF keysym = 0FF56H THEN (* Page Down *)
  3271. PageDown(flags * Inputs.Shift # {})
  3272. ELSIF keysym = 0FF55H THEN (* Page Up *)
  3273. PageUp(flags * Inputs.Shift # {})
  3274. ELSIF keysym = 0FF50H THEN (* Cursor Home *)
  3275. Home(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
  3276. ELSIF keysym = 0FF57H THEN (* Cursor End *)
  3277. End(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
  3278. END
  3279. END KeyEvent;
  3280. (* called by users that override the KeyEvents to allow copy drag drop *)
  3281. PROCEDURE SetFlags*(flags : SET);
  3282. BEGIN
  3283. modifierFlags := flags;
  3284. dragCopy := modifierFlags * Inputs.Ctrl # {};
  3285. END SetFlags;
  3286. PROCEDURE FindCommandRange*(pos: LONGINT; VAR start, end, nofLastSelections : LONGINT);
  3287. VAR ch : LONGINT; string : ARRAY 23 OF CHAR; i : LONGINT; sDoCommands, lastWasTilde : BOOLEAN;
  3288. escapeString: ARRAY 32 OF LONGINT; escapePos: LONGINT; escape: BOOLEAN;
  3289. (* note: this simple algorithm can be emplyed if the substring to be implicitly searched for does not contain its first character *)
  3290. PROCEDURE String(escape: BOOLEAN; CONST escapeString: ARRAY OF LONGINT);
  3291. VAR done: BOOLEAN; escapePos: LONGINT;
  3292. BEGIN
  3293. done := FALSE; escapePos := -1;
  3294. REPEAT
  3295. utilreader.ReadCh(ch);
  3296. IF ch = ORD('"') THEN
  3297. IF escape THEN
  3298. escapePos := 0;
  3299. ELSE
  3300. done := TRUE
  3301. END;
  3302. ELSIF escapePos >= 0 THEN
  3303. IF escapeString[escapePos] = 0 THEN
  3304. IF ch =ORD("\") THEN done := TRUE
  3305. ELSE escapePos := -1
  3306. END;
  3307. ELSIF escapeString[escapePos] # ch THEN
  3308. escapePos := -1;
  3309. ELSE
  3310. INC(escapePos);
  3311. END;
  3312. END;
  3313. UNTIL done OR utilreader.eot;
  3314. END String;
  3315. BEGIN
  3316. nofLastSelections := 0;
  3317. text.AcquireRead;
  3318. utilreader.SetDirection(-1); utilreader.SetPosition(pos);
  3319. REPEAT utilreader.ReadCh(ch) UNTIL TextUtilities.IsWhiteSpace(ch,text.isUTF) OR utilreader.eot;
  3320. start := utilreader.GetPosition() + 2;
  3321. IF utilreader.eot THEN DEC(start, 2) END;
  3322. (* search ~ *)
  3323. i := 0; sDoCommands := FALSE; lastWasTilde := FALSE;
  3324. utilreader.SetDirection(1); utilreader.SetPosition(start);
  3325. REPEAT
  3326. utilreader.ReadCh(ch);
  3327. IF ch = ORD('"') THEN
  3328. escapeString[escapePos] := 0;
  3329. String(escape, escapeString);
  3330. ELSIF ch =ORD("\") THEN
  3331. escape := TRUE;
  3332. escapePos := 0;
  3333. ELSIF escape THEN
  3334. IF TextUtilities.IsWhiteSpace(ch,text.isUTF) THEN escape := FALSE
  3335. ELSE escapeString[escapePos] := ch; INC(escapePos);
  3336. END;
  3337. END;
  3338. (* check whether the command is System.DoCommands *)
  3339. IF (i < 17) THEN
  3340. string[i] := CHR(ch);
  3341. INC(i);
  3342. IF (i = 17) THEN
  3343. string[17] := 0X;
  3344. IF (string = "System.DoCommands") OR Strings.StartsWith2("PreliminaryCommands",string) THEN
  3345. sDoCommands := TRUE;
  3346. END;
  3347. END;
  3348. END;
  3349. IF (CHR(ch) = "^") THEN
  3350. INC(nofLastSelections);
  3351. END;
  3352. (* We do a special treatment of the command System.DoCommands since we don't want a single
  3353. tilde character to delimit the parameter string for the particular command - but two tilde characters *)
  3354. IF sDoCommands THEN
  3355. IF (ch = ORD("~")) THEN
  3356. IF ~lastWasTilde THEN
  3357. lastWasTilde := TRUE;
  3358. utilreader.ReadCh(ch);
  3359. ELSE
  3360. (* Two tilde characters only separated with whitespace means this is the
  3361. end of the System.DoCommands parameter string *)
  3362. END;
  3363. ELSIF lastWasTilde & ~TextUtilities.IsWhiteSpace(ch,text.isUTF) THEN
  3364. lastWasTilde := FALSE;
  3365. END;
  3366. END;
  3367. UNTIL (ch = ORD("~")) OR (utilreader.eot);
  3368. end := utilreader.GetPosition() - 1;
  3369. IF utilreader.eot THEN INC(end) END;
  3370. text.ReleaseRead
  3371. END FindCommandRange;
  3372. PROCEDURE FindCommand*(pos: LONGINT; VAR start, end : LONGINT);
  3373. VAR ch : LONGINT;
  3374. BEGIN
  3375. text.AcquireRead;
  3376. utilreader.SetDirection(-1); utilreader.SetPosition(pos);
  3377. REPEAT utilreader.ReadCh(ch) UNTIL TextUtilities.IsWhiteSpace(ch,text.isUTF) OR utilreader.eot;
  3378. start := utilreader.GetPosition() + 2;
  3379. IF utilreader.eot THEN DEC(start, 2) END;
  3380. utilreader.SetDirection(1); utilreader.SetPosition(pos);
  3381. REPEAT utilreader.ReadCh(ch) UNTIL TextUtilities.IsWhiteSpace(ch,text.isUTF) OR utilreader.eot;
  3382. end := utilreader.GetPosition() - 1;
  3383. IF utilreader.eot THEN INC(end) END;
  3384. text.ReleaseRead;
  3385. END FindCommand;
  3386. (** Start the command in the text, starting on pos (or wordboundary before),
  3387. caller should hold lock on text to make the pos stable *)
  3388. PROCEDURE StartCommand*(pos : LONGINT; openFile : BOOLEAN);
  3389. VAR
  3390. start, end, bufSize : LONGINT;
  3391. context : Commands.Context;
  3392. arg : Streams.StringReader;
  3393. command : ARRAY MaxCommandLength OF CHAR;
  3394. parameters : POINTER TO ARRAY OF CHAR;
  3395. s : Strings.String;
  3396. msg : ARRAY 128 OF CHAR;
  3397. ignore : Modules.Name;
  3398. paramSize, nofLastSelections, i, j, a, b: LONGINT; res: WORD;
  3399. selectionText : Texts.Text;
  3400. selectionOk : BOOLEAN;
  3401. from, to: Texts.TextPosition;
  3402. commandCaller:OBJECT;
  3403. commandWriter, errorWriter: Streams.Writer;
  3404. BEGIN
  3405. Acquire;
  3406. text.AcquireRead;
  3407. IF openFile THEN FindCommand(pos, start, end)
  3408. ELSE FindCommandRange(pos, start, end, nofLastSelections)
  3409. END;
  3410. bufSize := MAX(MIN((end - start) * 5 + 1 (* for UTF *), MaxCallParameterBuf), 1);
  3411. NEW(s, bufSize);
  3412. paramSize := 0;
  3413. TextUtilities.SubTextToStrAt(text, start, end - start, paramSize, s^);
  3414. INC(paramSize);
  3415. text.ReleaseRead;
  3416. Release;
  3417. IF Inputs.Shift * modifierFlags # {} THEN
  3418. (*
  3419. Command / open will not see the caller => called as if no calling context was specified.
  3420. => Opening a text while holding a shift key down will usually result in a new viewer being opened.
  3421. *)
  3422. commandCaller := NIL
  3423. ELSE
  3424. commandCaller := SELF.commandCaller;
  3425. END;
  3426. IF openFile THEN
  3427. FileHandlers.OpenFile(s^, NIL, commandCaller)
  3428. ELSE
  3429. command := "";
  3430. i := 0;
  3431. WHILE (i < MaxCommandLength) & (s[i] # 0X) & (s[i] # ";") & (s[i] # " ") & (s[i] # 09X) & (s[i] # 0DX) & (s[i] # 0AX) DO
  3432. command[i] := s[i]; INC(i);
  3433. END;
  3434. IF i < MaxCommandLength THEN
  3435. command[i] := 0X;
  3436. INC(i);
  3437. Commands.Split(command, ignore, ignore, res, msg);
  3438. IF res # Commands.Ok THEN
  3439. KernelLog.String("WMTextView: Command parsing error, res: "); KernelLog.Int(res, 0);
  3440. KernelLog.String(" ("); KernelLog.String(msg); KernelLog.String(")"); KernelLog.Ln;
  3441. RETURN;
  3442. END;
  3443. ELSE
  3444. KernelLog.String("WMTextView: Command execution error: Command too long"); KernelLog.Ln;
  3445. RETURN;
  3446. END;
  3447. IF (Inputs.Alt * modifierFlags # {}) THEN
  3448. (* execute AltMMCommand with actual command and its parameters as parameter *)
  3449. COPY(AltMMCommand, command);
  3450. commandWriter := NIL; errorWriter := NIL;
  3451. i := 0;
  3452. ELSE
  3453. commandWriter := SELF.commandWriter;
  3454. errorWriter := SELF.errorWriter;
  3455. END;
  3456. IF (i < LEN(s)) THEN (* copy parameter string *)
  3457. selectionOk := FALSE;
  3458. IF (nofLastSelections > 0) THEN
  3459. IF Texts.GetLastSelection(selectionText, from, to) THEN
  3460. selectionOk := TRUE;
  3461. selectionText.AcquireRead;
  3462. a := MIN(from.GetPosition(), to.GetPosition());
  3463. b := MAX(from.GetPosition(), to.GetPosition());
  3464. INC(paramSize, b - a + 1);
  3465. END;
  3466. END;
  3467. NEW(parameters, paramSize);
  3468. j := 0;
  3469. WHILE (i < LEN(s)) & (j < LEN(parameters)-1) DO
  3470. IF (s[i] = "^") & selectionOk THEN
  3471. TextUtilities.SubTextToStrAt(selectionText, a, b - a, j, parameters^);
  3472. ELSE
  3473. parameters[j] := s[i]; INC(j);
  3474. END;
  3475. INC(i);
  3476. END;
  3477. parameters[j] := 0X;
  3478. IF selectionOk THEN
  3479. selectionText.ReleaseRead;
  3480. END;
  3481. ELSE
  3482. NEW(parameters, 1); parameters[0] := 0X;
  3483. END;
  3484. NEW(arg, LEN(parameters)); arg.SetRaw(parameters^, 0, LEN(parameters));
  3485. NEW(context, NIL, arg, commandWriter, errorWriter, commandCaller);
  3486. IF TraceCommands IN Trace THEN
  3487. KernelLog.String("WMTextView: Executing command: '"); KernelLog.String(command); KernelLog.String("'");
  3488. KernelLog.String(", parameters: ");
  3489. IF (parameters[0] = 0X) THEN KernelLog.String("None"); ELSE KernelLog.String("'"); KernelLog.String(parameters^); KernelLog.String("'"); END;
  3490. KernelLog.Ln;
  3491. END;
  3492. Commands.Activate(command, context, {}, res, msg);
  3493. IF (res # Commands.Ok) THEN
  3494. IF commandWriter # NIL THEN
  3495. commandWriter.String("WMTextView: Command execution error, res: "); commandWriter.Int(res, 0);
  3496. commandWriter.String(" ("); commandWriter.String(msg); commandWriter.String(")"); commandWriter.Ln;
  3497. commandWriter.Update;
  3498. ELSE
  3499. KernelLog.String("WMTextView: Command execution error, res: "); KernelLog.Int(res, 0);
  3500. KernelLog.String(" ("); KernelLog.String(msg); KernelLog.String(")"); KernelLog.Ln;
  3501. END;
  3502. END;
  3503. END;
  3504. END StartCommand;
  3505. PROCEDURE Start(sender, data: ANY);
  3506. VAR msg: ARRAY 512 OF CHAR; res: WORD;
  3507. BEGIN
  3508. IF (data # NIL) & (data IS ClickInfo) THEN
  3509. IF data(ClickInfo).cmdPar # NIL THEN
  3510. Commands.Call(data(ClickInfo).cmdPar^, {}, res, msg);
  3511. IF res # 0 THEN KernelLog.String("WMTextView: "); KernelLog.String(msg); KernelLog.Ln END;
  3512. END
  3513. END
  3514. END Start;
  3515. PROCEDURE Open(sender, data: ANY);
  3516. BEGIN
  3517. IF (data # NIL) & (data IS ClickInfo) THEN
  3518. IF data(ClickInfo).cmd # NIL THEN
  3519. FileHandlers.OpenFile(data(ClickInfo).cmd^, NIL, commandCaller)
  3520. END
  3521. END
  3522. END Open;
  3523. PROCEDURE PieMenuStart(sender, data: ANY);
  3524. BEGIN
  3525. Start(piemenu, piemenu.userData)
  3526. END PieMenuStart;
  3527. PROCEDURE PieMenuOpen(sender, data: ANY);
  3528. BEGIN
  3529. Open(piemenu, piemenu.userData)
  3530. END PieMenuOpen;
  3531. PROCEDURE PieMenuCopy(sender, data: ANY);
  3532. BEGIN
  3533. CopySelection;
  3534. END PieMenuCopy;
  3535. PROCEDURE PieMenuPaste(sender, data: ANY);
  3536. BEGIN
  3537. Paste;
  3538. END PieMenuPaste;
  3539. PROCEDURE ShowContextMenu*(x, y: LONGINT);
  3540. VAR
  3541. popup : WMPopups.Popup;
  3542. start, end, bufSize : LONGINT;
  3543. command, s : Strings.String;
  3544. clickInfo : ClickInfo;
  3545. str : ARRAY 256 OF CHAR;
  3546. window : WMWindowManager.Window;
  3547. nofLastSelections : LONGINT;
  3548. BEGIN
  3549. ASSERT(IsCallFromSequencer());
  3550. text.AcquireRead;
  3551. FindCommand(cursor.GetPosition(), start, end);
  3552. bufSize := MAX(MIN((end - start) * 5 + 1 (* for UTF *), 4096), 1);
  3553. NEW(command, bufSize);
  3554. TextUtilities.SubTextToStr(text, start, end - start, command^);
  3555. FindCommandRange(cursor.GetPosition(), start, end, nofLastSelections);
  3556. bufSize := MAX(MIN((end - start) * 5 + 1 (* for UTF *), MaxCallParameterBuf), 1);
  3557. NEW(s, bufSize);
  3558. TextUtilities.SubTextToStr(text, start, end - start, s^);
  3559. text.ReleaseRead;
  3560. NEW(clickInfo);
  3561. clickInfo.cmd := command;
  3562. clickInfo.cmdPar := s;
  3563. IF UsePieMenu THEN
  3564. NEW(piemenu); piemenu.SetEnabled({0, 1, 2, 3});
  3565. piemenu.SetText(1, Strings.NewString("Open"));
  3566. piemenu.SetText(3, Strings.NewString("Start"));
  3567. piemenu.SetText(2, Strings.NewString("Copy"));
  3568. piemenu.SetText(0, Strings.NewString("Paste"));
  3569. piemenu.userData := clickInfo;
  3570. piemenu.on1.Add(PieMenuOpen);
  3571. piemenu.on2.Add(PieMenuCopy);
  3572. piemenu.on3.Add(PieMenuStart);
  3573. piemenu.on0.Add(PieMenuPaste);
  3574. manager := WMWindowManager.GetDefaultManager();
  3575. window := manager.GetPositionOwner(x, y);
  3576. IF window = NIL THEN RETURN END;
  3577. Acquire; ToWMCoordinates(x, y, x, y); Release;
  3578. piemenu.Show(window, x, y, FALSE);
  3579. (* TODO: Can't set := NIL, since its used by the button handlers *)
  3580. ELSE
  3581. NEW(popup);
  3582. str := "Start "; Strings.Append(str, command^); popup.AddParButton(str, Start, clickInfo);
  3583. str := "Open "; Strings.Append(str, command^); popup.AddParButton(str, Open, clickInfo);
  3584. Acquire; ToWMCoordinates(x, y, x, y); Release;
  3585. popup.Popup(x, y);
  3586. END
  3587. END ShowContextMenu;
  3588. PROCEDURE HandleInternal*(VAR x: WMMessages.Message);
  3589. VAR pos : LONGINT; obj : ANY; link : Texts.Link;
  3590. BEGIN
  3591. ASSERT(IsCallFromSequencer());
  3592. IF (x.msgType = WMMessages.MsgKey) & objHasFocus THEN (* forward KeyMsg *)
  3593. WITH focusObject : WMComponents.VisualComponent DO
  3594. focusObject.Handle(x);
  3595. InvalidateRange(focusPos, focusPos+1)
  3596. END
  3597. ELSIF (x.msgType # WMMessages.MsgKey) & HitObject(x.x, x.y, pos, obj) THEN (* forward Msg *)
  3598. SetFocus; cursor.SetVisible(FALSE);
  3599. IF obj IS WMComponents.VisualComponent THEN
  3600. WITH obj : WMComponents.VisualComponent DO
  3601. (* remove oldObject first *)
  3602. IF (oldObject # NIL) & (oldObject # obj) THEN
  3603. oldObject(WMComponents.VisualComponent).Handle(x);
  3604. InvalidateRange(oldPos, oldPos+1);
  3605. END;
  3606. TransformCoordinates(x.x, x.y, obj); (* transform to obj coords *)
  3607. obj.Handle(x); (* call obj Handle *)
  3608. ChangePointer(obj.GetPointerInfo()); (* change the pointer Image *)
  3609. InvalidateRange(pos, pos+1); (* redraw obj *)
  3610. oldObject := obj; oldPos := pos; (* store last object *)
  3611. (* transfer focus to Object *)
  3612. IF (x.msgType = WMMessages.MsgPointer) & (x.flags * {0, 1, 2} = {0}) THEN
  3613. (* remove old focus first *)
  3614. IF (focusObject # NIL) & (focusObject # obj) THEN
  3615. focusObject(WMComponents.VisualComponent).FocusLost;
  3616. InvalidateRange(focusPos, focusPos+1)
  3617. END;
  3618. objHasFocus := TRUE;
  3619. focusObject := obj; focusPos := pos;
  3620. (* FocusLost *)
  3621. END
  3622. END
  3623. END
  3624. ELSIF (x.msgType = WMMessages.MsgPointer) & HitLink(x.x, x.y, pos, link) THEN (* Link *)
  3625. ChangePointer(manager.pointerLink);
  3626. IF (x.msgSubType = 2) &(oldFlags / x.flags = {CallURLPointer}) THEN LinkClick(link); END;
  3627. oldFlags := x.flags;
  3628. ELSE
  3629. ChangePointer(manager.pointerText); (* change Pointer back *)
  3630. (* transfer focus back to TextView *)
  3631. IF (focusObject # NIL) & (x.msgType = WMMessages.MsgPointer) & (x.flags * {0, 1, 2} = {0}) THEN
  3632. focusObject(WMComponents.VisualComponent).FocusLost;
  3633. objHasFocus := FALSE;
  3634. InvalidateRange(focusPos, focusPos+1);
  3635. FocusReceived;
  3636. focusObject := NIL
  3637. END;
  3638. (* update last Object *)
  3639. IF (oldObject # NIL) & (x.msgType = WMMessages.MsgPointer) THEN
  3640. oldObject(WMComponents.VisualComponent).Handle(x);
  3641. InvalidateRange(oldPos, oldPos+1);
  3642. oldObject := NIL
  3643. END;
  3644. IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) & (x.ext IS Texts.StyleChangedMsg) THEN
  3645. layout.FullLayout(TRUE); Invalidate; CheckNumberOfLines;
  3646. ELSE
  3647. HandleInternal^(x);
  3648. END;
  3649. END
  3650. END HandleInternal;
  3651. END TextView;
  3652. TYPE
  3653. FontEntry = OBJECT
  3654. VAR
  3655. name : ARRAY 256 OF CHAR;
  3656. attributes : FontAttributes;
  3657. next : FontEntry;
  3658. PROCEDURE &Init(CONST name : ARRAY OF CHAR);
  3659. BEGIN
  3660. COPY(name, SELF.name);
  3661. attributes := NIL;
  3662. next := NIL;
  3663. END Init;
  3664. END FontEntry;
  3665. FontAttributes = OBJECT
  3666. VAR
  3667. font : WMGraphics.Font; (* { font # {} *)
  3668. size : LONGINT;
  3669. style : SET;
  3670. next : FontAttributes;
  3671. PROCEDURE &Init(size : LONGINT; style : SET);
  3672. BEGIN
  3673. font := NIL;
  3674. SELF.size := size;
  3675. SELF.style := style;
  3676. next := NIL;
  3677. END Init;
  3678. END FontAttributes;
  3679. (* not thread-safe! not global to avoid locking and keep size smaller *)
  3680. FontCache = OBJECT
  3681. VAR
  3682. entries : FontEntry;
  3683. defaultFont : WMGraphics.Font;
  3684. PROCEDURE &Init;
  3685. BEGIN
  3686. NEW(entries, "head"); (* head of list *)
  3687. defaultFont := WMGraphics.GetDefaultFont();
  3688. END Init;
  3689. PROCEDURE Find(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : WMGraphics.Font;
  3690. VAR font : WMGraphics.Font; e : FontEntry; a : FontAttributes;
  3691. BEGIN
  3692. font := NIL;
  3693. e := entries.next;
  3694. WHILE (e # NIL ) & (e.name < name) DO e := e.next; END;
  3695. IF (e # NIL) & (e.name = name) THEN
  3696. a := e.attributes;
  3697. WHILE (a # NIL) & (a.size < size) DO a := a.next; END;
  3698. WHILE (a # NIL) & (a.size = size) & (a.style # style) DO a := a.next; END;
  3699. IF (a # NIL) & (a.size = size) THEN
  3700. ASSERT(a.font # NIL);
  3701. font := a.font;
  3702. END;
  3703. END;
  3704. RETURN font;
  3705. END Find;
  3706. PROCEDURE Add(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : WMGraphics.Font;
  3707. VAR entry, e : FontEntry; attribute, a : FontAttributes;
  3708. BEGIN
  3709. e := entries;
  3710. WHILE (e.next # NIL) & (e.next.name < name) DO e := e.next; END;
  3711. IF (e.next # NIL) & (e.next.name = name) THEN
  3712. entry := e.next;
  3713. ELSE
  3714. NEW(entry, name);
  3715. entry.next := e.next;
  3716. e.next := entry;
  3717. END;
  3718. ASSERT(entry # NIL);
  3719. NEW(attribute, size, style);
  3720. attribute.font := WMGraphics.GetFont(name, size, style);
  3721. IF (entry.attributes = NIL) THEN
  3722. entry.attributes := attribute;
  3723. ELSIF (entry.attributes.size >= attribute.size) THEN
  3724. attribute.next := entry.attributes;
  3725. entry.attributes := attribute;
  3726. ELSE
  3727. a := entry.attributes;
  3728. WHILE (a.next # NIL) & (a.next.size < attribute.size) DO a := a.next; END;
  3729. attribute.next := a.next;
  3730. a.next := attribute;
  3731. END;
  3732. ASSERT(attribute.font # NIL);
  3733. RETURN attribute.font;
  3734. END Add;
  3735. (* Get specified font. If not available, the system default font is returned *)
  3736. PROCEDURE GetFont(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : WMGraphics.Font;
  3737. VAR font : WMGraphics.Font;
  3738. BEGIN
  3739. font := Find(name, size, style);
  3740. IF (font = NIL) THEN
  3741. font := Add(name, size, style);
  3742. END;
  3743. ASSERT(font # NIL);
  3744. RETURN font;
  3745. END GetFont;
  3746. END FontCache;
  3747. VAR
  3748. manager : WMWindowManager.WindowManager;
  3749. cursorBlinker- : CursorBlinker;
  3750. PTVIsMultiLine, PTVIsPassword, PTVShowBorder, PTValwaysShowCursor, PTVShowLabels : WMProperties.BooleanProperty;
  3751. PTVAllowCommandExecution, PTVAllowTextSelection, PTVAllowPiemenu : WMProperties.BooleanProperty;
  3752. PTVWrapMode, PTVMouseWheelScrollSpeed, PVTtextAlignV : WMProperties.Int32Property;
  3753. PTVfirstLine, PTVleftShift, PTVPasswordChar : WMProperties.Int32Property;
  3754. PTVdefaultTextColor, PTVdefaultTextBgColor : WMProperties.ColorProperty;
  3755. PTVborders : WMProperties.RectangleProperty;
  3756. PTVonLinkClick, PTVonLinkClickInfo : Strings.String;
  3757. PTVonCtrlLinkClick, PTVonCtrlLinkClickInfo : Strings.String;
  3758. PTVShowLineNumbers, PTVIndicateTabs : WMProperties.BooleanProperty;
  3759. PTVHighlighting : WMProperties.StringProperty;
  3760. PTVLineNumberColor, PTVLineNumberBgColor, PTVclBgCurrentLine : WMProperties.ColorProperty;
  3761. currentTextView : TextView;
  3762. StrTextView : Strings.String;
  3763. DefaultStyle : POINTER TO RECORD END;
  3764. PROCEDURE Limit(x, min, max : LONGINT) : LONGINT;
  3765. BEGIN
  3766. IF x < min THEN x := min END;
  3767. IF x > max THEN x := max END;
  3768. RETURN x
  3769. END Limit;
  3770. (* Actually, this is a hack... but for now, do it. *)
  3771. PROCEDURE GetNewSize(CONST fontname : ARRAY OF CHAR; value, currentSize : LONGINT; VAR newSize : LONGINT);
  3772. BEGIN
  3773. IF (fontname = "Oberon") THEN
  3774. IF (value >0) THEN
  3775. IF (currentSize <= 8) THEN newSize := 10;
  3776. ELSIF (currentSize <= 10) THEN newSize := 12;
  3777. ELSIF (currentSize <= 12) THEN newSize := 14;
  3778. ELSIF (currentSize <= 14) THEN newSize := 16;
  3779. ELSIF (currentSize <= 16) THEN newSize := 20;
  3780. ELSIF (currentSize <= 20) THEN newSize := 24;
  3781. ELSE (* go to default *)
  3782. newSize := 24; (* max. size of Oberon font *)
  3783. END;
  3784. ELSE
  3785. IF (currentSize >= 24) THEN newSize := 20;
  3786. ELSIF (currentSize >= 20) THEN newSize := 16;
  3787. ELSIF (currentSize >= 16) THEN newSize := 14;
  3788. ELSIF (currentSize >= 14) THEN newSize := 12;
  3789. ELSIF (currentSize >= 12) THEN newSize := 10;
  3790. ELSE
  3791. newSize := 8;
  3792. END;
  3793. END;
  3794. ELSIF (fontname = "Courier") THEN
  3795. IF (value > 0) THEN
  3796. IF (currentSize <= 8) THEN newSize := 10;
  3797. ELSE
  3798. newSize := 12;
  3799. END;
  3800. ELSE
  3801. IF (currentSize >= 12) THEN newSize := 10;
  3802. ELSE
  3803. newSize := 8;
  3804. END;
  3805. END;
  3806. ELSE
  3807. newSize := currentSize + value * currentSize DIV 4;
  3808. END;
  3809. IF (newSize < 8) THEN newSize := 8; END;
  3810. END GetNewSize;
  3811. TYPE
  3812. DZ = OBJECT (Texts.Attributes);
  3813. VAR value: LONGINT;
  3814. PROCEDURE &Init(v: LONGINT);
  3815. BEGIN
  3816. value := v;
  3817. END Init;
  3818. END DZ;
  3819. PROCEDURE EnsureAttribute(VAR attr : Texts.Attributes);
  3820. BEGIN
  3821. IF (attr = NIL) THEN
  3822. attr := Texts.defaultAttributes.Clone();
  3823. END;
  3824. END EnsureAttribute;
  3825. PROCEDURE ChangeAttribute(VAR attr : Texts.Attributes; userData : ANY);
  3826. VAR dz: DZ;
  3827. BEGIN
  3828. IF (userData # NIL) & (userData IS DZ) THEN
  3829. EnsureAttribute(attr);
  3830. dz := userData(DZ);
  3831. GetNewSize(attr.fontInfo.name,-dz.value, attr.fontInfo.size, attr.fontInfo.size);
  3832. attr.fontInfo.fontcache := NIL;
  3833. END;
  3834. END ChangeAttribute;
  3835. PROCEDURE GetFontFromAttr(info : Texts.FontInfo) : WMGraphics.Font;
  3836. BEGIN
  3837. RETURN WMGraphics.GetFont(info.name, info.size, info.style);
  3838. END GetFontFromAttr;
  3839. PROCEDURE IsSameFont(f1, f2 : WMGraphics.Font) : BOOLEAN;
  3840. BEGIN
  3841. RETURN (f1 = f2) OR ((f1 # NIL) & (f2 # NIL) & (f1.size = f2.size) & (f1.style = f2.style) & (f1.name = f2.name));
  3842. END IsSameFont;
  3843. PROCEDURE CheckFont(style : SyntaxHighlighter.Style; font : WMGraphics.Font; VAR fontCache : FontCache);
  3844. VAR fontname : ARRAY 256 OF CHAR; fontsize : LONGINT; fontstyle : SET;
  3845. BEGIN
  3846. ASSERT(style # NIL);
  3847. IF (fontCache = NIL) THEN NEW(fontCache); END;
  3848. IF (style.defined * SyntaxHighlighter.FontMask = SyntaxHighlighter.FontMask) OR (font = NIL) THEN
  3849. COPY(style.attributes.fontInfo.name, fontname);
  3850. fontsize := style.attributes.fontInfo.size;
  3851. fontstyle := style.attributes.fontInfo.style;
  3852. ELSIF (font # NIL) THEN
  3853. IF (SyntaxHighlighter.FontName IN style.defined) THEN COPY(style.attributes.fontInfo.name, fontname); ELSE COPY(font.name, fontname); END;
  3854. IF (SyntaxHighlighter.FontSize IN style.defined) THEN fontsize := style.attributes.fontInfo.size; ELSE fontsize := font.size; END;
  3855. IF (SyntaxHighlighter.FontStyle IN style.defined) THEN fontstyle := style.attributes.fontInfo.style; ELSE fontstyle := font.style; END;
  3856. END;
  3857. IF (style.attributes.fontInfo.fontcache = NIL) OR ~IsSameFont(style.attributes.fontInfo.fontcache (WMGraphics.Font), font) THEN
  3858. style.attributes.fontInfo.fontcache := fontCache.GetFont(fontname, fontsize, fontstyle);
  3859. END;
  3860. ASSERT(style.attributes.fontInfo.fontcache # NIL);
  3861. END CheckFont;
  3862. PROCEDURE InitStrings;
  3863. BEGIN
  3864. StrTextView := Strings.NewString("TextView");
  3865. PTVonLinkClick := Strings.NewString("Link Click Event");
  3866. PTVonLinkClickInfo := Strings.NewString("fired when a link is pressed");
  3867. PTVonCtrlLinkClick := Strings.NewString("Ctrl Click Event");
  3868. PTVonCtrlLinkClickInfo := Strings.NewString("fired when Ctrl pressend and clicked");
  3869. END InitStrings;
  3870. PROCEDURE InitPrototypes;
  3871. BEGIN
  3872. NEW(PTVIsMultiLine, NIL, Strings.NewString("multiLine"), Strings.NewString("defines if more than one line is visible"));
  3873. PTVIsMultiLine.Set(TRUE);
  3874. NEW(PTVShowBorder, NIL, Strings.NewString("ShowBorder"), Strings.NewString("show border"));
  3875. PTVShowBorder.Set(FALSE);
  3876. NEW(PTVIsPassword, NIL, Strings.NewString("password"),
  3877. Strings.NewString("defines if the view is a password text. Characters are replaced by passwordChar"));
  3878. NEW(PTVPasswordChar, NIL, Strings.NewString("passwordChar"),
  3879. Strings.NewString("character that is the placeholder for a character in a password"));
  3880. PTVPasswordChar.Set(43);
  3881. NEW(PTValwaysShowCursor, NIL, Strings.NewString("alwaysShowCursor"),
  3882. Strings.NewString("set to true, if the cursor should not be hidden when focus is lost"));
  3883. PTValwaysShowCursor.Set(FALSE);
  3884. NEW(PTVShowLabels, NIL, Strings.NewString("ShowLabels"),
  3885. Strings.NewString("set to true, if the labels should be shown in the text"));
  3886. PTVShowLabels.Set(FALSE);
  3887. NEW(PTVMouseWheelScrollSpeed, NIL, Strings.NewString("MouseWheelScrollSpeed"),
  3888. Strings.NewString("Multiplier for mouse wheel, 0 to disable mouse wheel scrolling"));
  3889. PTVMouseWheelScrollSpeed.Set(3);
  3890. NEW(PTVAllowCommandExecution, NIL, Strings.NewString("allowCommandExecution"),
  3891. Strings.NewString("if set to true, middle-clicked words are executed as command"));
  3892. PTVAllowCommandExecution.Set(TRUE);
  3893. NEW(PTVAllowTextSelection, NIL, Strings.NewString("allowTextSelection"),
  3894. Strings.NewString("is the user allowed to select text using the mouse?"));
  3895. PTVAllowTextSelection.Set(TRUE);
  3896. NEW(PTVAllowPiemenu, NIL, Strings.NewString("allowPiemenu"),
  3897. Strings.NewString("if set to true, a mouse right-click opens the pie menu"));
  3898. PTVAllowPiemenu.Set(TRUE);
  3899. NEW(PTVWrapMode, NIL, Strings.NewString("wrapMode"), Strings.NewString("Set text wrapping mode"));
  3900. PTVWrapMode.Set(WrapWord);
  3901. NEW(PTVfirstLine, NIL, Strings.NewString("firstLine"),
  3902. Strings.NewString("the first visible line of text in the view"));
  3903. PTVfirstLine.Set(0);
  3904. NEW(PTVleftShift, NIL, Strings.NewString("leftShift"),
  3905. Strings.NewString("how many pixels the text in the view is shifted to the left"));
  3906. PTVleftShift.Set(0);
  3907. NEW(PTVdefaultTextColor, NIL, Strings.NewString("defaultTextColor"),
  3908. Strings.NewString("the color of a text that does not explicitly specify a color"));
  3909. PTVdefaultTextColor.Set(0FFH);
  3910. NEW(PTVdefaultTextBgColor, NIL, Strings.NewString("defaultTextBgColor"),
  3911. Strings.NewString("The color of a text background if not specified otherwise in the text"));
  3912. PTVdefaultTextBgColor.Set(0);
  3913. NEW(PTVborders, NIL, Strings.NewString("borders"),
  3914. Strings.NewString("spaces from bounds of the component to the text"));
  3915. PTVborders.Set(WMRectangles.MakeRect(5, 5, 5, 5));
  3916. NEW(PTVIndicateTabs, NIL, Strings.NewString("IndicateTabs"), Strings.NewString("Indicate tabs?"));
  3917. PTVIndicateTabs.Set(FALSE);
  3918. NEW(PTVShowLineNumbers, NIL, Strings.NewString("ShowLineNumbers"), Strings.NewString("Show line numbers?"));
  3919. PTVShowLineNumbers.Set(FALSE);
  3920. NEW(PTVLineNumberColor, NIL, Strings.NewString("LineNumberColor"), Strings.NewString("Color of line numbers"));
  3921. PTVLineNumberColor.Set(WMGraphics.Black);
  3922. NEW(PTVLineNumberBgColor, NIL, Strings.NewString("LineNumberBgColor"), Strings.NewString("Background color of line numbers"));
  3923. PTVLineNumberBgColor.Set(0CCCCCCFFH);
  3924. NEW(PTVHighlighting, NIL, Strings.NewString("Highlighting"), Strings.NewString("Name of highlighting to be applied"));
  3925. PTVHighlighting.Set(NIL);
  3926. NEW(PTVclBgCurrentLine, NIL, Strings.NewString("ClBgCurrentLine"), Strings.NewString("Background color of currently edited line"));
  3927. PTVclBgCurrentLine.Set(0);
  3928. NEW(PVTtextAlignV, NIL, Strings.NewString("TextAlignV"), Strings.NewString("vertical Text Alignment"));
  3929. PVTtextAlignV.Set(WMGraphics.AlignTop);
  3930. END InitPrototypes;
  3931. PROCEDURE EnablePiemenu*;
  3932. BEGIN
  3933. PTVAllowPiemenu.Set( TRUE );
  3934. KernelLog.String( "Piemenu enabled" ); KernelLog.Ln
  3935. END EnablePiemenu;
  3936. PROCEDURE DisablePiemenu*;
  3937. BEGIN
  3938. PTVAllowPiemenu.Set( FALSE );
  3939. KernelLog.String( "Piemenu disabled" ); KernelLog.Ln
  3940. END DisablePiemenu;
  3941. PROCEDURE TextViewFactory*() : XML.Element;
  3942. VAR e : TextView;
  3943. BEGIN
  3944. NEW(e); RETURN e
  3945. END TextViewFactory;
  3946. (* Inserts a ocharacter from the outside into the current textView's text at its current position *)
  3947. PROCEDURE InsertChar*(newChar : Char32) : INTEGER;
  3948. BEGIN
  3949. IF currentTextView # NIL THEN
  3950. RETURN currentTextView.InsertChar(newChar);
  3951. ELSE
  3952. RETURN -3;
  3953. END;
  3954. END InsertChar;
  3955. PROCEDURE Refresh*;
  3956. BEGIN
  3957. IF currentTextView # NIL THEN
  3958. currentTextView.layout.FullLayout(TRUE);
  3959. currentTextView.Invalidate;
  3960. currentTextView.CheckNumberOfLines;
  3961. END;
  3962. END Refresh;
  3963. PROCEDURE Cleanup;
  3964. BEGIN
  3965. cursorBlinker.Finalize;
  3966. END Cleanup;
  3967. PROCEDURE GenTextView*(): XML.Element;
  3968. VAR e : TextView;
  3969. BEGIN
  3970. NEW(e); RETURN e
  3971. END GenTextView;
  3972. BEGIN
  3973. NEW(cursorBlinker);
  3974. NEW(DefaultStyle);
  3975. Modules.InstallTermHandler(Cleanup);
  3976. InitStrings;
  3977. InitPrototypes;
  3978. manager := WMWindowManager.GetDefaultManager();
  3979. END WMTextView.