WMTextView.Mod 144 KB

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