Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. (*
  2.     Copyright 2016-2022 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, Write, Read, 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.     SpaceW: INTEGER
  58.  
  59.   END;
  60.  
  61.   StackItem = POINTER TO TStackItem;
  62.  
  63.   TStackItem = RECORD (XML.DESC_ELEMENT)
  64.  
  65.     body : XML.TAG;
  66.     Ycur : INTEGER;
  67.     d    : REAL
  68.  
  69.   END;
  70.  
  71.  
  72. VAR
  73.  
  74.   Settings* : TSettings;
  75.   Canvas_X, Canvas_Y: INTEGER;
  76.   ColLeft   : Window.TRect;
  77.   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 layout(body: XML.ELEMENT);
  432. VAR
  433.   cur       : XML.ELEMENT;
  434.   tag       : XML.TAG;
  435.   word      : XML.WORD;
  436.   text      : XML.TEXT;
  437.   tag_value : INTEGER;
  438.   _align    : INTEGER;
  439.   title     : XML.ELEMENT;
  440.   width     : INTEGER;
  441.   height1   : INTEGER;
  442.   height2   : INTEGER;
  443.  
  444.  
  445.   PROCEDURE Image (VAR tag: XML.TAG);
  446.   VAR
  447.     note  : BOOLEAN;
  448.     img   : XML.TAG;
  449.     URL   : INTEGER;
  450.     chars : S.CHARS;
  451.     sizeY : INTEGER;
  452.     FName : S.STRING;
  453.     path  : S.STRING;
  454.   BEGIN
  455.     IF tag.img # 0 THEN
  456.       LibImg.img_destroy(tag.img)
  457.     END;
  458.     img := XML.GetRef(tag, note, URL);
  459.     IF img # NIL THEN
  460.       IF img.child.first IS XML.WORD THEN
  461.         chars := img.child.first(XML.WORD).value;
  462.         tag.img := LibImg.GetImg(chars.first, chars.last - chars.first + 1, W, sizeY);
  463.         IF tag.img # 0 THEN
  464.           INC(Y, (sizeY DIV LineH) * LineH);
  465.           NewLine;
  466.           tag.Ymax := Y - Y MOD LineH
  467.         END
  468.       END
  469.     ELSIF URL # 0 THEN
  470.       S.PtrToString(URL, FName);
  471.       tag.img := LibImg.LoadFromFile(FName, W, sizeY);
  472.       IF tag.img = 0 THEN
  473.         path := FilePath;
  474.         IF FName[0] # "/" THEN
  475.           S.Append(path, "/")
  476.         END;
  477.         S.Append(path, FName);
  478.         tag.img := LibImg.LoadFromFile(path, W, sizeY);
  479.       END;
  480.       IF tag.img # 0 THEN
  481.         INC(Y, (sizeY DIV LineH) * LineH);
  482.         NewLine;
  483.         tag.Ymax := Y - Y MOD LineH
  484.       END
  485.     END
  486.   END Image;
  487.  
  488.  
  489. BEGIN
  490.   cur := body;
  491.   WHILE cur # NIL DO
  492.     IF cur IS XML.TAG THEN
  493.       tag := cur(XML.TAG);
  494.       tag_value := tag.value;
  495.       CASE tag_value OF
  496.       |XML.tag_p, XML.tag_v:
  497.         Trim;
  498.         IF TextCount > 0 THEN
  499.           NewLine
  500.         END;
  501.         X := Settings.PARAGRAPH + Epigraph()
  502.       |XML.tag_epigraph:
  503.         NewLine;
  504.         INC(epigraph)
  505.       |XML.tag_contents_item:
  506.         INC(ref_depth);
  507.         Settings.EPIGRAPH := Settings.LEVEL * Depth(tag);
  508.         _align := align;
  509.         align := 3
  510.       |XML.tag_title:
  511.         INC(strong);
  512.         Font.Bold(TRUE);
  513.         _align := align;
  514.         align := 1;
  515.         IF MainBody THEN
  516.           tag.value := XML.tag_contents_item;
  517.           title := XML.Copy(tag);
  518.           XML.AddChild(contents, title);
  519.           title.parent := tag.parent;
  520.           tag.value := XML.tag_title
  521.         END
  522.       |XML.tag_subtitle:
  523.         NewLine;
  524.         _align := align;
  525.         align := 1
  526.       |XML.tag_text_author, XML.tag_date:
  527.         _align := align;
  528.         align := 2
  529.       |XML.tag_section, XML.tag_body, XML.tag_empty_line, XML.tag_poem, XML.tag_stanza, XML.tag_annotation, XML.tag_cite:
  530.         NewLine
  531.       |XML.tag_a:
  532.         INC(ref_depth);
  533.         IF XML.IsNote(tag) THEN
  534.           Sup(TRUE)
  535.         END
  536.       |XML.tag_sup:
  537.         Sup(TRUE)
  538.       |XML.tag_sub:
  539.         Sub(TRUE)
  540.       |XML.tag_code:
  541.         Font.sysfont(TRUE);
  542.         INC(code)
  543.       |XML.tag_image:
  544.         tag.X := 0;
  545.         NewLine;
  546.         NewLine
  547.       |XML.tag_coverpage:
  548.         cover := tag
  549.  
  550.       |XML.tag_table:
  551.         NewLine;
  552.         tables.destroy(tag.table);
  553.         NEW(tag.table);
  554.         table(tag.table, tag, TRUE)
  555.       |XML.tag_td, XML.tag_th:
  556.         IF tag_value = XML.tag_th THEN
  557.           INC(strong);
  558.           Font.Bold(TRUE);
  559.         END;
  560.         SU.ErrorIf(tag.parent(XML.TAG).value # XML.tag_tr, 21);
  561.         NewLine; DEC(Y, LineH);
  562.         tag.Width := tables.get_width(tag.table, tag.cell);
  563.         tag.X := tables.get_x(tag.table, tag.cell);
  564.         width := W;
  565.         W := tag.Width - 2 * CellPadding;
  566.         IF W <= 0 THEN
  567.           W := 1
  568.         END
  569.       |XML.tag_tr:
  570.         SU.ErrorIf(tag.parent(XML.TAG).value # XML.tag_table, 20)
  571.  
  572.       |XML.tag_strong:
  573.         INC(strong);
  574.         Font.Bold(TRUE)
  575.       ELSE
  576.       END;
  577.  
  578.       tag.Ymin := Y - Y MOD LineH;
  579.       layout(tag.child.first);
  580.       tag.Ymax := Y - Y MOD LineH;
  581.  
  582.       CASE tag_value OF
  583.       |XML.tag_epigraph:
  584.         NewLine;
  585.         DEC(epigraph)
  586.       |XML.tag_subtitle:
  587.         NewLine;
  588.         NewLine;
  589.         align := _align
  590.       |XML.tag_title, XML.tag_text_author, XML.tag_date:
  591.         DEC(strong);
  592.         Font.Bold(strong > 0);
  593.         NewLine;
  594.         align := _align
  595.       |XML.tag_contents_item:
  596.         DEC(ref_depth);
  597.         align := _align;
  598.       |XML.tag_section, XML.tag_poem, XML.tag_v, XML.tag_p, XML.tag_annotation, XML.tag_cite:
  599.         NewLine
  600.       |XML.tag_a:
  601.         DEC(ref_depth);
  602.         IF XML.IsNote(tag) THEN
  603.           Sup(FALSE)
  604.         END
  605.       |XML.tag_sup:
  606.         Sup(FALSE)
  607.       |XML.tag_sub:
  608.         Sub(FALSE)
  609.       |XML.tag_code:
  610.         DEC(code);
  611.         Font.sysfont(code > 0)
  612.       |XML.tag_image:
  613.         Image(tag)
  614.  
  615.       |XML.tag_table:
  616.         Y := tag.Ymin + tables.get_table_height(tag.table);
  617.         tag.Ymax := Y - Y MOD LineH;
  618.         NewLine;
  619.       |XML.tag_td, XML.tag_th:
  620.         IF tag_value = XML.tag_th THEN
  621.           DEC(strong);
  622.           Font.Bold(strong > 0)
  623.         END;
  624.         W := width;
  625.         NewLine;
  626.         Y := tag.Ymin + Settings.SUP; //!!!
  627.         height1 := tables.get_height(tag.table, tag.cell);
  628.         height2 := tag.Ymax - tag.Ymin + LineH;
  629.         IF height2 > height1 THEN
  630.           tables.set_height(tag.table, tag.cell, height2)
  631.         END;
  632.         INC(tag.Ymin, tables.get_y(tag.table, tag.cell));
  633.         INC(tag.Ymax, tables.get_height(tag.table, tag.cell));
  634.         shift(tag, tag.X + CellPadding, tables.get_y(tag.table, tag.cell));
  635.  
  636.       |XML.tag_strong:
  637.         DEC(strong);
  638.         Font.Bold(strong > 0)
  639.       ELSE
  640.       END
  641.     ELSIF cur IS XML.WORD THEN
  642.       word := cur(XML.WORD);
  643.       word.length := S.Utf8Length(word.value);
  644.       word.width := Font.TextWidth(word.value, word.length);
  645.       IF W - X < word.width THEN
  646.         Align;
  647.         NewLine
  648.       END;
  649.       IF W < word.width THEN
  650.         Split(word)
  651.       END
  652.     ELSIF cur IS XML.SPACE THEN
  653.       IF W - X < SpaceWidth() THEN
  654.           cur(XML.SPACE).width := 0
  655.       ELSE
  656.           cur(XML.SPACE).width := SpaceWidth()
  657.       END
  658.     END;
  659.     IF cur IS XML.TEXT THEN
  660.       IF ref_depth > 0 THEN
  661.         V.push(references, cur)
  662.       END;
  663.       text := cur(XML.TEXT);
  664.       text.X := X;
  665.       text.Y := Y;
  666.       INC(X, text.width);
  667.       AddToLine(text)
  668.     END;
  669.     cur := cur.next
  670.   END
  671. END layout;
  672.  
  673.  
  674. PROCEDURE layout2(body: XML.ELEMENT);
  675. VAR
  676.   color : INTEGER;
  677.   cur   : XML.ELEMENT;
  678.   text  : XML.TEXT;
  679.   tag   : XML.TAG;
  680.   y, y0 : INTEGER;
  681.   value : INTEGER;
  682.  
  683.   PROCEDURE DrawText(Col: Window.TRect; min, max, y0, y: INTEGER; right: BOOLEAN; VAR text: XML.TEXT);
  684.   VAR word: XML.WORD;
  685.   BEGIN
  686.     IF (min <= y0) & (y0 <= max) THEN
  687.       Font.sysfont(code > 0);
  688.       IF text IS XML.WORD THEN
  689.         word := text(XML.WORD);
  690.         Font.Text(Col, word.X, y - Col.Height * ORD(right), word.value.first, word.length);
  691.       END;
  692.       Font.StrikeText(Col, text.X, y - Col.Height * ORD(right), text.width)
  693.     END
  694.   END DrawText;
  695.  
  696.   PROCEDURE Image(VAR tag: XML.TAG);
  697.   VAR sizeX, sizeY, img, y: INTEGER;
  698.   BEGIN
  699.     IF tag.img # 0 THEN
  700.       y := Ycur;
  701.       LibImg.GetInf(tag.img, sizeX, sizeY, img);
  702.       IF (y <= tag.Ymax) & (tag.Ymin <= y + ColLeft.Height) THEN
  703.         G.Image(ColLeft.Left + tag.X, tag.Ymin - y + ColLeft.Top, sizeX, sizeY, img, ColLeft.Top, ColLeft.Top + ColLeft.Height - 1)
  704.       END;
  705.       IF Settings.TwoCol THEN
  706.         y := Ycur + ColLeft.Height;
  707.         IF (y <= tag.Ymax) & (tag.Ymin <= y + ColRight.Height) THEN
  708.           G.Image(ColRight.Left + tag.X, tag.Ymin - y + ColLeft.Top, sizeX, sizeY, img, ColRight.Top, ColRight.Top + ColRight.Height - 1)
  709.         END
  710.       END
  711.     END
  712.   END Image;
  713.  
  714.   PROCEDURE td(VAR tag: XML.TAG);
  715.   VAR x1, y1, x2, y2, cl: INTEGER;
  716.   BEGIN
  717.     x1 := tag.X + ColLeft.Left;
  718.     y1 := tag.Ymin - Ycur + ColLeft.Top;
  719.     x2 := x1 + tag.Width;
  720.     y2 := y1 + tables.get_height(tag.table, tag.cell);
  721.     cl := G.GetColor();
  722.     G.SetColor(Settings.Colors[TEXT_COLOR]);
  723.     G.Rect(x1, y1, x2, y2);
  724.     IF Settings.TwoCol THEN
  725.       x1 := x1 - ColLeft.Left + ColRight.Left;
  726.       x2 := x2 - ColLeft.Left + ColRight.Left;
  727.       y1 := y1 - ColLeft.Height;
  728.       y2 := y2 - ColLeft.Height;
  729.       G.Rect(x1, y1, x2, y2)
  730.     END;
  731.     G.SetColor(cl)
  732.   END td;
  733.  
  734. BEGIN
  735.   cur := body;
  736.   WHILE cur # NIL DO
  737.     IF cur IS XML.TAG THEN
  738.       tag := cur(XML.TAG);
  739.       IF (tag.value = XML.tag_td) OR (tag.value = XML.tag_th) THEN
  740.          tag.Ymax := tag.Ymin + tables.get_height(tag.table, tag.cell)
  741.       END;
  742.  
  743.       IF (tag.Ymin < Ycur + LineH * Lines * (ORD(Settings.TwoCol) + 1)) & (tag.Ymax >= Ycur) OR (tag.value = XML.tag_tr) THEN
  744.  
  745.       value := tag.value;
  746.       CASE value OF
  747.       |XML.tag_a:
  748.         INC(refer);
  749.         color := Font.Font.color;
  750.         IF tag.Clicked THEN
  751.           Font.SetFontColor(Settings.Colors[CLICKED_COLOR])
  752.         ELSE
  753.           IF tag.Visited THEN
  754.             Font.SetFontColor(Settings.Colors[VISITED_COLOR])
  755.           ELSE
  756.             Font.SetFontColor(Settings.Colors[LINK_COLOR])
  757.           END
  758.         END
  759.       |XML.tag_contents_item:
  760.                 IF tag.Clicked THEN
  761.                         INC(refer);
  762.                         color := Font.Font.color;
  763.                         Font.SetFontColor(Settings.Colors[CLICKED_COLOR])
  764.                 ELSIF tag.Visited THEN
  765.                         INC(refer);
  766.                         color := Font.Font.color;
  767.                         Font.SetFontColor(Settings.Colors[VISITED_COLOR])
  768.                 END
  769.       |XML.tag_title, XML.tag_strong, XML.tag_th:
  770.         INC(strong);
  771.         Font.Bold(TRUE)
  772.       |XML.tag_strikethrough:
  773.         INC(strike);
  774.         Font.Strike(TRUE)
  775.       |XML.tag_epigraph, XML.tag_cite, XML.tag_emphasis:
  776.         INC(italic);
  777.         Font.Italic(TRUE, refer = 0)
  778.       |XML.tag_image:
  779.         Image(tag)
  780.       |XML.tag_code:
  781.         INC(code)
  782.       ELSE
  783.       END;
  784.       layout2(tag.child.first);
  785.       CASE value OF
  786.       |XML.tag_a:
  787.         DEC(refer);
  788.         Font.SetFontColor(color)
  789.       |XML.tag_contents_item:
  790.                 IF tag.Clicked OR tag.Visited THEN
  791.                         DEC(refer);
  792.                         Font.SetFontColor(color)
  793.                 END
  794.       |XML.tag_title, XML.tag_strong:
  795.         DEC(strong);
  796.         Font.Bold(strong > 0)
  797.       |XML.tag_strikethrough:
  798.         DEC(strike);
  799.         Font.Strike(strike > 0)
  800.       |XML.tag_epigraph, XML.tag_cite, XML.tag_emphasis:
  801.         DEC(italic);
  802.         Font.Italic(italic > 0, refer = 0)
  803.       |XML.tag_td:
  804.         td(tag)
  805.       |XML.tag_th:
  806.         DEC(strong);
  807.         Font.Bold(strong > 0);
  808.         td(tag)
  809.       |XML.tag_code:
  810.         DEC(code)
  811.       ELSE
  812.       END
  813.  
  814.       END
  815.     ELSIF cur IS XML.TEXT THEN
  816.       text := cur(XML.TEXT);
  817.       y  := text.Y - Ycur;
  818.       y0 := y - y MOD LineH;
  819.       DrawText(ColLeft, 0, ColLeft.Height - LineH, y0, y, FALSE, text);
  820.       IF Settings.TwoCol THEN
  821.         DrawText(ColRight, ColLeft.Height, ColLeft.Height + ColRight.Height - LineH, y0, y, TRUE, text)
  822.       END
  823.     END;
  824.     cur := cur.next
  825.   END
  826. END layout2;
  827.  
  828.  
  829. PROCEDURE DrawProgress(progress_color: INTEGER);
  830. VAR max_X, max_Y: INTEGER;
  831. BEGIN
  832.   max_X := G.Buffer.Width - 1;
  833.   max_Y := G.Buffer.Height - 1;
  834.   G.SetColor(0);
  835.   G.HLine(0, max_X, 0);
  836.   G.HLine(0, max_X, max_Y);
  837.   G.VLine(0, 0, max_Y);
  838.   sb.max_area := (Ymax - Ymin) DIV LineH + 50;
  839.   sb.cur_area := 50;
  840.   sb.position := (Ycur - Ymin) DIV LineH;
  841.   box_lib.scrollbar_v_draw(sb)
  842. END DrawProgress;
  843.  
  844.  
  845. PROCEDURE Draw*;
  846. VAR back, max_X, max_Y: INTEGER;
  847. BEGIN
  848.   back  := Settings.Colors[BACK_COLOR];
  849.   max_X := G.Buffer.Width - 1;
  850.   max_Y := G.Buffer.Height - 1;
  851.   G.Copy(G.Buffer3, G.Buffer, 0, G.Buffer.Height, 0);
  852.   Font.SetFontColor(Settings.Colors[TEXT_COLOR]);
  853.   IF ((body = description) OR (body = contents)) & Settings.TwoCol THEN
  854.     Settings.TwoCol := FALSE;
  855.     layout2(body.child.first);
  856.     Settings.TwoCol := TRUE;
  857.     Search.draw(body, ColLeft, ColRight, Ycur, LineH, FALSE)
  858.   ELSE
  859.     layout2(body.child.first);
  860.     Search.draw(body, ColLeft, ColRight, Ycur, LineH, Settings.TwoCol)
  861.   END;
  862.   G.Copy(G.Buffer3, G.Buffer, 0, ColLeft.Top + 1, 0);
  863.   G.Copy(G.Buffer3, G.Buffer, max_Y - ColLeft.Top, ColLeft.Top + 1, max_Y - ColLeft.Top);
  864.   DrawProgress(0);
  865.   G.Draw(Canvas_X, Canvas_Y);
  866.   DrawToolbar;
  867.   DrawStatus
  868. END Draw;
  869.  
  870.  
  871. PROCEDURE BackEnabled* (): BOOLEAN;
  872.         RETURN b_stk.first # NIL
  873. END BackEnabled;
  874.  
  875.  
  876. PROCEDURE FrwEnabled* (): BOOLEAN;
  877.         RETURN f_stk.first # NIL
  878. END FrwEnabled;
  879.  
  880.  
  881. PROCEDURE ContentsEnabled* (): BOOLEAN;
  882.         RETURN (contents # NIL) (*& (body # contents)*)
  883. END ContentsEnabled;
  884.  
  885.  
  886. PROCEDURE DescrEnabled* (): BOOLEAN;
  887.         RETURN (description # NIL) (*& (body # description)*)
  888. END DescrEnabled;
  889.  
  890.  
  891. PROCEDURE Back*;
  892. BEGIN
  893.   IF b_stk.first # NIL THEN
  894.     Push(f_stk);
  895.     Pop(b_stk)
  896.   END
  897. END Back;
  898.  
  899.  
  900. PROCEDURE Forward*;
  901. BEGIN
  902.   IF f_stk.first # NIL THEN
  903.     Push(b_stk);
  904.     Pop(f_stk)
  905.   END
  906. END Forward;
  907.  
  908.  
  909. PROCEDURE Contents*;
  910. BEGIN
  911.   IF (contents # NIL) & (body # contents) THEN
  912.     Push(b_stk);
  913.     Clear(f_stk);
  914.     body := contents;
  915.     Ycur := Ycont;
  916.     Ymin := 0;
  917.     Ymax := body.Ymax
  918.   END
  919. END Contents;
  920.  
  921.  
  922. PROCEDURE Descr*;
  923. BEGIN
  924.   IF (description # NIL) & (body # description) THEN
  925.     Push(b_stk);
  926.     Clear(f_stk);
  927.     body := description;
  928.     Ycur := 0;
  929.     Ymin := 0;
  930.     Ymax := body.Ymax
  931.   END
  932. END Descr;
  933.  
  934.  
  935. PROCEDURE Up*;
  936. BEGIN
  937.   DEC(Ycur, LineH);
  938.   SU.MinMax(Ycur, Ymin, Ymax)
  939. END Up;
  940.  
  941.  
  942. PROCEDURE Down*;
  943. BEGIN
  944.   INC(Ycur, LineH);
  945.   SU.MinMax(Ycur, Ymin, Ymax)
  946. END Down;
  947.  
  948.  
  949. PROCEDURE PageUp*;
  950. VAR i: INTEGER;
  951. BEGIN
  952.   FOR i := 1 TO Lines * (ORD(Settings.TwoCol) + 1) DO
  953.     Up
  954.   END
  955. END PageUp;
  956.  
  957.  
  958. PROCEDURE PageDown*;
  959. VAR i: INTEGER;
  960. BEGIN
  961.   FOR i := 1 TO Lines * (ORD(Settings.TwoCol) + 1) DO
  962.     Down
  963.   END
  964. END PageDown;
  965.  
  966.  
  967. PROCEDURE Home*;
  968. BEGIN
  969.   IF Ycur # Ymin THEN
  970.     Push(b_stk);
  971.     Clear(f_stk);
  972.     Ycur := Ymin
  973.   END
  974. END Home;
  975.  
  976.  
  977. PROCEDURE End*;
  978. BEGIN
  979.   IF Ycur # Ymax THEN
  980.     Push(b_stk);
  981.     Clear(f_stk);
  982.     Ycur := Ymax
  983.   END
  984. END End;
  985.  
  986.  
  987. PROCEDURE ScrollBar*;
  988. BEGIN
  989.   Ycur := sb.position * LineH + Ymin
  990. END ScrollBar;
  991.  
  992.  
  993. PROCEDURE GetBody(tag: XML.TAG): XML.TAG;
  994. BEGIN
  995.   WHILE (tag # NIL) & (tag.value # XML.tag_body) DO
  996.     tag := tag.parent(XML.TAG)
  997.   END
  998.   RETURN tag
  999. END GetBody;
  1000.  
  1001.  
  1002. PROCEDURE layout3(Body: XML.ELEMENT; X, Y: INTEGER);
  1003. VAR
  1004.   ptr   : V.ANYPTR;
  1005.   text  : XML.TEXT;
  1006.   sect  : XML.TAG;
  1007.   y     : INTEGER;
  1008.   i     : INTEGER;
  1009. BEGIN
  1010.   i := 0;
  1011.   WHILE i < references.count DO
  1012.     ptr := V.get(references, i);
  1013.     text := ptr(XML.TEXT);
  1014.     y  := text.Y - Ycur;
  1015.     IF (y <= Y) & (Y <= y + Font.FontH()) & (text.X <= X) & (X <= text.X + text.width) THEN
  1016.       sect := text.parent(XML.TAG);
  1017.       IF Body = contents THEN
  1018.         WHILE (sect # NIL) & (sect.value # XML.tag_contents_item) DO
  1019.           sect := sect.parent(XML.TAG)
  1020.         END
  1021.       ELSE
  1022.         WHILE (sect # NIL) & (sect # Body) DO
  1023.           IF sect.value = XML.tag_contents_item THEN
  1024.             sect := NIL
  1025.           ELSE
  1026.             sect := sect.parent(XML.TAG)
  1027.           END
  1028.         END
  1029.       END;
  1030.  
  1031.       IF sect # NIL THEN
  1032.         sect := text.parent(XML.TAG);
  1033.         WHILE sect # NIL DO
  1034.           IF (sect.value = XML.tag_contents_item) & (Body = contents) OR (sect.value = XML.tag_a) THEN
  1035.             ref := sect;
  1036.             sect := NIL;
  1037.             i := references.count
  1038.           ELSE
  1039.             sect := sect.parent(XML.TAG)
  1040.           END
  1041.         END
  1042.       END
  1043.     END;
  1044.     INC(i)
  1045.   END
  1046. END layout3;
  1047.  
  1048.  
  1049. PROCEDURE MouseDown;
  1050. BEGIN
  1051.         IF ~mouseDown THEN
  1052.                 mouseDown := TRUE;
  1053.                 clickRef := ref;
  1054.                 ref.Clicked := TRUE;
  1055.                 Draw
  1056.         END
  1057. END MouseDown;
  1058.  
  1059.  
  1060. PROCEDURE MouseUp;
  1061. VAR
  1062.         note : BOOLEAN;
  1063.         URL  : INTEGER;
  1064.         redraw: BOOLEAN;
  1065. BEGIN
  1066.         redraw := FALSE;
  1067.         mouseDown := FALSE;
  1068.         IF (ref # NIL) & (clickRef = ref) & ref.Clicked THEN
  1069.                 redraw := TRUE;
  1070.         ref.Clicked := FALSE;
  1071.                 note := FALSE;
  1072.                 URL := 0;
  1073.                 IF ref.value = XML.tag_a THEN
  1074.                         ref := XML.GetRef(ref, note, URL)
  1075.                 ELSE
  1076.                         ref := ref.parent(XML.TAG)
  1077.                 END;
  1078.                 IF ref # NIL THEN
  1079.                         Push(b_stk);
  1080.                         Clear(f_stk);
  1081.                         Ycur := ref.Ymin;
  1082.                         IF note THEN
  1083.                                 body := ref
  1084.                         ELSE
  1085.                                 body := GetBody(ref)
  1086.                         END;
  1087.                         Ymax := body.Ymax;
  1088.                         Ymin := body.Ymin;
  1089.  
  1090.                         IF ~clickRef.Visited THEN
  1091.                                 clickRef.Visited := TRUE;
  1092.                                 PushRef(clickRef)
  1093.                         END
  1094.                 ELSIF URL # 0 THEN
  1095.                         SU.Run(Ini.Browser, URL);
  1096.                         IF ~clickRef.Visited THEN
  1097.                                 clickRef.Visited := TRUE;
  1098.                                 PushRef(clickRef)
  1099.                         END
  1100.                 END;
  1101.  
  1102.         END;
  1103.         IF clickRef # NIL THEN
  1104.                 clickRef.Clicked := FALSE;
  1105.                 clickRef := NIL;
  1106.                 redraw := TRUE
  1107.         END;
  1108.         IF hoverRef # NIL THEN
  1109.                 hoverRef.Clicked := FALSE;
  1110.                 hoverRef := NIL;
  1111.                 redraw := TRUE
  1112.         END;
  1113.         IF redraw THEN
  1114.                 Draw
  1115.         END
  1116. END MouseUp;
  1117.  
  1118.  
  1119. PROCEDURE Click*(X, Y: INTEGER; clicked: BOOLEAN);
  1120. VAR
  1121.   note : BOOLEAN;
  1122.   URL  : INTEGER;
  1123.   urlchars: S.CHARS;
  1124.   urlstr1 : S.STRING;
  1125. BEGIN
  1126.   DEC(Y, Settings.PADDING.Top);
  1127.   DEC(X, Settings.PADDING.Left);
  1128.   IF (0 <= Y) & (Y <= Lines * LineH) THEN
  1129.     ref := NIL;
  1130.     layout3(body, X, Y);
  1131.     IF (ref = NIL) & Settings.TwoCol THEN
  1132.       layout3(body, X - ColLeft.Width - Settings.PADDING.ColInter, Y + Lines * LineH);
  1133.     END;
  1134.     hoverRef := ref;
  1135.         IF clicked THEN
  1136.                 MouseDown
  1137.         ELSE
  1138.                 MouseUp
  1139.         END;
  1140.     IF ref # NIL THEN
  1141.       SU.SetCursor(cursor);
  1142.       note := FALSE;
  1143.       URL := 0;
  1144.       IF ref.value = XML.tag_a THEN
  1145.         ref := XML.GetRef(ref, note, URL)
  1146.       END;
  1147.       IF URL # 0 THEN
  1148.         S.PtrToString(URL, urlstr1);
  1149.         S.StrToChars(urlstr1, urlchars)
  1150.       END
  1151.     ELSE
  1152.       SU.SetCursor(0);
  1153.       urlstr1 := ""
  1154.     END;
  1155.     IF urlstr1 # urlstr THEN
  1156.       urlstr := urlstr1;
  1157.       DrawStatus
  1158.     END
  1159.   ELSE
  1160.       SU.SetCursor(0);
  1161.       urlstr := "";
  1162.       ref := NIL;
  1163.       DrawStatus
  1164.   END
  1165. END Click;
  1166.  
  1167.  
  1168. PROCEDURE Scroll*(value: INTEGER);
  1169. BEGIN
  1170.   value := 2 * value;
  1171.   WHILE value > 0 DO
  1172.     Down;
  1173.     DEC(value)
  1174.   ELSIF value < 0 DO
  1175.     Up;
  1176.     INC(value)
  1177.   END
  1178. END Scroll;
  1179.  
  1180.  
  1181. PROCEDURE main(fb: XML.ELEMENT; Contents: BOOLEAN);
  1182. VAR
  1183.   cur: XML.ELEMENT;
  1184.   tag: XML.TAG;
  1185.   par, epi: INTEGER;
  1186.  
  1187.  
  1188.   PROCEDURE lout(body: XML.ELEMENT);
  1189.   BEGIN
  1190.     TextCount := 0;
  1191.     X := 0;
  1192.     Y := Settings.SUP;
  1193.     layout(body(XML.TAG).child.first);
  1194.     body(XML.TAG).Ymax := Y - Settings.SUP
  1195.   END lout;
  1196.  
  1197.   PROCEDURE lout_one_col(body: XML.ELEMENT);
  1198.   BEGIN
  1199.     IF body # NIL THEN
  1200.       IF Settings.TwoCol THEN
  1201.         W := W2;
  1202.         Settings.TwoCol := FALSE;
  1203.         lout(body);
  1204.         Settings.TwoCol := TRUE;
  1205.         W := W1
  1206.       ELSE
  1207.         lout(body)
  1208.       END
  1209.     END
  1210.   END lout_one_col;
  1211.  
  1212. BEGIN
  1213.   TextCount := 0;
  1214.   sup := 0;
  1215.   sub := 0;
  1216.   epigraph := 0;
  1217.   align := 0;
  1218.   code := 0;
  1219.   strong := 0;
  1220.   italic := 0;
  1221.   strike := 0;
  1222.   refer := 0;
  1223.   SU.ErrorIf(fb = NIL, 11);
  1224.   MainBody := FALSE;
  1225.   description := NIL;
  1226.   mainbody := NIL;
  1227.   cover := NIL;
  1228.   cur := fb;
  1229.   cur := cur(XML.TAG).child.first;
  1230.   WHILE (cur # NIL) & (mainbody = NIL) DO
  1231.     IF cur IS XML.TAG THEN
  1232.       tag := cur(XML.TAG);
  1233.       IF tag.value = XML.tag_description THEN
  1234.         description := tag
  1235.       ELSIF tag.value = XML.tag_body THEN
  1236.         mainbody := tag
  1237.       END
  1238.     END;
  1239.     cur := cur.next
  1240.   END;
  1241.   SU.ErrorIf(mainbody = NIL, 12);
  1242.  
  1243.   WHILE cur # NIL DO
  1244.     IF (cur IS XML.TAG) & (cur(XML.TAG).value = XML.tag_body) THEN
  1245.       lout(cur)
  1246.     END;
  1247.     cur := cur.next
  1248.   END;
  1249.  
  1250.   IF Contents THEN
  1251.     contents := XML.CreateTag();
  1252.     MainBody := TRUE;
  1253.   END;
  1254.   lout(mainbody);
  1255.   IF Contents & (contents.child.first = NIL) THEN
  1256.     DISPOSE(contents)
  1257.   END;
  1258.   MainBody := FALSE;
  1259.   epigraph := 1;
  1260.   par := Settings.PARAGRAPH;
  1261.   epi := Settings.EPIGRAPH;
  1262.   Settings.PARAGRAPH := 0;
  1263.   Settings.EPIGRAPH := 0;
  1264.   lout_one_col(contents);
  1265.   Settings.EPIGRAPH := epi;
  1266.   Settings.PARAGRAPH := par;
  1267.   epigraph := 0;
  1268.   lout_one_col(description);
  1269.   body := mainbody;
  1270.   Ymax := body.Ymax;
  1271.   Ycur := 0;
  1272.   Ymin := 0;
  1273.   Ycont := 0
  1274. END main;
  1275.  
  1276.  
  1277. PROCEDURE Find* (d: INTEGER);
  1278. VAR
  1279.     y, min, max: INTEGER;
  1280.  
  1281. BEGIN
  1282.     Search.fnext(body, y, d);
  1283.     IF y >= 0 THEN
  1284.         DEC(y, y MOD LineH);
  1285.         min := Ycur;
  1286.         IF Settings.TwoCol THEN
  1287.             max := min + ColLeft.Height + ColRight.Height - LineH
  1288.         ELSE
  1289.             max := min + ColLeft.Height - LineH
  1290.         END;
  1291.  
  1292.         IF (y < min) OR (y > max) THEN
  1293.             Ycur := MAX(y - ColLeft.Height DIV 2, 0)
  1294.         END;
  1295.  
  1296.         DEC(Ycur, Ycur MOD LineH)
  1297.     END
  1298. END Find;
  1299.  
  1300.  
  1301. PROCEDURE OpenSearch*;
  1302. BEGIN
  1303.     Search.open(Find)
  1304. END OpenSearch;
  1305.  
  1306.  
  1307. PROCEDURE CloseSearch*;
  1308. BEGIN
  1309.     Search.close
  1310. END CloseSearch;
  1311.  
  1312.  
  1313. PROCEDURE found* (): BOOLEAN;
  1314.     RETURN Search.found(body)
  1315. END found;
  1316.  
  1317.  
  1318. PROCEDURE FontSizeChange(fs: INTEGER);
  1319. BEGIN
  1320.   Settings.SUP               :=  fs DIV 4;
  1321.   Settings.SUB               :=  fs DIV 4;
  1322.   Settings.SpaceW            :=  fs DIV 2;
  1323.   Settings.LEVEL             :=  Settings.PARAGRAPH;
  1324.   Settings.PADDING.Bottom    :=  Settings.PADDING.Top;
  1325.   Settings.PADDING.Left      :=  G.Buffer.Width * Settings.PADDING.LRpc DIV 100;
  1326.   IF Settings.PADDING.Left = 0 THEN
  1327.     Settings.PADDING.Left := 1
  1328.   END;
  1329.   Settings.PADDING.Right     :=  Settings.PADDING.Left;
  1330.   Settings.PADDING.ColInter  :=  G.Buffer.Width * Settings.PADDING.CInt DIV 100;
  1331.  
  1332.   LineH := Font.FontH() + Settings.SUP + Settings.SUB + Settings.InterLin;
  1333.   Window.InitRect(
  1334.     ColLeft, Settings.PADDING.Left, Settings.PADDING.Top,
  1335.     G.Buffer.Width - Settings.PADDING.Left - Settings.PADDING.Right,
  1336.     G.Buffer.Height - Settings.PADDING.Top - Settings.PADDING.Bottom);
  1337.   IF Settings.TwoCol THEN
  1338.     ColLeft.Width := (ColLeft.Width - Settings.PADDING.ColInter) DIV 2;
  1339.     ColRight := ColLeft;
  1340.     ColRight.Left := ColLeft.Left + ColLeft.Width + Settings.PADDING.ColInter
  1341.   END;
  1342.   W := ColLeft.Width;
  1343.   Lines := ColLeft.Height DIV LineH;
  1344.   ColLeft.Height  := Lines * LineH;
  1345.   ColRight.Height := ColLeft.Height;
  1346. END FontSizeChange;
  1347.  
  1348.  
  1349. PROCEDURE Resize*(Width, Height: INTEGER);
  1350. VAR d: REAL; resize: BOOLEAN; sizeX, sizeY, data: INTEGER;
  1351.  
  1352.   PROCEDURE stk1(stk: XML.LIST);
  1353.   VAR cur: StackItem;
  1354.   BEGIN
  1355.     cur := stk.first(StackItem);
  1356.     WHILE cur # NIL DO
  1357.       cur.d := FLT(cur.Ycur - cur.body.Ymin) / FLT(cur.body.Ymax - cur.body.Ymin);
  1358.       cur := cur.next(StackItem)
  1359.     END
  1360.   END stk1;
  1361.  
  1362.   PROCEDURE stk2(stk: XML.LIST);
  1363.   VAR cur: StackItem;
  1364.   BEGIN
  1365.     cur := stk.first(StackItem);
  1366.     WHILE cur # NIL DO
  1367.       cur.Ycur := FLOOR(FLT(cur.body.Ymax - cur.body.Ymin) * cur.d) + cur.body.Ymin;
  1368.       cur.Ycur := cur.Ycur - cur.Ycur MOD LineH;
  1369.       SU.MinMax(cur.Ycur, cur.body.Ymin, cur.body.Ymax);
  1370.       cur := cur.next(StackItem)
  1371.     END
  1372.   END stk2;
  1373.  
  1374. BEGIN
  1375.   resize := (Width # G.Buffer.Width) OR resized;
  1376.   G.Resize(Width, Height);
  1377.   G.SetColor(Settings.Colors[BACK_COLOR]);
  1378.   IF (Settings.Picture # 0) & Settings.b_pict THEN
  1379.     LibImg.GetInf(Settings.Picture, sizeX, sizeY, data);
  1380.     G.BackImage(sizeX, sizeY, data);
  1381.   ELSE
  1382.     G.Clear;
  1383.     G.Copy(G.Buffer, G.Buffer3, 0, G.Buffer.Height, 0)
  1384.   END;
  1385.  
  1386.   IF Font.FontH() # 0 THEN
  1387.     FontSizeChange(Font.FontH());
  1388.   ELSE
  1389.     FontSizeChange(Settings.FontSize);
  1390.   END;
  1391.  
  1392.   ColLeft.Width  := G.Buffer.Width - Settings.PADDING.Left - Settings.PADDING.Right;
  1393.   IF Settings.TwoCol THEN
  1394.     ColLeft.Width  := (ColLeft.Width - Settings.PADDING.ColInter) DIV 2;
  1395.     ColRight.Width := ColLeft.Width;
  1396.     ColRight.Left  := ColLeft.Left + ColLeft.Width + Settings.PADDING.ColInter
  1397.   END;
  1398.   ColLeft.Height  := G.Buffer.Height - Settings.PADDING.Top  - Settings.PADDING.Bottom;
  1399.   Lines           := ColLeft.Height DIV LineH;
  1400.   ColLeft.Height  := Lines * LineH;
  1401.   ColRight.Height := ColLeft.Height;
  1402.  
  1403.   IF done & resize THEN
  1404.     resized := FALSE;
  1405.     Push(b_stk);
  1406.     stk1(b_stk);
  1407.     stk1(f_stk);
  1408.     IF contents # NIL THEN
  1409.       d := FLT(Ycont) / FLT(contents.Ymax)
  1410.     END;
  1411.     W := ColLeft.Width;
  1412.     W2 := ColLeft.Width + ColRight.Width + Settings.PADDING.ColInter;
  1413.     W1 := W;
  1414.     main(XML.FB, FALSE);
  1415.     Search.resize;
  1416.     stk2(b_stk);
  1417.     stk2(f_stk);
  1418.     IF contents # NIL THEN
  1419.       Ycont := FLOOR(FLT(contents.Ymax) * d);
  1420.       Ycont := Ycont - Ycont MOD LineH;
  1421.       SU.MinMax(Ycont, 0, contents.Ymax)
  1422.     END;
  1423.     Pop(b_stk);
  1424.   END
  1425. END Resize;
  1426.  
  1427.  
  1428. PROCEDURE SetColors*;
  1429. BEGIN
  1430.   Settings.Colors[BACK_COLOR]     := Ini.GetColor("back",    Settings.Colors[BACK_COLOR]);
  1431.   Settings.Colors[TEXT_COLOR]     := Ini.GetColor("text",    Settings.Colors[TEXT_COLOR]);
  1432.   Settings.Colors[ITALIC_COLOR]   := Ini.GetColor("italic",  Settings.Colors[ITALIC_COLOR]);
  1433.   Settings.Colors[LINK_COLOR]     := Ini.GetColor("link",    Settings.Colors[LINK_COLOR]);
  1434.   Settings.Colors[VISITED_COLOR]  := Ini.GetColor("visited", Settings.Colors[LINK_COLOR]);
  1435. END SetColors;
  1436.  
  1437.  
  1438. PROCEDURE Resized(set1, set2: TSettings): BOOLEAN;
  1439.   RETURN (set1.FontSize # set2.FontSize) OR (set1.TwoCol # set2.TwoCol) OR
  1440.          (set1.PARAGRAPH # set2.PARAGRAPH) OR (set1.EPIGRAPH # set2.EPIGRAPH) OR
  1441.          (set1.PADDING.LRpc # set2.PADDING.LRpc) OR (set1.PADDING.CInt # set2.PADDING.CInt)
  1442.          OR (set1.InterLin # set2.InterLin)
  1443. END Resized;
  1444.  
  1445.  
  1446. PROCEDURE SetSettings*(NewSet: TSettings);
  1447. BEGIN
  1448.   resized := Resized(Settings, NewSet) OR resized;
  1449.   Settings := NewSet;
  1450.   Font.Init(Settings.Colors[ITALIC_COLOR], Settings.Colors[TEXT_COLOR], Settings.FontSize);
  1451.   Resize(G.Buffer.Width, G.Buffer.Height)
  1452. END SetSettings;
  1453.  
  1454.  
  1455. PROCEDURE Init*(Left, Top, Width, Height: INTEGER);
  1456. BEGIN
  1457.   G.Resize(Width, Height);
  1458.   Canvas_X := Left;
  1459.   Canvas_Y := Top
  1460. END Init;
  1461.  
  1462.  
  1463. PROCEDURE Start;
  1464. BEGIN
  1465.   XML.Open(FileName);
  1466.   main(XML.FB, TRUE);
  1467.   done := TRUE;
  1468.   SU.Halt
  1469. END Start;
  1470.  
  1471.  
  1472. PROCEDURE CleanHistory*(fname: S.STRING);
  1473. VAR F: File.FS; pos, pos2, fsize, size, buf, buf2: INTEGER; c: CHAR;
  1474. BEGIN
  1475.   F := File.Open(fname);
  1476.   IF F # NIL THEN
  1477.     fsize := File.Seek(F, 0, 2);
  1478.     pos := File.Seek(F, 0, 0);
  1479.     buf := K.malloc(fsize + 1024);
  1480.     buf2 := K.malloc(fsize + 1024);
  1481.     pos := File.Read(F, buf, fsize);
  1482.     File.Close(F);
  1483.     pos := 0;
  1484.     pos2 := 0;
  1485.     WHILE pos < fsize DO
  1486.       sys.GET(buf + pos, size);
  1487.       sys.GET(buf + pos + 4, c);
  1488.       IF c = 0X THEN
  1489.         sys.MOVE(buf + pos, buf2 + pos2, size);
  1490.         pos2 := pos2 + size
  1491.       END;
  1492.       pos := pos + size
  1493.     END;
  1494.     F := File.Create(fname);
  1495.     pos := File.Write(F, buf2, pos2);
  1496.     File.Close(F);
  1497.     buf := K.free(buf);
  1498.     buf2 := K.free(buf2)
  1499.   END
  1500. END CleanHistory;
  1501.  
  1502.  
  1503. PROCEDURE Save;
  1504. VAR history: File.FS; win_size_x, win_size_y, size, pos: INTEGER;
  1505.  
  1506.   PROCEDURE WriteInt(history: File.FS; x: INTEGER);
  1507.   BEGIN
  1508.     IF Write.Int(history, x) THEN END
  1509.   END WriteInt;
  1510.  
  1511.   PROCEDURE WriteStk(history: File.FS; VAR stk: XML.LIST; links: BOOLEAN);
  1512.   VAR
  1513.       cur: StackItem;
  1514.   BEGIN
  1515.     WriteInt(history, XML.ListCount(stk));
  1516.     cur := stk.first(StackItem);
  1517.     WHILE cur # NIL DO
  1518.       WriteInt(history, cur.body.num);
  1519.       IF ~links THEN
  1520.         WriteInt(history, cur.Ycur)
  1521.       END;
  1522.       cur := cur.next(StackItem)
  1523.     END
  1524.   END WriteStk;
  1525.  
  1526. BEGIN
  1527.   Ini.Save(Settings.Colors, Settings.b_pict);
  1528.   history := File.Open(Ini.History);
  1529.   IF history = NIL THEN
  1530.     history := File.Create(Ini.History)
  1531.   ELSE
  1532.     pos := File.Seek(history, 0 , 2)
  1533.   END;
  1534.   size := 1 + 18*4 + 1 + 8*(XML.ListCount(b_stk) + XML.ListCount(f_stk)) + 4*XML.ListCount(vis_ref) + 12;
  1535.   WriteInt(history, size);
  1536.   IF Write.Char(history, 0X) THEN END;
  1537.   WriteInt(history, fsize2);
  1538.   WriteInt(history, chksum);
  1539.   SU.GetWindowSize(win_size_x, win_size_y);
  1540.   WriteInt(history, win_size_x);
  1541.   WriteInt(history, win_size_y);
  1542.   WriteInt(history, Settings.PADDING.LRpc);
  1543.   WriteInt(history, Settings.PADDING.Top);
  1544.   WriteInt(history, Settings.PADDING.CInt);
  1545.   WriteInt(history, Settings.PARAGRAPH);
  1546.   WriteInt(history, Settings.EPIGRAPH);
  1547.   WriteInt(history, Settings.InterLin);
  1548.  
  1549.   IF Write.Boolean(history, Settings.TwoCol) THEN END;
  1550.  
  1551.   WriteInt(history, Settings.FontSize);
  1552.   WriteInt(history, body.num);
  1553.   WriteInt(history, Ymin);
  1554.   WriteInt(history, Ymax);
  1555.   WriteInt(history, Ycur);
  1556.   WriteInt(history, Ycont);
  1557.  
  1558.   WriteStk(history, b_stk, FALSE);
  1559.   WriteStk(history, f_stk, FALSE);
  1560.   WriteStk(history, vis_ref, TRUE);
  1561.  
  1562.   WriteInt(history, size);
  1563.  
  1564.   File.Close(history);
  1565.   CleanHistory(Ini.History)
  1566. END Save;
  1567.  
  1568.  
  1569. PROCEDURE ReadInt(VAR x: INTEGER);
  1570. BEGIN
  1571.   IF Read.Int(history, x) THEN END
  1572. END ReadInt;
  1573.  
  1574.  
  1575. PROCEDURE Load;
  1576. VAR body_num, ycur, size, pos: INTEGER;
  1577.  
  1578.   PROCEDURE ReadStk(VAR stk: XML.LIST);
  1579.   VAR n, num: INTEGER;
  1580.   BEGIN
  1581.     ReadInt(n);
  1582.     WHILE n > 0 DO
  1583.       ReadInt(num);
  1584.       body := XML.GetTagByNum(num);
  1585.       ReadInt(Ycur);
  1586.       Push(stk);
  1587.       DEC(n)
  1588.     END
  1589.   END ReadStk;
  1590.  
  1591.         PROCEDURE ReadRef;
  1592.         VAR
  1593.                 n, num: INTEGER;
  1594.                 ref: XML.TAG;
  1595.         BEGIN
  1596.                 ReadInt(n);
  1597.                 WHILE n > 0 DO
  1598.                         ReadInt(num);
  1599.                         ref := XML.GetTagByNum(num);
  1600.                         IF ref # NIL THEN
  1601.                                 PushRef(ref);
  1602.                                 ref.Visited := TRUE
  1603.                         END;
  1604.                         DEC(n)
  1605.                 END
  1606.         END ReadRef;
  1607.  
  1608. BEGIN
  1609.   ReadInt(Settings.PADDING.LRpc);
  1610.   ReadInt(Settings.PADDING.Top);
  1611.   ReadInt(Settings.PADDING.CInt);
  1612.   ReadInt(Settings.PARAGRAPH);
  1613.   ReadInt(Settings.EPIGRAPH);
  1614.   ReadInt(Settings.InterLin);
  1615.   IF Read.Boolean(history, Settings.TwoCol) THEN END;
  1616.   ReadInt(Settings.FontSize);
  1617.  
  1618.   SetSettings(Settings);
  1619.  
  1620.   ReadInt(body_num);
  1621.   ReadInt(Ymin);
  1622.   ReadInt(Ymax);
  1623.   ReadInt(ycur);
  1624.   ReadInt(Ycont);
  1625.  
  1626.   ReadStk(b_stk);
  1627.   ReadStk(f_stk);
  1628.   ReadRef;
  1629.  
  1630.   ReadInt(size);
  1631.   pos := File.Seek(history, -size, 1);
  1632.   pos := File.Seek(history, 4, 1);
  1633.   IF Write.Char(history, 1X) THEN END;
  1634.  
  1635.   Ycur := ycur;
  1636.   body := XML.GetTagByNum(body_num);
  1637.   File.Close(history)
  1638. END Load;
  1639.  
  1640.  
  1641. PROCEDURE GetWinSize*(hist_fn: S.STRING; VAR win_size_x, win_size_y: INTEGER);
  1642. VAR c: CHAR; size, pos, x, y, fsize, _chksum: INTEGER; found: BOOLEAN;
  1643. BEGIN
  1644.   fsize2 := RF.FileSize(hist_fn);
  1645.   chksum := RF.ChkSum(hist_fn);
  1646.   found := FALSE;
  1647.   history := File.Open(Ini.History);
  1648.   pos := File.Seek(history, -4, 2);
  1649.   last := FALSE;
  1650.   WHILE pos >= 0 DO
  1651.     IF Read.Int(history, size) THEN
  1652.       pos := File.Seek(history, -size + 4, 1);
  1653.     END;
  1654.     IF Read.Char(history, c) THEN END;
  1655.     ReadInt(fsize);
  1656.     ReadInt(_chksum);
  1657.     IF (c = 0X) & (fsize = fsize2) & (_chksum = chksum) THEN
  1658.       found := TRUE;
  1659.       IF Read.Int(history, x) & Read.Int(history, y) THEN
  1660.         win_size_x := x;
  1661.         win_size_y := y;
  1662.       ELSE
  1663.         found := FALSE
  1664.       END;
  1665.       pos := -1
  1666.     ELSE
  1667.       IF ~last THEN
  1668.         last := TRUE;
  1669.         ReadInt(x);
  1670.         ReadInt(y);
  1671.         ReadInt(Settings.PADDING.LRpc);
  1672.         ReadInt(Settings.PADDING.Top);
  1673.         ReadInt(Settings.PADDING.CInt);
  1674.         ReadInt(Settings.PARAGRAPH);
  1675.         ReadInt(Settings.EPIGRAPH);
  1676.         ReadInt(Settings.InterLin);
  1677.         IF Read.Boolean(history, Settings.TwoCol) THEN END;
  1678.         ReadInt(Settings.FontSize);
  1679.       END;
  1680.       pos := File.Seek(history, pos - 8, 0)
  1681.     END
  1682.   END;
  1683.   IF ~found THEN
  1684.     File.Close(history)
  1685.   END
  1686. END GetWinSize;
  1687.  
  1688.  
  1689. PROCEDURE Open*(FName: S.STRING; DrawWindow, _DrawStatus, _DrawToolbar: SU.ENTRY);
  1690. VAR PID, event: INTEGER;
  1691. BEGIN
  1692.   DrawStatus := _DrawStatus;
  1693.   DrawToolbar := _DrawToolbar;
  1694.   cursor := SU.LoadCursor(Cursor.GetCursor());
  1695.   references := V.create(1024);
  1696.   ref_depth := 0;
  1697.   done := FALSE;
  1698.   loaded := FALSE;
  1699.   FilePath := FName;
  1700.   FileName := FName;
  1701.   S.GetPath(FilePath);
  1702.   W  := ColLeft.Width;
  1703.   W1 := W;
  1704.   W2 := ColLeft.Width + ColRight.Width + Settings.PADDING.ColInter;
  1705.   Lines := ColLeft.Height DIV LineH;
  1706.   ColLeft.Height := Lines * LineH;
  1707.   PID := SU.NewThread(Start, Stack);
  1708.   WHILE ~SU.IsTerminated(PID) DO
  1709.     event := SU.CheckEvent();
  1710.     IF event = 3 THEN
  1711.       SU.TerminateThreadId(PID);
  1712.       SU.Halt
  1713.     END;
  1714.     G.Progress(RF.Progress());
  1715.     G.Draw(Canvas_X, Canvas_Y);
  1716.     DrawWindow;
  1717.     SU.Pause(30)
  1718.   END;
  1719.   IF ~done THEN
  1720.     SU.Halt
  1721.   END;
  1722.   loaded := TRUE;
  1723.   resized := TRUE;
  1724.   IF history # NIL THEN
  1725.     Load
  1726.   ELSE
  1727.     SetSettings(Settings)
  1728.   END
  1729. END Open;
  1730.  
  1731.  
  1732. PROCEDURE Close*;
  1733. BEGIN
  1734.   SU.DelCursor(cursor);
  1735.   Save;
  1736.   SU.Halt
  1737. END Close;
  1738.  
  1739.  
  1740. PROCEDURE SetScrollBar*(_sb: box_lib.scrollbar);
  1741. BEGIN
  1742.   sb := _sb
  1743. END SetScrollBar;
  1744.  
  1745.  
  1746. PROCEDURE Set_b_pict*(b_pict: BOOLEAN);
  1747. BEGIN
  1748.   Settings.b_pict := b_pict
  1749. END Set_b_pict;
  1750.  
  1751.  
  1752. BEGIN
  1753.         clickRef := NIL;
  1754.         hoverRef := NIL;
  1755.         mouseDown := FALSE
  1756. END DOM.
  1757.