Subversion Repositories Kolibri OS

Rev

Rev 9896 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
9896 akron1 1
(*
9898 akron1 2
    Copyright 2016-2023 Anton Krotov
9896 akron1 3
 
4
    This file is part of fb2read.
5
 
6
    fb2read is free software: you can redistribute it and/or modify
7
    it under the terms of the GNU General Public License as published by
8
    the Free Software Foundation, either version 3 of the License, or
9
    (at your option) any later version.
10
 
11
    fb2read is distributed in the hope that it will be useful,
12
    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
    GNU General Public License for more details.
15
 
16
    You should have received a copy of the GNU General Public License
17
    along with fb2read. If not, see .
18
*)
19
 
20
MODULE DOM;
21
 
22
IMPORT XML, SU := SysUtils, S := Strings, Font, Window, G := Graph, LibImg,
9898 akron1 23
       RF := ReadFile, File, Ini, K := KOSAPI, sys := SYSTEM,
9896 akron1 24
       V := Vector, Cursor, box_lib, tables, Search;
25
 
26
 
27
CONST
28
 
29
  BACK_COLOR*       = 0;
30
  TEXT_COLOR*       = 1;
31
  ITALIC_COLOR*     = 2;
32
  LINK_COLOR*       = 3;
33
  VISITED_COLOR*    = 4;
34
  CLICKED_COLOR*    = 5;
35
 
36
  CellPadding       = 5;
37
 
38
 
39
TYPE
40
 
41
  TSettings* = RECORD
42
 
43
    Colors*   : ARRAY 6 OF INTEGER;
44
    FontSize* : INTEGER;
45
    TwoCol*   : BOOLEAN;
46
    b_pict*   : BOOLEAN;
47
 
48
    PADDING*   : RECORD Left, Right, Top*, Bottom, ColInter, LRpc*, CInt*: INTEGER END;
49
 
50
    PARAGRAPH*,
51
    EPIGRAPH*,
52
    LEVEL,
53
    SUB,
54
    SUP,
55
    InterLin*,
56
    Picture*,
9898 akron1 57
    picture_fsize*,
9896 akron1 58
    SpaceW: INTEGER
59
 
60
  END;
61
 
62
  StackItem = POINTER TO TStackItem;
63
 
64
  TStackItem = RECORD (XML.DESC_ELEMENT)
65
 
66
    body : XML.TAG;
67
    Ycur : INTEGER;
68
    d    : REAL
69
 
70
  END;
71
 
72
 
73
VAR
74
 
75
  Settings* : TSettings;
76
  Canvas_X, Canvas_Y: INTEGER;
9898 akron1 77
  ColLeft, ColRight: Window.tRect;
9896 akron1 78
 
79
  Ymin, Ymax, Ycur : INTEGER;
80
 
81
  X, Y, W, LineH, W1, W2: INTEGER;
82
 
83
  epigraph  : INTEGER;
84
  sup, sub  : INTEGER;
85
  ref_depth : INTEGER;
86
  align     : INTEGER;
87
  code      : INTEGER;
88
  strong    : INTEGER;
89
  italic    : INTEGER;
90
  strike    : INTEGER;
91
  refer     : INTEGER;
92
 
93
  Text      : ARRAY 100000 OF XML.TEXT;
94
  TextCount : INTEGER;
95
 
96
  Lines: INTEGER;
97
 
98
  description, contents, mainbody, body, ref, cover, clickRef, hoverRef: XML.TAG;
99
 
100
  MainBody: BOOLEAN;
101
 
102
  f_stk, b_stk, vis_ref: XML.LIST;
103
 
104
  FilePath, FileName: S.STRING;
105
 
106
  done, last, resized, loaded*, mouseDown: BOOLEAN;
107
 
108
  Stack*: ARRAY 1000000 OF CHAR;
109
 
110
  Ycont: INTEGER;
111
 
112
  history: File.FS;
113
 
114
  references: V.VECTOR;
115
  cursor: INTEGER;
116
 
117
  fsize2, chksum: INTEGER;
118
 
119
  sb: box_lib.scrollbar;
120
  urlstr* : S.STRING;
121
  DrawStatus, DrawToolbar: PROCEDURE;
122
 
123
 
124
PROCEDURE PushRef(ref: XML.TAG);
125
VAR item: StackItem;
126
BEGIN
127
  NEW(item);
128
  item.body := ref;
129
  XML.AddItem(vis_ref, item);
130
END PushRef;
131
 
132
 
133
PROCEDURE Push(VAR stk: XML.LIST);
134
VAR item: StackItem;
135
BEGIN
136
  NEW(item);
137
  item.body := body;
138
  item.Ycur := Ycur;
139
  XML.AddItem(stk, item);
140
  IF body = contents THEN
141
    Ycont := Ycur
142
  END
143
END Push;
144
 
145
 
146
PROCEDURE Pop(VAR stk: XML.LIST);
147
VAR item : StackItem;
148
BEGIN
149
  item := stk.last(StackItem);
150
  IF item # NIL THEN
151
    body := item.body;
152
    Ymin := body.Ymin;
153
    Ymax := body.Ymax;
154
    Ycur := item.Ycur;
155
    XML.DelLastItem(stk)
156
  END
157
END Pop;
158
 
159
 
160
PROCEDURE Clear(VAR stk: XML.LIST);
161
BEGIN
162
  REPEAT
163
    XML.DelLastItem(stk)
164
  UNTIL stk.last = NIL
165
END Clear;
166
 
167
 
168
PROCEDURE AddToLine(text: XML.TEXT);
169
BEGIN
170
  Text[TextCount] := text;
171
  INC(TextCount)
172
END AddToLine;
173
 
174
 
175
PROCEDURE Epigraph(): INTEGER;
176
  RETURN ORD(epigraph > 0) * Settings.EPIGRAPH
177
END Epigraph;
178
 
179
 
180
PROCEDURE SpaceWidth(): INTEGER;
181
VAR Result: INTEGER;
182
BEGIN
183
  IF code > 0 THEN
184
    Result := Font.MonoWidth()
185
  ELSE
186
    Result := Settings.SpaceW
187
  END
188
  RETURN Result
189
END SpaceWidth;
190
 
191
 
192
PROCEDURE Trim;
193
VAR n: INTEGER;
194
BEGIN
195
  IF TextCount > 0 THEN
196
    n := TextCount - 1;
197
    WHILE (n >= 0) & (Text[n] IS XML.SPACE) DO
198
      Text[n].width := -1;
199
      DEC(n)
200
    END;
201
    TextCount := n + 1
202
  END
203
END Trim;
204
 
205
 
206
PROCEDURE Align;
207
VAR
208
  i, n, sp, d, quo, rem, x: INTEGER;
209
  text: XML.TEXT;
210
BEGIN
211
  IF (TextCount > 0) & (code = 0) & (align # 3) THEN
212
    sp := 0;
213
    Trim;
214
    n := TextCount - 1;
215
    IF n >= 0 THEN
216
      d := W - Text[n].X - Text[n].width
217
    END;
218
    IF align = 1 THEN
219
      x := (d + Text[0].X) DIV 2
220
    ELSIF align = 2 THEN
221
      x := d + Text[0].X
222
    ELSIF align = 0 THEN
223
      x := Text[0].X;
224
      FOR i := 0 TO n DO
225
        IF Text[i] IS XML.SPACE THEN
226
          INC(sp)
227
        END
228
      END;
229
      IF sp > 0 THEN
230
        quo := d DIV sp;
231
        rem := d MOD sp;
232
        FOR i := 0 TO n DO
233
          IF Text[i] IS XML.SPACE THEN
234
            text := Text[i];
235
            text.width := text.width + quo + ORD(rem > 0);
236
            DEC(rem)
237
          END
238
        END
239
      END
240
    END;
241
    FOR i := 0 TO n DO
242
      text := Text[i];
243
      text.X := x;
244
      INC(x, text.width)
245
    END
246
  END
247
END Align;
248
 
249
 
250
PROCEDURE NewLine;
251
BEGIN
252
  IF align # 0 THEN
253
    Align
254
  END;
255
  X := Epigraph();
256
  INC(Y, LineH);
257
  TextCount := 0
258
END NewLine;
259
 
260
 
261
PROCEDURE Sup(open: BOOLEAN);
262
BEGIN
263
  IF open THEN
264
    IF sup = 0 THEN
265
      DEC(Y, Settings.SUP)
266
    END;
267
    INC(sup)
268
  ELSE
269
    DEC(sup);
270
    IF sup = 0 THEN
271
      INC(Y, Settings.SUP)
272
    END
273
  END
274
END Sup;
275
 
276
 
277
PROCEDURE Sub(open: BOOLEAN);
278
BEGIN
279
  IF open THEN
280
    IF sub = 0 THEN
281
      INC(Y, Settings.SUB)
282
    END;
283
    INC(sub)
284
  ELSE
285
    DEC(sub);
286
    IF sub = 0 THEN
287
      DEC(Y, Settings.SUB)
288
    END
289
  END
290
END Sub;
291
 
292
 
293
PROCEDURE Split(word: XML.WORD);
294
VAR
295
  i, n, max, len: INTEGER;
296
  c: CHAR;
297
  rem: XML.WORD;
298
BEGIN
299
  WHILE Font.TextWidth(word.value, max) <= W DO
300
    INC(max)
301
  END;
302
  DEC(max);
303
  IF max = 0 THEN
304
    max := 1
305
  END;
306
  i := 0;
307
  n := 0;
308
  len := word.value.last - word.value.first + 1;
309
  WHILE (n <= max) & (i < len) DO
310
    c := S.GetChar(word.value, i);
311
    INC(n);
312
    IF (80X <= c) & (c <= 0BFX) THEN
313
      DEC(n)
314
    END;
315
    INC(i)
316
  END;
317
  IF n > max THEN
318
    DEC(i);
319
    rem := XML.CreateWord();
320
    rem^ := word^;
321
    rem.value.first := word.value.first + i;
322
    word.next := rem;
323
    word.value.last := rem.value.first - 1;
324
    word.length := S.Utf8Length(word.value);
325
    word.width := Font.TextWidth(word.value, word.length)
326
  END
327
END Split;
328
 
329
 
330
PROCEDURE Depth(tag: XML.ELEMENT): INTEGER;
331
VAR n: INTEGER;
332
BEGIN
333
  n := 0;
334
  WHILE tag # NIL DO
335
    IF tag(XML.TAG).value = XML.tag_section THEN
336
      INC(n)
337
    END;
338
    tag := tag.parent
339
  END
340
  RETURN n
341
END Depth;
342
 
343
 
344
PROCEDURE shift(tag: XML.TAG; shx, shy: INTEGER);
345
VAR cur: XML.ELEMENT; t: XML.TAG;
346
BEGIN
347
  cur := tag.child.first;
348
  WHILE cur # NIL DO
349
    IF cur IS XML.TAG THEN
350
      t := cur(XML.TAG);
351
      INC(t.X, shx);
352
      INC(t.Ymin, shy);
353
      INC(t.Ymax, shy);
354
      shift(t, shx, shy)
355
    ELSIF cur IS XML.TEXT THEN
356
      INC(cur(XML.TEXT).X, shx);
357
      INC(cur(XML.TEXT).Y, shy)
358
    END;
359
    cur := cur.next
360
  END
361
END shift;
362
 
363
 
364
PROCEDURE getspan(td: XML.TAG; span: S.STRING): INTEGER;
365
VAR res: INTEGER;
366
    attr_value: S.CHARS;
367
    err: BOOLEAN;
368
BEGIN
369
  IF XML.GetAttr(td, span, attr_value) THEN
370
    res := S.CharsToInt(attr_value, err);
371
    IF err OR (res <= 0) THEN
372
      res := 1
373
    END
374
  ELSE
375
    res := 1
376
  END
377
  RETURN res
378
END getspan;
379
 
380
 
381
PROCEDURE td(t: tables.Table; tag: XML.TAG);
382
BEGIN
383
  tag.cell := t.cells.count;
384
  tables.td(t, getspan(tag, "colspan"), getspan(tag, "rowspan"))
385
END td;
386
 
387
 
388
PROCEDURE tr(t: tables.Table; tag: XML.TAG);
389
VAR
390
  cur       : XML.ELEMENT;
391
  cell      : XML.TAG;
392
BEGIN
393
  tables.tr(t);
394
  cur := tag.child.first;
395
  WHILE cur # NIL DO
396
    IF cur IS XML.TAG THEN
397
      cell := cur(XML.TAG);
398
      IF (cell.value = XML.tag_td) OR (cell.value = XML.tag_th) THEN
399
        cell.table := t;
400
        td(t, cell)
401
      END
402
    END;
403
    cur := cur.next
404
  END
405
END tr;
406
 
407
 
408
PROCEDURE table(t: tables.Table; tag: XML.TAG; open: BOOLEAN);
409
VAR
410
  cur       : XML.ELEMENT;
411
  row       : XML.TAG;
412
BEGIN
413
  IF open THEN
414
    tables.table(t, W, TRUE);
415
    cur := tag.child.first;
416
    WHILE cur # NIL DO
417
      IF cur IS XML.TAG THEN
418
        row := cur(XML.TAG);
419
        IF row.value = XML.tag_tr THEN
420
          row.table := t;
421
          tr(t, row)
422
        END
423
      END;
424
      cur := cur.next
425
    END;
426
    tables.table(t, W, FALSE)
427
  END
428
END table;
429
 
430
 
9898 akron1 431
PROCEDURE Image (VAR tag: XML.TAG; destroy: BOOLEAN);
432
VAR
433
	note  : BOOLEAN;
434
	img   : XML.TAG;
435
	URL   : INTEGER;
436
	chars : S.CHARS;
437
	sizeY : INTEGER;
438
	FName : S.STRING;
439
	path  : S.STRING;
440
BEGIN
441
	LibImg.Destroy(tag.img);
442
	img := XML.GetRef(tag, note, URL);
443
	IF img # NIL THEN
444
		IF img.child.first IS XML.WORD THEN
445
			chars := img.child.first(XML.WORD).value;
446
			tag.img := LibImg.GetImg(chars.first, chars.last - chars.first + 1, W, sizeY)
447
		END
448
	ELSIF URL # 0 THEN
449
		S.PtrToString(URL, FName);
450
		tag.img := LibImg.LoadFromFile(FName, W, sizeY);
451
		IF tag.img = 0 THEN
452
			path := FilePath;
453
			IF FName[0] # "/" THEN
454
				S.Append(path, "/")
455
			END;
456
			S.Append(path, FName);
457
			tag.img := LibImg.LoadFromFile(path, W, sizeY)
458
		END
459
	END;
460
	IF (tag.img # 0) & destroy THEN
461
		INC(Y, (sizeY DIV LineH) * LineH);
462
		NewLine;
463
		tag.Ymax := Y - Y MOD LineH;
464
		LibImg.Destroy(tag.img)
465
	END
466
END Image;
467
 
468
 
9896 akron1 469
PROCEDURE layout(body: XML.ELEMENT);
470
VAR
471
  cur       : XML.ELEMENT;
472
  tag       : XML.TAG;
473
  word      : XML.WORD;
474
  text      : XML.TEXT;
475
  tag_value : INTEGER;
476
  _align    : INTEGER;
477
  title     : XML.ELEMENT;
478
  width     : INTEGER;
479
  height1   : INTEGER;
480
  height2   : INTEGER;
481
 
482
BEGIN
483
  cur := body;
484
  WHILE cur # NIL DO
485
    IF cur IS XML.TAG THEN
486
      tag := cur(XML.TAG);
487
      tag_value := tag.value;
488
      CASE tag_value OF
489
      |XML.tag_p, XML.tag_v:
490
        Trim;
491
        IF TextCount > 0 THEN
492
          NewLine
493
        END;
494
        X := Settings.PARAGRAPH + Epigraph()
495
      |XML.tag_epigraph:
496
        NewLine;
497
        INC(epigraph)
498
      |XML.tag_contents_item:
499
        INC(ref_depth);
500
        Settings.EPIGRAPH := Settings.LEVEL * Depth(tag);
501
        _align := align;
502
        align := 3
503
      |XML.tag_title:
504
        INC(strong);
505
        Font.Bold(TRUE);
506
        _align := align;
507
        align := 1;
508
        IF MainBody THEN
509
          tag.value := XML.tag_contents_item;
510
          title := XML.Copy(tag);
511
          XML.AddChild(contents, title);
512
          title.parent := tag.parent;
513
          tag.value := XML.tag_title
514
        END
515
      |XML.tag_subtitle:
516
        NewLine;
517
        _align := align;
518
        align := 1
519
      |XML.tag_text_author, XML.tag_date:
520
        _align := align;
521
        align := 2
522
      |XML.tag_section, XML.tag_body, XML.tag_empty_line, XML.tag_poem, XML.tag_stanza, XML.tag_annotation, XML.tag_cite:
523
        NewLine
524
      |XML.tag_a:
525
        INC(ref_depth);
526
        IF XML.IsNote(tag) THEN
527
          Sup(TRUE)
528
        END
529
      |XML.tag_sup:
530
        Sup(TRUE)
531
      |XML.tag_sub:
532
        Sub(TRUE)
533
      |XML.tag_code:
534
        Font.sysfont(TRUE);
535
        INC(code)
536
      |XML.tag_image:
537
        tag.X := 0;
538
        NewLine;
539
        NewLine
540
      |XML.tag_coverpage:
541
        cover := tag
542
 
543
      |XML.tag_table:
544
        NewLine;
545
        tables.destroy(tag.table);
546
        NEW(tag.table);
547
        table(tag.table, tag, TRUE)
548
      |XML.tag_td, XML.tag_th:
549
        IF tag_value = XML.tag_th THEN
550
          INC(strong);
551
          Font.Bold(TRUE);
552
        END;
553
        SU.ErrorIf(tag.parent(XML.TAG).value # XML.tag_tr, 21);
554
        NewLine; DEC(Y, LineH);
555
        tag.Width := tables.get_width(tag.table, tag.cell);
556
        tag.X := tables.get_x(tag.table, tag.cell);
557
        width := W;
558
        W := tag.Width - 2 * CellPadding;
559
        IF W <= 0 THEN
560
          W := 1
561
        END
562
      |XML.tag_tr:
563
        SU.ErrorIf(tag.parent(XML.TAG).value # XML.tag_table, 20)
564
 
565
      |XML.tag_strong:
566
        INC(strong);
567
        Font.Bold(TRUE)
568
      ELSE
569
      END;
570
 
571
      tag.Ymin := Y - Y MOD LineH;
572
      layout(tag.child.first);
573
      tag.Ymax := Y - Y MOD LineH;
574
 
575
      CASE tag_value OF
576
      |XML.tag_epigraph:
577
        NewLine;
578
        DEC(epigraph)
579
      |XML.tag_subtitle:
580
        NewLine;
581
        NewLine;
582
        align := _align
583
      |XML.tag_title, XML.tag_text_author, XML.tag_date:
584
        DEC(strong);
585
        Font.Bold(strong > 0);
586
        NewLine;
587
        align := _align
588
      |XML.tag_contents_item:
589
        DEC(ref_depth);
590
        align := _align;
591
      |XML.tag_section, XML.tag_poem, XML.tag_v, XML.tag_p, XML.tag_annotation, XML.tag_cite:
592
        NewLine
593
      |XML.tag_a:
594
        DEC(ref_depth);
595
        IF XML.IsNote(tag) THEN
596
          Sup(FALSE)
597
        END
598
      |XML.tag_sup:
599
        Sup(FALSE)
600
      |XML.tag_sub:
601
        Sub(FALSE)
602
      |XML.tag_code:
603
        DEC(code);
604
        Font.sysfont(code > 0)
605
      |XML.tag_image:
9898 akron1 606
        Image(tag, TRUE)
9896 akron1 607
      |XML.tag_table:
608
        Y := tag.Ymin + tables.get_table_height(tag.table);
609
        tag.Ymax := Y - Y MOD LineH;
610
        NewLine;
611
      |XML.tag_td, XML.tag_th:
612
        IF tag_value = XML.tag_th THEN
613
          DEC(strong);
614
          Font.Bold(strong > 0)
615
        END;
616
        W := width;
617
        NewLine;
618
        Y := tag.Ymin + Settings.SUP; //!!!
619
        height1 := tables.get_height(tag.table, tag.cell);
620
        height2 := tag.Ymax - tag.Ymin + LineH;
621
        IF height2 > height1 THEN
622
          tables.set_height(tag.table, tag.cell, height2)
623
        END;
624
        INC(tag.Ymin, tables.get_y(tag.table, tag.cell));
625
        INC(tag.Ymax, tables.get_height(tag.table, tag.cell));
626
        shift(tag, tag.X + CellPadding, tables.get_y(tag.table, tag.cell));
627
 
628
      |XML.tag_strong:
629
        DEC(strong);
630
        Font.Bold(strong > 0)
631
      ELSE
632
      END
633
    ELSIF cur IS XML.WORD THEN
634
      word := cur(XML.WORD);
635
      word.length := S.Utf8Length(word.value);
636
      word.width := Font.TextWidth(word.value, word.length);
637
      IF W - X < word.width THEN
638
        Align;
639
        NewLine
640
      END;
641
      IF W < word.width THEN
642
        Split(word)
643
      END
644
    ELSIF cur IS XML.SPACE THEN
645
      IF W - X < SpaceWidth() THEN
646
          cur(XML.SPACE).width := 0
647
      ELSE
648
          cur(XML.SPACE).width := SpaceWidth()
649
      END
650
    END;
651
    IF cur IS XML.TEXT THEN
652
      IF ref_depth > 0 THEN
653
        V.push(references, cur)
654
      END;
655
      text := cur(XML.TEXT);
656
      text.X := X;
657
      text.Y := Y;
658
      INC(X, text.width);
659
      AddToLine(text)
660
    END;
661
    cur := cur.next
662
  END
663
END layout;
664
 
665
 
666
PROCEDURE layout2(body: XML.ELEMENT);
667
VAR
668
  color : INTEGER;
669
  cur   : XML.ELEMENT;
670
  text  : XML.TEXT;
671
  tag   : XML.TAG;
672
  y, y0 : INTEGER;
673
  value : INTEGER;
674
 
9898 akron1 675
  PROCEDURE DrawText(Col: Window.tRect; min, max, y0, y: INTEGER; right: BOOLEAN; VAR text: XML.TEXT);
9896 akron1 676
  VAR word: XML.WORD;
677
  BEGIN
678
    IF (min <= y0) & (y0 <= max) THEN
679
      Font.sysfont(code > 0);
680
      IF text IS XML.WORD THEN
681
        word := text(XML.WORD);
9898 akron1 682
        Font.Text(Col, word.X, y - Col.height * ORD(right), word.value.first, word.length);
9896 akron1 683
      END;
9898 akron1 684
      Font.StrikeText(Col, text.X, y - Col.height * ORD(right), text.width)
9896 akron1 685
    END
686
  END DrawText;
687
 
9898 akron1 688
  PROCEDURE DrawImage(VAR tag: XML.TAG);
9896 akron1 689
  VAR sizeX, sizeY, img, y: INTEGER;
690
  BEGIN
691
    IF tag.img # 0 THEN
692
      y := Ycur;
693
      LibImg.GetInf(tag.img, sizeX, sizeY, img);
9898 akron1 694
      IF (y <= tag.Ymax) & (tag.Ymin <= y + ColLeft.height) THEN
695
        G.Image(ColLeft.left + tag.X, tag.Ymin - y + ColLeft.top, sizeX, sizeY, img, ColLeft.top, ColLeft.top + ColLeft.height - 1)
9896 akron1 696
      END;
697
      IF Settings.TwoCol THEN
9898 akron1 698
        y := Ycur + ColLeft.height;
699
        IF (y <= tag.Ymax) & (tag.Ymin <= y + ColRight.height) THEN
700
          G.Image(ColRight.left + tag.X, tag.Ymin - y + ColLeft.top, sizeX, sizeY, img, ColRight.top, ColRight.top + ColRight.height - 1)
9896 akron1 701
        END
702
      END
703
    END
9898 akron1 704
  END DrawImage;
9896 akron1 705
 
706
  PROCEDURE td(VAR tag: XML.TAG);
707
  VAR x1, y1, x2, y2, cl: INTEGER;
708
  BEGIN
9898 akron1 709
    x1 := tag.X + ColLeft.left;
710
    y1 := tag.Ymin - Ycur + ColLeft.top;
9896 akron1 711
    x2 := x1 + tag.Width;
712
    y2 := y1 + tables.get_height(tag.table, tag.cell);
713
    cl := G.GetColor();
714
    G.SetColor(Settings.Colors[TEXT_COLOR]);
715
    G.Rect(x1, y1, x2, y2);
716
    IF Settings.TwoCol THEN
9898 akron1 717
      x1 := x1 - ColLeft.left + ColRight.left;
718
      x2 := x2 - ColLeft.left + ColRight.left;
719
      y1 := y1 - ColLeft.height;
720
      y2 := y2 - ColLeft.height;
9896 akron1 721
      G.Rect(x1, y1, x2, y2)
722
    END;
723
    G.SetColor(cl)
724
  END td;
725
 
726
BEGIN
727
  cur := body;
728
  WHILE cur # NIL DO
729
    IF cur IS XML.TAG THEN
730
      tag := cur(XML.TAG);
731
      IF (tag.value = XML.tag_td) OR (tag.value = XML.tag_th) THEN
732
         tag.Ymax := tag.Ymin + tables.get_height(tag.table, tag.cell)
733
      END;
734
 
735
      IF (tag.Ymin < Ycur + LineH * Lines * (ORD(Settings.TwoCol) + 1)) & (tag.Ymax >= Ycur) OR (tag.value = XML.tag_tr) THEN
736
 
737
      value := tag.value;
738
      CASE value OF
739
      |XML.tag_a:
740
      	INC(refer);
741
        color := Font.Font.color;
742
        IF tag.Clicked THEN
743
          Font.SetFontColor(Settings.Colors[CLICKED_COLOR])
744
        ELSE
745
          IF tag.Visited THEN
746
            Font.SetFontColor(Settings.Colors[VISITED_COLOR])
747
          ELSE
748
            Font.SetFontColor(Settings.Colors[LINK_COLOR])
749
          END
750
        END
751
      |XML.tag_contents_item:
752
		IF tag.Clicked THEN
753
			INC(refer);
754
			color := Font.Font.color;
755
			Font.SetFontColor(Settings.Colors[CLICKED_COLOR])
756
		ELSIF tag.Visited THEN
757
			INC(refer);
758
			color := Font.Font.color;
759
			Font.SetFontColor(Settings.Colors[VISITED_COLOR])
760
		END
761
      |XML.tag_title, XML.tag_strong, XML.tag_th:
762
        INC(strong);
763
        Font.Bold(TRUE)
764
      |XML.tag_strikethrough:
765
        INC(strike);
766
        Font.Strike(TRUE)
767
      |XML.tag_epigraph, XML.tag_cite, XML.tag_emphasis:
768
        INC(italic);
769
        Font.Italic(TRUE, refer = 0)
770
      |XML.tag_image:
9898 akron1 771
      	Image(tag, FALSE);
772
        DrawImage(tag);
773
        LibImg.Destroy(tag.img)
9896 akron1 774
      |XML.tag_code:
775
        INC(code)
776
      ELSE
777
      END;
778
      layout2(tag.child.first);
779
      CASE value OF
780
      |XML.tag_a:
781
      	DEC(refer);
782
        Font.SetFontColor(color)
783
      |XML.tag_contents_item:
784
		IF tag.Clicked OR tag.Visited THEN
785
			DEC(refer);
786
			Font.SetFontColor(color)
787
		END
788
      |XML.tag_title, XML.tag_strong:
789
        DEC(strong);
790
        Font.Bold(strong > 0)
791
      |XML.tag_strikethrough:
792
        DEC(strike);
793
        Font.Strike(strike > 0)
794
      |XML.tag_epigraph, XML.tag_cite, XML.tag_emphasis:
795
        DEC(italic);
796
        Font.Italic(italic > 0, refer = 0)
797
      |XML.tag_td:
798
        td(tag)
799
      |XML.tag_th:
800
        DEC(strong);
801
        Font.Bold(strong > 0);
802
        td(tag)
803
      |XML.tag_code:
804
        DEC(code)
805
      ELSE
806
      END
807
 
808
      END
809
    ELSIF cur IS XML.TEXT THEN
810
      text := cur(XML.TEXT);
811
      y  := text.Y - Ycur;
812
      y0 := y - y MOD LineH;
9898 akron1 813
      DrawText(ColLeft, 0, ColLeft.height - LineH, y0, y, FALSE, text);
9896 akron1 814
      IF Settings.TwoCol THEN
9898 akron1 815
        DrawText(ColRight, ColLeft.height, ColLeft.height + ColRight.height - LineH, y0, y, TRUE, text)
9896 akron1 816
      END
817
    END;
818
    cur := cur.next
819
  END
820
END layout2;
821
 
822
 
9898 akron1 823
PROCEDURE DrawFrame (color: INTEGER);
9896 akron1 824
VAR max_X, max_Y: INTEGER;
825
BEGIN
826
  max_X := G.Buffer.Width - 1;
827
  max_Y := G.Buffer.Height - 1;
9898 akron1 828
  G.SetColor(color);
9896 akron1 829
  G.HLine(0, max_X, 0);
830
  G.HLine(0, max_X, max_Y);
831
  G.VLine(0, 0, max_Y);
832
  sb.max_area := (Ymax - Ymin) DIV LineH + 50;
833
  sb.cur_area := 50;
834
  sb.position := (Ycur - Ymin) DIV LineH;
835
  box_lib.scrollbar_v_draw(sb)
9898 akron1 836
END DrawFrame;
9896 akron1 837
 
838
 
839
PROCEDURE Draw*;
9898 akron1 840
(*VAR max_Y: INTEGER;*)
9896 akron1 841
BEGIN
9898 akron1 842
  (*max_Y := G.Buffer.Height - 1;*)
843
  IF Settings.b_pict & (Settings.Picture # 0) THEN
844
      G.Copy(G.BackImg, G.Buffer, 0, G.Buffer.Height, 0)
845
  ELSE
846
      G.Fill(G.Buffer, Settings.Colors[BACK_COLOR])
847
  END;
9896 akron1 848
  Font.SetFontColor(Settings.Colors[TEXT_COLOR]);
849
  IF ((body = description) OR (body = contents)) & Settings.TwoCol THEN
850
    Settings.TwoCol := FALSE;
851
    layout2(body.child.first);
852
    Settings.TwoCol := TRUE;
853
    Search.draw(body, ColLeft, ColRight, Ycur, LineH, FALSE)
854
  ELSE
855
    layout2(body.child.first);
856
    Search.draw(body, ColLeft, ColRight, Ycur, LineH, Settings.TwoCol)
857
  END;
9898 akron1 858
  (*G.Copy(G.BackImg, G.Buffer, 0, ColLeft.top + 1, 0);
859
  G.Copy(G.BackImg, G.Buffer, max_Y - ColLeft.top, ColLeft.top + 1, max_Y - ColLeft.top);*)
860
  DrawFrame(0);
9896 akron1 861
  G.Draw(Canvas_X, Canvas_Y);
862
  DrawToolbar;
863
  DrawStatus
864
END Draw;
865
 
866
 
867
PROCEDURE BackEnabled* (): BOOLEAN;
868
	RETURN b_stk.first # NIL
869
END BackEnabled;
870
 
871
 
872
PROCEDURE FrwEnabled* (): BOOLEAN;
873
	RETURN f_stk.first # NIL
874
END FrwEnabled;
875
 
876
 
877
PROCEDURE ContentsEnabled* (): BOOLEAN;
878
	RETURN (contents # NIL) (*& (body # contents)*)
879
END ContentsEnabled;
880
 
881
 
882
PROCEDURE DescrEnabled* (): BOOLEAN;
883
	RETURN (description # NIL) (*& (body # description)*)
884
END DescrEnabled;
885
 
886
 
887
PROCEDURE Back*;
888
BEGIN
889
  IF b_stk.first # NIL THEN
890
    Push(f_stk);
891
    Pop(b_stk)
892
  END
893
END Back;
894
 
895
 
896
PROCEDURE Forward*;
897
BEGIN
898
  IF f_stk.first # NIL THEN
899
    Push(b_stk);
900
    Pop(f_stk)
901
  END
902
END Forward;
903
 
904
 
905
PROCEDURE Contents*;
906
BEGIN
907
  IF (contents # NIL) & (body # contents) THEN
908
    Push(b_stk);
909
    Clear(f_stk);
910
    body := contents;
911
    Ycur := Ycont;
912
    Ymin := 0;
913
    Ymax := body.Ymax
914
  END
915
END Contents;
916
 
917
 
918
PROCEDURE Descr*;
919
BEGIN
920
  IF (description # NIL) & (body # description) THEN
921
    Push(b_stk);
922
    Clear(f_stk);
923
    body := description;
924
    Ycur := 0;
925
    Ymin := 0;
926
    Ymax := body.Ymax
927
  END
928
END Descr;
929
 
930
 
9898 akron1 931
PROCEDURE Scroll* (n: INTEGER);
9896 akron1 932
BEGIN
9898 akron1 933
	INC(Ycur, LineH*n);
934
	SU.MinMax(Ycur, Ymin, Ymax)
935
END Scroll;
9896 akron1 936
 
937
 
938
PROCEDURE PageUp*;
939
BEGIN
9898 akron1 940
    Scroll(-Lines * (ORD(Settings.TwoCol) + 1))
9896 akron1 941
END PageUp;
942
 
943
 
944
PROCEDURE PageDown*;
945
BEGIN
9898 akron1 946
    Scroll(Lines * (ORD(Settings.TwoCol) + 1))
9896 akron1 947
END PageDown;
948
 
949
 
950
PROCEDURE Home*;
951
BEGIN
952
  IF Ycur # Ymin THEN
953
    Push(b_stk);
954
    Clear(f_stk);
955
    Ycur := Ymin
956
  END
957
END Home;
958
 
959
 
960
PROCEDURE End*;
961
BEGIN
962
  IF Ycur # Ymax THEN
963
    Push(b_stk);
964
    Clear(f_stk);
965
    Ycur := Ymax
966
  END
967
END End;
968
 
969
 
970
PROCEDURE ScrollBar*;
971
BEGIN
972
  Ycur := sb.position * LineH + Ymin
973
END ScrollBar;
974
 
975
 
976
PROCEDURE GetBody(tag: XML.TAG): XML.TAG;
977
BEGIN
978
  WHILE (tag # NIL) & (tag.value # XML.tag_body) DO
979
    tag := tag.parent(XML.TAG)
980
  END
981
  RETURN tag
982
END GetBody;
983
 
984
 
985
PROCEDURE layout3(Body: XML.ELEMENT; X, Y: INTEGER);
986
VAR
987
  ptr   : V.ANYPTR;
988
  text  : XML.TEXT;
989
  sect  : XML.TAG;
990
  y     : INTEGER;
991
  i     : INTEGER;
992
BEGIN
993
  i := 0;
994
  WHILE i < references.count DO
995
    ptr := V.get(references, i);
996
    text := ptr(XML.TEXT);
997
    y  := text.Y - Ycur;
998
    IF (y <= Y) & (Y <= y + Font.FontH()) & (text.X <= X) & (X <= text.X + text.width) THEN
999
      sect := text.parent(XML.TAG);
1000
      IF Body = contents THEN
1001
        WHILE (sect # NIL) & (sect.value # XML.tag_contents_item) DO
1002
          sect := sect.parent(XML.TAG)
1003
        END
1004
      ELSE
1005
        WHILE (sect # NIL) & (sect # Body) DO
1006
          IF sect.value = XML.tag_contents_item THEN
1007
            sect := NIL
1008
          ELSE
1009
            sect := sect.parent(XML.TAG)
1010
          END
1011
        END
1012
      END;
1013
 
1014
      IF sect # NIL THEN
1015
        sect := text.parent(XML.TAG);
1016
        WHILE sect # NIL DO
1017
          IF (sect.value = XML.tag_contents_item) & (Body = contents) OR (sect.value = XML.tag_a) THEN
1018
            ref := sect;
1019
            sect := NIL;
1020
            i := references.count
1021
          ELSE
1022
            sect := sect.parent(XML.TAG)
1023
          END
1024
        END
1025
      END
1026
    END;
1027
    INC(i)
1028
  END
1029
END layout3;
1030
 
1031
 
9898 akron1 1032
PROCEDURE getRefProp (VAR ref, body: XML.TAG; VAR URL: INTEGER; VAR note: BOOLEAN; VAR Y: INTEGER);
1033
BEGIN
1034
	note := FALSE;
1035
	URL := 0;
1036
	Y := 0;
1037
	IF ref.value = XML.tag_a THEN
1038
		ref := XML.GetRef(ref, note, URL)
1039
	ELSE
1040
		ref := ref.parent(XML.TAG)
1041
	END;
1042
	IF ref # NIL THEN
1043
		Y := ref.Ymin;
1044
	END;
1045
	IF note THEN
1046
	  	body := ref
1047
	ELSE
1048
	  	body := GetBody(ref)
1049
	END
1050
END getRefProp;
1051
 
1052
 
1053
PROCEDURE zstreq (s1, s2: INTEGER): BOOLEAN;
1054
VAR
1055
    c1, c2: CHAR;
1056
BEGIN
1057
    REPEAT
1058
        sys.GET(s1, c1); INC(s1);
1059
        sys.GET(s2, c2); INC(s2);
1060
    UNTIL (c1 = 0X) OR (c2 = 0X)
1061
 
1062
    RETURN c1 = c2
1063
END zstreq;
1064
 
1065
 
1066
PROCEDURE refeq (ref1, ref2: XML.TAG): BOOLEAN;
1067
VAR
1068
	body1, body2: XML.TAG;
1069
	URL1, URL2: INTEGER;
1070
	note1, note2: BOOLEAN;
1071
	Y1, Y2: INTEGER;
1072
BEGIN
1073
	getRefProp(ref1, body1, URL1, note1, Y1);
1074
	getRefProp(ref2, body2, URL2, note2, Y2);
1075
	RETURN (ref1 = ref2) & (body1 = body2) & (URL1 = 0) & (URL2 = 0) & (note1 = note2) & (Y1 = Y2) OR
1076
			(URL1 # 0) & (URL2 # 0) & zstreq(URL1, URL2)
1077
END refeq;
1078
 
1079
 
1080
PROCEDURE setVisited (ref: XML.TAG);
1081
VAR
1082
	i: INTEGER;
1083
	cur: V.ANYPTR;
1084
BEGIN
1085
	FOR i := 0 TO references.count - 1 DO
1086
		cur := V.get(references, i);
1087
		IF cur IS XML.TEXT THEN
1088
			cur := cur(XML.TEXT).parent;
1089
			IF refeq(cur(XML.TAG), ref) THEN
1090
				cur(XML.TAG).Visited := TRUE
1091
			END
1092
		END
1093
	END
1094
END setVisited;
1095
 
1096
 
9896 akron1 1097
PROCEDURE MouseDown;
1098
BEGIN
1099
	IF ~mouseDown THEN
1100
		mouseDown := TRUE;
1101
		clickRef := ref;
9898 akron1 1102
		IF ref # NIL THEN
1103
			ref.Clicked := TRUE
1104
		END;
9896 akron1 1105
		Draw
1106
	END
1107
END MouseDown;
1108
 
1109
 
1110
PROCEDURE MouseUp;
1111
VAR
1112
	note : BOOLEAN;
1113
	URL  : INTEGER;
1114
	redraw: BOOLEAN;
1115
BEGIN
1116
	redraw := FALSE;
1117
	mouseDown := FALSE;
1118
	IF (ref # NIL) & (clickRef = ref) & ref.Clicked THEN
1119
		redraw := TRUE;
1120
        ref.Clicked := FALSE;
1121
		note := FALSE;
1122
		URL := 0;
1123
		IF ref.value = XML.tag_a THEN
1124
			ref := XML.GetRef(ref, note, URL)
1125
		ELSE
1126
			ref := ref.parent(XML.TAG)
1127
		END;
1128
		IF ref # NIL THEN
1129
			Push(b_stk);
1130
			Clear(f_stk);
1131
			Ycur := ref.Ymin;
1132
			IF note THEN
1133
			  	body := ref
1134
			ELSE
1135
			  	body := GetBody(ref)
1136
			END;
1137
			Ymax := body.Ymax;
1138
			Ymin := body.Ymin;
1139
 
1140
			IF ~clickRef.Visited THEN
9898 akron1 1141
				setVisited(clickRef);
9896 akron1 1142
				clickRef.Visited := TRUE;
1143
				PushRef(clickRef)
1144
			END
1145
		ELSIF URL # 0 THEN
1146
			SU.Run(Ini.Browser, URL);
1147
			IF ~clickRef.Visited THEN
9898 akron1 1148
				setVisited(clickRef);
9896 akron1 1149
				clickRef.Visited := TRUE;
1150
				PushRef(clickRef)
1151
			END
9898 akron1 1152
		END
9896 akron1 1153
	END;
1154
	IF clickRef # NIL THEN
1155
		clickRef.Clicked := FALSE;
1156
		clickRef := NIL;
1157
		redraw := TRUE
1158
	END;
1159
	IF hoverRef # NIL THEN
1160
		hoverRef.Clicked := FALSE;
1161
		hoverRef := NIL;
1162
		redraw := TRUE
1163
	END;
1164
	IF redraw THEN
1165
		Draw
1166
	END
1167
END MouseUp;
1168
 
1169
 
1170
PROCEDURE Click*(X, Y: INTEGER; clicked: BOOLEAN);
1171
VAR
1172
  note : BOOLEAN;
1173
  URL  : INTEGER;
1174
  urlchars: S.CHARS;
1175
  urlstr1 : S.STRING;
1176
BEGIN
1177
  DEC(Y, Settings.PADDING.Top);
1178
  DEC(X, Settings.PADDING.Left);
1179
  IF (0 <= Y) & (Y <= Lines * LineH) THEN
1180
    ref := NIL;
1181
    layout3(body, X, Y);
1182
    IF (ref = NIL) & Settings.TwoCol THEN
9898 akron1 1183
      layout3(body, X - ColLeft.width - Settings.PADDING.ColInter, Y + Lines * LineH);
9896 akron1 1184
    END;
1185
    hoverRef := ref;
1186
	IF clicked THEN
1187
		MouseDown
1188
	ELSE
1189
		MouseUp
1190
	END;
1191
    IF ref # NIL THEN
1192
      SU.SetCursor(cursor);
1193
      note := FALSE;
1194
      URL := 0;
1195
      IF ref.value = XML.tag_a THEN
1196
        ref := XML.GetRef(ref, note, URL)
1197
      END;
1198
      IF URL # 0 THEN
1199
        S.PtrToString(URL, urlstr1);
1200
        S.StrToChars(urlstr1, urlchars)
1201
      END
1202
    ELSE
1203
      SU.SetCursor(0);
1204
      urlstr1 := ""
1205
    END;
1206
    IF urlstr1 # urlstr THEN
1207
      urlstr := urlstr1;
1208
      DrawStatus
1209
    END
1210
  ELSE
1211
      SU.SetCursor(0);
1212
      urlstr := "";
1213
      ref := NIL;
1214
      DrawStatus
1215
  END
1216
END Click;
1217
 
1218
 
1219
PROCEDURE main(fb: XML.ELEMENT; Contents: BOOLEAN);
1220
VAR
1221
  cur: XML.ELEMENT;
1222
  tag: XML.TAG;
1223
  par, epi: INTEGER;
1224
 
1225
 
1226
  PROCEDURE lout(body: XML.ELEMENT);
1227
  BEGIN
1228
    TextCount := 0;
1229
    X := 0;
1230
    Y := Settings.SUP;
1231
    layout(body(XML.TAG).child.first);
1232
    body(XML.TAG).Ymax := Y - Settings.SUP
1233
  END lout;
1234
 
1235
  PROCEDURE lout_one_col(body: XML.ELEMENT);
1236
  BEGIN
1237
    IF body # NIL THEN
1238
      IF Settings.TwoCol THEN
1239
        W := W2;
1240
        Settings.TwoCol := FALSE;
1241
        lout(body);
1242
        Settings.TwoCol := TRUE;
1243
        W := W1
1244
      ELSE
1245
        lout(body)
1246
      END
1247
    END
1248
  END lout_one_col;
1249
 
1250
BEGIN
1251
  TextCount := 0;
1252
  sup := 0;
1253
  sub := 0;
1254
  epigraph := 0;
1255
  align := 0;
1256
  code := 0;
1257
  strong := 0;
1258
  italic := 0;
1259
  strike := 0;
1260
  refer := 0;
1261
  SU.ErrorIf(fb = NIL, 11);
1262
  MainBody := FALSE;
1263
  description := NIL;
1264
  mainbody := NIL;
1265
  cover := NIL;
1266
  cur := fb;
1267
  cur := cur(XML.TAG).child.first;
1268
  WHILE (cur # NIL) & (mainbody = NIL) DO
1269
    IF cur IS XML.TAG THEN
1270
      tag := cur(XML.TAG);
1271
      IF tag.value = XML.tag_description THEN
1272
        description := tag
1273
      ELSIF tag.value = XML.tag_body THEN
1274
        mainbody := tag
1275
      END
1276
    END;
1277
    cur := cur.next
1278
  END;
1279
  SU.ErrorIf(mainbody = NIL, 12);
1280
 
1281
  WHILE cur # NIL DO
1282
    IF (cur IS XML.TAG) & (cur(XML.TAG).value = XML.tag_body) THEN
1283
      lout(cur)
1284
    END;
1285
    cur := cur.next
1286
  END;
1287
 
1288
  IF Contents THEN
1289
    contents := XML.CreateTag();
1290
    MainBody := TRUE;
1291
  END;
1292
  lout(mainbody);
1293
  IF Contents & (contents.child.first = NIL) THEN
1294
    DISPOSE(contents)
1295
  END;
1296
  MainBody := FALSE;
1297
  epigraph := 1;
1298
  par := Settings.PARAGRAPH;
1299
  epi := Settings.EPIGRAPH;
1300
  Settings.PARAGRAPH := 0;
1301
  Settings.EPIGRAPH := 0;
1302
  lout_one_col(contents);
1303
  Settings.EPIGRAPH := epi;
1304
  Settings.PARAGRAPH := par;
1305
  epigraph := 0;
1306
  lout_one_col(description);
1307
  body := mainbody;
1308
  Ymax := body.Ymax;
1309
  Ycur := 0;
1310
  Ymin := 0;
1311
  Ycont := 0
1312
END main;
1313
 
1314
 
1315
PROCEDURE Find* (d: INTEGER);
1316
VAR
1317
    y, min, max: INTEGER;
1318
 
1319
BEGIN
1320
    Search.fnext(body, y, d);
1321
    IF y >= 0 THEN
1322
        DEC(y, y MOD LineH);
1323
        min := Ycur;
9898 akron1 1324
        max := min + ColLeft.height - LineH;
9896 akron1 1325
        IF Settings.TwoCol THEN
9898 akron1 1326
            INC(max, ColRight.height)
9896 akron1 1327
        END;
1328
 
1329
        IF (y < min) OR (y > max) THEN
9898 akron1 1330
            Ycur := MAX(y - ColLeft.height DIV 2, 0)
9896 akron1 1331
        END;
1332
 
1333
        DEC(Ycur, Ycur MOD LineH)
1334
    END
1335
END Find;
1336
 
1337
 
1338
PROCEDURE OpenSearch*;
1339
BEGIN
1340
    Search.open(Find)
1341
END OpenSearch;
1342
 
1343
 
1344
PROCEDURE CloseSearch*;
1345
BEGIN
1346
    Search.close
1347
END CloseSearch;
1348
 
1349
 
1350
PROCEDURE found* (): BOOLEAN;
1351
    RETURN Search.found(body)
1352
END found;
1353
 
1354
 
1355
PROCEDURE FontSizeChange(fs: INTEGER);
1356
BEGIN
1357
  Settings.SUP               :=  fs DIV 4;
1358
  Settings.SUB               :=  fs DIV 4;
1359
  Settings.SpaceW            :=  fs DIV 2;
1360
  Settings.LEVEL             :=  Settings.PARAGRAPH;
1361
  Settings.PADDING.Bottom    :=  Settings.PADDING.Top;
1362
  Settings.PADDING.Left      :=  G.Buffer.Width * Settings.PADDING.LRpc DIV 100;
1363
  IF Settings.PADDING.Left = 0 THEN
1364
    Settings.PADDING.Left := 1
1365
  END;
1366
  Settings.PADDING.Right     :=  Settings.PADDING.Left;
1367
  Settings.PADDING.ColInter  :=  G.Buffer.Width * Settings.PADDING.CInt DIV 100;
1368
 
1369
  LineH := Font.FontH() + Settings.SUP + Settings.SUB + Settings.InterLin;
9898 akron1 1370
  Window.initRect(
9896 akron1 1371
    ColLeft, Settings.PADDING.Left, Settings.PADDING.Top,
1372
    G.Buffer.Width - Settings.PADDING.Left - Settings.PADDING.Right,
1373
    G.Buffer.Height - Settings.PADDING.Top - Settings.PADDING.Bottom);
1374
  IF Settings.TwoCol THEN
9898 akron1 1375
    ColLeft.width := (ColLeft.width - Settings.PADDING.ColInter) DIV 2;
9896 akron1 1376
    ColRight := ColLeft;
9898 akron1 1377
    ColRight.left := ColLeft.left + ColLeft.width + Settings.PADDING.ColInter
9896 akron1 1378
  END;
9898 akron1 1379
  W := ColLeft.width;
1380
  Lines := ColLeft.height DIV LineH;
1381
  ColLeft.height  := Lines * LineH;
1382
  ColRight.height := ColLeft.height;
9896 akron1 1383
END FontSizeChange;
1384
 
1385
 
1386
PROCEDURE Resize*(Width, Height: INTEGER);
9898 akron1 1387
VAR d: REAL; resize: BOOLEAN; sizeX, sizeY, data, back_picture: INTEGER;
9896 akron1 1388
 
1389
  PROCEDURE stk1(stk: XML.LIST);
1390
  VAR cur: StackItem;
1391
  BEGIN
1392
    cur := stk.first(StackItem);
1393
    WHILE cur # NIL DO
1394
      cur.d := FLT(cur.Ycur - cur.body.Ymin) / FLT(cur.body.Ymax - cur.body.Ymin);
1395
      cur := cur.next(StackItem)
1396
    END
1397
  END stk1;
1398
 
1399
  PROCEDURE stk2(stk: XML.LIST);
1400
  VAR cur: StackItem;
1401
  BEGIN
1402
    cur := stk.first(StackItem);
1403
    WHILE cur # NIL DO
1404
      cur.Ycur := FLOOR(FLT(cur.body.Ymax - cur.body.Ymin) * cur.d) + cur.body.Ymin;
1405
      cur.Ycur := cur.Ycur - cur.Ycur MOD LineH;
1406
      SU.MinMax(cur.Ycur, cur.body.Ymin, cur.body.Ymax);
1407
      cur := cur.next(StackItem)
1408
    END
1409
  END stk2;
1410
 
1411
BEGIN
1412
  resize := (Width # G.Buffer.Width) OR resized;
1413
  G.Resize(Width, Height);
1414
  IF (Settings.Picture # 0) & Settings.b_pict THEN
9898 akron1 1415
  	back_picture := LibImg.GetImg(Settings.Picture, Settings.picture_fsize, 1000000, sizeY);
1416
  	IF back_picture # 0 THEN
1417
	    LibImg.GetInf(back_picture, sizeX, sizeY, data);
1418
    	G.CreateBackImg;
1419
	    G.BackImage(sizeX, sizeY, data);
1420
    	LibImg.Destroy(back_picture)
1421
    END
9896 akron1 1422
  ELSE
9898 akron1 1423
    G.DestroyBackImg;
1424
    G.Fill(G.Buffer, Settings.Colors[BACK_COLOR]);
1425
    //G.Fill(G.BackImg, Settings.Colors[BACK_COLOR])
9896 akron1 1426
  END;
1427
 
1428
  IF Font.FontH() # 0 THEN
1429
    FontSizeChange(Font.FontH());
1430
  ELSE
1431
    FontSizeChange(Settings.FontSize);
1432
  END;
1433
 
9898 akron1 1434
  ColLeft.width  := G.Buffer.Width - Settings.PADDING.Left - Settings.PADDING.Right;
9896 akron1 1435
  IF Settings.TwoCol THEN
9898 akron1 1436
    ColLeft.width  := (ColLeft.width - Settings.PADDING.ColInter) DIV 2;
1437
    ColRight.width := ColLeft.width;
1438
    ColRight.left  := ColLeft.left + ColLeft.width + Settings.PADDING.ColInter
9896 akron1 1439
  END;
9898 akron1 1440
  ColLeft.height  := G.Buffer.Height - Settings.PADDING.Top  - Settings.PADDING.Bottom;
1441
  Lines           := ColLeft.height DIV LineH;
1442
  ColLeft.height  := Lines * LineH;
1443
  ColRight.height := ColLeft.height;
9896 akron1 1444
 
1445
  IF done & resize THEN
1446
    resized := FALSE;
1447
    Push(b_stk);
1448
    stk1(b_stk);
1449
    stk1(f_stk);
1450
    IF contents # NIL THEN
1451
      d := FLT(Ycont) / FLT(contents.Ymax)
1452
    END;
9898 akron1 1453
    W := ColLeft.width;
1454
    W2 := ColLeft.width + ColRight.width + Settings.PADDING.ColInter;
9896 akron1 1455
    W1 := W;
1456
    main(XML.FB, FALSE);
1457
    Search.resize;
1458
    stk2(b_stk);
1459
    stk2(f_stk);
1460
    IF contents # NIL THEN
1461
      Ycont := FLOOR(FLT(contents.Ymax) * d);
1462
      Ycont := Ycont - Ycont MOD LineH;
1463
      SU.MinMax(Ycont, 0, contents.Ymax)
1464
    END;
1465
    Pop(b_stk);
1466
  END
1467
END Resize;
1468
 
1469
 
1470
PROCEDURE SetColors*;
1471
BEGIN
1472
  Settings.Colors[BACK_COLOR]     := Ini.GetColor("back",    Settings.Colors[BACK_COLOR]);
1473
  Settings.Colors[TEXT_COLOR]     := Ini.GetColor("text",    Settings.Colors[TEXT_COLOR]);
1474
  Settings.Colors[ITALIC_COLOR]   := Ini.GetColor("italic",  Settings.Colors[ITALIC_COLOR]);
1475
  Settings.Colors[LINK_COLOR]     := Ini.GetColor("link",    Settings.Colors[LINK_COLOR]);
1476
  Settings.Colors[VISITED_COLOR]  := Ini.GetColor("visited", Settings.Colors[LINK_COLOR]);
1477
END SetColors;
1478
 
1479
 
1480
PROCEDURE Resized(set1, set2: TSettings): BOOLEAN;
1481
  RETURN (set1.FontSize # set2.FontSize) OR (set1.TwoCol # set2.TwoCol) OR
1482
         (set1.PARAGRAPH # set2.PARAGRAPH) OR (set1.EPIGRAPH # set2.EPIGRAPH) OR
1483
         (set1.PADDING.LRpc # set2.PADDING.LRpc) OR (set1.PADDING.CInt # set2.PADDING.CInt)
1484
         OR (set1.InterLin # set2.InterLin)
1485
END Resized;
1486
 
1487
 
1488
PROCEDURE SetSettings*(NewSet: TSettings);
1489
BEGIN
1490
  resized := Resized(Settings, NewSet) OR resized;
1491
  Settings := NewSet;
1492
  Font.Init(Settings.Colors[ITALIC_COLOR], Settings.Colors[TEXT_COLOR], Settings.FontSize);
1493
  Resize(G.Buffer.Width, G.Buffer.Height)
1494
END SetSettings;
1495
 
1496
 
1497
PROCEDURE Init*(Left, Top, Width, Height: INTEGER);
1498
BEGIN
1499
  G.Resize(Width, Height);
1500
  Canvas_X := Left;
1501
  Canvas_Y := Top
1502
END Init;
1503
 
1504
 
1505
PROCEDURE Start;
1506
BEGIN
1507
  XML.Open(FileName);
1508
  main(XML.FB, TRUE);
1509
  done := TRUE;
1510
  SU.Halt
1511
END Start;
1512
 
1513
 
1514
PROCEDURE CleanHistory*(fname: S.STRING);
1515
VAR F: File.FS; pos, pos2, fsize, size, buf, buf2: INTEGER; c: CHAR;
1516
BEGIN
1517
  F := File.Open(fname);
1518
  IF F # NIL THEN
1519
    fsize := File.Seek(F, 0, 2);
1520
    pos := File.Seek(F, 0, 0);
1521
    buf := K.malloc(fsize + 1024);
1522
    buf2 := K.malloc(fsize + 1024);
1523
    pos := File.Read(F, buf, fsize);
1524
    File.Close(F);
1525
    pos := 0;
1526
    pos2 := 0;
1527
    WHILE pos < fsize DO
1528
      sys.GET(buf + pos, size);
1529
      sys.GET(buf + pos + 4, c);
1530
      IF c = 0X THEN
1531
        sys.MOVE(buf + pos, buf2 + pos2, size);
1532
        pos2 := pos2 + size
1533
      END;
1534
      pos := pos + size
1535
    END;
1536
    F := File.Create(fname);
1537
    pos := File.Write(F, buf2, pos2);
1538
    File.Close(F);
1539
    buf := K.free(buf);
1540
    buf2 := K.free(buf2)
1541
  END
1542
END CleanHistory;
1543
 
1544
 
1545
PROCEDURE Save;
1546
VAR history: File.FS; win_size_x, win_size_y, size, pos: INTEGER;
1547
 
1548
  PROCEDURE WriteInt(history: File.FS; x: INTEGER);
1549
  BEGIN
9898 akron1 1550
    IF File.WriteInt(history, x) THEN END
9896 akron1 1551
  END WriteInt;
1552
 
1553
  PROCEDURE WriteStk(history: File.FS; VAR stk: XML.LIST; links: BOOLEAN);
1554
  VAR
1555
      cur: StackItem;
1556
  BEGIN
1557
    WriteInt(history, XML.ListCount(stk));
1558
    cur := stk.first(StackItem);
1559
    WHILE cur # NIL DO
1560
      WriteInt(history, cur.body.num);
1561
      IF ~links THEN
1562
      	WriteInt(history, cur.Ycur)
1563
      END;
1564
      cur := cur.next(StackItem)
1565
    END
1566
  END WriteStk;
1567
 
1568
BEGIN
1569
  Ini.Save(Settings.Colors, Settings.b_pict);
1570
  history := File.Open(Ini.History);
1571
  IF history = NIL THEN
1572
    history := File.Create(Ini.History)
1573
  ELSE
1574
    pos := File.Seek(history, 0 , 2)
1575
  END;
1576
  size := 1 + 18*4 + 1 + 8*(XML.ListCount(b_stk) + XML.ListCount(f_stk)) + 4*XML.ListCount(vis_ref) + 12;
1577
  WriteInt(history, size);
9898 akron1 1578
  IF File.WriteChar(history, 0X) THEN END;
9896 akron1 1579
  WriteInt(history, fsize2);
1580
  WriteInt(history, chksum);
1581
  SU.GetWindowSize(win_size_x, win_size_y);
1582
  WriteInt(history, win_size_x);
1583
  WriteInt(history, win_size_y);
1584
  WriteInt(history, Settings.PADDING.LRpc);
1585
  WriteInt(history, Settings.PADDING.Top);
1586
  WriteInt(history, Settings.PADDING.CInt);
1587
  WriteInt(history, Settings.PARAGRAPH);
1588
  WriteInt(history, Settings.EPIGRAPH);
1589
  WriteInt(history, Settings.InterLin);
1590
 
9898 akron1 1591
  IF File.WriteBool(history, Settings.TwoCol) THEN END;
9896 akron1 1592
 
1593
  WriteInt(history, Settings.FontSize);
1594
  WriteInt(history, body.num);
1595
  WriteInt(history, Ymin);
1596
  WriteInt(history, Ymax);
1597
  WriteInt(history, Ycur);
1598
  WriteInt(history, Ycont);
1599
 
1600
  WriteStk(history, b_stk, FALSE);
1601
  WriteStk(history, f_stk, FALSE);
1602
  WriteStk(history, vis_ref, TRUE);
1603
 
1604
  WriteInt(history, size);
1605
 
1606
  File.Close(history);
1607
  CleanHistory(Ini.History)
1608
END Save;
1609
 
1610
 
1611
PROCEDURE ReadInt(VAR x: INTEGER);
1612
BEGIN
9898 akron1 1613
  IF File.ReadInt(history, x) THEN END
9896 akron1 1614
END ReadInt;
1615
 
1616
 
1617
PROCEDURE Load;
1618
VAR body_num, ycur, size, pos: INTEGER;
1619
 
1620
  PROCEDURE ReadStk(VAR stk: XML.LIST);
1621
  VAR n, num: INTEGER;
1622
  BEGIN
1623
    ReadInt(n);
1624
    WHILE n > 0 DO
1625
      ReadInt(num);
1626
      body := XML.GetTagByNum(num);
1627
      ReadInt(Ycur);
1628
      Push(stk);
1629
      DEC(n)
1630
    END
1631
  END ReadStk;
1632
 
1633
	PROCEDURE ReadRef;
1634
	VAR
1635
		n, num: INTEGER;
1636
		ref: XML.TAG;
1637
	BEGIN
1638
		ReadInt(n);
1639
		WHILE n > 0 DO
1640
			ReadInt(num);
1641
			ref := XML.GetTagByNum(num);
1642
			IF ref # NIL THEN
9898 akron1 1643
				setVisited(ref);
1644
				ref.Visited := TRUE;
1645
				PushRef(ref)
9896 akron1 1646
			END;
1647
			DEC(n)
1648
		END
1649
	END ReadRef;
1650
 
1651
BEGIN
1652
  ReadInt(Settings.PADDING.LRpc);
1653
  ReadInt(Settings.PADDING.Top);
1654
  ReadInt(Settings.PADDING.CInt);
1655
  ReadInt(Settings.PARAGRAPH);
1656
  ReadInt(Settings.EPIGRAPH);
1657
  ReadInt(Settings.InterLin);
9898 akron1 1658
  IF File.ReadBool(history, Settings.TwoCol) THEN END;
9896 akron1 1659
  ReadInt(Settings.FontSize);
1660
 
1661
  SetSettings(Settings);
1662
 
1663
  ReadInt(body_num);
1664
  ReadInt(Ymin);
1665
  ReadInt(Ymax);
1666
  ReadInt(ycur);
1667
  ReadInt(Ycont);
1668
 
1669
  ReadStk(b_stk);
1670
  ReadStk(f_stk);
1671
  ReadRef;
1672
 
1673
  ReadInt(size);
1674
  pos := File.Seek(history, -size, 1);
1675
  pos := File.Seek(history, 4, 1);
9898 akron1 1676
  IF File.WriteChar(history, 1X) THEN END;
9896 akron1 1677
 
1678
  Ycur := ycur;
1679
  body := XML.GetTagByNum(body_num);
1680
  File.Close(history)
1681
END Load;
1682
 
1683
 
1684
PROCEDURE GetWinSize*(hist_fn: S.STRING; VAR win_size_x, win_size_y: INTEGER);
1685
VAR c: CHAR; size, pos, x, y, fsize, _chksum: INTEGER; found: BOOLEAN;
1686
BEGIN
1687
  fsize2 := RF.FileSize(hist_fn);
1688
  chksum := RF.ChkSum(hist_fn);
1689
  found := FALSE;
1690
  history := File.Open(Ini.History);
1691
  pos := File.Seek(history, -4, 2);
1692
  last := FALSE;
1693
  WHILE pos >= 0 DO
9898 akron1 1694
    IF File.ReadInt(history, size) THEN
9896 akron1 1695
      pos := File.Seek(history, -size + 4, 1);
1696
    END;
9898 akron1 1697
    IF File.ReadChar(history, c) THEN END;
9896 akron1 1698
    ReadInt(fsize);
1699
    ReadInt(_chksum);
1700
    IF (c = 0X) & (fsize = fsize2) & (_chksum = chksum) THEN
1701
      found := TRUE;
9898 akron1 1702
      IF File.ReadInt(history, x) & File.ReadInt(history, y) THEN
9896 akron1 1703
        win_size_x := x;
1704
        win_size_y := y;
1705
      ELSE
1706
        found := FALSE
1707
      END;
1708
      pos := -1
1709
    ELSE
1710
      IF ~last THEN
1711
        last := TRUE;
1712
        ReadInt(x);
1713
        ReadInt(y);
1714
        ReadInt(Settings.PADDING.LRpc);
1715
        ReadInt(Settings.PADDING.Top);
1716
        ReadInt(Settings.PADDING.CInt);
1717
        ReadInt(Settings.PARAGRAPH);
1718
        ReadInt(Settings.EPIGRAPH);
1719
        ReadInt(Settings.InterLin);
9898 akron1 1720
        IF File.ReadBool(history, Settings.TwoCol) THEN END;
9896 akron1 1721
        ReadInt(Settings.FontSize);
1722
      END;
1723
      pos := File.Seek(history, pos - 8, 0)
1724
    END
1725
  END;
1726
  IF ~found THEN
1727
    File.Close(history)
1728
  END
1729
END GetWinSize;
1730
 
1731
 
1732
PROCEDURE Open*(FName: S.STRING; DrawWindow, _DrawStatus, _DrawToolbar: SU.ENTRY);
1733
VAR PID, event: INTEGER;
1734
BEGIN
1735
  DrawStatus := _DrawStatus;
1736
  DrawToolbar := _DrawToolbar;
1737
  cursor := SU.LoadCursor(Cursor.GetCursor());
1738
  references := V.create(1024);
1739
  ref_depth := 0;
1740
  done := FALSE;
1741
  loaded := FALSE;
1742
  FilePath := FName;
1743
  FileName := FName;
1744
  S.GetPath(FilePath);
9898 akron1 1745
  W  := ColLeft.width;
9896 akron1 1746
  W1 := W;
9898 akron1 1747
  W2 := ColLeft.width + ColRight.width + Settings.PADDING.ColInter;
1748
  Lines := ColLeft.height DIV LineH;
1749
  ColLeft.height := Lines * LineH;
9896 akron1 1750
  PID := SU.NewThread(Start, Stack);
1751
  WHILE ~SU.IsTerminated(PID) DO
1752
    event := SU.CheckEvent();
1753
    IF event = 3 THEN
1754
      SU.TerminateThreadId(PID);
1755
      SU.Halt
1756
    END;
1757
    G.Progress(RF.Progress());
1758
    G.Draw(Canvas_X, Canvas_Y);
1759
    DrawWindow;
1760
    SU.Pause(30)
1761
  END;
1762
  IF ~done THEN
1763
    SU.Halt
1764
  END;
1765
  loaded := TRUE;
1766
  resized := TRUE;
1767
  IF history # NIL THEN
1768
    Load
1769
  ELSE
1770
    SetSettings(Settings)
1771
  END
1772
END Open;
1773
 
1774
 
1775
PROCEDURE Close*;
1776
BEGIN
1777
  SU.DelCursor(cursor);
1778
  Save;
1779
  SU.Halt
1780
END Close;
1781
 
1782
 
1783
PROCEDURE SetScrollBar*(_sb: box_lib.scrollbar);
1784
BEGIN
1785
  sb := _sb
1786
END SetScrollBar;
1787
 
1788
 
1789
PROCEDURE Set_b_pict*(b_pict: BOOLEAN);
1790
BEGIN
1791
  Settings.b_pict := b_pict
1792
END Set_b_pict;
1793
 
1794
 
1795
BEGIN
1796
	clickRef := NIL;
1797
	hoverRef := NIL;
1798
	mouseDown := FALSE
1799
END DOM.