Texts.Mod 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524
  1. MODULE Texts; (** AUTHOR "TF"; PURPOSE "Basic Unicode text system"; *)
  2. IMPORT
  3. KernelLog, Streams, Kernel, WMEvents, Locks, Strings, FP1616, UTF8Strings,
  4. XML, XMLParser, XMLScanner, XMLObjects, Files, Configuration;
  5. CONST
  6. OpInsert* = 0;
  7. OpDelete* = 1;
  8. OpAttributes* = 2;
  9. OpMulti* = 3;
  10. NewLineChar* = 10;
  11. TabChar* = 9;
  12. SpaceChar* = 32;
  13. ObjectChar* = -1;
  14. LabelChar* = -2;
  15. UsePieceTable = TRUE;
  16. TraceHard = FALSE;
  17. TYPE
  18. UCS32String* = ARRAY OF LONGINT;
  19. PUCS32String* = POINTER TO UCS32String;
  20. Char32* = LONGINT;
  21. FontInfo* = OBJECT
  22. VAR
  23. fontcache* : ANY;
  24. name* : ARRAY 32 OF CHAR;
  25. size* : LONGINT;
  26. style* : SET;
  27. PROCEDURE IsEqual*(f : FontInfo): BOOLEAN;
  28. BEGIN
  29. RETURN (name = f.name) & (size = f.size) & (style = f.style)
  30. END IsEqual;
  31. PROCEDURE Clone*() : FontInfo;
  32. VAR f : FontInfo;
  33. BEGIN
  34. NEW(f);
  35. f.fontcache := fontcache; COPY(name, f.name); f.size := size; f.style := style;
  36. RETURN f
  37. END Clone;
  38. END FontInfo;
  39. Attributes* = OBJECT
  40. VAR
  41. color*, bgcolor* : LONGINT;
  42. voff* : LONGINT;
  43. fontInfo* : FontInfo;
  44. PROCEDURE Set* (color, bgcolor, voff : LONGINT; CONST name : ARRAY OF CHAR; size : LONGINT; style : SET);
  45. BEGIN
  46. SELF.color := color;
  47. SELF.bgcolor := bgcolor;
  48. SELF.voff := voff;
  49. NEW(fontInfo);
  50. COPY(name, fontInfo.name);
  51. fontInfo.size := size;
  52. fontInfo.style := style
  53. END Set;
  54. PROCEDURE IsEqual*(attr : Attributes) : BOOLEAN;
  55. BEGIN
  56. RETURN (attr=SELF) OR (attr # NIL) & (color = attr.color) & (bgcolor = attr.bgcolor) & (voff = attr.voff) &
  57. ( (fontInfo = NIL) & (attr.fontInfo = NIL) OR fontInfo.IsEqual(attr.fontInfo))
  58. END IsEqual;
  59. PROCEDURE Clone*():Attributes;
  60. VAR a : Attributes;
  61. BEGIN
  62. NEW(a);
  63. a.color := color; a.bgcolor := bgcolor; a.voff := voff; IF fontInfo # NIL THEN a.fontInfo := fontInfo.Clone() END;
  64. RETURN a
  65. END Clone;
  66. END Attributes;
  67. AttributeChangerProc* = PROCEDURE {DELEGATE} (VAR attributes : Attributes; userData : ANY);
  68. StyleChangedMsg* = OBJECT
  69. END StyleChangedMsg;
  70. ParagraphStyle* = OBJECT
  71. VAR
  72. name* : ARRAY 128 OF CHAR; (* name of the paragraph style *)
  73. alignment* : LONGINT; (* 0 = left, 1 = center, 2 = right, 3 = justified *)
  74. spaceBefore* : LONGINT; (* space before paragraph [mm] *)
  75. spaceAfter* : LONGINT; (* space after paragrapg [mm] *)
  76. leftIndent* : LONGINT; (* left Indent [mm] *)
  77. rightIndent* : LONGINT; (* right Indent [mm] *)
  78. firstIndent* : LONGINT; (* first Line Indent [mm] *)
  79. charStyle* : CharacterStyle; (* default character style *)
  80. tabStops* : ARRAY 256 OF CHAR; (* tabStop String *)
  81. PROCEDURE IsEqual*(style : ParagraphStyle) : BOOLEAN;
  82. BEGIN
  83. RETURN (style # NIL) & (name = style.name) & (alignment = style.alignment) & (spaceBefore = style.spaceBefore) &
  84. (spaceAfter = style.spaceAfter) & (leftIndent = style.leftIndent) & (rightIndent = style.rightIndent) &
  85. (firstIndent = style.firstIndent) & (charStyle = style.charStyle) & (tabStops = style.tabStops)
  86. END IsEqual;
  87. PROCEDURE Clone*(): ParagraphStyle;
  88. VAR newStyle : ParagraphStyle; newName : ARRAY 128 OF CHAR;
  89. BEGIN
  90. NEW(newStyle);
  91. COPY(name,newName);
  92. Strings.Append(newName,"COPY");
  93. WHILE GetParagraphStyleByName(newName) # NIL DO
  94. Strings.Append(newName,"COPY");
  95. END;
  96. COPY(newName, newStyle.name);
  97. newStyle.alignment := alignment;
  98. newStyle.spaceBefore := spaceBefore;
  99. newStyle.spaceAfter := spaceAfter;
  100. newStyle.leftIndent := leftIndent;
  101. newStyle.rightIndent := rightIndent;
  102. newStyle.firstIndent := firstIndent;
  103. newStyle.charStyle := charStyle;
  104. COPY(tabStops, newStyle.tabStops);
  105. RETURN newStyle;
  106. END Clone;
  107. END ParagraphStyle;
  108. ParagraphStyleArray* = POINTER TO ARRAY OF ParagraphStyle;
  109. CharacterStyle* = OBJECT
  110. VAR
  111. fontcache* : ANY;
  112. name* : ARRAY 128 OF CHAR; (* name of the character style *)
  113. family* : ARRAY 32 OF CHAR; (* font family *)
  114. style* : SET; (* font style; 0 = bold, 1 = italic *)
  115. size* : LONGINT; (* font size [pt]; 1pt == 1/72inch == 0,3527777778mm *)
  116. leading* : LONGINT; (* baseline distance [pt] - usually 120% of font size *)
  117. baselineShift* : LONGINT; (* baseline shift up/down [pt] *)
  118. tracking* : LONGINT; (* character spacing [pt] *)
  119. scaleHorizontal* : LONGINT; (* horizontal character scale *)
  120. scaleVertical* : LONGINT; (* vertical character scale *)
  121. color* : LONGINT; (* character color *)
  122. bgColor* : LONGINT; (* character background color *)
  123. PROCEDURE &New*;
  124. BEGIN
  125. fontcache := NIL;
  126. END New;
  127. PROCEDURE IsEqual*(cstyle : CharacterStyle) : BOOLEAN;
  128. BEGIN
  129. RETURN (cstyle # NIL) & (name = cstyle.name) & (family = cstyle.family) & (style = cstyle.style) & (leading = cstyle.leading) &
  130. (baselineShift = cstyle.baselineShift) & (tracking = cstyle.tracking) &
  131. (scaleHorizontal = cstyle.scaleHorizontal) & (scaleVertical = cstyle.scaleVertical) & (color = cstyle.color) &
  132. (bgColor = cstyle.bgColor)
  133. END IsEqual;
  134. PROCEDURE Clone*(): CharacterStyle;
  135. VAR newStyle : CharacterStyle; newName : ARRAY 128 OF CHAR;
  136. BEGIN
  137. NEW(newStyle);
  138. COPY(name, newName);
  139. Strings.Append(newName, "COPY");
  140. WHILE GetCharacterStyleByName(newName) # NIL DO
  141. Strings.Append(newName,"COPY");
  142. END;
  143. COPY(newName, newStyle.name);
  144. COPY(family, newStyle.family);
  145. newStyle.style := style;
  146. newStyle.size := size;
  147. newStyle.leading := leading;
  148. newStyle.baselineShift := baselineShift;
  149. newStyle.tracking := tracking;
  150. newStyle.scaleHorizontal := scaleHorizontal;
  151. newStyle.scaleVertical := scaleVertical;
  152. newStyle.color := color;
  153. newStyle.bgColor := bgColor;
  154. RETURN newStyle;
  155. END Clone;
  156. END CharacterStyle;
  157. CharacterStyleArray* = POINTER TO ARRAY OF CharacterStyle;
  158. CONST
  159. HLOver* = 0; HLUnder* = 1; HLWave* = 2;
  160. TYPE
  161. HighlightStyle* = OBJECT
  162. VAR
  163. kind*: LONGINT;
  164. PROCEDURE IsEqual*(hstyle: HighlightStyle) : BOOLEAN;
  165. BEGIN
  166. RETURN (hstyle # NIL) & (kind = hstyle.kind);
  167. END IsEqual;
  168. END HighlightStyle;
  169. Link* = Strings.String;
  170. Piece* = OBJECT
  171. VAR
  172. next*, prev* : Piece;
  173. len*, startpos* : LONGINT;
  174. attributes* : Attributes;
  175. pstyle* : ParagraphStyle;
  176. cstyle* : CharacterStyle;
  177. link* : Link;
  178. (** Return a copy of the piece, prev/next pointers nil and pos 0 *)
  179. PROCEDURE Clone*() : Piece;
  180. BEGIN
  181. HALT(301); (* Abstract *)
  182. RETURN NIL
  183. END Clone;
  184. (** Split the UnicodePiece at pos in text position and return right piece *)
  185. PROCEDURE Split*(pos : LONGINT; VAR right : Piece);
  186. BEGIN
  187. HALT(301); (* Abstract *)
  188. END Split;
  189. (** Merge right to self; return true if ok *)
  190. PROCEDURE Merge*(right : Piece) : BOOLEAN;
  191. BEGIN
  192. HALT(301); (* Abstract *)
  193. RETURN FALSE
  194. END Merge;
  195. END Piece;
  196. UnicodePiece* = OBJECT(Piece)
  197. (** index in text position; index and (index + length) must be in the piece *)
  198. PROCEDURE GetUCS32Buf*(index : LONGINT; length : LONGINT; VAR ucs : UCS32String; VAR res : WORD);
  199. END GetUCS32Buf;
  200. (** index in text position; index and (index + length) must be in the piece *)
  201. PROCEDURE GetUCS32*(index : LONGINT; VAR ucs : Char32);
  202. END GetUCS32;
  203. END UnicodePiece;
  204. MemUnicodePiece* = OBJECT(UnicodePiece)
  205. VAR
  206. buffer : PUCS32String;
  207. PROCEDURE SetBuf(CONST buffer : UCS32String);
  208. VAR i : LONGINT;
  209. BEGIN
  210. WHILE buffer[i] # 0 DO INC(i) END; len := i;
  211. NEW(SELF.buffer, len);
  212. FOR i := 0 TO len - 1 DO SELF.buffer[i] := buffer[i] END
  213. END SetBuf;
  214. PROCEDURE SetBufAsUTF8(CONST buffer : ARRAY OF CHAR);
  215. VAR length, i, idx : LONGINT;
  216. BEGIN
  217. length := UTF8Strings.Length(buffer);
  218. NEW(SELF.buffer, length);
  219. i := 0; idx := 0;
  220. WHILE (i < length) & UTF8Strings.DecodeChar(buffer, idx, SELF.buffer[i]) DO INC(i); END;
  221. END SetBufAsUTF8;
  222. (** Return a copy of the piece, prev/next pointers nil and pos 0 *)
  223. PROCEDURE Clone*() : Piece;
  224. VAR m : MemUnicodePiece; i : LONGINT;
  225. BEGIN
  226. NEW(m);
  227. m.len := len;
  228. IF attributes # NIL THEN m.attributes := attributes.Clone() END;
  229. IF cstyle # NIL THEN m.cstyle := cstyle END;
  230. IF pstyle # NIL THEN m.pstyle := pstyle END;
  231. IF link # NIL THEN m.link := link END;
  232. NEW(m.buffer, LEN(buffer));
  233. FOR i := 0 TO LEN(buffer) - 1 DO m.buffer[i] := buffer[i] END;
  234. RETURN m
  235. END Clone;
  236. (** index in text position; index and (index + length) must be in the piece *)
  237. PROCEDURE GetUCS32Buf*(index : LONGINT; length : LONGINT; VAR ucs : UCS32String; VAR res : WORD);
  238. VAR i, j : LONGINT;
  239. BEGIN
  240. i := index - startpos; IF (i < 0) OR (i >= len) THEN ucs[0] := 0; res := -1; RETURN END;
  241. j := 0;
  242. WHILE (j < LEN(ucs)) & (j < length) & (i < len) DO ucs[j] := buffer[i]; INC(i); INC(j) END;
  243. IF (j < length) & (i >= len) THEN res := -1 ELSE res := 0 END;
  244. IF (j > LEN(ucs) - 1) THEN j := LEN(ucs) -1 END;
  245. ucs[j] := 0
  246. END GetUCS32Buf;
  247. PROCEDURE GetUCS32*(index : LONGINT; VAR ucs : Char32);
  248. VAR i: LONGINT;
  249. BEGIN
  250. i := index - startpos; IF (i < 0) OR (i >= len) THEN ucs := 0 ELSE ucs := buffer[i] END;
  251. END GetUCS32;
  252. (** Split the UnicodePiece at pos in text position and return right piece *)
  253. PROCEDURE Split*(pos : LONGINT; VAR right : Piece);
  254. VAR mp : MemUnicodePiece; i, j : LONGINT;
  255. BEGIN
  256. IF pos - startpos < len THEN
  257. (* create right part *)
  258. NEW(mp);
  259. IF attributes # NIL THEN mp.attributes := attributes.Clone() END;
  260. IF cstyle # NIL THEN mp.cstyle := cstyle END;
  261. IF pstyle # NIL THEN mp.pstyle := pstyle END;
  262. IF link # NIL THEN mp.link := link END;
  263. NEW(mp.buffer, len - (pos - startpos));
  264. mp.len := len - (pos - startpos); mp.startpos := pos;
  265. j := 0; FOR i := pos - startpos TO len - 1 DO mp.buffer[j] := buffer[i]; INC(j) END;
  266. (* adjust own length *)
  267. len := (pos - startpos);
  268. IF len <= 0 THEN
  269. KernelLog.String("BUG BUG BUG BUG BUG BUG BUG BUG"); KernelLog.Ln;
  270. END;
  271. (* linking *)
  272. mp.next := next; IF next # NIL THEN next.prev := mp END; mp.prev := SELF; next := mp;
  273. right := mp
  274. ELSE right := next
  275. END
  276. END Split;
  277. (** Merge right to self; return true if ok *)
  278. PROCEDURE Merge*(right : Piece) : BOOLEAN;
  279. VAR temp : PUCS32String; i, j : LONGINT;
  280. BEGIN
  281. IF right = NIL THEN RETURN FALSE END;
  282. IF right = SELF THEN KernelLog.String("Consistency Check in Texts Failed"); KernelLog.Ln END;
  283. IF (right.len > 1) & (right.next = NIL) THEN RETURN FALSE END; (* avoid overgreedily merging *)
  284. IF (right IS MemUnicodePiece) & (right # SELF) &
  285. ((attributes = NIL) & (right.attributes = NIL) OR (attributes # NIL) & attributes.IsEqual(right.attributes)) &
  286. ((cstyle = NIL) & (right.cstyle = NIL) & (pstyle = NIL) & (right.pstyle = NIL) OR
  287. (cstyle # NIL) & cstyle.IsEqual(right.cstyle) & (pstyle # NIL) & pstyle.IsEqual(right.pstyle)) &
  288. (link = right.link) &
  289. (len < 1000) THEN
  290. NEW(temp, len + right.len);
  291. FOR i := 0 TO len - 1 DO temp[i] := buffer[i] END;
  292. WITH right : MemUnicodePiece DO
  293. j := len; FOR i := 0 TO right.len - 1 DO temp[j] := right.buffer[i]; INC(j) END;
  294. END;
  295. buffer := temp;
  296. len := len + right.len; next := right.next; IF next # NIL THEN next.prev := SELF END;
  297. RETURN TRUE
  298. ELSE
  299. RETURN FALSE
  300. END
  301. END Merge;
  302. END MemUnicodePiece;
  303. ObjectPiece* = OBJECT(Piece)
  304. VAR
  305. object* : ANY;
  306. PROCEDURE &Init*;
  307. BEGIN
  308. len := 1
  309. END Init;
  310. (** Return a copy of the piece, prev/next pointers nil and pos 0 *)
  311. PROCEDURE Clone*() : Piece;
  312. VAR p : ObjectPiece;
  313. BEGIN
  314. NEW(p);
  315. p.len := len;
  316. IF attributes # NIL THEN p.attributes := attributes.Clone() END;
  317. IF cstyle # NIL THEN p.cstyle := cstyle END;
  318. IF pstyle # NIL THEN p.pstyle := pstyle END;
  319. IF link # NIL THEN p.link := link END;
  320. p.object := object;
  321. RETURN p
  322. END Clone;
  323. PROCEDURE Split*(pos : LONGINT; VAR right : Piece);
  324. BEGIN
  325. IF pos - startpos < len THEN
  326. KernelLog.String("Should never happen"); KernelLog.Ln;
  327. ELSE right := next; (* KernelLog.String("Huga right is next"); KernelLog.Ln; *)
  328. END
  329. END Split;
  330. (** Merge right to self; return true if ok *)
  331. PROCEDURE Merge*(right : Piece) : BOOLEAN;
  332. BEGIN
  333. RETURN FALSE
  334. END Merge;
  335. END ObjectPiece;
  336. LabelPiece* = OBJECT(Piece)
  337. VAR
  338. label* : Strings.String;
  339. PROCEDURE &Init*;
  340. BEGIN
  341. len := 1
  342. END Init;
  343. (** Return a copy of the piece, prev/next pointers nil and pos 0 *)
  344. PROCEDURE Clone*() : Piece;
  345. VAR p : LabelPiece;
  346. BEGIN
  347. NEW(p);
  348. p.len := len;
  349. p.label := label;
  350. RETURN p
  351. END Clone;
  352. PROCEDURE Split*(pos : LONGINT; VAR right : Piece);
  353. BEGIN
  354. IF pos - startpos < len THEN
  355. KernelLog.String("Should never happen"); KernelLog.Ln;
  356. ELSE right := next; (* KernelLog.String("Huga right is next"); KernelLog.Ln; *)
  357. END
  358. END Split;
  359. (** Merge right to self; return true if ok *)
  360. PROCEDURE Merge*(right : Piece) : BOOLEAN;
  361. BEGIN
  362. RETURN FALSE
  363. END Merge;
  364. END LabelPiece;
  365. (* Used to translate an internal position into a display position and vice versa *)
  366. PositionTranslator* = PROCEDURE {DELEGATE} (pos : LONGINT) : LONGINT;
  367. (** a TextPosition is assigned to a text and positioned with SetPosition. If
  368. the text is changed after the position was set, the position is updated *)
  369. TextPosition* = OBJECT
  370. VAR
  371. position : LONGINT;
  372. data* : ANY;
  373. text- : UnicodeText;
  374. GetInternalPos, GetDisplayPos : PositionTranslator;
  375. nextInternalPos* : LONGINT;
  376. PROCEDURE &New*(t : UnicodeText);
  377. BEGIN
  378. text := t; text.RegisterPositionObject(SELF); position := 0;
  379. END New;
  380. (** Listens for text changes *)
  381. PROCEDURE Changed*(op, pos, len : LONGINT);
  382. BEGIN
  383. IF TraceHard THEN
  384. KernelLog.String("TextPosition : ChangeRequest"); KernelLog.Int(op, 5); KernelLog.Int(pos, 5); KernelLog.Int(len, 5);KernelLog.Ln;
  385. END;
  386. IF (position >= pos) & (op = OpInsert) THEN
  387. IF ((GetInternalPos # NIL) & (GetDisplayPos # NIL)) THEN
  388. position := GetDisplayPos(nextInternalPos);
  389. ELSE
  390. INC(position, len);
  391. END;
  392. ELSIF (position >= pos) & (position <= pos + len) & (op = OpDelete) THEN
  393. position := pos;
  394. ELSIF (position > pos) & (op = OpDelete) THEN
  395. IF position < len THEN KernelLog.String("WRONG"); KernelLog.String(" pos ="); KernelLog.Int(pos, 5);
  396. KernelLog.String(" len ="); KernelLog.Int(len, 5);
  397. KernelLog.String(" position = "); KernelLog.Int(position, 0); KernelLog.Ln;
  398. KernelLog.Ln END;
  399. DEC(position, len)
  400. END
  401. END Changed;
  402. (** Position in elements from text start. *)
  403. PROCEDURE SetPosition*(pos : LONGINT);
  404. BEGIN
  405. IF pos < 0 THEN pos := 0 ELSIF pos > text.GetLength() THEN pos := text.GetLength() END;
  406. position := pos
  407. END SetPosition;
  408. (** Returns position in elements from the text start *)
  409. PROCEDURE GetPosition*():LONGINT;
  410. BEGIN
  411. RETURN position
  412. END GetPosition;
  413. (* Sets the callback function for display-to-internal-position translation *)
  414. PROCEDURE SetInternalPositionTranslator*(getInternalPos : PositionTranslator);
  415. BEGIN
  416. GetInternalPos := getInternalPos;
  417. END SetInternalPositionTranslator;
  418. (* Sets the callback function for internal-to-display-position translation *)
  419. PROCEDURE SetDisplayPositionTranslator*(getDisplayPos : PositionTranslator);
  420. BEGIN
  421. GetDisplayPos := getDisplayPos;
  422. END SetDisplayPositionTranslator;
  423. END TextPosition;
  424. (** a reader may not be shared by processes, must text must be hold by process *)
  425. TextReader* = OBJECT(TextPosition)
  426. VAR
  427. piece : Piece;
  428. backwards : BOOLEAN;
  429. eot- : BOOLEAN;
  430. voff-, color-, bgcolor- : LONGINT;
  431. font- : FontInfo;
  432. attributes- : Attributes;
  433. cstyle- : CharacterStyle;
  434. pstyle- : ParagraphStyle;
  435. link- : Link;
  436. object- : ANY;
  437. PROCEDURE &New*(t : UnicodeText);
  438. BEGIN
  439. New^(t); backwards := FALSE;
  440. END New;
  441. (* Clones the properties of an other reader to this reader *)
  442. PROCEDURE CloneProperties*(CONST otherReader : TextReader);
  443. BEGIN
  444. voff := otherReader.voff;
  445. color := otherReader.color;
  446. bgcolor := otherReader.bgcolor;
  447. IF font # NIL THEN font := otherReader.font.Clone(); END;
  448. IF otherReader.attributes # NIL THEN attributes := otherReader.attributes.Clone(); END;
  449. IF otherReader.cstyle # NIL THEN cstyle := otherReader.cstyle.Clone(); END;
  450. IF otherReader.pstyle # NIL THEN pstyle := otherReader.pstyle.Clone(); END;
  451. IF otherReader.link # NIL THEN link := Strings.NewString(otherReader.link^); END;
  452. object := otherReader.object;
  453. END CloneProperties;
  454. (** Listens for text changes *)
  455. PROCEDURE Changed*(op, pos, len : LONGINT);
  456. BEGIN
  457. Changed^(op, pos, len); piece := NIL
  458. END Changed;
  459. PROCEDURE ReadCh*(VAR ucs32 : LONGINT);
  460. VAR res : WORD; tfont: FontInfo;
  461. tempObj : ObjectPiece;
  462. BEGIN
  463. eot := (backwards) & (position = 0) OR (~backwards) & (position = text.GetLength());
  464. IF eot THEN ucs32 := 0; RETURN END;
  465. IF (piece = NIL) OR (piece.startpos > position) OR (piece.startpos + piece.len <= position) THEN
  466. text.FindPiece(position, piece);
  467. IF (piece # NIL) & (piece IS ObjectPiece) THEN tempObj := piece(ObjectPiece); object := tempObj.object;
  468. ELSIF (piece # NIL) & (piece IS LabelPiece) THEN object := piece(LabelPiece);
  469. ELSE object := NIL
  470. END;
  471. IF piece = NIL THEN res := -1; ucs32 := 0; RETURN END;
  472. attributes := piece.attributes;
  473. cstyle := piece.cstyle;
  474. pstyle := piece.pstyle;
  475. link := piece.link;
  476. IF cstyle # NIL THEN
  477. voff := cstyle.baselineShift; color := cstyle.color; bgcolor := cstyle.bgColor;
  478. NEW(tfont); COPY(cstyle.family, tfont.name); tfont.size := ENTIER(FP1616.FixpToFloat(cstyle.size)); tfont.style := cstyle.style;
  479. font := tfont;
  480. ELSIF pstyle # NIL THEN
  481. voff := pstyle.charStyle.baselineShift; color := pstyle.charStyle.color; bgcolor := pstyle.charStyle.bgColor;
  482. NEW(tfont); COPY(pstyle.charStyle.family, tfont.name); tfont.size := ENTIER(FP1616.FixpToFloat(pstyle.charStyle.size)); tfont.style := pstyle.charStyle.style;
  483. font := tfont;
  484. ELSIF attributes # NIL THEN voff := attributes.voff; color := attributes.color; bgcolor := attributes.bgcolor; font := attributes.fontInfo
  485. ELSE voff := 0; color := 0FFH; bgcolor := 0; font := NIL
  486. END;
  487. END;
  488. IF TraceHard THEN
  489. IF res # 0 THEN
  490. KernelLog.String(" piece "); KernelLog.Int(piece.startpos, 5); KernelLog.String(" piepst :"); KernelLog.Int(position, 5);
  491. KernelLog.Ln;
  492. HALT(99);
  493. END;
  494. END;
  495. IF piece IS UnicodePiece THEN piece(UnicodePiece).GetUCS32(position, ucs32)
  496. ELSIF piece IS ObjectPiece THEN ucs32 := ObjectChar
  497. ELSIF piece IS LabelPiece THEN ucs32 := LabelChar
  498. END;
  499. IF backwards THEN DEC(position) ELSE INC(position) END
  500. END ReadCh;
  501. (** Position in elements from text start. *)
  502. PROCEDURE SetPosition*(pos : LONGINT);
  503. VAR length : LONGINT;
  504. BEGIN
  505. length := text.GetLength();
  506. IF pos < 0 THEN
  507. pos := 0;
  508. ELSIF pos > length THEN
  509. pos := length;
  510. END;
  511. position := pos;
  512. eot := (backwards & (position = 0)) OR (~backwards & (position = length));
  513. END SetPosition;
  514. (** Direction the text is read. dir >= 0 --> forward; dir < 0 --> backwards
  515. Backwards can be very slow depending on the text *)
  516. PROCEDURE SetDirection*(dir : LONGINT);
  517. BEGIN
  518. backwards := dir < 0;
  519. eot := (backwards & (position = 0)) OR (~backwards & (position = text.GetLength()));
  520. END SetDirection;
  521. END TextReader;
  522. TextChangeInfo* = OBJECT
  523. VAR
  524. timestamp*, op*, pos*, len* : LONGINT;
  525. END TextChangeInfo;
  526. UndoManager*= OBJECT
  527. PROCEDURE InsertText*(pos: LONGINT; text: Text);
  528. END InsertText;
  529. PROCEDURE DeleteText*(pos: LONGINT; text: Text);
  530. END DeleteText;
  531. PROCEDURE BeginObjectChange*(pos: LONGINT);
  532. END BeginObjectChange;
  533. PROCEDURE ObjectChanged*(pos, len, type: LONGINT; obj: ANY);
  534. END ObjectChanged;
  535. PROCEDURE EndObjectChange*(len, type: LONGINT; to: ANY);
  536. END EndObjectChange;
  537. PROCEDURE SetText*(text: Text);
  538. END SetText;
  539. PROCEDURE Undo*;
  540. END Undo;
  541. PROCEDURE Redo*;
  542. END Redo;
  543. (** Called when the write lock on the associated text is released. Can be used to notify listeners
  544. that are interestes in the current number of available undos/redos *)
  545. PROCEDURE InformListeners*;
  546. END InformListeners;
  547. END UndoManager;
  548. TYPE
  549. (** UnicodeText offers an editable unicode text abstraction, basing on UnicodePiece *)
  550. UnicodeText* = OBJECT
  551. VAR
  552. first : Piece;
  553. length : LONGINT;
  554. nofPieces : LONGINT;
  555. posObjects : Kernel.FinalizedCollection;
  556. pop, ppos, plen : LONGINT;
  557. timestamp : LONGINT;
  558. upOp, upPos, upLen : LONGINT;
  559. onTextChanged* : WMEvents.EventSource;
  560. lock : Locks.RWLock;
  561. pieceTableOk : BOOLEAN;
  562. pieceTable : POINTER TO ARRAY OF Piece;
  563. isUTF- : BOOLEAN; (* is false by default, which prevents the text from being reformatted if not utf *)
  564. um: UndoManager;
  565. PROCEDURE &New*;
  566. BEGIN
  567. NEW(lock);
  568. IF UsePieceTable THEN NEW(pieceTable, 256) END;
  569. pieceTableOk := FALSE;
  570. NEW(posObjects); timestamp := 0;
  571. upOp := -1; upPos := 0; upLen := 0;
  572. nofPieces := 0;
  573. isUTF := FALSE;
  574. NEW(onTextChanged, SELF, onTextChangedStr, NIL, NIL);
  575. END New;
  576. (* Marks the text as utf-formatted. Only utf-formatted texts are treated by the bidi algorithm. *)
  577. PROCEDURE SetUTF*(utf : BOOLEAN);
  578. BEGIN
  579. IF forceUTF THEN
  580. isUTF := TRUE;
  581. ELSIF unforceUTF THEN
  582. isUTF := FALSE;
  583. ELSE
  584. isUTF := utf;
  585. END;
  586. END SetUTF;
  587. PROCEDURE SetUndoManager*(u: UndoManager);
  588. BEGIN
  589. um := u;
  590. IF um # NIL THEN
  591. um.SetText(SELF)
  592. END
  593. END SetUndoManager;
  594. (** acquire a write lock on the object *)
  595. PROCEDURE AcquireWrite*;
  596. BEGIN
  597. lock.AcquireWrite
  598. END AcquireWrite;
  599. (** release the write lock on the object *)
  600. PROCEDURE ReleaseWrite*;
  601. VAR removeLock : BOOLEAN;
  602. op, pos, len, localtimestamp : LONGINT;
  603. BEGIN
  604. removeLock := lock.GetWLockLevel() = 1;
  605. IF removeLock THEN op := upOp; pos := upPos; len := upLen; localtimestamp := GetTimestamp(); upOp := -1 END;
  606. lock.ReleaseWrite;
  607. IF removeLock & (op >= 0) THEN InformListeners(localtimestamp, op, pos, len) END;
  608. END ReleaseWrite;
  609. (** Returns TRUE if the calling thread owns the write lock for this text, FALSE otherwise *)
  610. PROCEDURE HasWriteLock*() : BOOLEAN;
  611. BEGIN
  612. RETURN lock.HasWriteLock();
  613. END HasWriteLock;
  614. (** acquire a write lock on the object *)
  615. PROCEDURE AcquireRead*;
  616. BEGIN
  617. lock.AcquireRead
  618. END AcquireRead;
  619. (** release the write lock on the object *)
  620. PROCEDURE ReleaseRead*;
  621. BEGIN
  622. lock.ReleaseRead
  623. END ReleaseRead;
  624. (** Returns TRUE if the calling thread owns the read lock for this text, FALSE otherwise *)
  625. PROCEDURE HasReadLock*() : BOOLEAN;
  626. BEGIN
  627. RETURN lock.HasReadLock();
  628. END HasReadLock;
  629. PROCEDURE InformListeners(timestamp, op, pos, len : LONGINT);
  630. VAR updateInfo : TextChangeInfo; um : UndoManager;
  631. BEGIN
  632. NEW(updateInfo);
  633. updateInfo.timestamp := timestamp; updateInfo.op := op; updateInfo.pos := pos; updateInfo.len := len;
  634. onTextChanged.Call(updateInfo);
  635. um := SELF.um;
  636. IF (um # NIL) THEN um.InformListeners; END;
  637. END InformListeners;
  638. PROCEDURE UpdatePieceTable;
  639. VAR cur : Piece; len, i : LONGINT;
  640. BEGIN
  641. IF LEN(pieceTable^) < nofPieces THEN NEW(pieceTable, nofPieces * 2) END;
  642. len := LEN(pieceTable^);
  643. cur := first; i := 0; pieceTable[0] := first;
  644. WHILE (cur # NIL) & (i < len) DO pieceTable[i] := cur; cur := cur.next; INC(i) END;
  645. pieceTableOk := i = nofPieces;
  646. IF ~pieceTableOk THEN KernelLog.Int(i, 0); KernelLog.String(" vs "); KernelLog.Int(nofPieces, 0); KernelLog.Ln END;
  647. END UpdatePieceTable;
  648. (* Return the piece that contains pos or the last piece if pos is not found *)
  649. PROCEDURE FindPiece(pos : LONGINT; VAR piece : Piece);
  650. VAR a, b, m : LONGINT;
  651. BEGIN
  652. IF UsePieceTable THEN
  653. IF ~pieceTableOk THEN UpdatePieceTable END
  654. END;
  655. IF pieceTableOk THEN
  656. a := 0; b := nofPieces - 1;
  657. ASSERT(pieceTable[0] = first);
  658. WHILE (a < b) DO m := (a + b) DIV 2;
  659. piece := pieceTable[m];
  660. IF piece.startpos + piece.len <= pos THEN a := m + 1 ELSE b := m END
  661. END;
  662. piece := pieceTable[a];
  663. IF piece = NIL THEN RETURN END;
  664. IF ~(piece.startpos + piece.len >= pos) THEN
  665. IF FALSE (*debug*) THEN
  666. KernelLog.String("pos = "); KernelLog.Int(pos, 0); KernelLog.Ln;
  667. KernelLog.String("startpos = "); KernelLog.Int(piece.startpos, 0); KernelLog.Ln;
  668. KernelLog.String("len = "); KernelLog.Int(piece.len, 0); KernelLog.Ln;
  669. END;
  670. END;
  671. ELSE
  672. piece := first; IF piece = NIL THEN RETURN END;
  673. LOOP
  674. IF (piece.next = NIL) OR (piece.startpos + piece.len > pos) THEN RETURN END;
  675. piece := piece.next
  676. END
  677. END
  678. END FindPiece;
  679. PROCEDURE SendPositionUpdate(obj: ANY; VAR cont: BOOLEAN);
  680. BEGIN
  681. cont := TRUE;
  682. IF obj IS TextPosition THEN
  683. obj(TextPosition).Changed(pop, ppos, plen)
  684. END
  685. END SendPositionUpdate;
  686. PROCEDURE UpdatePositionObjects(op, pos, len : LONGINT);
  687. BEGIN
  688. SELF.pop := op; SELF.ppos := pos; SELF.plen := len;
  689. posObjects.Enumerate(SendPositionUpdate)
  690. END UpdatePositionObjects;
  691. PROCEDURE AccumulateChanges(op, pos, len : LONGINT);
  692. BEGIN
  693. IF upOp >= 0 THEN
  694. IF (upOp = OpInsert) & (op = OpAttributes) & (pos = upPos) & (len = upLen) THEN (* ignore *)
  695. ELSE upOp := OpMulti
  696. END
  697. ELSE upOp := op; upPos := pos; upLen := len
  698. END;
  699. END AccumulateChanges;
  700. (** Register a position object on the text. The TextPosition objects are automatically be updated if the text is changed.
  701. TextPosition objects are automatically unregistred by the garbage collector *)
  702. PROCEDURE RegisterPositionObject*(po : TextPosition);
  703. BEGIN
  704. posObjects.Add(po, NIL)
  705. END RegisterPositionObject;
  706. (** Split the piece list at pos and return left and right. left or right can be NIL if at end/begin *)
  707. PROCEDURE GetSplittedPos(pos : LONGINT; VAR left, right: Piece);
  708. VAR p, t : Piece;
  709. BEGIN
  710. FindPiece(pos, p);
  711. IF p = NIL THEN left := NIL; right := NIL; RETURN END;
  712. IF p.startpos = pos THEN left := p.prev; right := p
  713. ELSE t := p.next; left := p; p.Split(pos, right);
  714. IF right # t THEN
  715. pieceTableOk := FALSE; INC(nofPieces)
  716. END
  717. END
  718. END GetSplittedPos;
  719. (** Insert a piece at position pos into the text. Index in characters/objects *)
  720. PROCEDURE InsertPiece*(pos : LONGINT; n : Piece);
  721. VAR l, r, cur : Piece;
  722. chpos, chlen : LONGINT;
  723. BEGIN
  724. ASSERT(lock.HasWriteLock(), 3000);
  725. INC(timestamp);
  726. IF pos > length THEN pos := length END;
  727. INC(length, n.len);
  728. chpos := pos; chlen := n.len;
  729. IF first = NIL THEN n.next := NIL; n.prev := NIL; first := n; nofPieces := 1; pieceTableOk := FALSE
  730. ELSE
  731. GetSplittedPos(pos, l, r);
  732. IF l = NIL THEN n.next := first; first.prev := n; first := n
  733. ELSE l.next := n; n.prev := l; n.next := r; IF r # NIL THEN r.prev := n END
  734. END;
  735. INC(nofPieces);
  736. IF r = NIL THEN
  737. (* optimize loading by re-establishing the pieceTable *)
  738. IF nofPieces < LEN(pieceTable^) THEN pieceTable[nofPieces - 1] := n
  739. ELSE pieceTableOk := FALSE
  740. END
  741. ELSE pieceTableOk := FALSE
  742. END;
  743. cur := n; WHILE cur # NIL DO cur.startpos := pos; INC(pos, cur.len); cur := cur.next END;
  744. cur := n; IF cur.Merge(cur.next) THEN DEC(nofPieces); pieceTableOk := FALSE END;
  745. IF (cur.prev # NIL) & cur.prev.Merge(cur) THEN DEC(nofPieces); pieceTableOk := FALSE END
  746. END;
  747. AccumulateChanges(OpInsert, chpos, chlen);
  748. UpdatePositionObjects(OpInsert, chpos, chlen)
  749. END InsertPiece;
  750. PROCEDURE InsertObject*(obj: XML.Element);
  751. END InsertObject;
  752. (** Insert a UCS32 buffer at position pos into the text. Index in characters/objects *)
  753. PROCEDURE InsertUCS32* (pos : LONGINT; CONST buf : UCS32String);
  754. VAR n : MemUnicodePiece; p : Piece; t: Text;
  755. BEGIN
  756. ASSERT(lock.HasWriteLock(), 3000);
  757. IF buf[0] = 0 THEN RETURN END;
  758. IF pos > GetLength() THEN pos := GetLength() END;
  759. NEW(n); n.SetBuf(buf);
  760. FindPiece(pos, p);
  761. IF (p # NIL) THEN
  762. n.attributes := p.attributes;
  763. n.cstyle := p.cstyle;
  764. n.pstyle := p.pstyle;
  765. n.link := p.link;
  766. END;
  767. IF um # NIL THEN
  768. NEW(t);
  769. t.AcquireWrite;
  770. t.InsertUCS32(0, buf);
  771. um.InsertText(pos, t);
  772. t.ReleaseWrite;
  773. END;
  774. InsertPiece(pos, n);
  775. END InsertUCS32;
  776. PROCEDURE InsertUTF8*(pos : LONGINT; CONST buf : ARRAY OF CHAR);
  777. VAR n : MemUnicodePiece; p : Piece; text : Text;
  778. BEGIN
  779. ASSERT(lock.HasWriteLock(), 3000);
  780. IF (buf[0] # 0X) THEN
  781. IF (pos > GetLength()) THEN pos := GetLength(); END;
  782. NEW(n); n.SetBufAsUTF8(buf);
  783. FindPiece(pos, p);
  784. IF (p # NIL) THEN
  785. n.attributes := p.attributes;
  786. n.cstyle := p.cstyle;
  787. n.pstyle := p.pstyle;
  788. n.link := p.link;
  789. END;
  790. IF (um # NIL) THEN
  791. NEW(text);
  792. text.AcquireWrite;
  793. text.InsertUTF8(0, buf);
  794. um.InsertText(pos, text);
  795. text.ReleaseWrite;
  796. END;
  797. InsertPiece(pos, n);
  798. END;
  799. END InsertUTF8;
  800. (** Delete len characters from position pos *)
  801. PROCEDURE Delete* (pos, len : LONGINT);
  802. VAR al, ar, bl, br, cur: Piece; p : LONGINT; t: Text;
  803. BEGIN
  804. ASSERT(lock.HasWriteLock(), 3000);
  805. INC(timestamp);
  806. (* don't do illegal changes *)
  807. IF (len <= 0) OR (pos < 0) OR (pos + len > length) OR (first = NIL) THEN RETURN END;
  808. IF length = 0 THEN first := NIL; nofPieces := 0; pieceTableOk := FALSE
  809. ELSE
  810. GetSplittedPos(pos, al, ar);
  811. GetSplittedPos(pos + len, bl, br);
  812. IF um # NIL THEN
  813. NEW(t);
  814. t.AcquireWrite;
  815. t.CopyFromText(SELF, pos, len, 0);
  816. um.DeleteText(pos, t);
  817. t.ReleaseWrite;
  818. END;
  819. IF al # NIL THEN
  820. cur := al.next; WHILE (cur # NIL) & (cur # br) DO pieceTableOk := FALSE; DEC(nofPieces); cur := cur.next END;
  821. al.next := br; IF br # NIL THEN br.prev := al END;
  822. cur := al
  823. ELSE
  824. cur := first; WHILE (cur # NIL) & (cur # br) DO pieceTableOk := FALSE; DEC(nofPieces); cur := cur.next END;
  825. IF br # NIL THEN br.startpos := 0; br.prev := NIL END;
  826. first := br; cur := first
  827. END;
  828. IF cur # NIL THEN
  829. (* update the start positions of all the following pieces *)
  830. p := cur.startpos; WHILE cur # NIL DO cur.startpos := p; INC(p, cur.len); cur := cur.next END;
  831. IF (al # NIL) & al.Merge(al.next) THEN DEC(nofPieces) END
  832. END
  833. END;
  834. DEC(length, len);
  835. IF (first = NIL) & (length # 0) THEN KernelLog.String("ERROR : No text but length > 0 ! "); KernelLog.Ln END;
  836. AccumulateChanges(OpDelete, pos, len);
  837. UpdatePositionObjects(OpDelete, pos, len)
  838. END Delete;
  839. PROCEDURE CopyFromText*(fromText: UnicodeText; fromPos, len, destPos : LONGINT);
  840. VAR fromP, toP, curP : Piece; pos : LONGINT; t: Text;
  841. BEGIN
  842. ASSERT(lock.HasWriteLock(), 3000);
  843. ASSERT(fromText.lock.HasReadLock(), 3000);
  844. ASSERT(fromText # NIL);
  845. ASSERT(fromPos >= 0);
  846. ASSERT(len >= 0);
  847. ASSERT(fromPos + len <= fromText.length);
  848. ASSERT(destPos >= 0);
  849. ASSERT((fromText # SELF) OR ((destPos < fromPos) OR (destPos > fromPos + len))); (* Avoid recursive copy *)
  850. fromText.GetSplittedPos(fromPos, curP, fromP);
  851. fromText.GetSplittedPos(fromPos + len, curP, toP);
  852. curP := fromP; pos := destPos;
  853. WHILE (curP # NIL) & (curP # toP) DO
  854. InsertPiece(pos, curP.Clone());
  855. INC(pos, curP.len);
  856. curP := curP.next
  857. END;
  858. IF um # NIL THEN
  859. NEW(t);
  860. t.AcquireWrite;
  861. t.CopyFromText(SELF, destPos, len, 0);
  862. um.InsertText(destPos, t);
  863. t.ReleaseWrite;
  864. END;
  865. END CopyFromText;
  866. PROCEDURE AttributeChanger(VAR attr : Attributes; userData : ANY);
  867. BEGIN
  868. IF (userData # NIL) & (userData IS Attributes) THEN attr := userData(Attributes) END;
  869. END AttributeChanger;
  870. (** Set piece attributes for charater at position pos to pos + len. [Must hold write lock] *)
  871. PROCEDURE SetAttributes*(pos, len : LONGINT; attr : Attributes);
  872. BEGIN
  873. UpdateAttributes(pos, len, AttributeChanger, attr)
  874. END SetAttributes;
  875. (** Calls the attributeChanger procedure for all pieces so the attributes can be changed. userData
  876. is forwarded to the attributeChanger as context.
  877. [Must hold write lock] *)
  878. PROCEDURE UpdateAttributes*(pos, len : LONGINT; attributeChanger : AttributeChangerProc; userData : ANY);
  879. VAR al, ar, bl, br, cur : Piece; attributes: Attributes;
  880. BEGIN
  881. IF len = 0 THEN RETURN END;
  882. (* don't do illegal changes *)
  883. IF (len <= 0) OR (pos < 0) OR (pos + len > length) OR (first = NIL) THEN RETURN END;
  884. ASSERT(attributeChanger # NIL);
  885. ASSERT(lock.HasWriteLock(), 3000);
  886. INC(timestamp);
  887. GetSplittedPos(pos, al, ar);
  888. GetSplittedPos(pos + len, bl, br);
  889. cur := ar;
  890. IF um # NIL THEN um.BeginObjectChange(pos) END;
  891. WHILE (cur # br) DO
  892. attributeChanger(cur.attributes, userData);
  893. attributes := cur.attributes;
  894. REPEAT
  895. IF um # NIL THEN
  896. IF cur.attributes = NIL THEN
  897. um.ObjectChanged(cur.startpos, cur.len, 102, NIL)
  898. ELSE
  899. um.ObjectChanged(cur.startpos, cur.len, 102, cur.attributes.Clone())
  900. END
  901. END;
  902. cur := cur.next;
  903. UNTIL (cur = br) OR (cur.attributes # attributes);
  904. END;
  905. IF um # NIL THEN
  906. IF userData # NIL THEN
  907. um.EndObjectChange(len, 102, userData(Attributes).Clone())
  908. ELSE
  909. um.EndObjectChange(len, 102, NIL)
  910. END
  911. END;
  912. (* try merging *)
  913. WHILE (cur # NIL) & (cur # al) DO
  914. IF cur.prev # NIL THEN
  915. IF cur.prev.Merge(cur) THEN DEC(nofPieces); pieceTableOk := FALSE END
  916. END;
  917. cur := cur.prev
  918. END;
  919. AccumulateChanges(OpAttributes, pos, len);
  920. UpdatePositionObjects(OpAttributes, pos, len)
  921. END UpdateAttributes;
  922. (** Set piece character style for character at position pos to pos + len. [Must hold lock] *)
  923. PROCEDURE SetCharacterStyle*(pos, len : LONGINT; cstyle : CharacterStyle);
  924. VAR al, ar, bl, br, cur : Piece;
  925. BEGIN
  926. IF len = 0 THEN RETURN END;
  927. (* don't do illegal changes *)
  928. IF (len <= 0) OR (pos < 0) OR (pos + len > length) OR (first = NIL) THEN RETURN END;
  929. ASSERT(lock.HasWriteLock(), 3000);
  930. INC(timestamp);
  931. GetSplittedPos(pos, al, ar);
  932. GetSplittedPos(pos + len, bl, br);
  933. cur := ar;
  934. IF um # NIL THEN um.BeginObjectChange(pos) END;
  935. WHILE cur # br DO
  936. IF um # NIL THEN um.ObjectChanged(cur.startpos, cur.len, 100, cur.cstyle) END;
  937. cur.cstyle := cstyle;
  938. cur := cur.next
  939. END;
  940. IF um # NIL THEN um.EndObjectChange(len, 100, cstyle) END;
  941. (* try merging *)
  942. WHILE (cur # NIL) & (cur # al) DO
  943. IF cur.prev # NIL THEN
  944. IF cur.prev.Merge(cur) THEN DEC(nofPieces); pieceTableOk := FALSE END
  945. END;
  946. cur := cur.prev
  947. END;
  948. AccumulateChanges(OpAttributes, pos, len);
  949. UpdatePositionObjects(OpAttributes, pos, len)
  950. END SetCharacterStyle;
  951. (** Set piece paragraph style for charater at position pos to pos + len. [Must hold lock] *)
  952. PROCEDURE SetParagraphStyle*(pos, len : LONGINT; pstyle : ParagraphStyle);
  953. VAR al, ar, bl, br, cur : Piece;
  954. BEGIN
  955. IF len = 0 THEN RETURN END;
  956. (* don't do illegal changes *)
  957. IF (len <= 0) OR (pos < 0) OR (pos + len > length) OR (first = NIL) THEN RETURN END;
  958. ASSERT(lock.HasWriteLock(), 3000);
  959. INC(timestamp);
  960. GetSplittedPos(pos, al, ar);
  961. GetSplittedPos(pos + len, bl, br);
  962. cur := ar;
  963. IF um # NIL THEN um.BeginObjectChange(pos) END;
  964. WHILE cur # br DO
  965. IF um # NIL THEN um.ObjectChanged(cur.startpos, cur.len, 101, cur.pstyle) END;
  966. cur.pstyle := pstyle;
  967. cur := cur.next
  968. END;
  969. IF um # NIL THEN um.EndObjectChange(len, 101, pstyle) END;
  970. (* try merging *)
  971. WHILE (cur # NIL) & (cur # al) DO
  972. IF cur.prev # NIL THEN
  973. IF cur.prev.Merge(cur) THEN DEC(nofPieces); pieceTableOk := FALSE END
  974. END;
  975. cur := cur.prev
  976. END;
  977. AccumulateChanges(OpAttributes, pos, len);
  978. UpdatePositionObjects(OpAttributes, pos, len)
  979. END SetParagraphStyle;
  980. (** Set piece link for charater at position pos to pos + len. [Must hold lock] *)
  981. PROCEDURE SetLink*(pos, len : LONGINT; link :Link);
  982. VAR al, ar, bl, br, cur : Piece;
  983. BEGIN
  984. IF TraceHard THEN
  985. KernelLog.String("Setting Link: "); KernelLog.String("pos= "); KernelLog.Int(pos, 0);
  986. KernelLog.String(" length= "); KernelLog.Int(len, 0); KernelLog.Ln;
  987. END;
  988. IF len = 0 THEN RETURN END;
  989. (* don't do illegal changes *)
  990. IF (len <= 0) OR (pos < 0) OR (pos + len > length) OR (first = NIL) THEN RETURN END;
  991. ASSERT(lock.HasWriteLock(), 3000);
  992. INC(timestamp);
  993. GetSplittedPos(pos, al, ar);
  994. GetSplittedPos(pos + len, bl, br);
  995. cur := ar;
  996. WHILE cur # br DO cur.link := link; cur := cur.next END;
  997. (* try merging *)
  998. WHILE (cur # NIL) & (cur # al) DO
  999. IF cur.prev # NIL THEN
  1000. IF cur.prev.Merge(cur) THEN DEC(nofPieces); pieceTableOk := FALSE END
  1001. END;
  1002. cur := cur.prev
  1003. END;
  1004. AccumulateChanges(OpAttributes, pos, len);
  1005. UpdatePositionObjects(OpAttributes, pos, len)
  1006. END SetLink;
  1007. (** Return length in characters [Must hold lock]*)
  1008. PROCEDURE GetLength* () : LONGINT;
  1009. BEGIN
  1010. ASSERT(lock.HasReadLock(), 3000);
  1011. RETURN length
  1012. END GetLength;
  1013. (** Return the current timestamp [Must hold lock].
  1014. The timestamp can be used to check if an asynchronous change notification reflects the last change. Text
  1015. viewers can use this knowledge to incrementally update the layout. If the notification timestamp #
  1016. GetTimestamp then a full update is needed *)
  1017. PROCEDURE GetTimestamp*() : LONGINT;
  1018. BEGIN
  1019. ASSERT(lock.HasReadLock(), 3000);
  1020. RETURN timestamp
  1021. END GetTimestamp;
  1022. PROCEDURE CheckHealth*;
  1023. VAR cur : Piece;
  1024. pos, i, nof : LONGINT; errors : BOOLEAN;
  1025. BEGIN
  1026. ASSERT(lock.HasReadLock(), 3000);
  1027. nof := 0; pos := 0; cur := first; i := 0; errors := FALSE;
  1028. WHILE cur # NIL DO
  1029. INC(nof);
  1030. IF cur.startpos # pos THEN
  1031. KernelLog.String("Piece #"); KernelLog.Int(i, 4); KernelLog.String(" has wrong start pos"); KernelLog.Ln;
  1032. errors := TRUE
  1033. END;
  1034. IF cur.len = 0 THEN
  1035. KernelLog.String("Piece #"); KernelLog.Int(i, 4); KernelLog.String(" has zero length"); KernelLog.Ln;
  1036. errors := TRUE
  1037. END;
  1038. pos := pos + cur.len;
  1039. cur := cur.next; INC(i);
  1040. END;
  1041. IF pos # length THEN
  1042. KernelLog.String("Piece-List-Length is inconsistent"); KernelLog.Ln;
  1043. KernelLog.String("Measured length "); KernelLog.Int(pos, 4); KernelLog.Ln;
  1044. KernelLog.String("Internal length "); KernelLog.Int(length, 4); KernelLog.Ln;
  1045. errors := TRUE
  1046. END;
  1047. IF nof # nofPieces THEN errors := TRUE; KernelLog.String("ERROR : piece count failed"); KernelLog.Ln;
  1048. KernelLog.String(" nof = "); KernelLog.Int(nof, 0); KernelLog.String(" nofPieces = "); KernelLog.Int(nofPieces, 0); KernelLog.Ln
  1049. END;
  1050. IF ~errors THEN KernelLog.String("Piece list is healthy"); KernelLog.Ln;
  1051. ELSE KernelLog.String("!!! Piece list contains errors !!!!"); KernelLog.Ln
  1052. END;
  1053. END CheckHealth;
  1054. PROCEDURE DumpPieces*;
  1055. VAR cur : Piece; buf : PUCS32String;
  1056. BEGIN
  1057. cur := first;
  1058. NEW(buf, 128);
  1059. IF first = NIL THEN KernelLog.String("Empty piece list..."); KernelLog.Ln END;
  1060. WHILE cur # NIL DO
  1061. KernelLog.String("Piece pos = "); KernelLog.Int(cur.startpos, 5); KernelLog.String(" len "); KernelLog.Int(cur.len, 5);
  1062. IF cur.attributes # NIL THEN
  1063. KernelLog.String(" [Attributes : color = ");KernelLog.Hex(cur.attributes.color, 8);
  1064. KernelLog.String(", bgcolor = "); KernelLog.Hex(cur.attributes.bgcolor, 8); KernelLog.String(" ]");
  1065. END;
  1066. IF cur IS UnicodePiece THEN KernelLog.String("[unicode]")
  1067. ELSIF cur IS ObjectPiece THEN KernelLog.String("[object]")
  1068. END;
  1069. KernelLog.Ln;
  1070. cur := cur.next
  1071. END
  1072. END DumpPieces;
  1073. END UnicodeText;
  1074. Text* = UnicodeText;
  1075. VAR
  1076. clipboard* : UnicodeText;
  1077. onTextChangedStr : Strings.String;
  1078. lastSelText : Text;
  1079. lastSelFrom, lastSelTo : TextPosition;
  1080. lastText : Text;
  1081. onLastSelectionChanged-,
  1082. onLastTextChanged- : WMEvents.EventSource;
  1083. pStyles : ParagraphStyleArray; nofPStyles : LONGINT;
  1084. cStyles : CharacterStyleArray; nofCStyles : LONGINT;
  1085. forceUTF*, unforceUTF* : BOOLEAN;
  1086. defaultAttributes- : Attributes;
  1087. (** Insert the given Paragraph Style into the Paragraph Style Array *)
  1088. PROCEDURE AddParagraphStyle*(style: ParagraphStyle);
  1089. VAR
  1090. newStyles: ParagraphStyleArray;
  1091. oldStyle: ParagraphStyle;
  1092. cStyle: CharacterStyle;
  1093. i : LONGINT;
  1094. BEGIN
  1095. oldStyle := GetParagraphStyleByName(style.name);
  1096. IF (oldStyle = NIL) THEN (* style does not exist yet - create one *)
  1097. INC(nofPStyles);
  1098. IF nofPStyles > LEN(pStyles) THEN
  1099. NEW(newStyles, LEN(pStyles) * 2);
  1100. FOR i := 0 TO LEN(pStyles)-1 DO newStyles[i] := pStyles[i]; END;
  1101. pStyles := newStyles;
  1102. END;
  1103. pStyles[nofPStyles-1] := style;
  1104. ELSE (* style does exist - only update style *)
  1105. oldStyle.alignment := style.alignment;
  1106. oldStyle.firstIndent := style.firstIndent;
  1107. oldStyle.leftIndent := style.leftIndent;
  1108. oldStyle.rightIndent := style.rightIndent;
  1109. oldStyle.spaceBefore := style.spaceBefore;
  1110. oldStyle.spaceAfter := style.spaceAfter;
  1111. cStyle := GetCharacterStyleByName(style.charStyle.name);
  1112. IF cStyle # NIL THEN oldStyle.charStyle := cStyle; END;
  1113. COPY(style.tabStops, oldStyle.tabStops);
  1114. END;
  1115. END AddParagraphStyle;
  1116. (** Remove the given Paragraph Style from the Paragraph Style Array *)
  1117. PROCEDURE RemoveParagraphStyle*(style: ParagraphStyle);
  1118. VAR i : LONGINT;
  1119. BEGIN
  1120. i := 0; WHILE (i < nofPStyles) & (pStyles[i] # style) DO INC(i) END;
  1121. IF i < nofPStyles THEN
  1122. WHILE (i < nofPStyles-1) DO pStyles[i] := pStyles[i+1]; INC(i); END;
  1123. DEC(nofPStyles);
  1124. pStyles[nofPStyles] := NIL;
  1125. END;
  1126. END RemoveParagraphStyle;
  1127. (** Return the Paragraph Style with the given Name if any *)
  1128. PROCEDURE GetParagraphStyleByName*(CONST name: ARRAY OF CHAR): ParagraphStyle;
  1129. VAR
  1130. styleObject: ParagraphStyle;
  1131. i : LONGINT;
  1132. found : BOOLEAN;
  1133. match: Strings.String;
  1134. BEGIN
  1135. styleObject := NIL;
  1136. i := 0; found := FALSE;
  1137. WHILE ((i < nofPStyles) & ~found) DO
  1138. match := Strings.NewString(pStyles[i].name);
  1139. IF Strings.Match(match^, name) THEN
  1140. styleObject := pStyles[i]; found := TRUE;
  1141. END;
  1142. INC(i);
  1143. END;
  1144. RETURN styleObject;
  1145. END GetParagraphStyleByName;
  1146. (** Insert the given Character Style into the Character Style Array *)
  1147. PROCEDURE AddCharacterStyle*(style: CharacterStyle);
  1148. VAR
  1149. newStyles: CharacterStyleArray;
  1150. oldStyle: CharacterStyle;
  1151. i : LONGINT;
  1152. BEGIN
  1153. oldStyle := GetCharacterStyleByName(style.name);
  1154. IF (oldStyle = NIL) THEN (* style does not exist yet - create one *)
  1155. INC(nofCStyles);
  1156. IF nofCStyles > LEN(cStyles) THEN
  1157. NEW(newStyles, LEN(cStyles) * 2);
  1158. FOR i := 0 TO LEN(cStyles)-1 DO newStyles[i] := cStyles[i]; END;
  1159. cStyles := newStyles;
  1160. END;
  1161. cStyles[nofCStyles-1] := style;
  1162. ELSE (* style does exist - only update style *)
  1163. IF ~oldStyle.IsEqual(style) THEN
  1164. oldStyle.fontcache := NIL;
  1165. COPY(style.family, oldStyle.family);
  1166. oldStyle.style := style.style;
  1167. oldStyle.size := style.size;
  1168. oldStyle.leading := style.leading;
  1169. oldStyle.baselineShift := style.baselineShift;
  1170. oldStyle.color := style.color;
  1171. oldStyle.bgColor := style.bgColor;
  1172. oldStyle.tracking := style.tracking;
  1173. oldStyle.scaleHorizontal := style.scaleHorizontal;
  1174. oldStyle.scaleVertical := style.scaleVertical;
  1175. END;
  1176. END;
  1177. END AddCharacterStyle;
  1178. (** Remove the given Character Style from the Character Style Array *)
  1179. PROCEDURE RemoveCharacterStyle*(style: CharacterStyle);
  1180. VAR i : LONGINT;
  1181. BEGIN
  1182. i := 0; WHILE (i < nofCStyles) & (cStyles[i] # style) DO INC(i) END;
  1183. IF i < nofCStyles THEN
  1184. WHILE (i < nofCStyles-1) DO cStyles[i] := cStyles[i+1]; INC(i); END;
  1185. DEC(nofCStyles);
  1186. cStyles[nofCStyles] := NIL;
  1187. END;
  1188. END RemoveCharacterStyle;
  1189. (** Returns the Character Style with the given Name if any *)
  1190. PROCEDURE GetCharacterStyleByName*(CONST name: ARRAY OF CHAR): CharacterStyle;
  1191. VAR
  1192. styleObject: CharacterStyle;
  1193. i : LONGINT;
  1194. found : BOOLEAN;
  1195. match: Strings.String;
  1196. BEGIN
  1197. styleObject := NIL;
  1198. i := 0; found := FALSE;
  1199. WHILE ((i < nofCStyles) & ~found) DO
  1200. match := Strings.NewString(cStyles[i].name);
  1201. IF Strings.Match(match^, name) THEN
  1202. styleObject := cStyles[i]; found := TRUE;
  1203. END;
  1204. INC(i);
  1205. END;
  1206. RETURN styleObject;
  1207. END GetCharacterStyleByName;
  1208. PROCEDURE GetCharacterStyleArray*(): CharacterStyleArray;
  1209. BEGIN
  1210. RETURN cStyles;
  1211. END GetCharacterStyleArray;
  1212. PROCEDURE GetParagraphStyleArray*(): ParagraphStyleArray;
  1213. BEGIN
  1214. RETURN pStyles;
  1215. END GetParagraphStyleArray;
  1216. (* loads the default styles from the default-style XML *)
  1217. PROCEDURE InitDefaultStyles;
  1218. VAR reader : Files.Reader; f : Files.File;
  1219. BEGIN
  1220. (* Load Default Styles *)
  1221. f := Files.Old("DefaultTextStyles.XML");
  1222. IF f = NIL THEN RETURN END;
  1223. NEW(reader, f, 0);
  1224. LoadStyles(reader, FALSE);
  1225. (* Load User Styles *)
  1226. f := Files.Old("UserTextStyles.XML");
  1227. IF f = NIL THEN RETURN END;
  1228. NEW(reader, f, 0);
  1229. LoadStyles(reader, FALSE);
  1230. END InitDefaultStyles;
  1231. (* loads the styles from the given reader *)
  1232. PROCEDURE LoadStyles*(r: Streams.Reader; verbose: BOOLEAN);
  1233. VAR
  1234. parser : XMLParser.Parser;
  1235. scanner : XMLScanner.Scanner;
  1236. defaultStyles : XML.Document;
  1237. root: XML.Element;
  1238. content : XMLObjects.Enumerator;
  1239. ptr : ANY;
  1240. str: Strings.String;
  1241. cStyle : CharacterStyle;
  1242. pStyle : ParagraphStyle;
  1243. tempReal: LONGREAL; tempInt: LONGINT; tempRes : WORD;
  1244. BEGIN
  1245. NEW(scanner, r);
  1246. NEW(parser, scanner);
  1247. defaultStyles := parser.Parse();
  1248. root := defaultStyles.GetRoot();
  1249. content := root.GetContents(); content.Reset();
  1250. WHILE content.HasMoreElements() DO
  1251. ptr := content.GetNext();
  1252. IF ptr IS XML.Element THEN
  1253. str := ptr(XML.Element).GetName();
  1254. IF (str # NIL) & (str^ = "character-style") THEN (* character styles *)
  1255. NEW(cStyle);
  1256. str := ptr(XML.Element).GetAttributeValue("name"); IF str # NIL THEN COPY(str^, cStyle.name) END;
  1257. str := ptr(XML.Element).GetAttributeValue("font-family"); IF str # NIL THEN COPY(str^, cStyle.family) END;
  1258. str := ptr(XML.Element).GetAttributeValue("font-style");
  1259. IF str # NIL THEN
  1260. IF (str^ = "0") THEN cStyle.style := {};
  1261. ELSIF (str^ = "1") THEN cStyle.style := {0};
  1262. ELSIF (str^ = "2") THEN cStyle.style := {1};
  1263. ELSIF (str^ = "3") THEN cStyle.style := {0,1};
  1264. ELSE cStyle.style := {};
  1265. END;
  1266. END;
  1267. str := ptr(XML.Element).GetAttributeValue("font-size"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); cStyle.size := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1268. str := ptr(XML.Element).GetAttributeValue("leading"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); cStyle.leading := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1269. str := ptr(XML.Element).GetAttributeValue("baseline-shift"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); cStyle.baselineShift := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1270. str := ptr(XML.Element).GetAttributeValue("color"); IF str # NIL THEN Strings.HexStrToInt(str^, tempInt, tempRes); cStyle.color := tempInt; END;
  1271. str := ptr(XML.Element).GetAttributeValue("bgcolor"); IF str # NIL THEN Strings.HexStrToInt(str^, tempInt, tempRes); cStyle.bgColor := tempInt; END;
  1272. str := ptr(XML.Element).GetAttributeValue("tracking"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); cStyle.tracking := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1273. str := ptr(XML.Element).GetAttributeValue("h-scale"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); cStyle.scaleHorizontal := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1274. str := ptr(XML.Element).GetAttributeValue("v-scale"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); cStyle.scaleVertical := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1275. AddCharacterStyle(cStyle); (* Load the Style into Texts *)
  1276. IF verbose THEN KernelLog.String("Texts Loading Character Style: "); KernelLog.String(cStyle.name); KernelLog.Ln; END;
  1277. ELSIF (str # NIL) & (str^ = "paragraph-style") THEN (* paragraph styles *)
  1278. NEW(pStyle);
  1279. str := ptr(XML.Element).GetAttributeValue("name"); IF str # NIL THEN COPY(str^, pStyle.name) END;
  1280. str := ptr(XML.Element).GetAttributeValue("alignment"); IF str # NIL THEN Strings.StrToInt(str^, pStyle.alignment) END;
  1281. str := ptr(XML.Element).GetAttributeValue("first-indent"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); pStyle.firstIndent := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1282. str := ptr(XML.Element).GetAttributeValue("left-indent"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); pStyle.leftIndent := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1283. str := ptr(XML.Element).GetAttributeValue("right-indent"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); pStyle.rightIndent := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1284. str := ptr(XML.Element).GetAttributeValue("space-before"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); pStyle.spaceBefore := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1285. str := ptr(XML.Element).GetAttributeValue("space-after"); IF str # NIL THEN Strings.StrToFloat(str^, tempReal); pStyle.spaceAfter := FP1616.FloatToFixp(SHORT(tempReal)); END;
  1286. str := ptr(XML.Element).GetAttributeValue("character-style");
  1287. IF str # NIL THEN
  1288. cStyle := GetCharacterStyleByName(str^);
  1289. IF cStyle # NIL THEN pStyle.charStyle := cStyle; END;
  1290. END;
  1291. str := ptr(XML.Element).GetAttributeValue("tab-stops"); IF str # NIL THEN COPY(str^, pStyle.tabStops) END;
  1292. AddParagraphStyle(pStyle); (* Load the Style into Texts *)
  1293. IF verbose THEN KernelLog.String("Texts Loading Paragraph Style: "); KernelLog.String(pStyle.name); KernelLog.Ln; END;
  1294. END;
  1295. END;
  1296. END;
  1297. END LoadStyles;
  1298. PROCEDURE SetLastSelection*(text : Text; from, to : TextPosition);
  1299. BEGIN
  1300. ASSERT((text # NIL) & (from # NIL) & (to # NIL));
  1301. BEGIN {EXCLUSIVE}
  1302. lastSelText := text; lastSelFrom := from; lastSelTo := to
  1303. END;
  1304. onLastSelectionChanged.Call(text)
  1305. END SetLastSelection;
  1306. PROCEDURE ClearLastSelection*;
  1307. BEGIN {EXCLUSIVE}
  1308. lastSelText := NIL; lastSelFrom := NIL; lastSelTo := NIL
  1309. END ClearLastSelection;
  1310. PROCEDURE GetLastSelection*(VAR text : Text; VAR from, to : TextPosition) : BOOLEAN;
  1311. BEGIN {EXCLUSIVE}
  1312. text := lastSelText; from := lastSelFrom; to := lastSelTo;
  1313. RETURN text # NIL
  1314. END GetLastSelection;
  1315. PROCEDURE SetLastText*(text : Text);
  1316. BEGIN
  1317. BEGIN {EXCLUSIVE}
  1318. lastText := text
  1319. END;
  1320. onLastTextChanged.Call(text)
  1321. END SetLastText;
  1322. PROCEDURE GetLastText*() : Text;
  1323. BEGIN {EXCLUSIVE}
  1324. RETURN lastText
  1325. END GetLastText;
  1326. PROCEDURE GetDefaultAttributes ();
  1327. VAR
  1328. res : WORD;
  1329. textColor := 0xFF, textBackColor := 0x00: LONGINT;
  1330. fontSize : LONGINT;
  1331. fontName : ARRAY 256 OF CHAR;
  1332. BEGIN
  1333. NEW( defaultAttributes );
  1334. Configuration.GetColor( "WindowManager.ColorScheme.Default.TextBackColor", textBackColor, res );
  1335. Configuration.GetColor( "WindowManager.ColorScheme.Default.TextColor", textColor, res );
  1336. Configuration.Get( "WindowManager.FontManager.DefaultFont.Name", fontName, res );
  1337. IF (res # Configuration.Ok) OR (fontName = "") THEN fontName := "Vera"; END;
  1338. Configuration.GetInteger( "WindowManager.FontManager.DefaultFont.Size", fontSize, res );
  1339. IF (res # Configuration.Ok) OR (fontSize < 1) THEN fontSize := 14; END;
  1340. defaultAttributes.Set( textColor, textBackColor, 0, fontName, fontSize, {} );
  1341. END GetDefaultAttributes;
  1342. BEGIN
  1343. NEW(pStyles, 4); nofPStyles := 0;
  1344. NEW(cStyles, 4); nofCStyles := 0;
  1345. InitDefaultStyles;
  1346. NEW(onTextChangedStr, 16); COPY("onTextChanged", onTextChangedStr^);
  1347. NEW(onLastTextChanged, NIL, Strings.NewString("OnLastTextChanged"),
  1348. Strings.NewString("fired when the last selection is changed"), NIL);
  1349. NEW(onLastSelectionChanged, NIL, Strings.NewString("OnLastSelectionChanged"),
  1350. Strings.NewString("fired when the last marked text is changed"), NIL);
  1351. NEW(clipboard);
  1352. forceUTF := FALSE;
  1353. unforceUTF := TRUE;
  1354. GetDefaultAttributes;
  1355. END Texts.