DEADSOFTWARE

* -> old; Trurl-based -> new
[bbcp.git] / new / Text / Mod / Setters.txt
1 MODULE TextSetters;
3 (* THIS IS TEXT COPY OF BlackBox 1.6-rc6 Text/Mod/Setters.odc *)
4 (* DO NOT EDIT *)
6 (* correct NextPage postcond in docu *)
7 (* make s.r, s.rd reducible? *)
8 (* paraShutoff needs to be controlled by an approx flag to certain ops (later ...?) *)
10 IMPORT
11 Fonts, Ports, Printers, Stores, Models, Views, Properties,
12 TextModels, TextRulers;
14 CONST
15 (** Pref.opts, options of setter-aware views; 0 overrides 1 **)
16 lineBreak* = 0; wordJoin* = 1; wordPart* = 2; flexWidth* = 3;
18 tab = TextModels.tab; line = TextModels.line; para = TextModels.para;
19 zwspace = TextModels.zwspace; nbspace = TextModels.nbspace;
20 hyphen = TextModels.hyphen; nbhyphen = TextModels.nbhyphen;
21 digitspace = TextModels.digitspace;
22 softhyphen = TextModels.softhyphen;
24 mm = Ports.mm;
25 minTabWidth = 2 * Ports.point; stdTabWidth = 4 * mm;
26 leftLineGap = 2 * Ports.point; rightLineGap = 3 * Ports.point;
27 adjustMask = {TextRulers.leftAdjust, TextRulers.rightAdjust};
28 centered = {}; leftFlush = {TextRulers.leftAdjust}; rightFlush = {TextRulers.rightAdjust};
29 blocked = adjustMask;
31 boxCacheLen = 64;
32 seqCacheLen = 16;
34 paraShutoff = MAX(INTEGER); (* longest stretch read backwards to find start of paragraph *)
35 (* unsafe: disabled *)
36 cachedRulers = FALSE; (* caching ruler objects trades speed against GC effectiveness *)
37 periodInWords = FALSE;
38 colonInWords = FALSE;
40 minVersion = 0; maxVersion = 0; maxStdVersion = 0;
43 TYPE
44 Pref* = RECORD (Properties.Preference)
45 opts*: SET;
46 endW*: INTEGER; (** preset (to width of view) **)
47 dsc*: INTEGER (** preset (to dominating line descender) **)
48 END;
51 Reader* = POINTER TO ABSTRACT RECORD
52 r-: TextModels.Reader; (** look-ahead state **)
53 (** unit **)
54 string*: ARRAY 64 OF CHAR; (** single chars in string[0] **)
55 view*: Views.View;
56 (** unit props **)
57 textOpts*: SET;
58 mask*: CHAR;
59 setterOpts*: SET;
60 w*, endW*, h*, dsc*: INTEGER;
61 attr*: TextModels.Attributes;
62 (** reading state **)
63 eot*: BOOLEAN;
64 pos*: INTEGER;
65 x*: INTEGER; (** to be advanced by client! **)
66 adjStart*: INTEGER;
67 spaces*: INTEGER;
68 tabIndex*: INTEGER; (** tabs being processed; initially -1 **)
69 tabType*: SET; (** type of tab being processed; initially {} **)
70 (** line props **)
71 vw*: INTEGER;
72 hideMarks*: BOOLEAN;
73 ruler*: TextRulers.Ruler;
74 rpos*: INTEGER
75 END;
77 Setter* = POINTER TO ABSTRACT RECORD (Stores.Store)
78 text-: TextModels.Model; (** connected iff text # NIL **)
79 defRuler-: TextRulers.Ruler;
80 vw-: INTEGER;
81 hideMarks-: BOOLEAN
82 END;
85 LineBox* = RECORD
86 len*: INTEGER;
87 ruler*: TextRulers.Ruler;
88 rpos*: INTEGER;
89 left*, right*, asc*, dsc*: INTEGER;
90 rbox*, bop*, adj*, eot*: BOOLEAN; (** adj => adjW > 0; adj & blocked => spaces > 0 **)
91 views*: BOOLEAN;
92 skipOff*: INTEGER; (** chars in [skipOff, len) take endW **)
93 adjOff*: INTEGER; (** offset of last block in box - adjust only this block **)
94 spaces*: INTEGER; (** valid, > 0 if adj & blocked **)
95 adjW*: INTEGER; (** valid if adj - to be distributed over spaces **)
96 tabW*: ARRAY TextRulers.maxTabs OF INTEGER (** delta width of tabs (<= 0) **)
97 END;
100 Directory* = POINTER TO ABSTRACT RECORD END;
103 Worder = RECORD
104 box: LineBox; next: INTEGER;
105 i: INTEGER
106 END;
108 StdReader = POINTER TO RECORD (Reader) END;
110 StdSetter = POINTER TO RECORD (Setter)
111 rd: Reader; (* subject to reduction? *)
112 r: TextModels.Reader; (* subject to reduction? *)
113 ruler: TextRulers.Ruler;
114 rpos: INTEGER;
115 key: INTEGER
116 END;
118 StdDirectory = POINTER TO RECORD (Directory) END;
121 VAR
122 dir-, stdDir-: Directory;
124 nextKey: INTEGER;
125 boxIndex, seqIndex: INTEGER;
126 boxCache: ARRAY boxCacheLen OF RECORD
127 key: INTEGER; (* valid iff key > 0 *)
128 start: INTEGER;
129 line: LineBox (* inv ruler = NIL *)
130 END;
131 seqCache: ARRAY seqCacheLen OF RECORD
132 key: INTEGER; (* valid iff key > 0 *)
133 start, pos: INTEGER (* sequence [start, end), end >= pos *)
134 END;
137 (** Reader **)
139 PROCEDURE (rd: Reader) Set* (
140 old: TextModels.Reader;
141 text: TextModels.Model; x, pos: INTEGER;
142 ruler: TextRulers.Ruler; rpos: INTEGER; vw: INTEGER; hideMarks: BOOLEAN
143 ), NEW, EXTENSIBLE;
144 BEGIN
145 ASSERT(text # NIL, 20);
146 ASSERT(ruler # NIL, 22);
147 rd.r := text.NewReader(old); rd.r.SetPos(pos); rd.r.Read;
148 rd.string[0] := 0X; rd.view := NIL;
149 rd.textOpts := {};
150 rd.setterOpts := {}; rd.w := 0; rd.endW := 0; rd.h := 0; rd.dsc := 0;
151 rd.attr := NIL;
152 rd.eot := FALSE; rd.pos := pos; rd.x := x;
153 rd.tabIndex := -1; rd.tabType := {};
154 rd.adjStart := pos; rd.spaces := 0;
155 rd.ruler := ruler; rd.rpos := rpos; rd.vw := vw; rd.hideMarks := hideMarks
156 END Set;
158 PROCEDURE (rd: Reader) Read*, NEW, EXTENSIBLE;
159 (** pre: rd set **)
160 (** post: rd.pos = rd.pos' + Length(rd.string) **)
161 BEGIN
162 rd.string[0] := rd.r.char; rd.string[1] := 0X;
163 rd.view := rd.r.view;
164 rd.textOpts := {};
165 rd.setterOpts := {};
166 rd.w := rd.r.w; rd.endW := rd.w; rd.h := rd.r.h; rd.dsc := 0;
167 rd.attr := rd.r.attr;
168 rd.eot := rd.r.eot;
169 INC(rd.pos);
170 rd.r.Read
171 END Read;
173 PROCEDURE (rd: Reader) AdjustWidth* (start, pos: INTEGER; IN box: LineBox;
174 VAR w: INTEGER
175 ), NEW, ABSTRACT;
177 PROCEDURE (rd: Reader) SplitWidth* (w: INTEGER): INTEGER, NEW, ABSTRACT;
180 (** Setter **)
182 PROCEDURE (s: Setter) CopyFrom- (source: Stores.Store), EXTENSIBLE;
183 BEGIN
184 WITH source: Setter DO
185 s.text := source.text; s.defRuler := source.defRuler;
186 s.vw := source.vw; s.hideMarks := source.hideMarks
187 END
188 END CopyFrom;
190 PROCEDURE (s: Setter) Internalize- (VAR rd: Stores.Reader), EXTENSIBLE;
191 VAR thisVersion: INTEGER;
192 BEGIN
193 s.Internalize^(rd);
194 IF rd.cancelled THEN RETURN END;
195 rd.ReadVersion(minVersion, maxVersion, thisVersion)
196 END Internalize;
198 PROCEDURE (s: Setter) Externalize- (VAR wr: Stores.Writer), EXTENSIBLE;
199 BEGIN
200 s.Externalize^(wr);
201 wr.WriteVersion(maxVersion)
202 END Externalize;
204 PROCEDURE (s: Setter) ConnectTo* (text: TextModels.Model;
205 defRuler: TextRulers.Ruler; vw: INTEGER; hideMarks: BOOLEAN
206 ), NEW, EXTENSIBLE;
207 BEGIN
208 IF text # NIL THEN
209 s.text := text; s.defRuler := defRuler; s.vw := vw; s.hideMarks := hideMarks
210 ELSE
211 s.text := NIL; s.defRuler := NIL
212 END
213 END ConnectTo;
216 PROCEDURE (s: Setter) ThisPage* (pageH: INTEGER; pageNo: INTEGER): INTEGER, NEW, ABSTRACT;
217 (** pre: connected, 0 <= pageNo **)
218 (** post: (result = -1) & (pageNo >= maxPageNo) OR (result = pageStart(pageNo)) **)
220 PROCEDURE (s: Setter) NextPage* (pageH: INTEGER; start: INTEGER): INTEGER, NEW, ABSTRACT;
221 (** pre: connected, ThisPage(pageH, pageNo) = start [with pageNo = NumberOfPageAt(start)] **)
222 (** post: (result = start) & last-page(start) OR result = next-pageStart(start) **)
225 PROCEDURE (s: Setter) ThisSequence* (pos: INTEGER): INTEGER, NEW, ABSTRACT;
226 (** pre: connected, 0 <= pos <= s.text.Length() **)
227 (** post: (result = 0) OR (char(result - 1) IN {line, para}) **)
229 PROCEDURE (s: Setter) NextSequence* (start: INTEGER): INTEGER, NEW, ABSTRACT;
230 (** pre: connected, ThisSequence(start) = start **)
231 (** post: (result = start) & last-line(start) OR (ThisSequence(t, result - 1) = start) **)
233 PROCEDURE (s: Setter) PreviousSequence* (start: INTEGER): INTEGER, NEW, ABSTRACT;
234 (** pre: connected, ThisSequence(t, start) = start **)
235 (** post: (result = 0) & (start = 0) OR (result = ThisSequence(t, start - 1)) **)
238 PROCEDURE (s: Setter) ThisLine* (pos: INTEGER): INTEGER, NEW, ABSTRACT;
239 (** pre: connected, 0 <= pos <= s.text.Length() **)
240 (** post: result <= pos, (pos < NextLine(result)) OR last-line(result) **)
242 PROCEDURE (s: Setter) NextLine* (start: INTEGER): INTEGER, NEW, ABSTRACT;
243 (** pre: connected, ThisLine(start) = start **)
244 (** post: (result = 0) & (start = 0) OR
245 (result = start) & last-line(start) OR
246 (ThisLine(result - 1) = start) **)
248 PROCEDURE (s: Setter) PreviousLine* (start: INTEGER): INTEGER, NEW, ABSTRACT;
249 (** pre: connected, ThisLine(start) = start **)
250 (** post: (result = 0) & (start = 0) OR (result = ThisLine(start - 1)) **)
253 PROCEDURE (s: Setter) GetWord* (pos: INTEGER; OUT beg, end: INTEGER), NEW, ABSTRACT;
254 (** pre: connected, 0 <= pos <= s.text.Length() **)
255 (** post: c set, beg <= pos <= end **)
257 PROCEDURE (s: Setter) GetLine* (start: INTEGER; OUT box: LineBox), NEW, ABSTRACT;
258 (** pre: connected, ThisLine(start) = start, 0 <= start <= s.text.Length() **)
259 (** post: (c, box) set (=> box.ruler # NIL), (box.len > 0) OR box.eot,
260 0 <= box.left <= box.right <= ruler.right **)
262 PROCEDURE (s: Setter) GetBox* (start, end, maxW, maxH: INTEGER;
263 OUT w, h: INTEGER
264 ), NEW, ABSTRACT;
265 (** pre: connected, ThisLine(start) = start, 0 <= start <= end <= s.text.Length() **)
266 (** post: c set, maxW > undefined => w <= maxW, maxH > undefined => h <= maxH **)
269 PROCEDURE (s: Setter) NewReader* (old: Reader): Reader, NEW, ABSTRACT;
270 (** pre: connected **)
273 PROCEDURE (s: Setter) GridOffset* (dsc: INTEGER; IN box: LineBox): INTEGER, NEW, ABSTRACT;
274 (** pre: connected, dsc >= 0: dsc is descender of previous line; dsc = -1 for first line **)
275 (** post: dsc + GridOffset(dsc, box) + box.asc = k*ruler.grid (k >= 0) >= ruler.asc + ruler.grid **)
278 (** Directory **)
280 PROCEDURE (d: Directory) New* (): Setter, NEW, ABSTRACT;
283 (* line box cache *)
285 PROCEDURE InitCache;
286 VAR i: INTEGER;
287 BEGIN
288 nextKey := 1; boxIndex := 0; seqIndex := 0;
289 i := 0; WHILE i < boxCacheLen DO boxCache[i].key := -1; INC(i) END;
290 i := 0; WHILE i < seqCacheLen DO seqCache[i].key := -1; INC(i) END
291 END InitCache;
293 PROCEDURE ClearCache (key: INTEGER);
294 VAR i, j: INTEGER;
295 BEGIN
296 i := 0; j := boxIndex;
297 WHILE i < boxCacheLen DO
298 IF boxCache[i].key = key THEN boxCache[i].key := -1; j := i END;
299 INC(i)
300 END;
301 boxIndex := j;
302 i := 0; j := seqIndex;
303 WHILE i < seqCacheLen DO
304 IF seqCache[i].key = key THEN seqCache[i].key := -1; j := i END;
305 INC(i)
306 END;
307 seqIndex := j
308 END ClearCache;
311 PROCEDURE CacheIndex (key, start: INTEGER): INTEGER;
312 VAR i: INTEGER;
313 BEGIN
314 RETURN -1;
315 i := 0;
316 WHILE (i < boxCacheLen) & ~((boxCache[i].key = key) & (boxCache[i].start = start)) DO
317 INC(i)
318 END;
319 IF i = boxCacheLen THEN i := -1 END;
320 RETURN i
321 END CacheIndex;
323 PROCEDURE GetFromCache (s: StdSetter; i: INTEGER; VAR l: LineBox);
324 BEGIN
325 l := boxCache[i].line;
326 IF ~cachedRulers THEN
327 IF l.rpos >= 0 THEN
328 s.r := s.text.NewReader(s.r); s.r.SetPos(l.rpos); s.r.Read;
329 l.ruler := s.r.view(TextRulers.Ruler)
330 ELSE l.ruler := s.defRuler
331 END
332 END
333 END GetFromCache;
335 PROCEDURE AddToCache (key, start: INTEGER; VAR l: LineBox);
336 VAR i: INTEGER;
337 BEGIN
338 i := boxIndex; boxIndex := (i + 1) MOD boxCacheLen;
339 boxCache[i].key := key; boxCache[i].start := start; boxCache[i].line := l;
340 IF ~cachedRulers THEN
341 boxCache[i].line.ruler := NIL
342 END
343 END AddToCache;
346 PROCEDURE CachedSeqStart (key, pos: INTEGER): INTEGER;
347 VAR start: INTEGER; i: INTEGER;
348 BEGIN
349 i := 0;
350 WHILE (i < seqCacheLen)
351 & ~((seqCache[i].key = key) & (seqCache[i].start <= pos) & (pos <= seqCache[i].pos)) DO
352 INC(i)
353 END;
354 IF i < seqCacheLen THEN start := seqCache[i].start ELSE start := -1 END;
355 RETURN start
356 END CachedSeqStart;
358 PROCEDURE AddSeqStartToCache (key, pos, start: INTEGER);
359 VAR i: INTEGER;
360 BEGIN
361 i := 0;
362 WHILE (i < seqCacheLen) & ~((seqCache[i].key = key) & (seqCache[i].start = start)) DO
363 INC(i)
364 END;
365 IF i < seqCacheLen THEN
366 IF seqCache[i].pos < pos THEN seqCache[i].pos := pos END
367 ELSE
368 i := seqIndex; seqIndex := (i + 1) MOD seqCacheLen;
369 seqCache[i].key := key; seqCache[i].pos := pos; seqCache[i].start := start
370 END
371 END AddSeqStartToCache;
374 (* StdReader *)
376 (*
377 PROCEDURE WordPart (ch, ch1: CHAR): BOOLEAN;
378 (* needs more work ... put elsewhere? *)
379 BEGIN
380 CASE ORD(ch) OF
381 ORD("0") .. ORD("9"), ORD("A") .. ORD("Z"), ORD("a") .. ORD("z"),
382 ORD(digitspace), ORD(nbspace), ORD(nbhyphen), ORD("_"),
383 0C0H .. 0C6H, 0E0H .. 0E6H, (* ~ A *)
384 0C7H, 0E7H, (* ~ C *)
385 0C8H .. 0CBH, 0E8H .. 0EBH, (* ~ E *)
386 0CCH .. 0CFH, 0ECH .. 0EFH, (* ~ I *)
387 0D1H, 0F1H, (* ~ N *)
388 0D2H .. 0D6H, 0D8H, 0F2H .. 0F6H, 0F8H, (* ~ O *)
389 0D9H .. 0DCH, 0F9H .. 0FCH, (* ~ U *)
390 0DDH, 0FDH, 0FFH, (* ~ Y *)
391 0DFH: (* ~ ss *)
392 RETURN TRUE
393 | ORD("."), ORD(":"):
394 IF (ch = ".") & periodInWords OR (ch = ":") & colonInWords THEN
395 CASE ch1 OF
396 0X, TextModels.viewcode, tab, line, para, " ":
397 RETURN FALSE
398 ELSE RETURN TRUE
399 END
400 ELSE RETURN FALSE
401 END
402 ELSE RETURN FALSE
403 END
404 END WordPart;
405 *)
407 PROCEDURE WordPart (ch, ch1: CHAR): BOOLEAN;
408 (* Same as .net function System.Char.IsLetterOrDigit(ch)
409 + digit space, nonbreaking space, nonbreaking hyphen, & underscore
410 ch1 unused *)
411 VAR low: INTEGER;
412 BEGIN
413 low := ORD(ch) MOD 256;
414 CASE ORD(ch) DIV 256 OF
415 | 001H, 015H, 034H..04CH, 04EH..09EH, 0A0H..0A3H, 0ACH..0D6H, 0F9H, 0FCH: RETURN TRUE
416 | 000H: CASE low OF
417 | 030H..039H, 041H..05AH, 061H..07AH, 0AAH, 0B5H, 0BAH, 0C0H..0D6H, 0D8H..0F6H, 0F8H..0FFH,
418 ORD(digitspace), ORD(nbspace), ORD(nbhyphen), ORD("_"): RETURN TRUE
419 ELSE
420 END
421 | 002H: CASE low OF
422 | 000H..041H, 050H..0C1H, 0C6H..0D1H, 0E0H..0E4H, 0EEH: RETURN TRUE
423 ELSE
424 END
425 | 003H: CASE low OF
426 | 07AH, 086H, 088H..08AH, 08CH, 08EH..0A1H, 0A3H..0CEH, 0D0H..0F5H, 0F7H..0FFH: RETURN TRUE
427 ELSE
428 END
429 | 004H: CASE low OF
430 | 000H..081H, 08AH..0CEH, 0D0H..0F9H: RETURN TRUE
431 ELSE
432 END
433 | 005H: CASE low OF
434 | 000H..00FH, 031H..056H, 059H, 061H..087H, 0D0H..0EAH, 0F0H..0F2H: RETURN TRUE
435 ELSE
436 END
437 | 006H: CASE low OF
438 | 021H..03AH, 040H..04AH, 060H..069H, 06EH..06FH, 071H..0D3H, 0D5H, 0E5H..0E6H, 0EEH..0FCH, 0FFH: RETURN TRUE
439 ELSE
440 END
441 | 007H: CASE low OF
442 | 010H, 012H..02FH, 04DH..06DH, 080H..0A5H, 0B1H: RETURN TRUE
443 ELSE
444 END
445 | 009H: CASE low OF
446 | 004H..039H, 03DH, 050H, 058H..061H, 066H..06FH, 07DH, 085H..08CH, 08FH..090H, 093H..0A8H, 0AAH..0B0H, 0B2H, 0B6H..0B9H, 0BDH, 0CEH, 0DCH..0DDH, 0DFH..0E1H, 0E6H..0F1H: RETURN TRUE
447 ELSE
448 END
449 | 00AH: CASE low OF
450 | 005H..00AH, 00FH..010H, 013H..028H, 02AH..030H, 032H..033H, 035H..036H, 038H..039H, 059H..05CH, 05EH, 066H..06FH, 072H..074H, 085H..08DH, 08FH..091H, 093H..0A8H, 0AAH..0B0H, 0B2H..0B3H, 0B5H..0B9H, 0BDH, 0D0H, 0E0H..0E1H, 0E6H..0EFH: RETURN TRUE
451 ELSE
452 END
453 | 00BH: CASE low OF
454 | 005H..00CH, 00FH..010H, 013H..028H, 02AH..030H, 032H..033H, 035H..039H, 03DH, 05CH..05DH, 05FH..061H, 066H..06FH, 071H, 083H, 085H..08AH, 08EH..090H, 092H..095H, 099H..09AH, 09CH, 09EH..09FH, 0A3H..0A4H, 0A8H..0AAH, 0AEH..0B9H, 0E6H..0EFH: RETURN TRUE
455 ELSE
456 END
457 | 00CH: CASE low OF
458 | 005H..00CH, 00EH..010H, 012H..028H, 02AH..033H, 035H..039H, 060H..061H, 066H..06FH, 085H..08CH, 08EH..090H, 092H..0A8H, 0AAH..0B3H, 0B5H..0B9H, 0BDH, 0DEH, 0E0H..0E1H, 0E6H..0EFH: RETURN TRUE
459 ELSE
460 END
461 | 00DH: CASE low OF
462 | 005H..00CH, 00EH..010H, 012H..028H, 02AH..039H, 060H..061H, 066H..06FH, 085H..096H, 09AH..0B1H, 0B3H..0BBH, 0BDH, 0C0H..0C6H: RETURN TRUE
463 ELSE
464 END
465 | 00EH: CASE low OF
466 | 001H..030H, 032H..033H, 040H..046H, 050H..059H, 081H..082H, 084H, 087H..088H, 08AH, 08DH, 094H..097H, 099H..09FH, 0A1H..0A3H, 0A5H, 0A7H, 0AAH..0ABH, 0ADH..0B0H, 0B2H..0B3H, 0BDH, 0C0H..0C4H, 0C6H, 0D0H..0D9H, 0DCH..0DDH: RETURN TRUE
467 ELSE
468 END
469 | 00FH: CASE low OF
470 | 000H, 020H..029H, 040H..047H, 049H..06AH, 088H..08BH: RETURN TRUE
471 ELSE
472 END
473 | 010H: CASE low OF
474 | 000H..021H, 023H..027H, 029H..02AH, 040H..049H, 050H..055H, 0A0H..0C5H, 0D0H..0FAH, 0FCH: RETURN TRUE
475 ELSE
476 END
477 | 011H: CASE low OF
478 | 000H..059H, 05FH..0A2H, 0A8H..0F9H: RETURN TRUE
479 ELSE
480 END
481 | 012H: CASE low OF
482 | 000H..048H, 04AH..04DH, 050H..056H, 058H, 05AH..05DH, 060H..088H, 08AH..08DH, 090H..0B0H, 0B2H..0B5H, 0B8H..0BEH, 0C0H, 0C2H..0C5H, 0C8H..0D6H, 0D8H..0FFH: RETURN TRUE
483 ELSE
484 END
485 | 013H: CASE low OF
486 | 000H..010H, 012H..015H, 018H..05AH, 080H..08FH, 0A0H..0F4H: RETURN TRUE
487 ELSE
488 END
489 | 014H: IF low >= 001H THEN RETURN TRUE END
490 | 016H: CASE low OF
491 | 000H..06CH, 06FH..076H, 081H..09AH, 0A0H..0EAH: RETURN TRUE
492 ELSE
493 END
494 | 017H: CASE low OF
495 | 000H..00CH, 00EH..011H, 020H..031H, 040H..051H, 060H..06CH, 06EH..070H, 080H..0B3H, 0D7H, 0DCH, 0E0H..0E9H: RETURN TRUE
496 ELSE
497 END
498 | 018H: CASE low OF
499 | 010H..019H, 020H..077H, 080H..0A8H: RETURN TRUE
500 ELSE
501 END
502 | 019H: CASE low OF
503 | 000H..01CH, 046H..06DH, 070H..074H, 080H..0A9H, 0C1H..0C7H, 0D0H..0D9H: RETURN TRUE
504 ELSE
505 END
506 | 01AH: IF low < 017H THEN RETURN TRUE END
507 | 01DH: IF low < 0C0H THEN RETURN TRUE END
508 | 01EH: CASE low OF
509 | 000H..09BH, 0A0H..0F9H: RETURN TRUE
510 ELSE
511 END
512 | 01FH: CASE low OF
513 | 000H..015H, 018H..01DH, 020H..045H, 048H..04DH, 050H..057H, 059H, 05BH, 05DH, 05FH..07DH, 080H..0B4H, 0B6H..0BCH, 0BEH, 0C2H..0C4H, 0C6H..0CCH, 0D0H..0D3H, 0D6H..0DBH, 0E0H..0ECH, 0F2H..0F4H, 0F6H..0FCH: RETURN TRUE
514 ELSE
515 END
516 | 020H: CASE low OF
517 | 071H, 07FH, 090H..094H: RETURN TRUE
518 ELSE
519 END
520 | 021H: CASE low OF
521 | 002H, 007H, 00AH..013H, 015H, 019H..01DH, 024H, 026H, 028H, 02AH..02DH, 02FH..031H, 033H..039H, 03CH..03FH, 045H..049H: RETURN TRUE
522 ELSE
523 END
524 | 02CH: CASE low OF
525 | 000H..02EH, 030H..05EH, 080H..0E4H: RETURN TRUE
526 ELSE
527 END
528 | 02DH: CASE low OF
529 | 000H..025H, 030H..065H, 06FH, 080H..096H, 0A0H..0A6H, 0A8H..0AEH, 0B0H..0B6H, 0B8H..0BEH, 0C0H..0C6H, 0C8H..0CEH, 0D0H..0D6H, 0D8H..0DEH: RETURN TRUE
530 ELSE
531 END
532 | 030H: CASE low OF
533 | 005H..006H, 031H..035H, 03BH..03CH, 041H..096H, 09DH..09FH, 0A1H..0FAH, 0FCH..0FFH: RETURN TRUE
534 ELSE
535 END
536 | 031H: CASE low OF
537 | 005H..02CH, 031H..08EH, 0A0H..0B7H, 0F0H..0FFH: RETURN TRUE
538 ELSE
539 END
540 | 04DH: IF low < 0B6H THEN RETURN TRUE END
541 | 09FH: IF low < 0BCH THEN RETURN TRUE END
542 | 0A4H: IF low < 08DH THEN RETURN TRUE END
543 | 0A8H: CASE low OF
544 | 000H..001H, 003H..005H, 007H..00AH, 00CH..022H: RETURN TRUE
545 ELSE
546 END
547 | 0D7H: IF low < 0A4H THEN RETURN TRUE END
548 | 0FAH: CASE low OF
549 | 000H..02DH, 030H..06AH, 070H..0D9H: RETURN TRUE
550 ELSE
551 END
552 | 0FBH: CASE low OF
553 | 000H..006H, 013H..017H, 01DH, 01FH..028H, 02AH..036H, 038H..03CH, 03EH, 040H..041H, 043H..044H, 046H..0B1H, 0D3H..0FFH: RETURN TRUE
554 ELSE
555 END
556 | 0FDH: CASE low OF
557 | 000H..03DH, 050H..08FH, 092H..0C7H, 0F0H..0FBH: RETURN TRUE
558 ELSE
559 END
560 | 0FEH: CASE low OF
561 | 070H..074H, 076H..0FCH: RETURN TRUE
562 ELSE
563 END
564 | 0FFH: CASE low OF
565 | 010H..019H, 021H..03AH, 041H..05AH, 066H..0BEH, 0C2H..0C7H, 0CAH..0CFH, 0D2H..0D7H, 0DAH..0DCH: RETURN TRUE
566 ELSE
567 END
568 ELSE
569 END;
570 RETURN FALSE
571 END WordPart;
573 (*
574 PROCEDURE ExtendToEOL (x, right: INTEGER): INTEGER;
575 BEGIN
576 IF right - x > 5 * mm THEN RETURN right - x ELSE RETURN 5 * mm END
577 END ExtendToEOL;
578 *)
580 PROCEDURE Right (ra: TextRulers.Attributes; vw: INTEGER): INTEGER;
581 BEGIN
582 IF TextRulers.rightFixed IN ra.opts THEN
583 RETURN ra.right
584 ELSE
585 RETURN vw
586 END
587 END Right;
589 PROCEDURE GetViewPref (rd: StdReader);
590 CONST maxH = 1600 * Ports.point;
591 VAR ra: TextRulers.Attributes; tp: TextModels.Pref; sp: Pref;
592 BEGIN
593 ra := rd.ruler.style.attr;
594 tp.opts := {}; Views.HandlePropMsg(rd.view, tp);
595 rd.textOpts := tp.opts; rd.mask := tp.mask;
596 sp.opts := {}; sp.dsc := ra.dsc; sp.endW := rd.w; Views.HandlePropMsg(rd.view, sp);
597 rd.setterOpts := sp.opts; rd.dsc := sp.dsc; rd.endW := sp.endW;
598 IF rd.w >= 10000 * mm THEN rd.w := 10000 * mm END;
599 IF (TextModels.hideable IN tp.opts) & rd.hideMarks THEN
600 rd.h := 0; sp.dsc := 0;
601 (*
602 rd.w := 0;
603 *)
604 IF ~( (rd.view IS TextRulers.Ruler)
605 OR (TextModels.maskChar IN rd.textOpts) & (rd.mask = para) ) THEN
606 rd.w := 0
607 END
608 (**)
609 ELSIF rd.h > maxH THEN rd.h := maxH
610 END;
611 IF TextModels.maskChar IN rd.textOpts THEN
612 rd.string[0] := rd.mask; rd.string[1] := 0X
613 ELSE rd.string[0] := TextModels.viewcode
614 END
615 END GetViewPref;
617 PROCEDURE GatherString (rd: StdReader);
618 VAR i, len: INTEGER; ch: CHAR;
619 BEGIN
620 i := 1; len := LEN(rd.string) - 1; ch := rd.r.char;
621 WHILE (i < len)
622 & (rd.r.view = NIL) & (rd.r.attr = rd.attr)
623 & ( (" " < ch) & (ch <= "~") & (ch # "-")
624 OR (ch = digitspace)
625 OR (ch >= nbspace) & (ch < 100X) & (ch # softhyphen)
627 DO (* rd.r.char > " " => ~rd.eot *)
628 rd.string[i] := ch; INC(i);
629 rd.eot := rd.r.eot;
630 rd.r.Read; ch := rd.r.char; INC(rd.pos)
631 END;
632 rd.string[i] := 0X; rd.setterOpts := {wordJoin};
633 IF i = 1 THEN
634 IF WordPart(rd.string[0], 0X) THEN INCL(rd.setterOpts, wordPart) END
635 END;
636 rd.w := rd.attr.font.StringWidth(rd.string); rd.endW := rd.w
637 END GatherString;
639 PROCEDURE SpecialChar (rd: StdReader);
640 VAR ra: TextRulers.Attributes; i, tabs, spaceW, dW: INTEGER; type: SET;
641 BEGIN
642 ra := rd.ruler.style.attr;
643 CASE ORD(rd.string[0]) OF
644 ORD(tab):
645 rd.textOpts := {TextModels.hideable};
646 rd.endW := minTabWidth;
647 rd.adjStart := rd.pos; rd.spaces := 0;
648 (*
649 i := 0; WHILE (i < ra.tabs.len) & (ra.tabs.tab[i].stop < rd.x + minTabWidth) DO INC(i) END;
650 *)
651 i := rd.tabIndex + 1;
652 IF i < ra.tabs.len THEN
653 type := ra.tabs.tab[i].type;
654 rd.w := MAX(minTabWidth, ra.tabs.tab[i].stop - rd.x);
655 IF TextRulers.barTab IN type THEN
656 IF TextRulers.rightTab IN type THEN
657 rd.w := MAX(minTabWidth, rd.w - leftLineGap)
658 ELSIF ~(TextRulers.centerTab IN type) THEN
659 INC(rd.w, rightLineGap)
660 END
661 END;
662 rd.tabIndex := i; rd.tabType := type
663 ELSE (* for "reasonable" fonts: round to closest multiple of spaces of this font *)
664 spaceW := rd.attr.font.SStringWidth(" ");
665 IF (1 <= spaceW) & (spaceW <= stdTabWidth) THEN
666 rd.w := (stdTabWidth + spaceW DIV 2) DIV spaceW * spaceW
667 ELSE
668 rd.w := stdTabWidth
669 END;
670 rd.tabIndex := TextRulers.maxTabs; rd.tabType := {}
671 END
672 | ORD(line):
673 rd.setterOpts := {lineBreak}; rd.w := 0; rd.endW := 0
674 | ORD(para):
675 (*
676 IF rd.hideMarks THEN
677 rd.w := 0; rd.h := 0; rd.dsc := 0
678 ELSE
679 rd.w := ExtendToEOL(rd.x, Right(ra, rd.vw)) + 1
680 END;
681 INC(rd.h, ra.lead);
682 rd.textOpts := {TextModels.hideable};
683 rd.endW := rd.w
684 *)
685 (*
686 rd.setterOpts := {lineBreak};
687 *)
688 IF rd.hideMarks THEN rd.h := 0; rd.dsc := 0 END;
689 INC(rd.h, ra.lead); rd.textOpts := {TextModels.hideable};
690 IF (rd.view = NIL) OR ~(rd.view IS TextRulers.Ruler) THEN
691 rd.w := 10000 * Ports.mm (* ExtendToEOL(rd.x, Right(ra, rd.vw)) + 1 *)
692 END;
693 rd.endW := rd.w
694 (**)
695 | ORD(" "):
696 rd.setterOpts := {flexWidth};
697 rd.w := rd.attr.font.StringWidth(rd.string); rd.endW := 0; INC(rd.spaces)
698 | ORD(zwspace):
699 rd.w := 0; rd.endW := 0
700 | ORD(digitspace):
701 rd.setterOpts := {wordPart};
702 rd.w := rd.attr.font.StringWidth("0"); rd.endW := rd.w
703 | ORD("-"):
704 rd.setterOpts := {};
705 rd.w := rd.attr.font.StringWidth("-"); rd.endW := rd.w
706 | ORD(hyphen):
707 rd.setterOpts := {};
708 rd.string[0] := "-" (*softhyphen*);
709 rd.w := rd.attr.font.StringWidth("-" (*softhyphen*)); rd.endW := rd.w
710 | ORD(nbhyphen):
711 rd.setterOpts := {wordJoin, wordPart};
712 rd.string[0] := "-" (*softhyphen*);
713 rd.w := rd.attr.font.StringWidth("-" (*softhyphen*)); rd.endW := rd.w
714 | ORD(softhyphen):
715 rd.setterOpts := {wordPart}; rd.textOpts := {TextModels.hideable};
716 rd.string[0] := "-";
717 rd.endW := rd.attr.font.StringWidth("-" (*softhyphen*));
718 IF rd.hideMarks THEN rd.w := 0 ELSE rd.w := rd.endW END
719 ELSE
720 rd.setterOpts := {wordJoin};
721 IF WordPart(rd.string[0], rd.r.char) THEN INCL(rd.setterOpts, wordPart) END;
722 rd.w := rd.attr.font.StringWidth(rd.string); rd.endW := rd.w
723 END
724 END SpecialChar;
725 (*
726 PROCEDURE LongChar (rd: StdReader);
727 VAR ra: TextRulers.Attributes;
728 BEGIN
729 ra := rd.ruler.style.attr;
730 rd.setterOpts := {wordJoin, wordPart};
731 rd.w := rd.attr.font.StringWidth(rd.string); rd.endW := rd.w
732 END LongChar;
733 *)
735 PROCEDURE (rd: StdReader) Read;
736 (* pre: connected *)
737 VAR ra: TextRulers.Attributes; asc, dsc, w: INTEGER; ch: CHAR;
738 BEGIN
739 rd.Read^;
740 IF ~rd.eot THEN
741 IF rd.view = NIL THEN
742 rd.attr.font.GetBounds(asc, dsc, w);
743 rd.h := asc + dsc; rd.dsc := dsc
744 ELSE
745 GetViewPref(rd)
746 END;
747 IF (rd.view = NIL) OR (TextModels.maskChar IN rd.textOpts) THEN
748 ch := rd.string[0];
749 IF (rd.view = NIL)
750 & ( (" " < ch) & (ch < "~") & (ch # "-")
751 OR (ch = digitspace)
752 OR (ch >= nbspace) & (ch # softhyphen)
754 THEN
755 GatherString(rd)
756 ELSE
757 SpecialChar(rd)
758 END
759 END
760 ELSE
761 ra := rd.ruler.style.attr;
762 rd.w := 0; rd.endW := 0; rd.h := ra.asc + ra.dsc; rd.dsc := ra.dsc
763 END
764 END Read;
766 PROCEDURE (rd: StdReader) AdjustWidth (start, pos: INTEGER; IN box: LineBox; VAR w: INTEGER);
767 VAR i: INTEGER; form: SET;
768 BEGIN
769 IF box.adj & (pos >= start + box.adjOff) THEN
770 form := box.ruler.style.attr.opts * adjustMask;
771 IF (form = blocked) & (rd.string[0] = " ") THEN
772 INC(w, box.adjW DIV box.spaces)
773 ELSIF (form # blocked) & (rd.string[0] = tab) THEN
774 INC(w, box.adjW) (* is this correct ??? *)
775 END
776 END;
777 i := rd.tabIndex; (* rd.string[0] = tab => i >= 0 *)
778 IF (rd.string[0] = tab) & (i < box.ruler.style.attr.tabs.len) THEN
779 w := box.tabW[i]
780 END
781 END AdjustWidth;
783 PROCEDURE (rd: StdReader) SplitWidth (w: INTEGER): INTEGER;
784 BEGIN
785 IF (rd.string[1] = 0X) & (rd.view = NIL) THEN
786 RETURN (w + 1) DIV 2
787 ELSE RETURN w
788 END
789 END SplitWidth;
792 (* Worder *)
794 PROCEDURE SetWorder (VAR w: Worder; s: StdSetter; pos: INTEGER; OUT start: INTEGER);
795 CONST wordCutoff = LEN(s.rd.string);
796 BEGIN
797 start := s.ThisSequence(pos);
798 IF pos - start >= wordCutoff THEN
799 start := pos; WHILE pos - start < wordCutoff DO start := s.PreviousLine(start) END
800 END;
801 s.GetLine(start, w.box); w.next := start + w.box.len;
802 s.rd.Set(s.r, s.text, w.box.left, start, w.box.ruler, w.box.rpos, s.vw, s.hideMarks);
803 w.i := 0; s.rd.string[0] := 0X
804 END SetWorder;
806 PROCEDURE StepWorder (VAR w: Worder; s: StdSetter; VAR part: BOOLEAN);
807 VAR rd: Reader;
808 BEGIN
809 rd := s.rd;
810 IF rd.string[w.i] = 0X THEN
811 IF rd.pos < w.next THEN
812 rd.Read; w.i := 0
813 ELSE
814 IF ~w.box.eot THEN
815 s.GetLine(w.next, w.box);
816 s.rd.Set(s.r, s.text, w.box.left, w.next, w.box.ruler, w.box.rpos, s.vw, s.hideMarks);
817 rd.Read; w.i := 0;
818 INC(w.next, w.box.len)
819 ELSE
820 rd.string[0] := 0X
821 END
822 END
823 END;
824 IF rd.string[0] = 0X THEN (* end of text *)
825 part := TRUE
826 ELSIF rd.string[1] = 0X THEN (* special character *)
827 part := wordPart IN rd.setterOpts; INC(w.i)
828 ELSE (* gathered sString *)
829 part := WordPart(rd.string[w.i], rd.string[w.i + 1]); INC(w.i)
830 END
831 END StepWorder;
834 (* StdSetter *)
836 PROCEDURE (s: StdSetter) CopyFrom (source: Stores.Store);
837 BEGIN
838 s.CopyFrom^(source);
839 WITH source: StdSetter DO
840 s.ruler := source.ruler; s.rpos := source.rpos; s.key := source.key;
841 s.rd := NIL; s.r := NIL
842 END
843 END CopyFrom;
845 PROCEDURE (s: StdSetter) Externalize (VAR wr: Stores.Writer);
846 BEGIN
847 s.Externalize^(wr);
848 wr.WriteVersion(maxStdVersion)
849 END Externalize;
851 PROCEDURE (s: StdSetter) Internalize (VAR rd: Stores.Reader);
852 VAR thisVersion: INTEGER;
853 BEGIN
854 s.Internalize^(rd);
855 IF rd.cancelled THEN RETURN END;
856 rd.ReadVersion(minVersion, maxStdVersion, thisVersion);
857 IF rd.cancelled THEN RETURN END;
858 s.text := NIL; s.defRuler := NIL; s.ruler := NIL; s.rd := NIL; s.r := NIL
859 END Internalize;
862 PROCEDURE (s: StdSetter) ConnectTo (text: TextModels.Model;
863 defRuler: TextRulers.Ruler; vw: INTEGER; hideMarks: BOOLEAN
864 );
865 BEGIN
866 s.ConnectTo^(text, defRuler, vw, hideMarks);
867 ClearCache(s.key);
868 IF text # NIL THEN
869 s.ruler := defRuler; s.rpos := -1; s.key := nextKey; INC(nextKey)
870 ELSE
871 s.ruler := NIL
872 END
873 END ConnectTo;
876 PROCEDURE (s: StdSetter) ThisPage (pageH: INTEGER; pageNo: INTEGER): INTEGER;
877 (* pre: connected, 0 <= pageNo *)
878 (* post: (result = -1) & (pageNo >= maxPageNo) OR (result = pageStart(pageNo)) *)
879 VAR start, prev: INTEGER;
880 BEGIN
881 ASSERT(s.text # NIL, 20); ASSERT(pageNo >= 0, 21);
882 start := 0;
883 WHILE pageNo > 0 DO
884 prev := start; DEC(pageNo); start := s.NextPage(pageH, start);
885 IF start = prev THEN start := -1; pageNo := 0 END
886 END;
887 RETURN start
888 END ThisPage;
890 PROCEDURE (s: StdSetter) NextPage (pageH: INTEGER; start: INTEGER): INTEGER;
891 (* pre: connected, ThisPage(pageH, x) = start *)
892 (* post: (result = s.text.Length()) OR result = next-pageStart(start) *)
893 CONST
894 noBreakInside = TextRulers.noBreakInside;
895 pageBreak = TextRulers.pageBreak;
896 parJoin = TextRulers.parJoin;
897 regular = 0; protectInside = 1; joinFirst = 2; joinNext = 3; confirmSpace = 4; (* state *)
898 VAR
899 box: LineBox; ra: TextRulers.Attributes;
900 h, asc, dsc, backup, pos, state: INTEGER; isRuler: BOOLEAN;
902 PROCEDURE FetchNextLine;
903 BEGIN
904 s.GetLine(pos, box);
905 IF box.len > 0 THEN
906 ra := box.ruler.style.attr; isRuler := box.rpos = pos;
907 asc := box.asc + s.GridOffset(dsc, box); dsc := box.dsc; h := asc + dsc
908 END
909 END FetchNextLine;
911 PROCEDURE HandleRuler;
912 CONST norm = 0; nbi = 1; pj = 2;
913 VAR strength: INTEGER;
914 BEGIN
915 IF isRuler & (pos > start) & ~(pageBreak IN ra.opts) THEN
916 IF parJoin IN ra.opts THEN strength := pj
917 ELSIF noBreakInside IN ra.opts THEN strength := nbi
918 ELSE strength := norm
919 END;
920 CASE state OF
921 | regular:
922 CASE strength OF
923 | norm:
924 | nbi: state := protectInside; backup := pos
925 | pj: state := joinFirst; backup := pos
926 END
927 | protectInside:
928 CASE strength OF
929 | norm: state := regular
930 | nbi: backup := pos
931 | pj: state := joinFirst; backup := pos
932 END
933 | joinFirst:
934 CASE strength OF
935 | norm: state := confirmSpace
936 | nbi: state := protectInside
937 | pj: state := joinNext
938 END
939 | joinNext:
940 CASE strength OF
941 | norm: state := confirmSpace
942 | nbi: state := protectInside
943 | pj:
944 END
945 | confirmSpace:
946 CASE strength OF
947 | norm: state := regular
948 | nbi: state := protectInside; backup := pos
949 | pj: state := joinFirst; backup := pos
950 END
951 END
952 END
953 END HandleRuler;
955 PROCEDURE IsEmptyLine (): BOOLEAN;
956 BEGIN
957 RETURN (box.right = box.left) OR s.hideMarks & isRuler & ~(pageBreak IN ra.opts)
958 END IsEmptyLine;
960 BEGIN
961 ASSERT(s.text # NIL, 20);
962 ASSERT(0 <= start, 21); ASSERT(start <= s.text.Length(), 22);
963 pos := start; dsc := -1;
964 FetchNextLine;
965 IF box.len > 0 THEN
966 state := regular;
967 REPEAT (* at least one line per page *)
968 HandleRuler; DEC(pageH, h); INC(pos, box.len);
969 IF (state = confirmSpace) & ~IsEmptyLine() THEN state := regular END;
970 FetchNextLine
971 UNTIL (box.len = 0) OR (pageH - h < 0) OR isRuler & (pageBreak IN ra.opts);
972 IF ~isRuler OR ~(pageBreak IN ra.opts) THEN
973 WHILE (box.len > 0) & IsEmptyLine() DO (* skip empty lines at top of page *)
974 HandleRuler; INC(pos, box.len); FetchNextLine
975 END
976 END;
977 HandleRuler;
978 IF (state # regular) & ~(isRuler & (pageBreak IN ra.opts) OR (box.len = 0)) THEN pos := backup END
979 END;
980 RETURN pos
981 END NextPage;
984 PROCEDURE (s: StdSetter) NextSequence (start: INTEGER): INTEGER;
985 (* pre: connected, ThisSequence(start) = start *)
986 (* post: (result = start) & last-line(start) OR (ThisSequence(t, result - 1) = start) *)
987 VAR rd: TextModels.Reader; ch: CHAR;
988 BEGIN
989 ASSERT(s.text # NIL, 20);
990 s.r := s.text.NewReader(s.r); rd := s.r; rd.SetPos(start);
991 REPEAT rd.ReadChar(ch) UNTIL rd.eot OR (ch = line) OR (ch = para);
992 IF rd.eot THEN RETURN start ELSE RETURN rd.Pos() END
993 END NextSequence;
995 PROCEDURE (s: StdSetter) ThisSequence (pos: INTEGER): INTEGER;
996 (* pre: connected, 0 <= pos <= t.Length() *)
997 (* post: (result = 0) OR (char(result - 1) IN {line, para}) *)
998 VAR rd: TextModels.Reader; start, limit: INTEGER; ch: CHAR;
999 BEGIN
1000 ASSERT(s.text # NIL, 20); ASSERT(0 <= pos, 21); ASSERT(pos <= s.text.Length(), 22);
1001 IF pos = 0 THEN
1002 RETURN 0
1003 ELSE
1004 start := CachedSeqStart(s.key, pos);
1005 IF start < 0 THEN
1006 s.r := s.text.NewReader(s.r); rd := s.r; rd.SetPos(pos);
1007 limit := paraShutoff;
1008 REPEAT rd.ReadPrevChar(ch); DEC(limit)
1009 UNTIL rd.eot OR (ch = line) OR (ch = para) OR (limit = 0);
1010 IF rd.eot THEN start := 0 ELSE start := rd.Pos() + 1 END;
1011 AddSeqStartToCache(s.key, pos, start)
1012 END;
1013 RETURN start
1014 END
1015 END ThisSequence;
1017 PROCEDURE (s: StdSetter) PreviousSequence (start: INTEGER): INTEGER;
1018 (* pre: connected, ThisSequence(t, start) = start *)
1019 (* post: (result = 0) & (start = 0) OR (result = ThisSequence(t, start - 1)) *)
1020 BEGIN
1021 IF start <= 1 THEN RETURN 0 ELSE RETURN s.ThisSequence(start - 1) END
1022 END PreviousSequence;
1025 PROCEDURE (s: StdSetter) ThisLine (pos: INTEGER): INTEGER;
1026 (* pre: connected *)
1027 VAR start, next: INTEGER;
1028 BEGIN
1029 next := s.ThisSequence(pos);
1030 REPEAT start := next; next := s.NextLine(start) UNTIL (next > pos) OR (next = start);
1031 RETURN start
1032 END ThisLine;
1034 PROCEDURE (s: StdSetter) NextLine (start: INTEGER): INTEGER;
1035 (* pre: connected, ThisLine(start) = start *)
1036 (* post: (result = 0) & (start = 0) OR
1037 (result = start) & last-line(start) OR
1038 (ThisLine(result - 1) = start) *)
1039 VAR box: LineBox; len: INTEGER; i: INTEGER; eot: BOOLEAN;
1040 BEGIN
1041 i := CacheIndex(s.key, start);
1042 IF i >= 0 THEN
1043 len := boxCache[i].line.len; eot := boxCache[i].line.eot
1044 ELSE
1045 s.GetLine(start, box); len := box.len; eot := box.eot
1046 END;
1047 IF ~eot THEN RETURN start + len ELSE RETURN start END
1048 END NextLine;
1050 PROCEDURE (s: StdSetter) PreviousLine (start: INTEGER): INTEGER;
1051 (* pre: connected, ThisLine(start) = start *)
1052 (* post: (result = 0) & (start = 0) OR (result = ThisLine(start - 1)) *)
1053 BEGIN
1054 IF start <= 1 THEN start := 0 ELSE start := s.ThisLine(start - 1) END;
1055 RETURN start
1056 END PreviousLine;
1059 PROCEDURE (s: StdSetter) GetWord (pos: INTEGER; OUT beg, end: INTEGER);
1060 (* pre: connected, 0 <= pos <= s.text.Length() *)
1061 (* post: beg <= pos <= end *)
1062 CONST wordCutoff = LEN(s.rd.string);
1063 VAR w: Worder; part: BOOLEAN;
1064 BEGIN
1065 ASSERT(s.text # NIL, 20); ASSERT(0 <= pos, 21); ASSERT(pos <= s.text.Length(), 22);
1066 SetWorder(w, s, pos, beg); end := beg;
1067 REPEAT
1068 StepWorder(w, s, part); INC(end);
1069 IF ~part THEN beg := end END
1070 UNTIL end >= pos;
1071 DEC(end);
1072 REPEAT
1073 StepWorder(w, s, part); INC(end)
1074 UNTIL ~part OR (s.rd.string[0] = 0X) OR (end - beg > wordCutoff)
1075 END GetWord;
1077 PROCEDURE (s: StdSetter) GetLine (start: INTEGER; OUT box: LineBox);
1078 VAR rd: Reader; ra: TextRulers.Attributes; brk: LineBox;
1079 d, off, right, w: INTEGER; i, tabsN: INTEGER; form: SET; adj: BOOLEAN; ch: CHAR;
1081 PROCEDURE TrueW (VAR b: LineBox; w: INTEGER): INTEGER;
1082 VAR i: INTEGER; type: SET;
1083 BEGIN
1084 i := rd.tabIndex;
1085 IF (0 <= i ) & (i < TextRulers.maxTabs) & (rd.string[0] # tab) THEN
1086 type := rd.tabType * {TextRulers.centerTab, TextRulers.rightTab};
1087 IF type = {TextRulers.centerTab} THEN
1088 DEC(w, b.tabW[i] - MAX(minTabWidth, b.tabW[i] - w DIV 2))
1089 ELSIF type = {TextRulers.rightTab} THEN
1090 DEC(w, b.tabW[i] - MAX(minTabWidth, b.tabW[i] - w))
1091 END
1092 END;
1093 RETURN w
1094 END TrueW;
1096 PROCEDURE Enclose (VAR b: LineBox; w: INTEGER);
1097 VAR off, i, d: INTEGER; type: SET;
1098 BEGIN
1099 b.len := rd.pos - start; INC(b.right, w);
1100 off := rd.attr.offset; i := rd.tabIndex;
1101 IF rd.h - rd.dsc + off > b.asc THEN b.asc := rd.h - rd.dsc + off END;
1102 IF rd.dsc - off > b.dsc THEN b.dsc := rd.dsc - off END;
1103 IF rd.view # NIL THEN b.views := TRUE END;
1104 IF (0 <= i ) & (i < TextRulers.maxTabs) THEN
1105 IF rd.string[0] = tab THEN
1106 b.tabW[i] := w
1107 ELSE
1108 type := rd.tabType * {TextRulers.centerTab, TextRulers.rightTab};
1109 IF type = {TextRulers.centerTab} THEN
1110 d := b.tabW[i] - MAX(minTabWidth, b.tabW[i] - w DIV 2);
1111 DEC(b.tabW[i], d); DEC(b.right, d)
1112 ELSIF type = {TextRulers.rightTab} THEN
1113 d := b.tabW[i] - MAX(minTabWidth, b.tabW[i] - w);
1114 DEC(b.tabW[i], d); DEC(b.right, d)
1115 END
1116 END
1117 END
1118 END Enclose;
1120 BEGIN
1121 ASSERT(s.text # NIL, 20); ASSERT(0 <= start, 21); ASSERT(start <= s.text.Length(), 22);
1122 i := CacheIndex(s.key, start);
1123 IF i >= 0 THEN
1124 GetFromCache(s, i, box)
1125 ELSE
1126 TextRulers.GetValidRuler(s.text, start, s.rpos, s.ruler, s.rpos);
1127 IF s.rpos > start THEN s.ruler := s.defRuler; s.rpos := -1 END;
1128 box.ruler := s.ruler; box.rpos := s.rpos;
1129 ra := s.ruler.style.attr; tabsN := ra.tabs.len; right := Right(ra, s.vw);
1130 s.r := s.text.NewReader(s.r);
1131 IF start = 0 THEN s.r.SetPos(start); ch := para
1132 ELSE s.r.SetPos(start - 1); s.r.ReadChar(ch)
1133 END;
1134 s.r.Read;
1136 (*
1137 IF s.r.char = para THEN box.rbox := ~s.hideMarks; box.bop := s.hideMarks; box.left := 0
1138 ELSIF ch = para THEN box.rbox := FALSE; box.bop := TRUE; box.left := ra.first
1139 ELSE box.rbox := FALSE; box.bop := FALSE; box.left := ra.left
1140 END;
1141 *)
1142 IF s.r.char = para THEN box.rbox := TRUE; box.bop := FALSE; box.left := 0
1143 ELSIF ch = para THEN box.rbox := FALSE; box.bop := TRUE; box.left := ra.first
1144 ELSE box.rbox := FALSE; box.bop := FALSE; box.left := ra.left
1145 END;
1146 (**)
1147 box.views := FALSE;
1148 box.asc := 0; box.dsc := 0; box.right := box.left;
1149 box.len := 0; box.adjOff := 0; box.spaces := 0;
1150 brk.right := 0;
1152 s.rd := s.NewReader(s.rd); rd := s.rd;
1153 rd.Set(s.r, s.text, box.left, start, box.ruler, box.rpos, s.vw, s.hideMarks);
1154 rd.Read;
1155 WHILE ~rd.eot & (box.right + (*rd.w*) TrueW(box, rd.w) <= right)
1156 & ~(lineBreak IN rd.setterOpts) DO
1157 IF ~(wordJoin IN rd.setterOpts) & (box.right + rd.endW <= right) THEN
1158 (*brk := box;*)
1159 brk.len := box.len; brk.ruler := box.ruler; brk.rpos := box.rpos;
1160 brk.left := box.left; brk.right := box.right; brk.asc := box.asc; brk.dsc := box.dsc;
1161 brk.rbox := box.rbox; brk.bop := box.bop; brk.adj := box.adj; brk.eot := box.eot;
1162 brk.views := box.views; brk.skipOff := box.skipOff; brk.adjOff := box.adjOff;
1163 brk.spaces := box.spaces; brk.adjW := box.adjW;
1164 i := 0; WHILE i < tabsN DO brk.tabW[i] := box.tabW[i]; INC(i) END;
1165 (*---*)
1166 Enclose(brk, rd.endW);
1167 brk.eot := rd.r.eot (* rd.r.eot one ahead of rd.eot *)
1168 END;
1169 box.adjOff := rd.adjStart - start; box.spaces := rd.spaces;
1170 Enclose(box, rd.w);
1171 rd.x := box.right; rd.Read
1172 END;
1173 IF (lineBreak IN rd.setterOpts) (* & ~box.rbox *) THEN Enclose(box, 0) END;
1174 box.eot := rd.eot; adj := FALSE; box.skipOff := box.len;
1175 IF box.right + rd.w > right THEN (* rd.w > 0 => ~rd.eot & ~(lineBreak IN setterOpts) *)
1176 IF ~(wordJoin IN rd.setterOpts) & (box.right + rd.endW <= right) THEN
1177 IF rd.string[0] = " " THEN DEC(box.spaces) END;
1178 Enclose(box, rd.endW);
1179 adj := TRUE
1180 ELSIF brk.right > 0 THEN
1181 (*box := brk;*)
1182 box.len := brk.len; box.ruler := brk.ruler; box.rpos := brk.rpos;
1183 box.left := brk.left; box.right := brk.right; box.asc := brk.asc; box.dsc := brk.dsc;
1184 box.rbox := brk.rbox; box.bop := brk.bop; box.adj := brk.adj; box.eot := brk.eot;
1185 box.views := brk.views; box.skipOff := brk.skipOff; box.adjOff := brk.adjOff;
1186 box.spaces := brk.spaces; box.adjW := brk.adjW;
1187 i := 0; WHILE i < tabsN DO box.tabW[i] := brk.tabW[i]; INC(i) END;
1188 (*---*)
1189 box.skipOff := box.len - 1; adj := TRUE
1190 ELSIF box.right = box.left THEN
1191 Enclose(box, rd.w) (* force at least one per line *)
1192 END
1193 ELSIF (box.right = box.left) & box.eot THEN
1194 box.asc := ra.asc; box.dsc := ra.dsc (* force empty line to ruler's default height *)
1195 END;
1197 box.adj := FALSE;
1198 d := right - box.right;
1199 IF d > 0 THEN
1200 form := ra.opts * adjustMask;
1201 IF form = blocked THEN
1202 IF adj & (box.spaces > 0) THEN
1203 box.right := right; box.adj := TRUE; box.adjW := d
1204 END
1205 ELSIF form = rightFlush THEN
1206 IF box.adjOff > 0 THEN
1207 box.adjW := d; box.adj := TRUE
1208 ELSE
1209 INC(box.left, d)
1210 END;
1211 box.right := right
1212 ELSIF form = centered THEN
1213 IF box.adjOff > 0 THEN
1214 box.adjW := d DIV 2; box.adj := TRUE
1215 ELSE
1216 INC(box.left, d DIV 2)
1217 END;
1218 INC(box.right, d DIV 2)
1219 END
1220 END;
1222 AddToCache(s.key, start, box)
1223 END;
1225 ASSERT(box.eot OR (box.len > 0), 100)
1226 END GetLine;
1229 PROCEDURE (s: StdSetter) GetBox (start, end, maxW, maxH: INTEGER; OUT w, h: INTEGER);
1230 VAR box: LineBox; asc, dsc: INTEGER;
1231 BEGIN
1232 ASSERT(s.text # NIL, 20);
1233 ASSERT(0 <= start, 21);
1234 ASSERT(start <= end, 22);
1235 ASSERT(end <= s.text.Length(), 23);
1236 w := 0; h := 0; dsc := -1;
1237 IF maxW <= Views.undefined THEN maxW := MAX(INTEGER) END;
1238 IF maxH <= Views.undefined THEN maxH := MAX(INTEGER) END;
1239 WHILE (start < end) & (h < maxH) DO
1240 s.GetLine(start, box);
1241 IF box.rbox THEN w := MAX(w, Right(box.ruler.style.attr, s.vw))
1242 ELSE w := MAX(w, box.right)
1243 END;
1244 asc := box.asc + s.GridOffset(dsc, box); dsc := box.dsc;
1245 INC(start, box.len); INC(h, asc + dsc)
1246 END;
1247 w := MIN(w, maxW); h := MIN(h, maxH)
1248 END GetBox;
1251 PROCEDURE (s: StdSetter) NewReader (old: Reader): Reader;
1252 (* pre: connected *)
1253 VAR rd: StdReader;
1254 BEGIN
1255 ASSERT(s.text # NIL, 20);
1256 IF (old # NIL) & (old IS StdReader) THEN RETURN old
1257 ELSE NEW(rd); RETURN rd
1258 END
1259 END NewReader;
1262 PROCEDURE (s: StdSetter) GridOffset (dsc: INTEGER; IN box: LineBox): INTEGER;
1263 VAR ra: TextRulers.Attributes; h, h0: INTEGER;
1264 (* minimal possible line spacing h0, minimal legal line spacing h *)
1265 BEGIN
1266 IF ~box.rbox THEN
1267 ra := box.ruler.style.attr;
1268 IF dsc < 0 THEN
1269 RETURN 0 (* no longer try to correct first line's grid position -- should be done when printing... *)
1270 (*
1271 h0 := box.asc; h := ra.asc;
1272 IF h < h0 THEN (* override legal spacing if to small *)
1273 h := h - (h - h0) DIV ra.grid * ra.grid (* adjust to next larger grid line *)
1274 END;
1275 RETURN h - h0
1276 *)
1277 ELSE
1278 h0 := box.asc + dsc; h := ra.asc + ra.dsc;
1279 IF h < h0 THEN h := h0 END; (* override legal spacing if to small *)
1280 RETURN - (-h) DIV ra.grid * ra.grid - h0 (* adjust to next larger grid line *)
1281 END
1282 ELSE
1283 RETURN 0
1284 END
1285 END GridOffset;
1288 (* StdDirectory *)
1290 PROCEDURE (d: StdDirectory) New (): Setter;
1291 VAR s: StdSetter;
1292 BEGIN
1293 NEW(s); s.text := NIL; RETURN s
1294 END New;
1297 (** miscellaneous **)
1299 PROCEDURE Init;
1300 VAR d: StdDirectory;
1301 BEGIN
1302 InitCache;
1303 NEW(d); dir := d; stdDir := d
1304 END Init;
1306 PROCEDURE SetDir* (d: Directory);
1307 BEGIN
1308 ASSERT(d # NIL, 20); dir := d
1309 END SetDir;
1311 BEGIN
1312 Init
1313 END TextSetters.