Subversion Repositories Kolibri OS

Rev

Rev 9898 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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