WMTextView.Mod 142 KB

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