DEADSOFTWARE

2ee55eaddcfccebd093998fadbc5a16b494e8b93
[bbcp.git] / Trurl-based / Text / Mod / Views.txt
1 MODULE TextViews;
3 (* THIS IS TEXT COPY OF BlackBox 1.6-rc6 Text/Mod/Views.odc *)
4 (* DO NOT EDIT *)
6 (* could use +, $ in DrawLine cache implementation *)
8 IMPORT
9 Services, Fonts, Ports, Stores,
10 Models, Views, Controllers, Properties, Dialog, Printing, Containers,
11 TextModels, TextRulers, TextSetters;
13 CONST
14 (** v.DisplayMarks hide *)
15 show* = FALSE; hide* = TRUE;
17 (** v.ShowRange focusOnly **)
18 any* = FALSE; focusOnly* = TRUE;
20 parasign = 0B6X; (* paragraph sign, to mark non-ruler paragraph breaks *)
22 mm = Ports.mm; inch16 = Ports.inch DIV 16; point = Ports.point;
23 maxScrollHeight = 16 * point; maxScrollSteps = 100; fuseScrollHeight = maxScrollHeight DIV 2;
24 maxHeight = maxScrollHeight * maxScrollSteps;
25 adjustMask = {TextRulers.leftAdjust, TextRulers.rightAdjust};
27 (* SetOp.mode *)
28 setMarks = 0; setSetter = 1; setDefs = 2;
30 scrollingKey = "#System:Scrolling";
31 viewSettingKey = "#System:ViewSetting";
33 minVersion = 0; maxVersion = 0; maxStdVersion = 0;
36 TYPE
37 View* = POINTER TO ABSTRACT RECORD (Containers.View) END;
39 Directory* = POINTER TO ABSTRACT RECORD
40 defAttr-: TextModels.Attributes
41 END;
44 Location* = RECORD
45 (** start of line and position of location **)
46 start*, pos*: INTEGER;
47 (** coordinates of location **)
48 x*, y*: INTEGER;
49 (** line dimensions at location **)
50 asc*, dsc*: INTEGER;
51 (** if view at location: **)
52 view*: Views.View;
53 l*, t*, r*, b*: INTEGER
54 END;
57 PositionMsg* = RECORD (Models.Message)
58 focusOnly*: BOOLEAN;
59 beg*, end*: INTEGER
60 END;
63 PageMsg* = RECORD (Properties.Message)
64 current*: INTEGER
65 END;
68 Line = POINTER TO RECORD
69 next: Line;
70 start, asc, h: INTEGER;
71 attr: TextRulers.Attributes; (* attr = box.ruler.style.attr *)
72 box: TextSetters.LineBox (* caching of box.rpos not consistent *)
73 END;
75 StdView = POINTER TO RECORD (View)
76 (* model *)
77 text: TextModels.Model;
78 org: INTEGER;
79 dy: INTEGER; (* 0 <= dy < Height(first line) *)
80 defRuler: TextRulers.Ruler;
81 defAttr: TextModels.Attributes;
82 hideMarks: BOOLEAN;
83 (* general state *)
84 cachedRd: TextSetters.Reader;
85 (* line grid cache *)
86 trailer: Line; (* trailer # NIL => trailer.eot, trailer.next # trailer *)
87 bot: INTEGER; (* max(f : f seen by Restore : f.b) *)
88 (* setter *)
89 setter, setter0: TextSetters.Setter (* setter # setter0 lazily detects setter change *)
90 END;
92 StdDirectory = POINTER TO RECORD (Directory) END;
94 ScrollOp = POINTER TO RECORD (Stores.Operation)
95 v: StdView;
96 org, dy: INTEGER;
97 bunchOrg, bunchDy: INTEGER;
98 bunch: BOOLEAN; (* bunch => bunchOrg, bunchDy valid *)
99 silent: BOOLEAN (* original caller of Do(op) already handled situation *)
100 END;
102 SetOp = POINTER TO RECORD (Stores.Operation)
103 mode: INTEGER;
104 view: StdView;
105 hideMarks: BOOLEAN;
106 setter: TextSetters.Setter;
107 defRuler: TextRulers.Ruler;
108 defAttr: TextModels.Attributes
109 END;
111 FindAnyFrameMsg = RECORD (Views.Message)
112 (* find frame with smallest height (frame.b - frame.t) that displays view; NIL if none found *)
113 frame: Views.Frame (* OUT, initially NIL *)
114 END;
116 FindFocusFrameMsg = RECORD (Controllers.Message)
117 (* find outermost focus frame displaying view; NIL if none found *)
118 view: Views.View; (* IN *)
119 frame: Views.Frame (* OUT, initially NIL *)
120 END;
123 VAR
124 ctrlDir-: Containers.Directory;
125 dir-, stdDir-: Directory;
128 (* forward used in GetStart, UpdateView, ShowRangeIn *)
129 PROCEDURE ^ DoSetOrigin (v: StdView; org, dy: INTEGER; silent: BOOLEAN);
132 (** View **)
134 PROCEDURE (v: View) Internalize2- (VAR rd: Stores.Reader), EXTENSIBLE;
135 (** pre: ~v.init **)
136 (** post: v.init **)
137 VAR thisVersion: INTEGER;
138 BEGIN
139 (*v.Internalize^(rd);*)
140 IF rd.cancelled THEN RETURN END;
141 rd.ReadVersion(minVersion, maxVersion, thisVersion)
142 END Internalize2;
144 PROCEDURE (v: View) Externalize2- (VAR wr: Stores.Writer), EXTENSIBLE;
145 (** pre: v.init **)
146 BEGIN
147 (*v.Externalize^(wr);*)
148 wr.WriteVersion(maxVersion)
149 END Externalize2;
151 PROCEDURE (v: View) ThisModel* (): TextModels.Model, EXTENSIBLE;
152 VAR m: Containers.Model;
153 BEGIN
154 m := v.ThisModel^();
155 IF m # NIL THEN
156 RETURN m(TextModels.Model)
157 ELSE
158 RETURN NIL
159 END
160 END ThisModel;
162 PROCEDURE (v: View) DisplayMarks* (hide: BOOLEAN), NEW, ABSTRACT;
163 PROCEDURE (v: View) HidesMarks* (): BOOLEAN, NEW, ABSTRACT;
164 PROCEDURE (v: View) SetSetter* (setter: TextSetters.Setter), NEW, ABSTRACT;
165 PROCEDURE (v: View) ThisSetter* (): TextSetters.Setter, NEW, ABSTRACT;
166 PROCEDURE (v: View) SetOrigin* (org, dy: INTEGER), NEW, ABSTRACT;
167 (** post: org = ThisLine(org) => v.org = org, v.dy = dy; else v.org = ThisLine(org), v.dy = 0 **)
169 PROCEDURE (v: View) PollOrigin* (OUT org, dy: INTEGER), NEW, ABSTRACT;
170 PROCEDURE (v: View) SetDefaults* (r: TextRulers.Ruler; a: TextModels.Attributes),
171 NEW, ABSTRACT;
172 (** pre: r.init, a.init **)
174 PROCEDURE (v: View) PollDefaults* (OUT r: TextRulers.Ruler; OUT a: TextModels.Attributes),
175 NEW, ABSTRACT;
176 PROCEDURE (v: View) GetThisLocation* (f: Views.Frame; pos: INTEGER; OUT loc: Location),
177 NEW, ABSTRACT;
179 PROCEDURE (v: View) GetRect* (f: Views.Frame; view: Views.View; OUT l, t, r, b: INTEGER);
180 VAR con: Models.Context; loc: Location; pos: INTEGER;
181 BEGIN
182 con := view.context;
183 ASSERT(con # NIL, 20); ASSERT(con.ThisModel() = v.ThisModel(), 21);
184 pos := con(TextModels.Context).Pos();
185 v.GetThisLocation(f, pos, loc);
186 IF loc.view = view THEN
187 l := loc.l; t := loc.t; r := loc.r; b := loc.b
188 ELSE
189 l := MAX(INTEGER); t := MAX(INTEGER); r := l; b := t
190 END
191 END GetRect;
193 PROCEDURE (v: View) GetRange* (f: Views.Frame; OUT beg, end: INTEGER), NEW, ABSTRACT;
194 (** post: beg = beg of first visible line, end = end of last visible line **)
196 PROCEDURE (v: View) ThisPos* (f: Views.Frame; x, y: INTEGER): INTEGER, NEW, ABSTRACT;
197 PROCEDURE (v: View) ShowRangeIn* (f: Views.Frame; beg, end: INTEGER), NEW, ABSTRACT;
198 PROCEDURE (v: View) ShowRange* (beg, end: INTEGER; focusOnly: BOOLEAN), NEW, ABSTRACT;
199 (** post: in all frames (resp. in front or otherwise target frame if focusOnly):
200 if possible, first visible pos <= k <= last visible pos,
201 with k = beg if beg = end and beg <= k < end otherwise **)
204 (** Directory **)
206 PROCEDURE (d: Directory) Set* (defAttr: TextModels.Attributes), NEW, EXTENSIBLE;
207 BEGIN
208 ASSERT(defAttr # NIL, 20); ASSERT(defAttr.init, 21);
209 d.defAttr := defAttr
210 END Set;
212 PROCEDURE (d: Directory) New* (text: TextModels.Model): View, NEW, ABSTRACT;
215 (** miscellaneous **)
217 PROCEDURE SetCtrlDir* (d: Containers.Directory);
218 BEGIN
219 ASSERT(d # NIL, 20); ctrlDir := d
220 END SetCtrlDir;
222 PROCEDURE SetDir* (d: Directory);
223 BEGIN
224 ASSERT(d # NIL, 20); dir := d
225 END SetDir;
228 PROCEDURE Focus* (): View;
229 VAR v: Views.View;
230 BEGIN
231 v := Controllers.FocusView();
232 IF (v # NIL) & (v IS View) THEN RETURN v(View) ELSE RETURN NIL END
233 END Focus;
235 PROCEDURE FocusText* (): TextModels.Model;
236 VAR v: View;
237 BEGIN
238 v := Focus();
239 IF v # NIL THEN RETURN v.ThisModel() ELSE RETURN NIL END
240 END FocusText;
242 PROCEDURE Deposit*;
243 BEGIN
244 Views.Deposit(dir.New(NIL))
245 END Deposit;
248 PROCEDURE ShowRange* (text: TextModels.Model; beg, end: INTEGER; focusOnly: BOOLEAN);
249 (** post: in all front or target frames displaying a view displaying t:
250 if possible, first visible pos <= k <= last visible pos,
251 with k = beg if beg = end and beg <= k < end otherwise **)
252 VAR pm: PositionMsg;
253 BEGIN
254 ASSERT(text # NIL, 20);
255 pm.beg := beg; pm.end := end; pm.focusOnly := focusOnly;
256 Models.Broadcast(text, pm)
257 END ShowRange;
260 PROCEDURE ThisRuler* (v: View; pos: INTEGER): TextRulers.Ruler;
261 VAR r: TextRulers.Ruler; a: TextModels.Attributes; rpos: INTEGER;
262 BEGIN
263 v.PollDefaults(r, a); rpos := -1; TextRulers.GetValidRuler(v.ThisModel(), pos, -1, r, rpos);
264 RETURN r
265 END ThisRuler;
268 (* auxiliary procedures *)
270 PROCEDURE GetReader (v: StdView; start: INTEGER; IN box: TextSetters.LineBox
271 ): TextSetters.Reader;
272 VAR st: TextSetters.Setter; rd: TextSetters.Reader;
273 BEGIN
274 ASSERT(box.ruler # NIL, 100);
275 st := v.ThisSetter();
276 rd := v.cachedRd; v.cachedRd := NIL; (* reader recycling *)
277 rd := st.NewReader(rd);
278 rd.Set(rd.r, v.text, box.left, start, box.ruler, box.rpos, st.vw, st.hideMarks);
279 RETURN rd
280 END GetReader;
282 PROCEDURE CacheReader (v: StdView; rd: TextSetters.Reader);
283 BEGIN
284 ASSERT(v.cachedRd = NIL, 20);
285 v.cachedRd := rd
286 END CacheReader;
289 (* line descriptors *)
291 PROCEDURE SetLineAsc (st: TextSetters.Setter; t: Line; dsc: INTEGER);
292 (* pre: dsc: descender of previous line (-1 if first line) *)
293 BEGIN
294 t.asc := t.box.asc + st.GridOffset(dsc, t.box);
295 t.h := t.asc + t.box.dsc
296 END SetLineAsc;
298 PROCEDURE NewLine (st: TextSetters.Setter; start, dsc: INTEGER): Line;
299 (* pre: start: start of line to measure; dsc: descender of previous line (-1 if first line) *)
300 VAR t: Line;
301 BEGIN
302 NEW(t); st.GetLine(start, t.box);
303 t.start := start; SetLineAsc(st, t, dsc);
304 t.attr := t.box.ruler.style.attr;
305 RETURN t
306 END NewLine;
308 PROCEDURE AddLine (st: TextSetters.Setter; VAR t: Line; VAR start, y: INTEGER);
309 BEGIN
310 t.next := NewLine(st, start, t.box.dsc); t := t.next;
311 INC(start, t.box.len); INC(y, t.h)
312 END AddLine;
314 PROCEDURE InitLines (v: StdView);
315 VAR asc, dsc, w: INTEGER; t0, t: Line; start, y: INTEGER;
316 BEGIN
317 v.defAttr.font.GetBounds(asc, dsc, w);
318 NEW(t0); start := v.org; y := v.dy;
319 t0.box.dsc := -1; (* dsc = -1: trailer.next is first line *)
320 t := t0; AddLine(v.ThisSetter(), t, start, y); t.next := t0; (* at least one valid line desc *)
321 t0.start := start; t0.asc := asc; t0.h := asc + dsc; (* trailer.(asc, h) for caret display following last line *)
322 t0.attr := NIL;
323 t0.box.eot := TRUE; t0.box.len := 0;
324 t0.box.ruler := NIL;
325 t0.box.left := -1; (* make trailer async to every other line *)
326 v.trailer := t0; v.bot := 0
327 END InitLines;
329 PROCEDURE ExtendLines (v: StdView; bot: INTEGER);
330 VAR st: TextSetters.Setter; t0, t: Line; start, y: INTEGER;
331 BEGIN
332 IF bot >= v.bot THEN
333 t0 := v.trailer; start := t0.start;
334 y := v.dy; t := t0; WHILE t.next # t0 DO t := t.next; INC(y, t.h) END;
335 IF (y < bot) & ~t.box.eot THEN
336 st := v.ThisSetter();
337 REPEAT AddLine(st, t, start, y) UNTIL (y >= bot) OR t.box.eot;
338 t.next := t0; t0.start := start
339 END;
340 v.bot := bot
341 END
342 END ExtendLines;
344 PROCEDURE ReduceLines (v: StdView; bot: INTEGER);
345 VAR t0, t: Line; y: INTEGER;
346 BEGIN
347 IF bot <= v.bot THEN
348 t0 := v.trailer; y := v.dy;
349 t := t0; WHILE (t.next # t0) & (y < bot) DO t := t.next; INC(y, t.h) END;
350 t0.start := t.next.start; t.next := t0;
351 v.bot := bot
352 END
353 END ReduceLines;
355 PROCEDURE ValidateLines (v: StdView; bot: INTEGER);
356 VAR st: TextSetters.Setter; w, h, len: INTEGER;
357 BEGIN
358 IF v.setter # NIL THEN
359 v.context.GetSize(w, h); (* possibly adapt to changed width *)
360 IF v.setter.vw # w THEN v.setter0 := NIL; v.trailer := NIL END
361 END;
362 len := v.text.Length();
363 IF (v.org > len) OR (v.trailer # NIL) & (v.trailer.start > len) THEN v.trailer := NIL END;
364 IF v.trailer = NIL THEN
365 IF v.org > len THEN v.org := len END;
366 st := v.ThisSetter(); v.org := st.ThisLine(v.org);
367 InitLines(v)
368 END;
369 ExtendLines(v, bot)
370 END ValidateLines;
372 PROCEDURE PrependLines (v: StdView);
373 VAR st: TextSetters.Setter; t0, t1, t: Line; start, y: INTEGER;
374 BEGIN
375 t0 := v.trailer; start := v.org; y := v.dy;
376 IF t0.start # start THEN
377 st := v.ThisSetter();
378 t := t0; t1 := t0.next;
379 WHILE (t1.start # start) & (y < v.bot) DO AddLine(st, t, start, y) END;
380 IF y >= v.bot THEN
381 t.next := t0; t0.start := start
382 ELSE
383 t.next := t1;
384 IF t1 # v.trailer THEN SetLineAsc(st, t1, t.box.dsc) END
385 END
386 END
387 END PrependLines;
390 (* update frame after insert/delete/replace *)
392 PROCEDURE ThisViewLine (v: StdView; y: INTEGER): Line;
393 (* pre: 0 <= y < v.bot *)
394 VAR t: Line; py: INTEGER;
395 BEGIN
396 t := v.trailer.next; py := v.dy;
397 WHILE ~t.box.eot & (py + t.h < y) DO INC(py, t.h); t := t.next END;
398 RETURN t
399 END ThisViewLine;
401 PROCEDURE LocateThisLine (v: StdView; start: INTEGER; OUT t: Line; OUT y: INTEGER);
402 VAR t1: Line;
403 BEGIN
404 t := v.trailer.next; y := v.dy;
405 t1 := v.trailer.next;
406 WHILE t.start # start DO INC(y, t.h); t := t.next; ASSERT(t # t1, 100) END
407 END LocateThisLine;
409 PROCEDURE GetStart (st: TextSetters.Setter; v: StdView; beg: INTEGER; OUT start: INTEGER);
410 (* find start of line containing beg after text change; tuned using valid line descs *)
411 VAR s, t: Line;
412 BEGIN
413 s := v.trailer; t := s.next;
414 WHILE (t # v.trailer) & (t.start + t.box.len < beg) DO s := t; t := s.next END;
415 IF s # v.trailer THEN (* at least first line desc possibly still valid *)
416 start := st.NextLine(s.start); (* NextLine can be much cheaper than ThisLine *)
417 IF start # t.start THEN
418 GetStart(st, v, s.start, start)
419 ELSIF ~t.box.eot & (start + t.box.len = beg) & (st.NextLine(start) = beg) THEN
420 start := beg
421 END
422 ELSE
423 IF v.org <= v.text.Length() THEN
424 start := st.ThisLine(v.org)
425 ELSE
426 start := st.ThisLine(v.text.Length())
427 END;
428 IF start < v.org THEN
429 DoSetOrigin(v, start, 0, TRUE)
430 ELSIF start > v.org THEN
431 start := v.org
432 END
433 END
434 END GetStart;
436 PROCEDURE GetStringStart (v: StdView; t: Line; pos: INTEGER; OUT p1, x: INTEGER);
437 VAR rd: TextSetters.Reader;
438 BEGIN
439 p1 := t.start; x := t.box.left;
440 IF t.box.views THEN
441 rd := GetReader(v, p1, t.box); rd.Read;
442 WHILE ~rd.eot & (rd.pos <= pos) DO
443 rd.AdjustWidth(t.start, p1, t.box, rd.w); INC(rd.x, rd.w);
444 IF rd.view # NIL THEN p1 := rd.pos; x := rd.x END;
445 rd.Read
446 END;
447 CacheReader(v, rd)
448 END
449 END GetStringStart;
451 PROCEDURE InSynch (t0, t1: Line): BOOLEAN;
452 BEGIN
453 RETURN (t0.start = t1.start) & (t0.asc = t1.asc) & (t0.attr = t1.attr)
454 & (t0.box.left = t1.box.left) & (t0.box.asc = t1.box.asc) & (t0.box.dsc = t1.box.dsc)
455 & (t0.box.rbox = t1.box.rbox) & (t0.box.bop = t1.box.bop)
456 END InSynch;
458 PROCEDURE RebuildView (v: StdView);
459 BEGIN
460 v.setter0 := NIL;
461 IF v.trailer # NIL THEN v.trailer := NIL; v.bot := 0; Views.Update(v, Views.rebuildFrames) END
462 END RebuildView;
464 PROCEDURE UpdateIn (v: StdView; l, t, b: INTEGER);
465 BEGIN
466 Views.UpdateIn(v, l, t, MAX(INTEGER), b, Views.rebuildFrames)
467 END UpdateIn;
469 PROCEDURE UpdateFrames (v: StdView; t0, t1, u: Line; beg, y0, yu: INTEGER);
470 VAR t, te: Line; b, x, b0, b1, top, bot: INTEGER;
471 BEGIN
472 IF ((beg < t0.next.start) OR t0.box.eot) & ~t0.box.adj
473 & ((beg < t1.next.start) OR t1.box.eot) & ~t1.box.adj
474 & InSynch(t0, t1) THEN
475 GetStringStart(v, t1, beg, beg, x)
476 ELSE
477 beg := t1.start
478 END;
479 b := y0; t := t0; WHILE t # u DO INC(b, t.h); t := t.next END;
480 IF b = yu THEN
481 te := u
482 ELSE (* t = u *)
483 te := v.trailer;
484 b0 := b; WHILE t # v.trailer DO INC(b0, t.h); t := t.next END;
485 IF yu < b THEN ExtendLines(v, v.bot) ELSE ReduceLines(v, v.bot) END;
486 b1 := y0; t := t1; WHILE t # v.trailer DO INC(b1, t.h); t := t.next END;
487 IF b1 < b0 THEN UpdateIn(v, 0, b1, b0) END (* erase trailer *)
488 END;
489 IF t1.start < beg THEN (* conserve head of t1 *)
490 UpdateIn(v, x, y0, y0 + t1.h); (* redraw tail of t1 *)
491 top := y0 + t1.h
492 ELSE
493 top := y0
494 END;
495 bot := y0; REPEAT INC(bot, t1.h); t1 := t1.next UNTIL t1 = te;
496 IF top < bot THEN UpdateIn(v, 0, top, bot) END (* redraw affected lines *)
497 END UpdateFrames;
499 PROCEDURE UpdateView (v: StdView; beg, end, delta: INTEGER);
500 VAR st: TextSetters.Setter; r: TextRulers.Ruler; rpos: INTEGER;
501 s0, t0, t, tn, u: Line; start, y, y0: INTEGER;
502 BEGIN
503 IF v.trailer # NIL THEN
504 v.setter0 := NIL; st := v.ThisSetter();
505 IF (beg <= v.trailer.start) & ((end >= v.org) OR (end - delta >= v.org)) THEN
506 GetStart(st, v, beg, start);
507 y0 := v.dy; s0 := v.trailer;
508 WHILE s0.next.start < start DO s0 := s0.next; INC(y0, s0.h) END;
510 t := s0.next; WHILE (t # v.trailer) & (t.start < end) DO t := t.next END;
511 IF (t = v.trailer.next) & (t.start >= end) THEN
512 REPEAT
513 INC(t.start, delta);
514 IF t.box.rpos >= end THEN INC(t.box.rpos, delta) END;
515 t := t.next
516 UNTIL t = v.trailer.next
517 ELSE
518 WHILE (t # v.trailer.next) & (t.start >= end) DO
519 INC(t.start, delta);
520 IF t.box.rpos >= end THEN INC(t.box.rpos, delta) END;
521 t := t.next
522 END
523 END;
524 tn := s0; y := y0; t0 := s0.next; u := t0;
525 REPEAT
526 t := tn; AddLine(st, tn, start, y); (* start = end(tn), y = bot(tn) *)
527 WHILE (u # v.trailer) & (u.start < tn.start) DO u := u.next END
528 UNTIL tn.box.eot OR (y > v.bot)
529 OR (tn.start >= end) & (u.start = tn.start) & (u.box.len = tn.box.len)
530 & (u.asc = tn.asc) & (u.attr = tn.attr) & (u.box.dsc = tn.box.dsc)
531 & (u.box.rpos = tn.box.rpos); (* can be expensive ... *)
532 IF tn.box.eot OR (y > v.bot) THEN
533 t := tn; u := v.trailer; v.trailer.start := start
534 ELSE
535 DEC(y, tn.h)
536 END;
537 t.next := u;
538 IF (s0 # v.trailer) & (s0.next # v.trailer) THEN s0.box.eot := FALSE END;
539 ASSERT(v.trailer.start <= v.text.Length(), 100);
540 UpdateFrames(v, t0, s0.next, u, beg, y0, y)
541 ELSIF end <= v.org THEN
542 INC(v.org, delta);
543 (*
544 IF end < v.org - delta - 500 THEN start := v.org ELSE start := st.ThisLine(v.org) END;
545 (* this is not safe; even a change 500 characters away could force the view's origin to a
546 new position in order to maintain the invariant that the origin always falls on a line start;
547 however, ThisLine can be quite expensive -- can we rely on TextSetters cache ? *)
548 *)
549 start := st.ThisLine(v.org);
550 r := v.defRuler; rpos := -1; TextRulers.GetValidRuler(v.text, start, -1, r, rpos);
551 IF (v.org = start) & (v.trailer.next.attr = r.style.attr) THEN
552 t := v.trailer;
553 REPEAT
554 t := t.next; INC(t.start, delta);
555 IF t.box.rpos < start THEN t.box.rpos := rpos ELSE INC(t.box.rpos, delta) END
556 UNTIL t = v.trailer
557 ELSE
558 DoSetOrigin(v, start, 0, TRUE); RebuildView(v)
559 END
560 END
561 END
562 END UpdateView;
564 PROCEDURE StyleUpdate (v: StdView; oldAttr: TextRulers.Attributes);
565 VAR t: Line; beg: INTEGER; first: BOOLEAN;
566 BEGIN
567 IF v.trailer # NIL THEN
568 t := v.trailer.next; first := TRUE;
569 WHILE t # v.trailer DO
570 WHILE (t # v.trailer) & (t.attr # oldAttr) DO t := t.next END;
571 IF t # v.trailer THEN
572 IF first THEN v.Neutralize; first := FALSE END;
573 beg := t.start; t := t.next;
574 WHILE (t # v.trailer) & (t.attr = oldAttr) DO t := t.next END;
575 UpdateView(v, beg, t.start, 0)
576 END
577 END
578 END
579 END StyleUpdate;
582 (* line drawing *)
584 PROCEDURE DrawLine (v: StdView;
585 start: INTEGER; IN box: TextSetters.LineBox;
586 f: Views.Frame; l, r, y, t: INTEGER; pageF: BOOLEAN
587 );
588 (* pre: area cleared *)
589 (* [l,r) for high-level clipping to tune update after small change *)
590 CONST cacheLen = 128;
591 VAR rd: TextSetters.Reader; ra: TextRulers.Attributes;
592 v1: Views.View; c: Containers.Controller;
593 py, end, skip: INTEGER;
594 cache: RECORD (* initially: long = TRUE, len = 0 *)
595 x, y: INTEGER; color: Ports.Color; font: Fonts.Font;
596 len: INTEGER;
597 buf: ARRAY cacheLen OF CHAR
598 END;
600 PROCEDURE FlushCaches;
601 BEGIN
602 IF cache.len > 0 THEN
603 cache.buf[cache.len] := 0X;
604 f.DrawString(cache.x, cache.y, cache.color, cache.buf, cache.font)
605 END;
606 cache.len := 0
607 END FlushCaches;
609 PROCEDURE CacheString (x, y: INTEGER; c: INTEGER; IN s: ARRAY OF CHAR;
610 f: Fonts.Font
611 );
612 VAR i, j, len: INTEGER;
613 BEGIN
614 len := 0; WHILE s[len] # 0X DO INC(len) END;
615 IF (cache.len + len >= cacheLen) OR (cache.y # y) OR (cache.color # c) OR (cache.font # f) THEN
616 FlushCaches
617 END;
618 ASSERT(cache.len + len < cacheLen, 100);
619 IF cache.len = 0 THEN cache.x := x; cache.y := y; cache.color := c; cache.font := f END;
620 i := 0; j := cache.len;
621 WHILE i < len DO cache.buf[j] := s[i]; INC(i); INC(j) END;
622 cache.len := j
623 END CacheString;
625 (*
626 PROCEDURE CacheString (x, y: INTEGER; c: INTEGER; IN s: ARRAY OF CHAR;
627 f: Fonts.Font
628 );
629 VAR i, j, len: INTEGER;
630 BEGIN
631 (* flush first, then promote *)
632 len := 0; WHILE s[len] # 0X DO INC(len) END;
633 IF (cache.len + len >= cacheLen) OR (cache.y # y) OR (cache.color # c) OR (cache.font # f) THEN
634 FlushCaches
635 END;
636 IF (cache.len > 0) & cache.short THEN (* promote short chars to chars *)
637 i := 0; WHILE i < cache.len DO cache.buf[i] := cache.sbuf[i]; INC(i) END
638 END;
639 cache.short := FALSE;
640 ASSERT(cache.len + len < cacheLen, 100);
641 IF cache.len = 0 THEN cache.x := x; cache.y := y; cache.color := c; cache.font := f END;
642 i := 0; j := cache.len;
643 WHILE i < len DO cache.buf[j] := s[i]; INC(i); INC(j) END;
644 cache.len := j
645 END CacheString;
646 *)
648 BEGIN
649 IF box.len > 0 THEN
650 cache.len := 0;
651 end := start + box.len; skip := start + box.skipOff;
652 rd := GetReader(v, start, box); rd.Read;
653 WHILE ~rd.eot & (rd.pos <= end) & (rd.x < r) DO
654 IF rd.pos > skip THEN rd.w := rd.endW END;
655 rd.AdjustWidth(start, rd.pos, box, rd.w);
656 IF rd.x + rd.w > l THEN
657 v1 := rd.view;
658 IF v1 # NIL THEN
659 FlushCaches;
660 IF ~((TextModels.hideable IN rd.textOpts) & v.hideMarks) THEN
661 c := v.ThisController();
662 Views.InstallFrame(f, v1,
663 rd.x, y - rd.attr.offset + rd.dsc - rd.h, 0,
664 (c # NIL) & (v1 = c.ThisFocus()) )
665 END
666 ELSIF (rd.h > 0) & (rd.w > 0) THEN
667 IF box.rbox & ~v.hideMarks THEN rd.string[0] := parasign END; (* ¶ sign *)
668 py := y - rd.attr.offset;
669 IF rd.string[0] > " " THEN
670 CacheString(rd.x, py, rd.attr.color, rd.string, rd.attr.font);
671 IF ~v.hideMarks & (TextModels.hideable IN rd.textOpts) THEN
672 f.DrawRect(rd.x, py - box.asc + f.dot,
673 MIN(rd.x + rd.w, f.r), py + box.dsc - f.dot, 0, Ports.grey25)
674 END
675 ELSIF rd.string[0] # 0X THEN
676 FlushCaches;
677 IF ~v.hideMarks & (TextModels.hideable IN rd.textOpts) THEN
678 f.DrawRect(rd.x, py - box.asc + f.dot, rd.x + rd.w, py + box.dsc - f.dot, 0, Ports.grey25)
679 END
680 ELSE FlushCaches
681 END
682 END
683 END;
684 INC(rd.x, rd.w); rd.Read
685 END;
686 FlushCaches;
687 CacheReader(v, rd)
688 END;
689 IF v.hideMarks & ~pageF THEN
690 ra := box.ruler.style.attr;
691 IF TextRulers.pageBreak IN ra.opts THEN
692 IF (box.rpos = start) & (ra.lead >= f.dot) THEN
693 f.DrawLine(l, t, r - f.dot, t, 0, Ports.grey50)
694 ELSIF (box.rpos = start - 1) & (ra.lead < f.dot) THEN
695 f.DrawLine(l, t, r - f.dot, t, 0, Ports.grey50)
696 END
697 END
698 END
699 END DrawLine;
701 PROCEDURE DrawDecorations (v: StdView; u: Line; f: Views.Frame; l, t, r, b: INTEGER);
702 VAR a: TextRulers.Attributes; i, x: INTEGER; col: Ports.Color;
703 st: TextSetters.Setter; srd: TextSetters.Reader; rd: TextModels.Reader;
704 BEGIN
705 IF t < b THEN
706 i := 0; a := u.attr; srd := NIL;
707 WHILE i < a.tabs.len DO
708 IF TextRulers.barTab IN a.tabs.tab[i].type THEN
709 x := a.tabs.tab[i].stop;
710 IF (l <= x) & (x < r) THEN
711 IF u.box.rpos = -1 THEN col := v.defAttr.color
712 ELSIF srd = NIL THEN
713 st := v.ThisSetter();
714 srd := v.cachedRd; v.cachedRd := NIL;
715 srd := st.NewReader(srd);
716 srd.Set(srd.r, v.text, 0, 0, v.defRuler, 0, st.vw, st.hideMarks); rd := srd.r;
717 rd.SetPos(u.box.rpos); rd.Read; col := rd.attr.color
718 END;
719 f.DrawLine(x, t, x, b - f.dot, 0, col)
720 END
721 END;
722 INC(i)
723 END;
724 IF srd # NIL THEN CacheReader(v, srd) END
725 END
726 END DrawDecorations;
729 (* focus-message handling *)
731 PROCEDURE PollSection (v: StdView; f: Views.Frame; VAR msg: Controllers.PollSectionMsg);
732 CONST ms = maxScrollSteps; mh = maxScrollHeight;
733 VAR t: Line; steps, step: INTEGER;
734 BEGIN
735 IF msg.vertical THEN
736 ValidateLines(v, f.b); t := v.trailer.next;
737 IF t.h > 0 THEN
738 steps := -((-t.h) DIV mh); step := -(v.dy DIV mh)
739 ELSE steps := 1; step := 0
740 END;
741 msg.wholeSize := v.text.Length() * ms;
742 msg.partPos := v.org * ms + t.box.len * ms * step DIV steps;
743 msg.partSize := 0;
744 msg.valid := (v.org > 0) OR (t.h > mh) OR (t.next # v.trailer);
745 msg.done := TRUE
746 END
747 END PollSection;
749 PROCEDURE Scroll (v: StdView; f: Views.Frame; VAR msg: Controllers.ScrollMsg);
750 VAR st: TextSetters.Setter; box, box0: TextSetters.LineBox;
751 t, t1, trailer: Line; org, len, dy, h, h1, sh, steps, step: INTEGER;
752 poll: Controllers.PollSectionMsg;
753 BEGIN
754 IF msg.vertical THEN
755 poll.vertical := TRUE;
756 PollSection(v, f, poll)
757 END;
758 IF msg.vertical & poll.valid THEN
759 org := v.org; dy := v.dy; st := v.ThisSetter(); trailer := v.trailer;
760 CASE msg.op OF
761 Controllers.decLine:
762 IF dy <= -(maxScrollHeight + fuseScrollHeight) THEN
763 INC(dy, maxScrollHeight)
764 ELSIF dy < 0 THEN
765 dy := 0
766 ELSIF org > 0 THEN
767 org := st.PreviousLine(org); st.GetLine(org, box);
768 h1 := box.asc + box.dsc + st.GridOffset(-1, box);
769 IF h1 > maxScrollHeight + fuseScrollHeight THEN
770 sh := h1 - h1 MOD maxScrollHeight;
771 IF h1 - sh < fuseScrollHeight THEN DEC(sh, maxScrollHeight) END;
772 dy := -sh
773 ELSE dy := 0
774 END
775 END
776 | Controllers.incLine:
777 t := trailer.next;
778 IF t.h + dy > maxScrollHeight + fuseScrollHeight THEN
779 DEC(dy, maxScrollHeight)
780 ELSIF ~t.box.eot THEN
781 org := t.next.start; dy := 0
782 END
783 | Controllers.decPage:
784 sh := f.b; DEC(sh, maxScrollHeight + sh MOD maxScrollHeight);
785 IF dy <= -(sh + fuseScrollHeight) THEN
786 INC(dy, sh)
787 ELSE
788 t := trailer.next;
789 h := maxScrollHeight - dy;
790 IF t.h < h THEN h := t.h END;
791 box0 := t.box; h1:= h - st.GridOffset(-1, box0);
792 WHILE (org > 0) & (h + fuseScrollHeight < f.b) DO
793 org := st.PreviousLine(org); st.GetLine(org, box);
794 h1 := box.asc + box.dsc;
795 INC(h, h1 + st.GridOffset(box.dsc, box0));
796 box0 := box
797 END;
798 h1 := h1 + st.GridOffset(-1, box0);
799 sh := h1 - (h - f.b); DEC(sh, sh MOD maxScrollHeight);
800 IF h1 - sh >= fuseScrollHeight THEN dy := -sh ELSE dy := 0 END
801 END;
802 IF (org > v.org) OR (org = v.org) & (dy <= v.dy) THEN (* guarantee progress *)
803 org := st.PreviousLine(org); st.GetLine(org, box);
804 h1 := box.asc + box.dsc + st.GridOffset(-1, box);
805 IF h1 > maxScrollHeight + fuseScrollHeight THEN
806 dy := - (h1 DIV maxScrollHeight * maxScrollHeight)
807 ELSE
808 dy := 0
809 END
810 END
811 | Controllers.incPage:
812 t := trailer.next;
813 sh := f.b; DEC(sh, maxScrollHeight + sh MOD maxScrollHeight);
814 IF t.h + dy > sh + fuseScrollHeight THEN
815 DEC(dy, sh)
816 ELSE
817 t := ThisViewLine(v, f.b); LocateThisLine(v, t.start, t1, h);
818 IF (h + t.h >= f.b) & (t.h <= maxScrollHeight) THEN
819 org := st.PreviousLine(t.start)
820 ELSE org := t.start
821 END;
822 IF h + t.h - f.b > maxScrollHeight THEN
823 sh := f.b - h; DEC(sh, maxScrollHeight + sh MOD maxScrollHeight);
824 IF sh >= fuseScrollHeight THEN dy := -sh ELSE dy := 0 END
825 ELSE
826 dy := 0
827 END
828 END;
829 IF (org < v.org) OR (org = v.org) & (dy >= v.dy) THEN (* guarantee progress *)
830 IF t.h + dy > maxScrollHeight + fuseScrollHeight THEN
831 DEC(dy, maxScrollHeight)
832 ELSE
833 org := t.next.start; dy := 0
834 END
835 END
836 | Controllers.gotoPos:
837 org := st.ThisLine(msg.pos DIV maxScrollSteps); st.GetLine(org, box);
838 sh := box.asc + box.dsc + st.GridOffset(-1, box);
839 steps := -((-sh) DIV maxScrollHeight);
840 IF (steps > 0) & (box.len > 0) THEN
841 step := steps * (msg.pos - org * maxScrollSteps) DIV (maxScrollSteps * box.len);
842 (*
843 step := steps * (msg.pos MOD maxScrollSteps) DIV maxScrollSteps;
844 *)
845 dy := -(step * maxScrollHeight)
846 ELSE
847 dy := 0
848 END
849 ELSE
850 END;
851 len := v.text.Length();
852 IF org > len THEN org := len; dy := 0 END;
853 v.SetOrigin(org, dy);
854 msg.done := TRUE
855 END
856 END Scroll;
858 PROCEDURE NotifyViewsOnPage (v: StdView; beg, end, pageNo: INTEGER);
859 VAR st: TextSetters.Setter; rd: TextSetters.Reader; r: TextModels.Reader;
860 view: Views.View; current: INTEGER;
861 page: PageMsg;
862 BEGIN
863 IF pageNo >= 0 THEN current := pageNo
864 ELSIF Printing.par # NIL THEN current := Printing.Current() (* Printing.par.page.current *) + 1
865 ELSE current := -1
866 END;
867 IF current >= 0 THEN
868 st := v.ThisSetter();
869 rd := v.cachedRd; v.cachedRd := NIL; (* reader recycling *)
870 rd := st.NewReader(rd);
871 rd.Set(rd.r, v.text, 0, 0, v.defRuler, 0, st.vw, st.hideMarks);
872 r := rd.r; r.SetPos(beg); r.ReadView(view);
873 WHILE (r.Pos() <= end) & ~r.eot DO
874 page.current := current; Views.HandlePropMsg(view, page); r.ReadView(view)
875 END;
876 CacheReader(v, rd)
877 END
878 END NotifyViewsOnPage;
880 PROCEDURE Page (v: StdView; pageH: INTEGER; op, pageY: INTEGER; OUT done, eoy: BOOLEAN);
881 VAR st: TextSetters.Setter; org, prev, page: INTEGER;
882 BEGIN
883 IF ~v.hideMarks & ((v.context = NIL) OR v.context.Normalize()) THEN
884 v.DisplayMarks(hide)
885 END;
886 st := v.ThisSetter();
887 IF op = Controllers.nextPageY THEN
888 done := TRUE; org := st.NextPage(pageH, v.org); eoy := (org = v.text.Length());
889 IF ~eoy THEN NotifyViewsOnPage(v, org, st.NextPage(pageH, org), -1) END
890 ELSIF op = Controllers.gotoPageY THEN
891 ASSERT(pageY >= 0, 20);
892 done := TRUE; org := 0; eoy := FALSE; page := 0;
893 WHILE (page < pageY) & ~eoy DO
894 prev := org; org := st.NextPage(pageH, org); eoy := org = prev;
895 IF ~eoy THEN NotifyViewsOnPage(v, prev, org, page) END;
896 INC(page)
897 END;
898 IF ~eoy THEN NotifyViewsOnPage(v, org, st.NextPage(pageH, org), page) END
899 ELSE
900 done := FALSE
901 END;
902 IF done & ~eoy THEN v.org := org; v.dy := 0; v.trailer := NIL; v.bot := 0 END
903 END Page;
906 PROCEDURE ShowAdjusted (v: StdView; shift: INTEGER; rebuild: BOOLEAN);
907 BEGIN
908 IF shift # 0 THEN Views.Scroll(v, 0, shift)
909 ELSIF rebuild THEN UpdateIn(v, 0, 0, MAX(INTEGER))
910 END;
911 Views.RestoreDomain(v.Domain())
912 END ShowAdjusted;
914 PROCEDURE AdjustLines (v: StdView; org, dy: INTEGER;
915 OUT shift: INTEGER; OUT rebuild: BOOLEAN
916 );
917 (* post: shift = 0 OR ~rebuild *)
918 VAR d: Stores.Domain; c: Containers.Controller; t, t0, t1: Line; org0, dy0, y: INTEGER;
919 BEGIN
920 d := v.Domain(); t0 := v.trailer; org0 := v.org; rebuild := FALSE; shift := 0;
921 IF (d # NIL) & ((org # org0) OR (dy # v.dy)) THEN
922 Views.RestoreDomain(d); (* make sure that pixels are up-to-date before scrolling *)
923 c := v.ThisController();
924 IF c # NIL THEN
925 Containers.FadeMarks(c, Containers.hide) (* fade marks with overhang *)
926 END
927 END;
928 IF (t0 # NIL) & (org = org0) & (dy # v.dy) THEN (* sub-line shift *)
929 shift := dy - v.dy;
930 ELSIF (t0 # NIL) & (org > org0) & (org < t0.start) THEN (* shift up *)
931 LocateThisLine(v, org, t, y); t0.next := t;
932 shift := dy - y
933 ELSIF (t0 # NIL) & (org < org0) THEN (* shift down *)
934 t1 := t0.next; dy0 := v.dy + t1.asc; v.org := org; v.dy := dy;
935 IF t1.start = org0 THEN (* new lines need to be prepended *)
936 PrependLines(v) (* may change t1.asc *)
937 END;
938 ASSERT(t0.next.start = org, 100);
939 IF org0 < t0.start THEN (* former top still visible -> shift down *)
940 LocateThisLine(v, org0, t, y); shift := y - (dy0 - t1.asc)
941 ELSE (* rebuild all *)
942 rebuild := TRUE
943 END
944 ELSIF (t0 = NIL) OR (org # org0) OR (dy # v.dy) THEN (* rebuild all *)
945 rebuild := TRUE
946 END;
947 v.org := org; v.dy := dy;
948 IF rebuild THEN (* rebuild all *)
949 v.trailer := NIL; ValidateLines(v, v.bot)
950 ELSIF shift < 0 THEN (* shift up *)
951 INC(v.bot, shift); ExtendLines(v, v.bot - shift)
952 ELSIF shift > 0 THEN (* shift down *)
953 INC(v.bot, shift); ReduceLines(v, v.bot - shift)
954 END
955 END AdjustLines;
957 PROCEDURE Limit (v: StdView; bot: INTEGER; allLines: BOOLEAN): INTEGER;
958 CONST minH = 12 * point;
959 VAR s, t: Line; pos, y: INTEGER;
960 BEGIN
961 s := v.trailer.next; t := s; y := v.dy;
962 WHILE ~t.box.eot & (y + t.h <= bot) DO INC(y, t.h); s := t; t := t.next END;
963 IF ~allLines & (bot - y < t.h) & (bot - y < minH) THEN t := s END;
964 pos := t.start + t.box.len;
965 (*
966 IF t.box.eot THEN INC(pos) END;
967 *)
968 RETURN pos
969 END Limit;
972 (* ScrollOp *)
974 PROCEDURE (op: ScrollOp) Do;
975 VAR org0, dy0, org, dy, shift: INTEGER; rebuild: BOOLEAN;
976 BEGIN
977 IF op.bunch THEN org := op.bunchOrg; dy := op.bunchDy
978 ELSE org := op.org; dy := op.dy
979 END;
980 org0 := op.v.org; dy0 := op.v.dy;
981 IF op.silent THEN
982 op.v.org := org; op.v.dy := dy; op.silent := FALSE
983 ELSE
984 AdjustLines(op.v, org, dy, shift, rebuild); ShowAdjusted(op.v, shift, rebuild)
985 END;
986 IF op.bunch THEN op.bunch := FALSE ELSE op.org := org0; op.dy := dy0 END
987 END Do;
989 PROCEDURE DoSetOrigin (v: StdView; org, dy: INTEGER; silent: BOOLEAN);
990 (* pre: org = v.ThisSetter().ThisLine(org) *)
991 VAR con: Models.Context; last: Stores.Operation; op: ScrollOp;
992 shift: INTEGER; rebuild: BOOLEAN;
993 BEGIN
994 IF (org # v.org) OR (dy # v.dy) THEN
995 con := v.context;
996 IF con # NIL THEN
997 IF (v.Domain() = NIL) OR con.Normalize() THEN
998 IF silent THEN
999 v.org := org; v.dy := dy
1000 ELSE
1001 AdjustLines(v, org, dy, shift, rebuild); ShowAdjusted(v, shift, rebuild)
1002 END
1003 ELSE
1004 last := Views.LastOp(v);
1005 IF (last # NIL) & (last IS ScrollOp) THEN
1006 op := last(ScrollOp);
1007 op.bunch := TRUE; op.bunchOrg := org; op.bunchDy := dy;
1008 op.silent := silent;
1009 Views.Bunch(v)
1010 ELSE
1011 NEW(op); op.v := v; op.org := org; op.dy := dy;
1012 op.bunch := FALSE;
1013 op.silent := silent;
1014 Views.Do(v, scrollingKey, op)
1015 END
1016 END
1017 ELSE
1018 v.org := org; v.dy := dy
1019 END
1020 END
1021 END DoSetOrigin;
1024 (* SetOp *)
1026 PROCEDURE (op: SetOp) Do;
1027 VAR v: StdView; m: BOOLEAN;
1028 a: TextModels.Attributes; r: TextRulers.Ruler; s: TextSetters.Setter;
1029 BEGIN
1030 v := op.view;
1031 CASE op.mode OF
1032 setMarks:
1033 m := v.hideMarks; v.hideMarks := op.hideMarks; op.hideMarks := m
1034 | setSetter:
1035 s := v.setter;
1036 IF s # NIL THEN s.ConnectTo(NIL, NIL, 0, FALSE) END;
1037 v.setter := op.setter; op.setter := s
1038 | setDefs:
1039 r := v.defRuler; a := v.defAttr;
1040 v.defRuler := op.defRuler; v.defAttr := op.defAttr;
1041 op.defRuler := r; op.defAttr := a;
1042 (*
1043 IF (v.defAttr.Domain() # NIL) & (v.defAttr.Domain() # v.Domain()) THEN
1044 v.defAttr := Stores.CopyOf(v.defAttr)(TextModels.Attributes)
1045 END;
1046 Stores.Join(v, v.defAttr);
1047 *)
1048 IF v.defAttr # NIL THEN (* could be for undo operations *)
1049 IF ~Stores.Joined(v, v.defAttr) THEN
1050 IF ~Stores.Unattached(v.defAttr) THEN
1051 v.defAttr := Stores.CopyOf(v.defAttr)(TextModels.Attributes)
1052 END;
1053 Stores.Join(v, v.defAttr)
1054 END;
1055 END;
1057 IF v.defRuler # NIL THEN Stores.Join(v, v.defRuler) END;
1058 END;
1059 RebuildView(v)
1060 END Do;
1062 PROCEDURE DoSet (op: SetOp; mode: INTEGER; v: StdView);
1063 BEGIN
1064 op.mode := mode; op.view := v; Views.Do(v, viewSettingKey, op)
1065 END DoSet;
1068 (* StdView *)
1070 PROCEDURE (v: StdView) Internalize2 (VAR rd: Stores.Reader);
1071 VAR st: Stores.Store; r: TextRulers.Ruler; a: TextModels.Attributes;
1072 org, dy: INTEGER; thisVersion: INTEGER; hideMarks: BOOLEAN;
1073 BEGIN
1074 v.Internalize2^(rd);
1075 IF rd.cancelled THEN RETURN END;
1076 rd.ReadVersion(minVersion, maxStdVersion, thisVersion);
1077 IF rd.cancelled THEN RETURN END;
1078 rd.ReadBool(hideMarks);
1079 rd.ReadStore(st); ASSERT(st # NIL, 100);
1080 IF ~(st IS TextRulers.Ruler) THEN
1081 rd.TurnIntoAlien(Stores.alienComponent);
1082 Stores.Report("#Text:AlienDefaultRuler", "", "", "");
1083 RETURN
1084 END;
1085 r := st(TextRulers.Ruler);
1086 TextModels.ReadAttr(rd, a);
1087 rd.ReadInt(org); rd.ReadInt(dy);
1088 v.DisplayMarks(hideMarks);
1089 v.setter := TextSetters.dir.New(); v.setter0 := NIL;
1090 v.SetDefaults(r, a); v.SetOrigin(org, dy);
1091 v.trailer := NIL; v.bot := 0
1092 END Internalize2;
1094 PROCEDURE (v: StdView) Externalize2 (VAR wr: Stores.Writer);
1095 VAR org, dy: INTEGER; hideMarks: BOOLEAN;
1096 a: Stores.Store;
1097 BEGIN
1098 v.Externalize2^(wr);
1099 IF (v.context = NIL) OR v.context.Normalize() THEN
1100 org := 0; dy := 0; hideMarks := TRUE
1101 ELSE
1102 org := v.org; dy := v.dy; hideMarks := v.hideMarks
1103 END;
1104 wr.WriteVersion(maxStdVersion);
1105 wr.WriteBool(hideMarks);
1106 a := Stores.CopyOf(v.defAttr); (*Stores.InitDomain(a, v.Domain());*) Stores.Join(v, a);
1107 (* bkwd-comp hack: avoid link => so that pre release 1.3 Internalize can still read texts *)
1108 wr.WriteStore(v.defRuler);
1109 wr.WriteStore(a);
1110 wr.WriteInt(org); wr.WriteInt(dy)
1111 END Externalize2;
1113 PROCEDURE (v: StdView) CopyFromModelView2 (source: Views.View; model: Models.Model);
1114 VAR s: TextSetters.Setter; r: TextRulers.Ruler;
1115 BEGIN
1116 (* v.CopyFromModelView^(source, model); *)
1117 WITH source: StdView DO
1118 s := Stores.CopyOf(source.setter)(TextSetters.Setter);
1119 v.setter := s; v.setter0 := NIL;
1120 r := TextRulers.CopyOf(source.defRuler, Views.deep);
1121 v.DisplayMarks(source.HidesMarks());
1122 v.SetDefaults(r, source.defAttr);
1123 v.trailer := NIL; v.bot := 0;
1124 IF v.text = source.text THEN
1125 v.org := source.org; v.dy := source.dy
1126 END
1127 END
1128 END CopyFromModelView2;
1130 PROCEDURE (v: StdView) Restore (f: Views.Frame; l, t, r, b: INTEGER);
1131 VAR st: TextSetters.Setter; u0, u: Line;
1132 y0, y, w, h: INTEGER; end: INTEGER; pageF: BOOLEAN;
1133 BEGIN
1134 ASSERT(v.context # NIL, 20);
1135 IF v.setter # NIL THEN v.context.GetSize(w, h) END;
1136 IF (v.setter = NIL) OR (v.setter.vw # w) THEN
1137 Views.RemoveFrames(f, l, t, r, b)
1138 END;
1139 ValidateLines(v, b);
1140 u := v.trailer.next; y := v.dy;
1141 pageF := Views.IsPrinterFrame(f) & v.context.Normalize();
1142 IF pageF THEN (* on page-formatted frames do not display truncated lines at bottom *)
1143 st := v.ThisSetter(); end := st.NextPage(f.b - f.t, v.org)
1144 END;
1145 WHILE (u # v.trailer) & (y + u.h <= t) DO INC(y, u.h); u := u.next END;
1146 y0 := y; u0 := u;
1147 IF (u = v.trailer.next) & (y < b) THEN (* at least one line per page *)
1148 ASSERT((u.box.len > 0) OR u.box.eot OR (u.next = v.trailer), 100);
1149 DrawLine(v, u.start, u.box, f, l, r, y + u.asc, y + u.h - u.box.dsc - u.box.asc, pageF);
1150 INC(y, u.h); u := u.next
1151 END;
1152 WHILE (u # v.trailer) & (y < b) & (~pageF OR (u.start < end)) DO
1153 ASSERT((u.box.len > 0) OR u.box.eot OR (u.next = v.trailer), 101);
1154 IF u.box.ruler # u0.box.ruler THEN
1155 DrawDecorations(v, u0, f, l, y0, r, y); u0 := u; y0 := y
1156 END;
1157 DrawLine(v, u.start, u.box, f, l, r, y + u.asc, y + u.h - u.box.dsc - u.box.asc, pageF);
1158 INC(y, u.h); u := u.next
1159 END;
1160 IF y0 # y THEN DrawDecorations(v, u0, f, l, y0, r, y) END
1161 END Restore;
1163 PROCEDURE (v: StdView) DisplayMarks (hide: BOOLEAN);
1164 VAR op: SetOp; c: Containers.Controller;
1165 BEGIN
1166 IF v.hideMarks # hide THEN
1167 c := v.ThisController();
1168 IF c # NIL THEN Containers.FadeMarks(c, Containers.hide) END;
1169 IF (v.context # NIL) & ~v.context.Normalize() THEN
1170 NEW(op); op.hideMarks := hide; DoSet(op, setMarks, v)
1171 ELSE
1172 v.hideMarks := hide; RebuildView(v)
1173 END
1174 END
1175 END DisplayMarks;
1177 PROCEDURE (v: StdView) HidesMarks (): BOOLEAN;
1178 BEGIN
1179 RETURN v.hideMarks
1180 END HidesMarks;
1182 PROCEDURE (v: StdView) SetSetter (setter: TextSetters.Setter);
1183 VAR op: SetOp;
1184 BEGIN
1185 ASSERT(setter # NIL, 20);
1186 IF v.setter # setter THEN
1187 IF v.setter # NIL THEN
1188 NEW(op); op.setter := setter; DoSet(op, setSetter, v)
1189 ELSE v.setter := setter
1190 END
1191 END
1192 END SetSetter;
1194 PROCEDURE (v: StdView) ThisSetter (): TextSetters.Setter;
1195 VAR st: TextSetters.Setter; w, h: INTEGER;
1196 BEGIN
1197 st := v.setter; ASSERT(st # NIL, 20);
1198 IF st # v.setter0 THEN
1199 IF v.context # NIL THEN
1200 v.context.GetSize(w, h)
1201 ELSE
1202 IF Dialog.metricSystem THEN
1203 w := 165*mm
1204 ELSE
1205 w := 104*inch16
1206 END
1207 END;
1208 st.ConnectTo(v.text, v.defRuler, w, v.hideMarks);
1209 v.setter0 := st
1210 END;
1211 RETURN st
1212 END ThisSetter;
1214 PROCEDURE (d: StdView) AcceptableModel (m: Containers.Model): BOOLEAN;
1215 BEGIN
1216 RETURN m IS TextModels.Model
1217 END AcceptableModel;
1219 PROCEDURE (v: StdView) InitModel2 (m: Containers.Model);
1220 BEGIN
1221 ASSERT(m IS TextModels.Model, 23);
1222 v.text := m(TextModels.Model)
1223 END InitModel2;
1225 PROCEDURE (v: StdView) SetOrigin (org, dy: INTEGER);
1226 VAR st: TextSetters.Setter; start: INTEGER;
1227 BEGIN
1228 ASSERT(v.text # NIL, 20);
1229 st := v.ThisSetter(); start := st.ThisLine(org);
1230 IF start # org THEN org := start; dy := 0 END;
1231 DoSetOrigin(v, org, dy, FALSE)
1232 END SetOrigin;
1234 PROCEDURE (v: StdView) PollOrigin (OUT org, dy: INTEGER);
1235 BEGIN
1236 org := v.org; dy := v.dy
1237 END PollOrigin;
1239 PROCEDURE (v: StdView) SetDefaults (r: TextRulers.Ruler; a: TextModels.Attributes);
1240 VAR op: SetOp;
1241 BEGIN
1242 ASSERT(r # NIL, 20); ASSERT(r.style.attr.init, 21);
1243 ASSERT(a # NIL, 22); ASSERT(a.init, 23);
1244 IF (v.defRuler # r) OR (v.defAttr # a) THEN
1245 (*
1246 (*IF (v.context # NIL) & (r # v.defRuler) THEN*)
1247 IF (v.Domain() # NIL) & (r # v.defRuler) THEN
1248 Stores.InitDomain(r, v.Domain())
1249 END;
1250 *)
1251 IF r # v.defRuler THEN Stores.Join(v, r) END;
1252 NEW(op); op.defRuler := r; op.defAttr := a; DoSet(op, setDefs, v)
1253 END
1254 END SetDefaults;
1256 PROCEDURE (v: StdView) PollDefaults (OUT r: TextRulers.Ruler; OUT a: TextModels.Attributes);
1257 BEGIN
1258 r := v.defRuler; a := v.defAttr
1259 END PollDefaults;
1261 (*
1262 PROCEDURE (v: StdView) PropagateDomain;
1263 VAR m: Models.Model;
1264 BEGIN
1265 ASSERT(v.setter # NIL, 20); ASSERT(v.text # NIL, 21);
1266 ASSERT(v.defRuler # NIL, 22); ASSERT(v.defAttr # NIL, 23);
1267 v.PropagateDomain^;
1268 m := v.ThisModel();
1269 IF m # NIL THEN Stores.InitDomain(m, v.Domain()) END;
1270 Stores.InitDomain(v.defRuler, v.Domain())
1271 END PropagateDomain;
1272 *)
1273 (*
1274 PROCEDURE (v: StdView) Flush, NEW;
1275 BEGIN
1276 v.trailer := NIL; v.bot := 0; v.setter0 := NIL
1277 END Flush;
1278 *)
1279 PROCEDURE (v: StdView) HandleModelMsg2 (VAR msg: Models.Message);
1280 BEGIN
1281 IF msg.model = v.text THEN
1282 WITH msg: Models.UpdateMsg DO
1283 WITH msg: TextModels.UpdateMsg DO
1284 IF msg.op IN {TextModels.insert, TextModels.delete, TextModels.replace} THEN
1285 UpdateView(v, msg.beg, msg.end, msg.delta)
1286 ELSE (* unknown text op happened *)
1287 RebuildView(v)
1288 END
1289 ELSE (* unknown text update happened *)
1290 RebuildView(v)
1291 END
1292 | msg: PositionMsg DO
1293 v.ShowRange(msg.beg, msg.end, msg.focusOnly)
1294 ELSE
1295 END
1296 ELSE (* domaincast received *)
1297 WITH msg: TextRulers.UpdateMsg DO
1298 StyleUpdate(v, msg.oldAttr)
1299 | msg: Models.UpdateMsg DO (* forced rebuild *)
1300 RebuildView(v)
1301 ELSE
1302 END
1303 END
1304 END HandleModelMsg2;
1306 PROCEDURE (v: StdView) HandleViewMsg2 (f: Views.Frame; VAR msg: Views.Message);
1307 BEGIN
1308 IF msg.view = v THEN
1309 WITH msg: FindAnyFrameMsg DO
1310 IF (msg.frame = NIL) OR (msg.frame.b - msg.frame.t > f.b - f.t) THEN msg.frame := f END
1311 ELSE
1312 END
1313 ELSE
1314 WITH msg: Views.UpdateCachesMsg DO (* display view in new frame *)
1315 IF Views.Era(v) # Models.Era(v.text) THEN
1316 (* view/setter caches outdated - possible if v previous to this notification had no frame open *)
1317 v.setter0 := NIL; v.trailer := NIL; v.bot := 0
1318 END
1319 ELSE
1320 END
1321 END
1322 END HandleViewMsg2;
1324 PROCEDURE (v: StdView) HandleCtrlMsg2 (f: Views.Frame;
1325 VAR msg: Controllers.Message; VAR focus: Views.View
1326 );
1327 BEGIN
1328 WITH msg: Controllers.PollSectionMsg DO
1329 IF (focus = NIL) OR ~msg.focus THEN
1330 PollSection(v, f, msg);
1331 focus := NIL
1332 END
1333 | msg: FindFocusFrameMsg DO
1334 IF (msg.view = v) & (msg.frame = NIL) THEN msg.frame := f END
1335 | msg: Controllers.ScrollMsg DO
1336 IF (focus = NIL) OR ~msg.focus THEN
1337 Scroll(v, f, msg);
1338 focus := NIL
1339 END
1340 | msg: Controllers.PageMsg DO
1341 Page(v, f.b - f.t, msg.op, msg.pageY, msg.done, msg.eoy);
1342 focus := NIL
1343 ELSE
1344 END
1345 END HandleCtrlMsg2;
1347 PROCEDURE (v: StdView) HandlePropMsg2 (VAR p: Properties.Message);
1348 CONST minW = 5 * point; maxW = maxHeight; minH = 5 * point; maxH = maxHeight;
1349 VAR st: TextSetters.Setter;
1350 BEGIN
1351 WITH p: Properties.SizePref DO
1352 IF p.w = Views.undefined THEN p.w := v.defRuler.style.attr.right END;
1353 IF p.h = Views.undefined THEN p.h := MAX(INTEGER) END
1354 | p: Properties.BoundsPref DO
1355 st := v.ThisSetter();
1356 st.GetBox(0, v.text.Length(), maxW, maxH, p.w, p.h);
1357 IF p.w < minW THEN p.w := minW END;
1358 IF p.h < minH THEN p.h := minH END
1359 | p: Properties.ResizePref DO
1360 p.fixed := FALSE;
1361 p.horFitToPage := ~(TextRulers.rightFixed IN v.defRuler.style.attr.opts);
1362 p.verFitToWin := TRUE
1363 | p: Properties.TypePref DO
1364 IF Services.Is(v, p.type) THEN p.view := v END
1365 | p: Containers.DropPref DO
1366 p.okToDrop := TRUE
1367 ELSE
1368 END
1369 END HandlePropMsg2;
1372 PROCEDURE (v: StdView) GetThisLocation (f: Views.Frame; pos: INTEGER; OUT loc: Location);
1373 (* pre: f must be displayed *)
1374 (* if position lies outside view, the next best location inside will be taken *)
1375 VAR rd: TextSetters.Reader; t: Line; p1, y, w, h: INTEGER;
1376 BEGIN
1377 ValidateLines(v, f.b);
1378 y := v.dy;
1379 IF pos < v.org THEN
1380 t := v.trailer.next;
1381 loc.start := t.start; loc.pos := t.start;
1382 loc.x := 0; loc.y := y; loc.asc := t.asc; loc.dsc := t.h - t.asc; loc.view := NIL;
1383 RETURN
1384 ELSIF pos < v.trailer.start THEN
1385 t := v.trailer.next;
1386 WHILE ~t.box.eot & ~((t.start <= pos) & (pos < t.next.start)) DO
1387 INC(y, t.h); t := t.next
1388 END
1389 ELSE (* pos >= v.trailer.start *)
1390 t := v.trailer.next; WHILE ~t.box.eot DO INC(y, t.h); t := t.next END;
1391 IF t = v.trailer THEN
1392 loc.start := t.start; loc.pos := t.start;
1393 loc.x := 0; loc.y := y; loc.asc := t.asc; loc.dsc := t.h - t.asc; loc.view := NIL;
1394 RETURN
1395 END
1396 END;
1397 p1 := t.start;
1398 rd := GetReader(v, p1, t.box); rd.Read;
1399 WHILE rd.pos < pos DO
1400 p1 := rd.pos; rd.AdjustWidth(t.start, p1, t.box, rd.w); INC(rd.x, rd.w); rd.Read
1401 END;
1402 IF LEN(rd.string$) > 1 THEN (* collated subsequence *)
1403 rd.x := f.CharPos(rd.x, pos - p1, rd.string, rd.attr.font);
1404 IF rd.pos = pos THEN rd.Read END
1405 ELSIF rd.pos = pos THEN
1406 rd.AdjustWidth(t.start, pos, t.box, rd.w); INC(rd.x, rd.w); rd.Read
1407 ELSE
1408 ASSERT(p1 = pos, 100)
1409 END;
1410 loc.view := rd.view;
1411 loc.start := t.start; loc.pos := pos;
1412 loc.x := rd.x; loc.y := y; loc.asc := t.asc; loc.dsc := t.h - t.asc;
1413 IF loc.view # NIL THEN
1414 v.context.GetSize(w, h);
1415 IF rd.x + rd.w > w THEN rd.w := w - rd.x END;
1416 loc.l := rd.x; loc.t := y - rd.attr.offset + t.asc + rd.dsc - rd.h;
1417 loc.r := loc.l + rd.w; loc.b := loc.t + rd.h
1418 END;
1419 CacheReader(v, rd)
1420 END GetThisLocation;
1422 PROCEDURE (v: StdView) GetRange (f: Views.Frame; OUT beg, end: INTEGER);
1423 VAR t: Line;
1424 BEGIN
1425 ValidateLines(v, f.b);
1426 t := ThisViewLine(v, f.t); beg := t.start; end := Limit(v, f.b, TRUE)
1427 END GetRange;
1429 PROCEDURE (v: StdView) ThisPos (f: Views.Frame; x, y: INTEGER): INTEGER;
1430 (* pre: f must be displayed *)
1431 (* post: f.org <= result <= v.text.Length() *)
1432 VAR rd: TextSetters.Reader; t: Line; p1, end, py: INTEGER;
1433 BEGIN
1434 ValidateLines(v, f.b);
1435 t := v.trailer.next; py := v.dy;
1436 WHILE ~t.box.eot & (py + t.h <= y) DO INC(py, t.h); t := t.next END;
1437 p1 := t.start; end := p1 + t.box.len;
1438 IF py + t.h > y THEN
1439 IF (end > p1) & (y >= v.dy) THEN
1440 IF t.box.eot THEN INC(end) END;
1441 rd := GetReader(v, p1, t.box);
1442 rd.Read; rd.AdjustWidth(t.start, rd.pos, t.box, rd.w);
1443 WHILE (rd.x + rd.SplitWidth(rd.w) < x) & (rd.pos < end) DO
1444 p1 := rd.pos; INC(rd.x, rd.w);
1445 rd.Read; rd.AdjustWidth(t.start, rd.pos, t.box, rd.w)
1446 END;
1447 IF LEN(rd.string$) > 1 THEN (* collated subsequence *)
1448 INC(p1, f.CharIndex(rd.x, x, rd.string, rd.attr.font))
1449 END;
1450 CacheReader(v, rd)
1451 END
1452 ELSE p1 := end
1453 END;
1454 RETURN p1
1455 END ThisPos;
1457 PROCEDURE (v: StdView) ShowRangeIn (f: Views.Frame; beg, end: INTEGER);
1458 CONST minH = 12 * point;
1459 VAR c: Models.Context; st: TextSetters.Setter; t, t1: Line;
1460 org0, last, len, org, dy, p, q: INTEGER; y, h, mh: INTEGER;
1461 box, box0: TextSetters.LineBox; loc, loc1: Location;
1462 focus: BOOLEAN;
1463 BEGIN
1464 focus := f = Controllers.FocusFrame();
1465 c := v.context;
1466 st := v.ThisSetter(); ValidateLines(v, f.b); org0 := v.org;
1467 last := Limit(v, f.b, FALSE); len := v.text.Length();
1468 IF last = len THEN p := st.ThisLine(last); LocateThisLine(v, p, t1, y); h := f.b - y END;
1469 IF (beg > last)
1470 OR (beg = last) & ((last < len) OR (len > 0) & (h < t1.h) & (h < minH))
1471 OR (end < org0)
1472 OR (beg < end) & (end = org0) THEN
1473 org := -1; dy := 0;
1474 IF beg <= org0 THEN (* try to adjust by scrolling up *)
1475 p := st.PreviousLine(v.org);
1476 IF p <= beg THEN (* reveal one line at top *)
1477 org := p; st.GetLine(org, box);
1478 h := box.asc + box.dsc + st.GridOffset(-1, box);
1479 IF h > maxScrollHeight + fuseScrollHeight THEN
1480 dy := -(h - h MOD maxScrollHeight);
1481 IF h + dy < fuseScrollHeight THEN INC(dy, maxScrollHeight) END
1482 END
1483 END
1484 END;
1485 IF (org = -1) & (beg >= last) THEN (* try to adjust by scrolling down *)
1486 p := st.ThisLine(last); q := st.NextLine(p); st.GetLine(q, box);
1487 IF (beg < q + box.len) OR (p = q) THEN (* reveal one line at bottom *)
1488 LocateThisLine(v, p, t1, y);
1489 h := box.asc + box.dsc + st.GridOffset(t1.box.dsc, box);
1490 IF h > maxScrollHeight + fuseScrollHeight THEN h := maxScrollHeight END;
1491 mh := y + t1.h - f.b + h;
1492 t := v.trailer.next; h := v.dy;
1493 WHILE (t # v.trailer) & (h < mh) DO INC(h, t.h); t := t.next END;
1494 IF t.start > v.org THEN org := t.start END
1495 END
1496 END;
1497 IF org = -1 THEN (* adjust by moving into "nice" position *)
1498 mh := f.b DIV 3;
1499 org := st.ThisLine(beg); st.GetLine(org, box0);
1500 h := box0.asc + box0.dsc + st.GridOffset(-1, box0); p := org;
1501 WHILE (p > 0) & (h < mh) DO
1502 DEC(h, st.GridOffset(-1, box0)); org := p;
1503 p := st.PreviousLine(org); st.GetLine(p, box);
1504 INC(h, box.asc + box.dsc + st.GridOffset(box.dsc, box0));
1505 box0 := box
1506 END;
1507 IF (org = len) & (len > 0) THEN org := st.PreviousLine(org) END
1508 END;
1509 DoSetOrigin(v, org, dy, FALSE)
1510 END;
1511 IF focus THEN
1512 f := Controllers.FocusFrame();
1513 IF (f # NIL) & (f.view = v) THEN
1515 v.GetThisLocation(f, beg, loc);
1516 v.GetThisLocation(f, end, loc1);
1517 IF (loc.y = loc1.y) & (loc.x <= loc1.x) THEN
1518 c.MakeVisible(loc.x, loc.y, loc1.x, loc1.y)
1519 END
1520 ELSE
1521 HALT(100); (* this should not happen *)
1522 END
1523 END;
1524 (*
1525 IF c IS Documents.Context THEN
1526 v.GetThisLocation(f, beg, loc);
1527 v.GetThisLocation(f, end, loc1);
1528 IF (loc.y = loc1.y) & (loc.x <= loc1.x) THEN
1529 Documents.MakeVisible(c(Documents.Context).ThisDoc(), f, loc.x, loc.y, loc1.x, loc1.y)
1530 END
1531 END
1532 *)
1533 END ShowRangeIn;
1535 PROCEDURE (v: StdView) ShowRange (beg, end: INTEGER; focusOnly: BOOLEAN);
1536 VAR fmsg: FindFocusFrameMsg; amsg: FindAnyFrameMsg; f: Views.Frame;
1537 BEGIN
1538 IF focusOnly THEN
1539 fmsg.view := v; fmsg.frame := NIL; Controllers.Forward(fmsg); f := fmsg.frame
1540 ELSE
1541 amsg.frame := NIL; Views.Broadcast(v, amsg); f := amsg.frame
1542 END;
1543 IF f # NIL THEN v.ShowRangeIn(f, beg, end) END
1544 END ShowRange;
1547 (* StdDirectory *)
1549 PROCEDURE (d: StdDirectory) New (text: TextModels.Model): View;
1550 VAR v: StdView; c: Controllers.Controller; r: TextRulers.Ruler;
1551 BEGIN
1552 r := TextRulers.dir.New(NIL);
1553 IF text = NIL THEN text := TextModels.dir.New() END;
1554 (* IF text.Domain() # NIL THEN Stores.InitDomain(r, text.Domain()) END; *)
1555 Stores.Join(text, r);
1556 NEW(v); v.hideMarks := FALSE; v.bot := 0; v.org := 0; v.dy := 0;
1557 v.InitModel(text);
1558 v.SetDefaults(r, d.defAttr);
1559 v.SetSetter(TextSetters.dir.New());
1560 v.DisplayMarks(hide);
1561 IF ctrlDir # NIL THEN v.SetController(ctrlDir.New()) END;
1562 (* Stores.InitDomain(v, text.Domain()); *)
1563 Stores.Join(v, text);
1564 RETURN v
1565 END New;
1568 PROCEDURE Init;
1569 VAR d: StdDirectory; a: TextModels.Attributes; res: INTEGER;
1570 BEGIN
1571 Dialog.Call("TextControllers.Install", "#Text:CntrlInstallFailed", res);
1572 NEW(a); a.InitFromProp(NIL); (* use defaults *)
1573 NEW(d); d.defAttr := a;
1574 stdDir := d; dir := d
1575 END Init;
1577 BEGIN
1578 Init
1579 END TextViews.