WMTextView.Mod 142 KB

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