Subversion Repositories Kolibri OS

Rev

Rev 9896 | Go to most recent revision | 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.  
  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.  
  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.  
  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:
  606.         Image(tag, TRUE)
  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.  
  675.   PROCEDURE DrawText(Col: Window.tRect; min, max, y0, y: INTEGER; right: BOOLEAN; VAR text: XML.TEXT);
  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);
  682.         Font.Text(Col, word.X, y - Col.height * ORD(right), word.value.first, word.length);
  683.       END;
  684.       Font.StrikeText(Col, text.X, y - Col.height * ORD(right), text.width)
  685.     END
  686.   END DrawText;
  687.  
  688.   PROCEDURE DrawImage(VAR tag: XML.TAG);
  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);
  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)
  696.       END;
  697.       IF Settings.TwoCol THEN
  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)
  701.         END
  702.       END
  703.     END
  704.   END DrawImage;
  705.  
  706.   PROCEDURE td(VAR tag: XML.TAG);
  707.   VAR x1, y1, x2, y2, cl: INTEGER;
  708.   BEGIN
  709.     x1 := tag.X + ColLeft.left;
  710.     y1 := tag.Ymin - Ycur + ColLeft.top;
  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
  717.       x1 := x1 - ColLeft.left + ColRight.left;
  718.       x2 := x2 - ColLeft.left + ColRight.left;
  719.       y1 := y1 - ColLeft.height;
  720.       y2 := y2 - ColLeft.height;
  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:
  771.         Image(tag, FALSE);
  772.         DrawImage(tag);
  773.         LibImg.Destroy(tag.img)
  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;
  813.       DrawText(ColLeft, 0, ColLeft.height - LineH, y0, y, FALSE, text);
  814.       IF Settings.TwoCol THEN
  815.         DrawText(ColRight, ColLeft.height, ColLeft.height + ColRight.height - LineH, y0, y, TRUE, text)
  816.       END
  817.     END;
  818.     cur := cur.next
  819.   END
  820. END layout2;
  821.  
  822.  
  823. PROCEDURE DrawFrame (color: INTEGER);
  824. VAR max_X, max_Y: INTEGER;
  825. BEGIN
  826.   max_X := G.Buffer.Width - 1;
  827.   max_Y := G.Buffer.Height - 1;
  828.   G.SetColor(color);
  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)
  836. END DrawFrame;
  837.  
  838.  
  839. PROCEDURE Draw*;
  840. (*VAR max_Y: INTEGER;*)
  841. BEGIN
  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;
  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;
  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);
  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.  
  931. PROCEDURE Scroll* (n: INTEGER);
  932. BEGIN
  933.         INC(Ycur, LineH*n);
  934.         SU.MinMax(Ycur, Ymin, Ymax)
  935. END Scroll;
  936.  
  937.  
  938. PROCEDURE PageUp*;
  939. BEGIN
  940.     Scroll(-Lines * (ORD(Settings.TwoCol) + 1))
  941. END PageUp;
  942.  
  943.  
  944. PROCEDURE PageDown*;
  945. BEGIN
  946.     Scroll(Lines * (ORD(Settings.TwoCol) + 1))
  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.  
  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.  
  1097. PROCEDURE MouseDown;
  1098. BEGIN
  1099.         IF ~mouseDown THEN
  1100.                 mouseDown := TRUE;
  1101.                 clickRef := ref;
  1102.                 IF ref # NIL THEN
  1103.                         ref.Clicked := TRUE
  1104.                 END;
  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
  1141.                                 setVisited(clickRef);
  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
  1148.                                 setVisited(clickRef);
  1149.                                 clickRef.Visited := TRUE;
  1150.                                 PushRef(clickRef)
  1151.                         END
  1152.                 END
  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
  1183.       layout3(body, X - ColLeft.width - Settings.PADDING.ColInter, Y + Lines * LineH);
  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;
  1324.         max := min + ColLeft.height - LineH;
  1325.         IF Settings.TwoCol THEN
  1326.             INC(max, ColRight.height)
  1327.         END;
  1328.  
  1329.         IF (y < min) OR (y > max) THEN
  1330.             Ycur := MAX(y - ColLeft.height DIV 2, 0)
  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;
  1370.   Window.initRect(
  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
  1375.     ColLeft.width := (ColLeft.width - Settings.PADDING.ColInter) DIV 2;
  1376.     ColRight := ColLeft;
  1377.     ColRight.left := ColLeft.left + ColLeft.width + Settings.PADDING.ColInter
  1378.   END;
  1379.   W := ColLeft.width;
  1380.   Lines := ColLeft.height DIV LineH;
  1381.   ColLeft.height  := Lines * LineH;
  1382.   ColRight.height := ColLeft.height;
  1383. END FontSizeChange;
  1384.  
  1385.  
  1386. PROCEDURE Resize*(Width, Height: INTEGER);
  1387. VAR d: REAL; resize: BOOLEAN; sizeX, sizeY, data, back_picture: INTEGER;
  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
  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
  1422.   ELSE
  1423.     G.DestroyBackImg;
  1424.     G.Fill(G.Buffer, Settings.Colors[BACK_COLOR]);
  1425.     //G.Fill(G.BackImg, Settings.Colors[BACK_COLOR])
  1426.   END;
  1427.  
  1428.   IF Font.FontH() # 0 THEN
  1429.     FontSizeChange(Font.FontH());
  1430.   ELSE
  1431.     FontSizeChange(Settings.FontSize);
  1432.   END;
  1433.  
  1434.   ColLeft.width  := G.Buffer.Width - Settings.PADDING.Left - Settings.PADDING.Right;
  1435.   IF Settings.TwoCol THEN
  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
  1439.   END;
  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;
  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;
  1453.     W := ColLeft.width;
  1454.     W2 := ColLeft.width + ColRight.width + Settings.PADDING.ColInter;
  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
  1550.     IF File.WriteInt(history, x) THEN END
  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);
  1578.   IF File.WriteChar(history, 0X) THEN END;
  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.  
  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. BEGIN
  1796.         clickRef := NIL;
  1797.         hoverRef := NIL;
  1798.         mouseDown := FALSE
  1799. END DOM.
  1800.