Syntax10.Scn.FntSyntax10i.Scn.Fnt+IStampElemsAlloc6 Jan 99qInfoElemsAllocUSyntax10.Scn.Fnt`=IStampElemsAlloc6 Jan 990"Title": WebTables "Author": Andreas Helm "Abstract": Webtables is part of the Oberon-Web-Browser "Keywords": Web, Webbrowser, Tables "Version": no version "From": 1 March 98 "Until":  "Changes": no changes so far "Hints": no hints ͭqBalloonElemsAlloc]Syntax10.Scn.FntXSyntax10i.Scn.Fnt Syntax10m.Scn.Fnt oSyntax10b.Scn.Fnt G+. . 3. *m  B ' " !;% , |* !       0  l J uwA  T T   X" X  9 #; A 5   -4'3?#$-5 \$%F "9  " "+   42A  C  8  #e [8# &[  0B  `  q    *  ?%+ KR 5 4  <   +CS)_ E"- (+a ! S" \ &  1' F  e*6  >]   (  / .;    p    Z.? E E L Y-  R    $ ; { G    7 E B $# $ ]  < "  ! [ 7 &  k  1 #  c  # , Y    . Z         ^   N="WebTables" WebTables is an auxiliaray module for the Oberon-Web-Browser. "cellIndent" CONST cellIndent* = 2; cellIndent is the value in pixel a nested table is indented. "IntArray" POINTER TO ARRAY OF INTEGER; "Table" Table* = POINTER TO TableDesc; TableDesc* = RECORD first*: TableRow; (* first row of the table *) border*: INTEGER; (* width of the table border in pixels, 0 if the table has no border *) width*, height*: INTEGER; (* width and heigt of the table in pixel *) columns*, rows*: INTEGER; (* size of the table = columns * rows cells *) contents*: POINTER TO ARRAY OF ARRAY OF TableCell; (* Array of cells *) colWidth*: IntArray; (* size = columns, width of each column in pixel *) rowHeight*: IntArray; (* size = rows, heicht of each row in pixel *) next*: Table (* next table on the same nesting depth *) END; When Parsing the source file, the table is built as a dynamic list. After the table is completly read, FinalizeTable creates the contents array. Afterwards, the list of cells and the next-pointer in the TableCell type should not be used! "TableDesc" Table* = POINTER TO TableDesc; See Table for more details "TableCell" TableCell* = POINTER TO TableCellDesc; TableCellDesc* = RECORD first*: WebCellElems.Elem; (* list of text fragments *) table*: Table; (* list of nested tables *) width*, height*: INTEGER; (* preferred height and width of the cell in pixel*) colSpan*, rowSpan*: INTEGER; (* colspan and rowspan of the cell *) minWidth*: INTEGER; (* length of the longest word in the cell *) minElemWidth*: INTEGER; (* longest elem in the cell *) next*: TableCell (* next cell in the row *) END; minWidth is the minimum width of the cell, where no linebreaks in words occur. A cell always has at least 1 fragment. If a cell has n + 1 fragments, there are n nested tables embedded. "TableCellDesc" TableCell* = POINTER TO TableCellDesc; See TableCell for more details "TableRow" TableRow* = POINTER TO TableRowDesc; TableRowDesc* = RECORD first*: TableCell; (* first cell of this row *) next*: TableRow (* next line of the table *) END; "TableRowDesc" TableRow* = POINTER TO TableRowDesc; See TableRow for more details "maxWidth" VAR maxWidth*: INTEGER; maxWidth is the maximum width of a table. "showTables" VAR showTables-: BOOLEAN; showTables indicates the use of the new implementation (TRUE) or the old implementation of tables (FALSE). "warning" VAR warning*: BOOLEAN; warning is TRUE, if the warning, that a table is to large, was written. "table" VAR table*: TABLE; table is the last outmost Table, which was inserted in a text. This variable is used for the image linking only. "cache" VAR cache: RECORD table: Table; tablePos: LONGINT; outmost: Texts.Text END; cache stores the last table, for which notify was called. "InitCell" PROCEDURE InitCell*(cell: TableCell); InitCell sets the default values for a cell, this cell must be created with NEW! "InitRow" PROCEDURE InitRow*(row: TableRow); InitRow sets the default values for a row, this row must be created with NEW! "InitTable" PROCEDURE InitTable*(table: Table); InitTable sets the default values for a table, this table must be created with NEW! "CopyTable" PROCEDURE CopyTable(table: Table); CopyTable copies all cell fragments, to allow a new WriteElem on these elements. "MeasureWidth" PROCEDURE MeasureWidth*(t: Texts.Text; VAR width, minWidth, elemWidth: INTEGER); MeasureWidth calculates the preferred width of t and returns it in width, minWidth is the width of the text, where no breaks in words occur. elemWidth is the width of the greatest elem in the cell. "MeasureHeight" PROCEDURE MeasureHeight*(t: Texts.Text; width: INTEGER; VAR height: INTEGER); MeasureHeight calculates the height of t with width and returns it in height. "FinalizeTable" PROCEDURE FinalizeTable*(table: Table); FinalizeTable creates the contents array of table from the list of cells. "FinalizeFragment" PROCEDURE FinalizeFragment(frag: WebCellElems.Elem); FinalizeFragment adapts all StyleElems in the fragment frag to the width of the fragment. "MeasureCellWidth" PROCEDURE MeasureCellWidth(cell: TableCell; maxWidth: INTEGER); MeasureCellWidth calculates the preferred width of cell, which must be smaller or equal to maxWidth. "MeasureCellHeight" PROCEDURE MeasureCellHeight(cell: TableCell); MeasureCellHeight calculates the height of cell. It uses the value of cell.width to do this. "MeasureColumn" PROCEDURE MeasureColumn(table: Table; col, maxWidth: INTEGER; VAR width, minWidth: INTEGER); MeasureColumns calculates the width of the column col in table, uses maxWidth and returns the calulated values in width and minwidth. "MeasureRow" PROCEDURE MeasureRow(table: Table; row: INTEGER; VAR height: INTEGER); MeasureRow calculates the height of the row row in table. It uses the width values of the cells. "MeasureTable" PROCEDURE MeasureTable*(table: Table; maxWidth: INTEGER); MeasureTable calculates the dimensions of the table. Afterwards the arrays table.colWidth, table.minColWidth and table.rowHeight are valid. If the table is not finalized, FinalizeTable is called for it. "CalcRows" PROCEDURE CalcRows*(table: Table; y: INTEGER; VAR start, end: INTEGER); CalcRows calculates the rows, which beginning by y, reside in the same panel. It returns the first and last row in start and end. "LocateTable" PROCEDURE LocateTable*(text: Texts.Text; table: Table; cell: WebCellElems.Elem): LONGINT; LocateTable searches table in text and returns its position or -1, if it is not found. If cell = NIL, LocateTable looks for table.contens[0, 0].first, otherwise it searches cell (and ignores table!). "FindCell" PROCEDURE FindCell(table: Table; cell: WebCellElems.Elem; VAR t: Table; VAR x, y: INTEGER); FindCell searches in an outmost table table the cell cell and returns in t the table and in (x, y) the position in t of the cell. "FindTable" PROCEDURE FindTable(table, t: Table; VAR prev, father: Table; VAR x, y: INTEGER); FindTable searches in an outmost table table the nested table t and returns in (x, y) the position in father and in prev the predeccesor table in (x, y) or NIL. "GetPanel" PROCEDURE GetPanel(cell: TableCell): WebPanelElems.Elem; GetPanel returns the panel, which contains cell. "FillDimensions" PROCEDURE FillDimensions*(table: Table); FillDimensions calculates the values for the arrays table.colWidth and table.rowHeight. For this, FillDimensions uses the current width and heigt of the cells. It does not call MeasureTable and calls HALT(99), if the table is not finalized. "AnalyzeCell" PROCEDURE AnalyzeCell(panel: WebPanelElems.Elem; cell: TableCell); AnalyzeCell is called by AnalyzeRow, if a 'complex' cell with nested tables is detected. AnalyzeCell rebuilds the fragment list and the table list form panel.elemTxt. Therefore it calls AnalyzeTable. "AnalyzeRow" PROCEDURE AnalyzeRow(panel: WebPanelElems.Elem; curRow: TableRow; VAR pos: LONGINT; oldX: INTEGER): INTEGER; AnalyzeRow Is called by AnalyzeTable and RebuildTable to fill curRow with all cells in the same row. It opens panel.elemTxt at position pos and returns in pos the position of the first element in the next row. It returns -1, if it reaches the end of the text. oldX is the x-coordinate of the first element. AnalyzeRow returns the x-coordinate of the first element in the row. "AnalyzeTable" PROCEDURE AnalyzeTable(panel: WebPanelElems.Elem; table: Table; VAR pos: LONGINT); AnalyzeTable rebuilds a table in panel, beginning with pos. It returns the resulting table and the position of the first cell after the table. FinalizeTable is called for the resulting table. "RebuildTable" PROCEDURE RebuildTable*(text: Texts.Text; beg: LONGINT; VAR table: Table): LONGINT; RebuildTable rebuilds a table contained in text and beginning after beg. RebuildTable skips all characters and elements until a WebPanelElems.Elem is found. It then rebuilds the table until a character other than CR, Blank, TAB or another text element is read. It returns the first position after the table or -1, if no table was found. "ReformatCell" PROCEDURE ReformatCell(panel: WebPanelElems.Elem; cell: TableCell; x, y: INTEGER); ReformatCell reformats cell, which is in panel. It moves all fragments to x, y. This procedure calls ReformatTableInPanel for each nested table in cell. "ReformatPanel" PROCEDURE ReformatPanel(table: Table; panel: WebPanelElems.Elem; start, end: INTEGER; x0: INTEGER; VAR y: INTEGER); ReformatPanel calculates the new dimensions of panel, which contains the rows start to end from table. (x0, y) is the starting point. This procedure calls ReformatCell for each cell. "ReformatTableInPanel" PROCEDURE ReformatTableInPanel(table: Table; panel: WebPanelElems.Elem; x0: INTEGER; VAR y: INTEGER); ReformatTableInPanel calculates the new dimensions of a nested table in panel. The table should start at (x0, y). The return value of y is used to calculate the position of the cell fragment after the fragment. This procedure calls ReformatCell for each cell. "ReformatTable" PROCEDURE ReformatTable*(text: Texts.Text; table: Table; maxWidth: INTEGER; update: BOOLEAN); ReformatTable calculates the new dimensions of table in text and use maxWidth for that. This procedure calls ReformatPanel for all panels, which belong to the table. If update = TRUE, Elems.Update is called. "MyNotify" PROCEDURE MyNotify*(e: WebCellElems.Elem; update: BOOLEAN); MyNotify is installed in WebCellElems.notify. It is called every time, the size of e may have changed. "FindCaret" PROCEDURE FindCaret(VAR text: Texts.Text; VAR pos: LONGINT); FindCaret returns the text and the position of the caret in it. As a side-effect of this implementation, the caret is removed. "SetCaret" PROCEDURE SetCaret(pos: LONGINT); SetCaret sets the caret into Oberon.FocusViewer at pos. This procedure is used to avoid traps, when a textframe which contains the caret is removed. "OpenParams" PROCEDURE OpenParams(VAR s: Texts.Scanner); OpenParams looks, if s.ch = '^' and opens the selection, if so. It returns the new scanner in s. "InsertCell0" PROCEDURE InsertCell0(panel: WebPanelElems.Elem; cell: TableCell; x, y: INTEGER); InsertCell0 inserts cell at position (x, y) in panel. This procedure calls InsertTable for every nested table. "InsertRows0" PROCEDURE InsertRows0(w: Texts.Writer; panel: WebPanelElems.Elem; table: Table; start, end: INTEGER; x0: INTEGER; VAR y0: INTEGER); InsertRows0 inserts all rows from start to end of table into a panel. If panel = NIL then it creates a new panel and appends it to w. It uses (x0, y0) as starting position. The resulting value of y0 is returned for future calculations. This procedure calls InsertCell0 for every cell. "InsertTable" PROCEDURE InsertTable*(text: Texts.Text; panel: WebPanelElems.Elem; table: Table; x0, y0: INTEGER; textPos: LONGINT); InsertTable inserts table into text or panel at position textPos or (x0, y0). It calls InsertRows0 for the rows of the table. "GetOverlapCell" PROCEDURE GetOverlapCell(table: Table; x, y: INTEGER; VAR x1, y1: INTEGER); GetOverlapCell calculates the cell (x1, y1), which overlaps the cell (x, y) in table with colspan and/or rowspan >= 1. "AddRows" PROCEDURE AddRows(VAR table: Table; row, num: INTEGER); AddRows inserts beginning with row num rows in table. "AddColumns" PROCEDURE AddColumns(VAR table: Table; col, num: INTEGER); AddColumns inserts beginning with col num columns in table. "DeleteRows" PROCEDURE DeleteRows(VAR table: Table; row, num: INTEGER); DeleteRows deletes beginning with row num rows in table. "DeleteColumns" PROCEDURE DeleteColumns(VAR table: Table; col, num: INTEGER); DeleteColumns deletes beginning with col num columns in table. "ChangeSpan" PROCEDURE ChangeSpan(VAR table: Table; x, y, newColSpan, newRowSpan: INTEGER); ChangeSpan sets the colspan and rowspan fields of cell (x, y) from table to newColSpan and newRowSpan. "SetHeader0" PROCEDURE SetHeader0(VAR table: Table; x, y: INTEGER; header: BOOLEAN); SetHeader0 sets the header flag of cell (x, y) in table to header. "Tables" PROCEDURE Tables*; Calling syntax: WebTables.Tables "on"|"off" data types: switch: STRING, "on" for new implementation, "off" for old implementation Uses the new table implementation ("on") or the default implementation ("off"). "Insert" PROCEDURE Insert*; Calling syntax: WebTables.Insert columns rows border data types: columns: INTEGER rows: INTEGER border: BOOLEAN OR INTEGER Insert creates a new table and inserts it into a text. "Remove" PROCEDURE Remove*; Calling syntax: WebTables.Remove Remove deletes the table with the caret. If this table is an outmost table, one might also delete it like normal text. "Refresh" PROCEDURE Refresh*; Calling syntax: WebTables.Refresh Refresh recalculates the dimensions of the table with the caret. "SetBorder" PROCEDURE SetBorder*; Calling syntax: WebTables.SetBorder border data types: border: INTEGER, border = 0 results in a table without border SetBorder sets the border width of the table with the caret. "InsertColumns" PROCEDURE InsertColumns*; Calling syntax: WebTables.InsertColumns ''l | r'' ''number of columns to insert'' data types: where: CHAR; l = left of the cell with the caret, r = right of the cell with the caret num: INTEGER, number of columns to insert InsertColumns inserts num columns in the table with the caret, left or right of the cell with the caret, depending on where. "InsertRows" PROCEDURE InsertRows*; Calling syntax: WebTables.InsertRows ''a | b'' ''number of rows to insert'' data types: where: CHAR; a = above the cell with the caret, b = below the cell with the caret num: INTEGER, number of rows to insert InsertColumns inserts num rows in the table with the caret, above or below of the cell with the caret, depending on where. "RemoveColumns" PROCEDURE RemoveColumns*; Calling syntax: WebTables.RemoveColumns ''number of columns to remove'' data types: num: INTEGER, number of columns to delete RemoveColumns removes num columns from the table with the caret, starting with the column of the cell with the caret. "RemoveRows" PROCEDURE RemoveRows*; Calling syntax: WebTables.RemoveRows ''number of rows to remove'' data types: num: INTEGER, number of rows to delete RemoveRows removes num rows from the table with the caret, starting with the row of the cell with the caret. "CellSpan" PROCEDURE CellSpan*; Calling syntax: WebTables.CellSpan colSpan rowSpan data types: colSpan, rowSpan: INTEGER, new values for colspan and rowspan CellSpan sets the values of colspan and rowspan of the cell with the caret. To keep the old value, set the new value to 0. "SetHeader" PROCEDURE SetHeader*; Calling syntax: WebTables.SetHeader header data types: header: BOOLEAN, TRUE for , FALSE for -cells SetHeader converts the cell with the caret into a header or into a normal data cell. "InsertCell" PROCEDURE InsertCell*; Calling syntax: WebTables.InsertCell InsertCell creates a new cell right to the cell with the caret. This works only, if the position of the new cell is empty. "RemoveCell" PROCEDURE RemoveCell*; Calling syntax: WebTables.RemoveCell RemoveCell removes the cell with the caret from the table. One can only remove a cell from the last column of a table or a cell with colspan > 1, which overlaps the last columns. "InspectTable" PROCEDURE InspectTable*; InspectTable sets the values of the dialog elems to the values of the table with the caret. "InspectCell" PROCEDURE InspectCell*; InspectCell sets the values of the dialog elems to the values of the cell with the caret. !b8FoldElemsNewCSyntax10.Scn.FntSyntax10b.Scn.Fnt 9\ TAB = 9X; CR = 0DX; cellIndent* = 2; widthInc = 16; heightInc = 10; stdTabWidth = 14;8{8Syntax10.Scn.FntSyntax10b.Scn.Fnt" x8FoldElemsNewSyntax10.Scn.FntSyntax10b.Scn.Fnt Syntax10i.Scn.Fnt  ,    , p TableCellDesc* = RECORD first*: WebCellElems.Elem; (* list of text fragments *) table*: Table; (* list of nested tables *) width*, height*: INTEGER; (* preferred height and width of the cell *) colSpan*, rowSpan*: INTEGER; minWidth*, minElemWidth*: INTEGER; (* length of the longest word in the cell *) next*: TableCell (* next cell in the row *) END; 8 8Syntax10.Scn.FntSyntax10b.Scn.Fnt   Syntax10i.Scn.Fnt  TableRowDesc* = RECORD first*: TableCell; (* first cell of this row *) next*: TableRow (* next line of the table *) END; 8  8Syntax10.Scn.FntSyntax10b.Scn.Fnt Syntax10i.Scn.Fnt   . ,  *E first*: TableRow; (* first line of the table *) border*: INTEGER; width*, height*: INTEGER; columns*, rows*: INTEGER; (* size of the table = columns * rows cells *) contents*: POINTER TO ARRAY OF ARRAY OF TableCell; colWidth*, rowHeight*: IntArray; next*: Table (* next table on the same nesting depth *) END;8 IntArray* = POINTER TO ARRAY OF INTEGER; Table* = POINTER TO TableDesc; TableCell* = POINTER TO TableCellDesc;  TableRow* = POINTER TO TableRowDesc;  TableDesc* = RECORD 88Syntax10.Scn.FntSyntax10b.Scn.Fnt     8FoldElemsNew#Syntax10.Scn.FntAA table: Table; tablePos: LONGINT; outmost: Texts.Text END;8b maxWidth*: INTEGER; showTables-: BOOLEAN; warning*: BOOLEAN; table*: Table; cache: RECORD 8)8#Syntax10.Scn.Fnt99 BEGIN IF a > b THEN RETURN a ELSE RETURN b END END Max;8* Syntax10b.Scn.Fnt 8#Syntax10.Scn.Fnt BEGIN cell.first := NIL; cell.table := NIL; cell.width := 0; cell.height := 0; cell.colSpan := 1; cell.rowSpan := 1; cell.minWidth := 0; cell.minElemWidth := 0; cell.next := NIL END InitCell;8 8#Syntax10.Scn.Fnt77 BEGIN row.first := NIL; row.next := NIL END InitRow;8  8#Syntax10.Scn.Fnt BEGIN table.first := NIL; table.border := 0; table.width := 0; table.height := 0; table.columns := 0; table.rows := 0; table.contents := NIL; table.colWidth := NIL; table.rowHeight := NIL; table.next := NIL END InitTable;8c$\8#Syntax10.Scn.Fnt VAR x, y: INTEGER; curC, prev, next: WebCellElems.Elem; curT: Table; msg: Texts.CopyMsg; BEGIN FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF table.contents[x, y] # NIL THEN curC := table.contents[x, y].first; prev := NIL; curT := table.contents[x, y].table; WHILE curC # NIL DO msg.e := NIL; curC.handle(curC, msg); next := curC.next; msg.e(WebCellElems.Elem).txt := curC.txt; msg.e(WebCellElems.Elem).txt(WebCellElems.Text).elem := msg.e(WebCellElems.Elem); IF prev = NIL THEN table.contents[x, y].first := msg.e(WebCellElems.Elem); prev := table.contents[x, y].first ELSE prev.next := msg.e(WebCellElems.Elem); prev := prev.next END; curC.next := next; curC := next; IF curT # NIL THEN CopyTable(curT); curT := curT.next END END; END END END END CopyTable;88NSyntax10.Scn.FntSyntax10i.Scn.Fnt. Syntax10b.Scn.Fnt :8FoldElemsNew_Syntax10.Scn.FntSyntax10i.Scn.Fnt$% VAR r: Texts.Reader; ch, oldCh: CHAR; dx, x, y, w, h: INTEGER; p: LONGINT; lw: INTEGER; e: Texts.Elem; pFirst, pLeft: LONGINT; (* Parameters of the current Parc *) wordWidth: INTEGER; msg: TextFrames.DisplayMsg; BEGIN width := 0; lw := 0; ch := 0X; pFirst := 0; pLeft := 0; elemWidth := 0; minWidth := 0; wordWidth := 0; Texts.OpenReader(r, t, 0); WHILE ~r.eot DO oldCh := ch; Texts.Read(r, ch); IF ch = CR THEN width := Max(width, lw); lw := 0; IF oldCh # CR THEN minWidth := Max(minWidth, wordWidth); wordWidth := 0 END ELSIF r.elem # NIL THEN e := r.elem; minWidth := Max(minWidth, wordWidth); wordWidth := 0; WITH e: TextFrames.Parc DO pFirst := e.first; pLeft := e.left; lw := 0 | e: LineElems.Elem DO ELSE msg.prepare := TRUE; e.handle(e, msg); INC(lw, SHORT(e.W DIV TextFrames.Unit)); minWidth := Max(minWidth, SHORT(e.W DIV TextFrames.Unit)); elemWidth := Max(elemWidth, SHORT(e.W DIV TextFrames.Unit)) END ELSE IF ch # 0X THEN Display.GetChar(r.fnt.raster, ch, dx, x, y, w, h, p); IF ch = TAB THEN IF pFirst # 0 THEN INC(lw, SHORT(pLeft DIV TextFrames.Unit) - lw) ELSE INC(lw, stdTabWidth) (* standard tab width *) END ELSE INC(lw, dx) END; IF (ch # " ") & (ch # TAB) THEN INC(wordWidth, dx); ELSIF (oldCh # " ") & (oldCh # TAB) & (oldCh # CR) THEN minWidth := Max(minWidth, wordWidth); wordWidth := 0 END; IF oldCh = CR THEN (* first character after linebreak *) INC(lw, SHORT(pFirst DIV TextFrames.Unit + pLeft DIV TextFrames.Unit)) END END END END; width := Max(width, lw); minWidth := Max(minWidth, wordWidth) END MeasureWidth;87 6f8nSyntax10.Scn.FntSyntax10i.Scn.Fnt$;$^_8FoldElemsNew#Syntax10.Scn.Fnt VAR dx, x, y, w, h: INTEGER; p: LONGINT; BEGIN width := 0; height := 0; Texts.Read(r, lastChar); WHILE (~r.eot) & (lastChar # CR) & (lastChar # " ") & (lastChar # TAB) & (lastChar # 0X) & (r.elem = NIL) DO Display.GetChar(r.fnt.raster, lastChar, dx, x, y, w, h, p); INC(width, dx); height := Max(height, h); Texts.Read(r, lastChar) END END MeasureWord;8F5.*DY- VAR r: Texts.Reader; ch, oldCh: CHAR; lh, fontHeight, xPos: INTEGER; dx, x, y, w, h: INTEGER; p: LONGINT; e: Texts.Elem; pLeft, pFirst, minLineHeight: INTEGER; (* Parameters of the current Parc *) wWidth, wHeight: INTEGER; msg: TextFrames.DisplayMsg; (* Calculate the length of a word *) PROCEDURE MeasureWord(VAR r: Texts.Reader; VAR width, height: INTEGER; VAR lastChar: CHAR);  BEGIN height := 0; lh := 0; fontHeight := 0; ch := 0X; pLeft := 0; pFirst := 0; minLineHeight := 0; xPos := 0; Texts.OpenReader(r, t, 0); WHILE ~r.eot DO oldCh := ch; MeasureWord(r, wWidth, wHeight, ch); IF r.fnt # NIL THEN fontHeight := r.fnt.height END; lh := Max(lh, wHeight); IF lh < minLineHeight THEN lh := minLineHeight END; IF lh = 0 THEN lh := fontHeight END; IF r.elem # NIL THEN e := r.elem; WITH e: TextFrames.Parc DO pLeft := SHORT(e.left DIV TextFrames.Unit); pFirst := SHORT(e.first DIV TextFrames.Unit); minLineHeight := SHORT(e.lsp DIV TextFrames.Unit); xPos := pLeft + pFirst; INC(height, 1); lh := 0 | e: LineElems.Elem DO INC(height, lh + 1); (*lh := minLineHeight;*) lh := 1; xPos := pLeft; ELSE msg.prepare := TRUE; e.handle(e, msg); INC(xPos, SHORT(e.W DIV TextFrames.Unit)); IF xPos > width THEN INC(height, lh); lh := (*0*) Max(minLineHeight, SHORT(e.H DIV TextFrames.Unit)); xPos := pLeft ELSE lh := Max(lh, Max(SHORT(e.H DIV TextFrames.Unit), minLineHeight)) END END END; IF xPos + wWidth > width THEN (* word does not fit into line, asume break *) IF wWidth > width THEN (* word does not fit into a single line *) IF width > 0 THEN xPos := pLeft + wWidth MOD width; INC(height, Max(wHeight, minLineHeight) * SHORT(ENTIER(wWidth / width))) ELSE xPos := pLeft + wWidth; INC(height, Max(wHeight, minLineHeight)) END ELSE xPos := pLeft + wWidth; INC(height, lh) END; lh := Max(wHeight, minLineHeight); ELSE INC(xPos, wWidth) END; IF ch = CR THEN IF oldCh = CR THEN INC(height, minLineHeight) ELSE lh := Max(lh, Max(wHeight, minLineHeight)); INC(height, lh); (*lh := Max(wHeight, minLineHeight);*) lh := minLineHeight END; xPos := pFirst + pLeft END; IF (ch # 0X) & (ch # Texts.ElemChar) THEN Display.GetChar(r.fnt.raster, ch, dx, x, y, w, h, p); IF ch = TAB THEN IF pFirst # 0 THEN INC(xPos, pLeft - xPos) ELSE INC(xPos, stdTabWidth) (* standard tab width *) END ELSE INC(xPos, dx) END END END; INC(height, lh) END MeasureHeight;8 8P Syntax10.Scn.FntSyntax10i.Scn.Fntj8FoldElemsNew#Syntax10.Scn.Fnttt table.rows := 0; curRow := table.first; WHILE curRow # NIL DO INC(table.rows); curRow := curRow.next END; 88#Syntax10.Scn.Fnt NEW(overlapRows, table.rows); FOR y := 0 TO table.rows - 1 DO overlapRows[y] := 0 END; curRow := table.first; y := 0; WHILE curRow # NIL DO x := overlapRows[y]; curCell := curRow.first; WHILE curCell # NIL DO INC(x, curCell.colSpan); FOR i := 1 TO curCell.rowSpan - 1 DO IF y + i < table.rows THEN INC(overlapRows[y + i], curCell.colSpan) END END; curCell := curCell.next END; IF x > table.columns THEN table.columns := x END; INC(y); curRow := curRow.next END; 8:8#Syntax10.Scn.Fnt NEW(table.contents, table.columns, table.rows); NEW(filler); FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO table.contents[x, y] := filler END END; x := 0; y := 0; curRow := table.first; WHILE curRow # NIL DO x := 0; IF curRow.first # NIL THEN curCell := curRow.first; WHILE curCell # NIL DO WHILE (x < table.columns) & (table.contents[x, y] # filler) DO INC(x) END; IF x < table.columns THEN maxColSpan := curCell.colSpan; IF x + maxColSpan > table.columns THEN maxColSpan := table.columns - x END; maxRowSpan := curCell.rowSpan; IF y + maxRowSpan > table.rows THEN maxRowSpan := table.rows - y END; FOR i := 0 TO curCell.colSpan - 1 DO FOR j := 0 TO curCell.rowSpan - 1 DO IF (x + i < table.columns) & (y + j < table.rows) THEN IF table.contents[x + i, y + j] # filler THEN IF i + 1 < maxColSpan THEN maxColSpan := i + 1 END; IF j + 1 < maxRowSpan THEN maxRowSpan := j + 1 END END END END END; FOR i := 0 TO maxColSpan - 1 DO FOR j := 0 TO maxRowSpan - 1 DO IF (x + i < table.columns) & (y + j < table.rows) THEN IF table.contents[x + i, y + j] = filler THEN table.contents[x + i, y + j] := NIL END END END END; curCell.colSpan := Max(1, maxColSpan); curCell.rowSpan := Max(1, maxRowSpan); curC := curCell.first; WHILE curC # NIL DO curC.colSpan := curCell.colSpan; curC.rowSpan := curCell.rowSpan; curC := curC.next END; table.contents[x, y] := curCell END; INC(x, curCell.colSpan); curCell := curCell.next END; INC(y) END; curRow := curRow.next END; 8D8#Syntax10.Scn.Fnt FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF table.contents[x, y] = filler THEN table.contents[x, y] := NIL END END END"8 VAR x, y, i, j: INTEGER; curRow: TableRow; curCell, filler: TableCell; overlapRows: IntArray; maxColSpan, maxRowSpan: INTEGER; curC: WebCellElems.Elem; BEGIN IF (table = NIL) OR (table.first = NIL) OR (table.first.first = NIL) THEN RETURN END; (* empty table *)  Count number of rows   Calculate number of columns   Write list into array   replace all filler-fields with NIL  END FinalizeTable;86E8#Syntax10.Scn.Fnt VAR r: Texts.Reader; e: Texts.Elem; BEGIN Texts.OpenReader(r, frag.txt, 0); WHILE ~r.eot DO Texts.ReadElem(r); IF r.elem # NIL THEN e := r.elem; WITH e: TextFrames.Parc DO e.width := frag.W - (LONG(widthInc - 1) * TextFrames.Unit) - e.left ELSE IF r.col = 0 THEN Texts.ChangeLooks(frag.txt, Texts.Pos(r) - 1, Texts.Pos(r), {1}, NIL, 15, 0) END END END END END FinalizeFragment;8  $$A8#Syntax10.Scn.Fnt VAR curC: WebCellElems.Elem; curT: Table; w, temp, elemW: INTEGER; BEGIN cell.width := 0; cell.minWidth := 0; cell.minElemWidth := 0; curC := cell.first; WHILE curC # NIL DO MeasureWidth(curC.txt, w, temp, elemW); curC.wantedWidth := w; cell.minWidth := Max(cell.minWidth, temp + widthInc); cell.minElemWidth := Max(cell.minElemWidth, elemW + widthInc); INC(w, widthInc); IF (maxWidth # 0) & (w > maxWidth) THEN w := maxWidth END; IF w > cell.width THEN cell.width := w END; curC := curC.next END; curT := cell.table; WHILE curT # NIL DO MeasureTable(curT, maxWidth - cellIndent * 2); IF curT.width + cellIndent * 2 > cell.width THEN cell.width := curT.width + cellIndent * 2 END; curT := curT.next END END MeasureCellWidth;8%/H8#Syntax10.Scn.Fnt VAR curC: WebCellElems.Elem; curT: Table; h: INTEGER; w: Texts.Writer; BEGIN curC := cell.first; cell.height := 0; WHILE curC # NIL DO MeasureHeight(curC.txt, cell.width - widthInc, h); INC(h, heightInc); IF h < 25 THEN h := 25; Texts.OpenWriter(w); Texts.Write(w, CR); Texts.Append(curC.txt, w.buf) END; curC.W := LONG(cell.width) * TextFrames.Unit; curC.H := LONG(h) * TextFrames.Unit; FinalizeFragment(curC); INC(cell.height, h); curC := curC.next END; curT := cell.table; WHILE curT # NIL DO MeasureTable(curT, cell.width - cellIndent * 2); INC(cell.height, curT.height); curT := curT.next END END MeasureCellHeight;8%^S8CSyntax10.Scn.FntSyntax10i.Scn.Fnt/Fk VAR y: INTEGER; BEGIN width := 0; minWidth := 0; FOR y := 0 TO table.rows - 1 DO IF (table.contents[col, y] # NIL) THEN MeasureCellWidth(table.contents[col, y], maxWidth); IF (table.contents[col, y].colSpan = 1) & (table.contents[col, y].width > width) THEN width := table.contents[col, y].width END; IF (table.contents[col, y].colSpan = 1) & (table.contents[col, y].minWidth > minWidth) THEN minWidth := table.contents[col, y].minWidth END END END; IF width = 0 THEN (* only cells with colSpan > 1, weird column *) FOR y := 0 TO table.rows - 1 DO IF (table.contents[col, y] # NIL) THEN IF table.contents[col, y].width > width THEN width := table.contents[col, y].width END; IF table.contents[col, y].minWidth < minWidth THEN minWidth := table.contents[col, y].minWidth END END END END END MeasureColumn;8$Hm8CSyntax10.Scn.FntNSyntax10i.Scn.Fnt,Q VAR x: INTEGER; BEGIN height := 0; FOR x := 0 TO table.columns - 1 DO IF table.contents[x, row] # NIL THEN MeasureCellHeight(table.contents[x, row]); IF (table.contents[x, row].rowSpan = 1) & (table.contents[x, row].height > height) THEN height := table.contents[x, row].height END END END; IF height = 0 THEN (* only cells with rowSpan > 1, weird row *) FOR x := 0 TO table.columns - 1 DO IF table.contents[x, row] # NIL THEN IF table.contents[x, row].height > height THEN height := table.contents[x, row].height END END END END END MeasureRow;8I  #H8Syntax10.Scn.FntSyntax10i.Scn.Fnt&Ik8FoldElemsNew#Syntax10.Scn.Fntss FOR x := 0 TO table.columns - 1 DO MeasureColumn(table, x, maxWidth, table.colWidth[x], minColWidth[x]) END; A88#Syntax10.Scn.Fnt FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF (table.contents[x, y] # NIL) & (table.contents[x, y].colSpan # 1) THEN temp := table.colWidth[x]; FOR i := 1 TO table.contents[x, y].colSpan - 1 DO INC(temp, table.colWidth[x + i]) END; IF table.contents[x, y].width > temp THEN diff := (table.contents[x, y].width - temp) DIV table.contents[x, y].colSpan; FOR i := 0 TO table.contents[x, y].colSpan - 1 DO INC(table.colWidth[x + i], diff) END END END END END; %8|8#Syntax10.Scn.Fntbb table.width := 0; FOR x := 0 TO table.columns - 1 DO INC(table.width, table.colWidth[x]) END; 881Syntax10.Scn.Fnt!Syntax10i.Scn.FntR+-=8EQ1b2-=2 IF table.width > maxWidth THEN (* table width is too large *) temp := 0; FOR x := 0 TO table.columns - 1 DO INC(temp, minColWidth[x]) END; (* temp = minimal width *) IF temp < maxWidth THEN (* no breaks in words, all elems visible *) diff := maxWidth - temp; (* diff = available (horizontal) space for table *) temp := 0; FOR x := 0 TO table.columns - 1 DO IF table.colWidth[x] > widthInc THEN INC(temp, table.colWidth[x] - minColWidth[x]) END END; (* temp = difference between table width and minimal table width *) FOR x := 0 TO table.columns - 1 DO IF table.colWidth[x] > widthInc THEN i := SHORT(ENTIER((diff / temp) * (table.colWidth[x] - minColWidth[x]))); table.colWidth[x] := minColWidth[x] + i END END; table.width := maxWidth ELSE (* maybe breaks in words and/or not all elems visible *) (* Calculate the width of the columns, where all elems are visible *) NEW(minElemWidth, table.columns); FOR x := 0 TO table.columns - 1 DO minElemWidth[x] := 0 END; FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF table.contents[x, y] # NIL THEN minElemWidth[x] := Max(minElemWidth[x], table.contents[x, y].minElemWidth) END END END; temp := 0; (* temp = minimal width, where all elems are visible *) FOR x := 0 TO table.columns - 1 DO INC(temp, minElemWidth[x]) END; IF temp < maxWidth THEN (* maybe breaks in words, but all elems visible *) diff := maxWidth - temp; (* diff = available (horizontal) space for table *) temp := 0; FOR x := 0 TO table.columns - 1 DO IF table.colWidth[x] > widthInc THEN INC(temp, table.colWidth[x] - minElemWidth[x]) END END; (* temp = difference between table width and minimal table width *) FOR x := 0 TO table.columns - 1 DO IF table.colWidth[x] > widthInc THEN i := SHORT(ENTIER((diff / temp) * (table.colWidth[x] - minElemWidth[x]))); table.colWidth[x] := minElemWidth[x] + i END END; table.width := maxWidth ELSE (* breaks in words and/or not all elems visible *) IF ~ warning THEN Web.LogStr("Warning: Maybe not all elems of a table are visible!$"); warning := TRUE END; factor := maxWidth/table.width; temp := 0; i := 0; FOR x := 0 TO table.columns - 1 DO table.colWidth[x] := SHORT(ENTIER(table.colWidth[x] * factor)); IF table.colWidth[x] < widthInc THEN INC(temp, widthInc - table.colWidth[x]) ELSE INC(i) END; END; IF (temp # 0) & (i # 0) THEN FOR x := 0 TO table.columns - 1 DO IF table.colWidth[x] > widthInc THEN DEC(table.colWidth[x], temp DIV i) ELSE table.colWidth[x] := widthInc END END END; table.width := maxWidth END END; END; 8v8#Syntax10.Scn.Fnthh FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF table.contents[x, y] # NIL THEN table.contents[x, y].width := table.colWidth[x]; IF table.contents[x, y].colSpan # 1 THEN FOR i := 1 TO table.contents[x, y].colSpan - 1 DO INC(table.contents[x, y].width, table.colWidth[x + i]) END END END END END; 89'%8#Syntax10.Scn.FntQQ FOR y := 0 TO table.rows - 1 DO MeasureRow(table, y, table.rowHeight[y]) END; ?88#Syntax10.Scn.Fnt   FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF (table.contents[x, y] # NIL) & (table.contents[x, y].rowSpan # 1) THEN temp := table.rowHeight[y]; FOR i := 1 TO table.contents[x, y].rowSpan - 1 DO INC(temp, table.rowHeight[y + i]) END; IF table.contents[x, y].height > temp THEN diff := (table.contents[x, y].height - temp) DIV table.contents[x, y].rowSpan; FOR i := 0 TO table.contents[x, y].rowSpan - 1 DO INC(table.rowHeight[y + i], diff) END END END END END; #8|8#Syntax10.Scn.Fntbb table.height := 0; FOR y := 0 TO table.rows - 1 DO INC(table.height, table.rowHeight[y]) END; 8i8CSyntax10.Scn.Fnt]Syntax10i.Scn.Fnt)U FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF table.contents[x, y] # NIL THEN table.contents[x, y].height := table.rowHeight[y]; IF table.contents[x, y].rowSpan # 1 THEN FOR i := 1 TO table.contents[x, y].rowSpan - 1 DO INC(table.contents[x, y].height, table.rowHeight[y + i]) END END; (* increase last fragment to fill cell *) tempL := 0; curC := table.contents[x, y].first; prev := NIL; WHILE curC # NIL DO tempL := tempL + curC.H; prev := curC; curC := curC.next END; curT := table.contents[x, y].table; WHILE curT # NIL DO tempL := tempL + LONG(curT.height) * TextFrames.Unit; curT := curT.next END; IF prev # NIL THEN prev.H := prev.H + LONG(table.contents[x, y].height) * TextFrames.Unit - tempL END END END END 8: VAR x, y, i: INTEGER; diff: INTEGER; temp: INTEGER; tempL: LONGINT; prev, curC: WebCellElems.Elem; curT: Table; minElemWidth, minColWidth: IntArray; factor: REAL; BEGIN IF table.contents = NIL THEN FinalizeTable(table) END; IF table.contents = NIL THEN RETURN END; (* calculate the width of the cells *) NEW(table.colWidth, table.columns); NEW(minColWidth, table.columns);  calculate the preferred width of the columns, ignore colspan > 1   recalculate the width of the columns   calculate table width   adapt the width of the columns   set the width of all cells  (* width of the cells does not change after this point *) (* calculate the height of the cells *) NEW(table.rowHeight, table.rows);  calculate the preferred height of the rows, ignore rowspan > 1   recalculate the height of the rows   calculate table height   set the height of all cells  (* height of the cells does not change after this point *) END MeasureTable;8 (* Calculates the preferred width of a text *) PROCEDURE MeasureWidth*(t: Texts.Text; VAR width, minWidth, elemWidth: INTEGER);  (* Calculates the height of a text for a given width *) PROCEDURE MeasureHeight*(t: Texts.Text; width: INTEGER; VAR height: INTEGER);  (* fill the contents array *) PROCEDURE FinalizeTable*(table: Table);  (* adapts all StyleElems in the cell to the width. This procedure is called in MeasureCellHeight, because there the width of the cell is fixed *) PROCEDURE FinalizeFragment(frag: WebCellElems.Elem);  (* Forward Declaration*) PROCEDURE ^MeasureTable*(table: Table; maxWidth: INTEGER); (* Calculates the width of a cell *) PROCEDURE MeasureCellWidth(cell: TableCell; maxWidth: INTEGER);  (* Calculates the height of a cell *) PROCEDURE MeasureCellHeight(cell: TableCell);  (* Calculates the width of a column*) PROCEDURE MeasureColumn(table: Table; col, maxWidth: INTEGER; VAR width, minWidth: INTEGER);  (* Calculates the height of a row *) PROCEDURE MeasureRow(table: Table; row: INTEGER; VAR height: INTEGER);  (* Calculates the height of a table, the contents array must be filled *) PROCEDURE MeasureTable*(table: Table; maxWidth: INTEGER);  >882Syntax10.Scn.FntSyntax10i.Scn.Fnt/ Syntax10b.Scn.Fnt 58FoldElemsNew#Syntax10.Scn.Fnt VAR changed: BOOLEAN; x, i, oldEnd: INTEGER; BEGIN start := y; end := y; changed := TRUE; WHILE changed & (end < table.rows) DO changed := FALSE; oldEnd := end; FOR i := start TO oldEnd DO FOR x := 0 TO table.columns - 1 DO IF (table.contents[x, i] # NIL) & (table.contents[x, i].rowSpan > 1) THEN IF i + table.contents[x, i].rowSpan - 1 > end THEN end := i + table.contents[x, i].rowSpan - 1; changed := TRUE END END END END END END CalcRows;8  D8#Syntax10.Scn.Fnt** VAR r: Texts.Reader; firstPanel, panel: WebPanelElems.Elem; oldCh, ch: CHAR; base: Texts.Text; pos: LONGINT; BEGIN IF (cell = NIL) & (table = NIL) THEN HALT(99) END; IF cell = NIL THEN cell := table.contents[0, 0].first END; base := Texts.ElemBase(cell); panel := NIL; WHILE (base # NIL) & (base IS PanelElems.Text) DO IF base(PanelElems.Text).base IS WebPanelElems.Elem THEN panel := base(PanelElems.Text).base(WebPanelElems.Elem); base := Texts.ElemBase(base(PanelElems.Text).base) ELSE base :=NIL; panel := NIL END END; IF panel = NIL THEN RETURN -1 END; Texts.OpenReader(r, text, 0); pos := -1; firstPanel := NIL; ch := 0X; WHILE (pos = -1) & (~r.eot) DO oldCh := ch; Texts.Read(r, ch); IF ~((r.elem # NIL) OR ((ch = CR) & (oldCh = Texts.ElemChar))) THEN firstPanel := NIL END; IF r.elem # NIL THEN IF (firstPanel = NIL) & (r.elem IS WebPanelElems.Elem) THEN firstPanel := r.elem(WebPanelElems.Elem) END; IF r.elem = panel THEN pos := Texts.ElemPos(firstPanel) END END END; RETURN pos END LocateTable;8]8#Syntax10.Scn.Fnt++ VAR i, j: INTEGER; curC: WebCellElems.Elem; curT: Table; BEGIN x := -1; y := -1; FOR i := 0 TO table.columns - 1 DO FOR j := 0 TO table.rows - 1 DO IF table.contents[i, j] # NIL THEN curC := table.contents[i, j].first; curT := table.contents[i, j].table; WHILE curC # NIL DO IF curC =cell THEN x := i; y := j; t := table; RETURN END; IF curT # NIL THEN FindCell(curT, cell, t, x, y); IF (x # -1) THEN RETURN END; curT := curT.next END; curC := curC.next; END END END END END FindCell;8S8#Syntax10.Scn.Fnt VAR i, j: INTEGER; curT: Table; BEGIN x := -1; y := -1; father := NIL; FOR i := 0 TO table.columns - 1 DO FOR j := 0 TO table.rows - 1 DO IF table.contents[i, j] # NIL THEN curT := table.contents[i, j].table; prev := NIL; WHILE curT # NIL DO IF curT = t THEN father := table; x := i; y := j; RETURN END; FindTable(curT, t, prev, father, x, y); IF x # -1 THEN RETURN END; prev := curT; curT := curT.next END END END END END FindTable;80:8#Syntax10.Scn.Fnt)) VAR base: Texts.Text; BEGIN base := Texts.ElemBase(cell.first); IF (base # NIL) & (base IS PanelElems.Text) THEN IF base(PanelElems.Text).base IS WebPanelElems.Elem THEN RETURN base(PanelElems.Text).base(WebPanelElems.Elem) ELSE RETURN NIL END ELSE RETURN NIL END END GetPanel;8D 8#Syntax10.Scn.FntMM VAR i, j: INTEGER; BEGIN IF table.contents = NIL THEN HALT(99) END; table.width := 0; table.height := 0; NEW(table.colWidth, table.columns); FOR i := 0 TO table.columns - 1 DO j := 0; WHILE (j < table.rows) & ((table.contents[i, j] = NIL) OR (table.contents[i, j].colSpan # 1)) DO INC(j) END; IF j = table.rows THEN j := 0; WHILE (j < table.rows) & (table.contents[i, j] = NIL) DO INC(j) END END; IF j # table.rows THEN INC(table.width, table.contents[i, j].width); table.colWidth[i] := table.contents[i, j].width ELSE table.colWidth[i] := 0 END END; NEW(table.rowHeight, table.rows); FOR j := 0 TO table.rows - 1 DO i := 0; WHILE (i < table.columns) & ((table.contents[i, j] = NIL) OR (table.contents[i, j].rowSpan # 1)) DO INC(i) END; IF i = table.columns THEN i := 0; WHILE (i < table.columns) & (table.contents[i, j] = NIL) DO INC(i) END END; IF i # table.columns THEN INC(table.height, table.contents[i, j].height); table.rowHeight[j] := table.contents[i, j].height ELSE table.rowHeight[j] := 0 END END END FillDimensions;8I (* calculate rows, which reside in one panel *) PROCEDURE CalcRows*(table: Table; y: INTEGER; VAR start, end: INTEGER);  (* Searches a table in a text and returns its position or -1, if it is not found if cell = NIL, LocateTable looks for "table.contens[0, 0].first", otherwise it searches cell (and ignores table!)*) PROCEDURE LocateTable*(text: Texts.Text; table: Table; cell: WebCellElems.Elem): LONGINT;  (* searches in an outmost table "table" the cell "cell" and returns in "t" the table and in "(x, y)" the position in t of the cell *) PROCEDURE FindCell(table: Table; cell: WebCellElems.Elem; VAR t: Table; VAR x, y: INTEGER);  (* searches in an outmost table "table" the nested table "t" and returns in "(x, y)" the position in table and in prev the previous table *) PROCEDURE FindTable(table, t: Table; VAR prev, father: Table; VAR x, y: INTEGER);  (* returns the panel, which contains the cell *) PROCEDURE GetPanel(cell: TableCell): WebPanelElems.Elem;  (* calculates the width of all columns and heights of all columns *) PROCEDURE FillDimensions*(table: Table);  #88Syntax10.Scn.Fntn8FoldElemsNewQSyntax10.Scn.Fnt Syntax10i.Scn.Fnt"7/B VAR r: Texts.Reader; elem: Texts.Elem; curFrag: WebCellElems.Elem; curTable: Table; pos: LONGINT; case: PanelElems.Case; BEGIN cell.width := SHORT(panel.W DIV TextFrames.Unit); cell.height := SHORT(panel.H DIV TextFrames.Unit); curFrag := NIL; curTable := NIL; Texts.OpenReader(r, panel.elemTxt, 0); WHILE ~r.eot DO Texts.ReadElem(r); elem := r.elem; IF elem # NIL THEN case := PanelElems.MyCase(panel, elem); IF (elem IS WebCellElems.Elem) & (case.x = 0) THEN WITH elem: WebCellElems.Elem DO (* simple cell, no nested table *) elem.next := NIL; IF curFrag = NIL THEN curFrag := elem; cell.first := curFrag ELSE curFrag.next := elem; curFrag := curFrag.next END END ELSE IF curTable = NIL THEN NEW(curTable); cell.table := curTable ELSE NEW(curTable.next); curTable := curTable.next; END; InitTable(curTable); pos := Texts.Pos(r) - 1; AnalyzeTable(panel, curTable, pos); IF pos = -1 THEN cell.colSpan := cell.first.colSpan; cell.rowSpan := cell.first.rowSpan; RETURN END; (* Skip all elems, which belong to the table *) WHILE (~r.eot) & (Texts.Pos(r) < pos) DO Texts.ReadElem(r) END END END END; cell.colSpan := cell.first.colSpan; cell.rowSpan := cell.first.rowSpan END AnalyzeCell;8p8mSyntax10.Scn.FntSyntax10i.Scn.Fnt " %@ VAR r: Texts.Reader; elem: Texts.Elem; curCell: TableCell; case: PanelElems.Case; x, xBeg: INTEGER; BEGIN Texts.OpenReader(r, panel.elemTxt, pos); curCell := NIL; x := 0; xBeg := -1; WHILE ~r.eot DO Texts.ReadElem(r); elem := r.elem; IF elem # NIL THEN case := PanelElems.MyCase(panel, elem); IF xBeg = -1 THEN xBeg := case.x; IF xBeg < oldX THEN pos := Texts.Pos(r) - 1; RETURN -1 END END; IF (x # 0) & (x >= case.x) THEN (* end of row *) pos := Texts.Pos(r) - 1; RETURN xBeg END; x := case.x; IF curCell = NIL THEN NEW(curCell); curRow.first := curCell ELSE NEW(curCell.next); curCell := curCell.next END; InitCell(curCell); WITH elem: WebCellElems.Elem DO (* simple cell, no nested table *) curCell.first := elem; elem.next := NIL; curCell.colSpan := elem.colSpan; curCell.rowSpan := elem.rowSpan; curCell.width := SHORT(elem.W DIV TextFrames.Unit); curCell.height := SHORT(elem.H DIV TextFrames.Unit) | elem: WebPanelElems.Elem DO (* complex cell with nested tables *) AnalyzeCell(elem, curCell) END END END; pos := - 1; (* reached end of text *) RETURN xBeg END AnalyzeRow;8U8#Syntax10.Scn.Fnt VAR curRow, myRow: TableRow; oldX, x: INTEGER; BEGIN curRow := NIL; oldX := 0; x := 0; WHILE (pos # -1) & (x # -1) DO NEW(myRow); InitRow(myRow); x := AnalyzeRow(panel, myRow, pos, oldX); IF x # -1 THEN IF curRow = NIL THEN table.first := myRow ELSE curRow.next := myRow END; curRow := myRow; oldX := x END END; FinalizeTable(table); table.border := ABS(table.contents[0, 0].first.border) END AnalyzeTable;8Syntax10i.Scn.Fnt| Syntax10b.Scn.Fnt >8QSyntax10.Scn.Fnt Syntax10i.Scn.Fnt3k  VAR r: Texts.Reader; pos: LONGINT; ch, oldCh: CHAR; curRow: TableRow; dummy: INTEGER; BEGIN Texts.OpenReader(r, text, beg); WHILE (~r.eot) & (((r.elem # NIL) & ~(r.elem IS WebPanelElems.Elem)) OR (r.elem = NIL)) DO Texts.ReadElem(r) END; IF (~r.eot) & (r.elem # NIL) & (r.elem IS WebPanelElems.Elem) THEN NEW(table); InitTable(table); ch := Texts.ElemChar; oldCh := Texts.ElemChar; curRow := NIL; REPEAT IF (r.elem # NIL) & (r.elem IS WebPanelElems.Elem) THEN pos := 0; WHILE pos # -1 DO (* analyze the panel, until all rows are rebuilt *) IF curRow = NIL THEN NEW(curRow); table.first := curRow ELSE NEW(curRow.next); curRow := curRow.next END; InitRow(curRow); dummy := 0; dummy := AnalyzeRow(r.elem(WebPanelElems.Elem), curRow, pos, dummy) END END; oldCh := ch; Texts.Read(r, ch); UNTIL ((r.eot) OR (r.elem = NIL) OR ~(r.elem IS WebPanelElems.Elem)) & (ch # CR) & (ch # " ") & (ch # TAB) OR ((ch # Texts.ElemChar) & (oldCh # Texts.ElemChar)); FinalizeTable(table); table.border := ABS(table.contents[0, 0].first.border); RETURN Texts.Pos(r) - 1; ELSE table := NIL; RETURN -1 (* no table found to rebuild! *) END END RebuildTable;8: PROCEDURE^ AnalyzeTable(panel: WebPanelElems.Elem; table: Table; VAR pos: LONGINT); PROCEDURE AnalyzeCell(panel: WebPanelElems.Elem; cell: TableCell);  PROCEDURE AnalyzeRow(panel: WebPanelElems.Elem; curRow: TableRow; VAR pos: LONGINT; oldX: INTEGER): INTEGER;  PROCEDURE AnalyzeTable(panel: WebPanelElems.Elem; table: Table; VAR pos: LONGINT);  (* Rebuilds a table from a text and returns the first position after the table; FinalizeTable is called by this procedure *) PROCEDURE RebuildTable*(text: Texts.Text; beg: LONGINT; VAR table: Table): LONGINT;  %8;8Syntax10.Scn.Fnt Syntax10b.Scn.FntSyntax10i.Scn.FntT8FoldElemsNewQSyntax10.Scn.FntSyntax10i.Scn.Fnt%/ VAR curC: WebCellElems.Elem; curT: Table; myPanel: WebPanelElems.Elem; case: PanelElems.Case; BEGIN curC := cell.first; IF cell.table = NIL THEN (* no nested table *) case := PanelElems.MyCase(panel, curC); IF case = NIL THEN RETURN END; case.x := x; case.y := y + SHORT(curC.H DIV TextFrames.Unit); case.oldH := -1; ELSE (* search the cells panel in panel *) myPanel := GetPanel(cell); IF myPanel # NIL THEN case := PanelElems.MyCase(panel, myPanel); IF case = NIL THEN RETURN END; case.x := x; myPanel.W := LONG(cell.width) * TextFrames.Unit; myPanel.H := LONG(cell.height) * TextFrames.Unit; case.y := y + cell.height; case.oldH := -1; x := 0; y := 0; curT := cell.table; WHILE curC # NIL DO case := PanelElems.MyCase(myPanel, curC); case.x := x; INC(y, SHORT(curC.H DIV TextFrames.Unit)); case.y := y; case.oldH := -1; IF curT # NIL THEN ReformatTableInPanel(curT, myPanel, cellIndent, y); curT := curT.next END; curC := curC.next END; END END END ReformatCell;8'v8#Syntax10.Scn.Fnt VAR i, j: INTEGER; x, h: INTEGER; BEGIN h := 0; FOR j := start TO end DO h := h + table.rowHeight[j] END; panel.H := LONG(h) * TextFrames.Unit; panel.W := LONG(table.width + 1) * TextFrames.Unit; x := x0; FOR j := start TO end DO FOR i := 0 TO table.columns - 1 DO IF table.contents[i, j] # NIL THEN ReformatCell(panel, table.contents[i, j], x, y) END; INC(x, table.colWidth[i]) END; x := x0; INC(y, table.rowHeight[j]) END END ReformatPanel;8"h8#Syntax10.Scn.Fnt33 VAR i, j: INTEGER; x: INTEGER; BEGIN FOR j := 0 TO table.rows - 1 DO x := x0; FOR i := 0 TO table.columns - 1 DO IF table.contents[i, j] # NIL THEN ReformatCell(panel, table.contents[i, j], x, y) END; INC(x, table.colWidth[i]) END; INC(y, table.rowHeight[j]) END END ReformatTableInPanel;8!  GA8CSyntax10.Scn.FntSyntax10i.Scn.Fnt} VAR r: Texts.Reader; elem: Texts.Elem; pos: LONGINT; ch, oldCh: CHAR; y, y0: INTEGER; start, end: INTEGER; BEGIN WebCellElems.notify := NIL; pos := LocateTable(text, table, NIL); IF pos = -1 THEN HALT(99) END; (* table not found *) MeasureTable(table, maxWidth); Texts.OpenReader(r, text, pos); ch := Texts.ElemChar; oldCh := Texts.ElemChar; y := 0; WHILE (~r.eot) & ((ch = Texts.ElemChar) OR (ch = CR) OR (ch = TAB) OR (ch = " ")) & ((ch = Texts.ElemChar) OR (oldCh = Texts.ElemChar)) & (y < table.rows) DO oldCh := ch; Texts.Read(r, ch); elem := r.elem; IF elem # NIL THEN WITH elem: WebPanelElems.Elem DO CalcRows(table, y, start, end); y0 := 0; ReformatPanel(table, elem, start, end, 0, y0); IF update THEN Elems.UpdateElem(elem) END; y := end + 1 ELSE ch := 0X END END END; WebCellElems.notify := MyNotify END ReformatTable;8 *L8QSyntax10.Scn.FntSyntax10i.Scn.Fnt!"d VAR tableEnd: LONGINT; table: Table; x, y, w, h: INTEGER; BEGIN WebCellElems.notify := NIL; MeasureWidth(e.txt, w, h, h); h := 0; IF ABS(w - e.wantedWidth) < 5 THEN MeasureHeight(e.txt, SHORT(e.W DIV TextFrames.Unit) - widthInc, h) END; IF (ABS(w - e.wantedWidth) > 5) OR (LONG(h + heightInc) * TextFrames.Unit > e.H) THEN IF cache.table # NIL THEN FindCell(cache.table, e, table, x, y) END; IF (cache.table = NIL) OR (x = -1) THEN (* different table, rebuild it *) cache.outmost := WebCellElems.FindOutmostText(e.txt(WebCellElems.Text)); cache.tablePos := LocateTable(cache.outmost, NIL, e); tableEnd := RebuildTable(cache.outmost, cache.tablePos, cache.table) END; (* e is in table.contents[x, y] *) ReformatTable(cache.outmost, cache.table, maxWidth, update); Oberon.Collect(0) END; WebCellElems.notify := MyNotify END MyNotify;8 PROCEDURE ^MyNotify*(e: WebCellElems.Elem; update: BOOLEAN); PROCEDURE^ ReformatTableInPanel(table: Table; panel: WebPanelElems.Elem; x0: INTEGER; VAR y: INTEGER); (* reformats a cell *) PROCEDURE ReformatCell(panel: WebPanelElems.Elem; cell: TableCell; x, y: INTEGER);  (* reformats a panel with table rows *) PROCEDURE ReformatPanel(table: Table; panel: WebPanelElems.Elem; start, end: INTEGER; x0: INTEGER; VAR y: INTEGER);  (* reformats a table in a panel *) PROCEDURE ReformatTableInPanel(table: Table; panel: WebPanelElems.Elem; x0: INTEGER; VAR y: INTEGER);  (* reformats a table in a text *) PROCEDURE ReformatTable*(text: Texts.Text; table: Table; maxWidth: INTEGER; update: BOOLEAN);  PROCEDURE MyNotify*(e: WebCellElems.Elem; update: BOOLEAN);  ,8)8>Syntax10.Scn.Fnt>_8FoldElemsNew#Syntax10.Scn.Fnt VAR msg: TextFrames.InsertElemMsg; BEGIN text := NIL; pos := -1; msg.e := WebCellElems.Create(0, WebCellElems.NewText("")); Viewers.Broadcast(msg); IF Texts.ElemBase(msg.e) # NIL THEN text := Texts.ElemBase(msg.e); IF ~ ((text IS Texts.Text) OR (text IS Web.Text)) THEN text := NIL END; pos := Texts.ElemPos(msg.e); Texts.Delete(text, pos, pos + 1) END END FindCaret;8$A8#Syntax10.Scn.Fnt BEGIN IF Oberon.FocusViewer.dsc.next IS TextFrames.Frame THEN TextFrames.SetCaret(Oberon.FocusViewer.dsc.next(TextFrames.Frame), pos) END; END SetCaret;8.8#Syntax10.Scn.Fnt VAR text: Texts.Text; beg, end, time: LONGINT; BEGIN IF (s.class = Texts.Char) & (s.c = "^") THEN Oberon.GetSelection(text, beg, end, time); IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END; IF s.line # 0 THEN s.class := Texts.Inval END END END OpenParams;8 Syntax10b.Scn.Fnt 8CSyntax10.Scn.FntSyntax10i.Scn.FntP VAR curC: WebCellElems.Elem; curT: Table; myPanel: WebPanelElems.Elem; border: INTEGER; BEGIN curC := cell.first; IF cell.table = NIL THEN (* no nested table *) PanelElems.InsertElem(panel, cell.first, x, y + SHORT(curC.H DIV TextFrames.Unit), Fonts.Default, 0) ELSE border := ABS(cell.first.border); IF border # 0 THEN myPanel := WebPanelElems.Create(1) ELSE myPanel := WebPanelElems.Create(0) END; myPanel.W := LONG(cell.width) * TextFrames.Unit; myPanel.H := LONG(cell.height) * TextFrames.Unit; PanelElems.InsertElem(panel, myPanel, x, y + cell.height, Fonts.Default, 0); x := 0; y := 0; curT := cell.table; WHILE curC # NIL DO curC.border := - border; PanelElems.InsertElem(myPanel, curC, x, y + SHORT(curC.H DIV TextFrames.Unit), Fonts.Default, 0); INC(y, SHORT(curC.H DIV TextFrames.Unit)); IF curT # NIL THEN InsertTable(NIL, myPanel, curT, cellIndent, y, 0); INC(y, curT.height); curT := curT.next END; curC := curC.next END END END InsertCell0;88CSyntax10.Scn.FntVSyntax10i.Scn.FntX VAR i, j: INTEGER; x, y, h: INTEGER; oldCol: SHORTINT; BEGIN IF panel = NIL THEN (* insert into a text *) panel := WebPanelElems.Create(0); h := 0; FOR j := start TO end DO h := h + table.rowHeight[j] END; panel.H := LONG(h) * TextFrames.Unit; panel.W := LONG(table.width + 1) * TextFrames.Unit; IF h = 0 THEN RETURN END; oldCol := w.col; w.col := 0; Texts.WriteElem(w, panel); w.col := oldCol END; x := x0; y := y0; FOR j := start TO end DO FOR i := 0 TO table.columns - 1 DO IF table.contents[i, j] # NIL THEN InsertCell0(panel, table.contents[i, j], x, y) END; INC(x, table.colWidth[i]) END; x := x0; INC(y, table.rowHeight[j]); END; y0 := y END InsertRows0;8Syntax10i.Scn.Fnt,  `i8QSyntax10.Scn.FntSyntax10i.Scn.FntbG VAR y: INTEGER; start, end: INTEGER; w: Texts.Writer; BEGIN IF panel = NIL THEN Oberon.Collect(0); Texts.OpenWriter(w); w.fnt := WebCellElems.normalFont; Texts.SetColor(w, 15); Texts.Write(w, CR) END; (* Insert into a text *) y := 0; WHILE y < table.rows DO CalcRows(table, y, start, end); InsertRows0(w, panel, table, start, end, x0, y0); IF panel = NIL THEN y0 := 0; Texts.SetColor(w, 15); Texts.Write(w, CR); Texts.SetColor(w, 0) END; (* Insert into a text *) y := end + 1 END; IF panel = NIL THEN Texts.Insert(text, textPos, w.buf) END END InsertTable;8?Mh8#Syntax10.Scn.Fntvv VAR i, j: INTEGER; BEGIN x1 := -1; y1 := -1; IF table.contents[x, y] # NIL THEN x1 := x; y1 := y; RETURN END; i := x; WHILE i >= 0 DO j := y; WHILE j >= 0 DO IF (table.contents[i, j] # NIL) & (i + table.contents[i, j].colSpan > x) & (j + table.contents[i, j].rowSpan > y) THEN x1 := i; y1 := j; RETURN END; DEC(j) END; DEC(i) END END GetOverlapCell;8FP~8QSyntax10.Scn.FntSyntax10i.Scn.Fnt!72 VAR i, j, p, q, im, jm: INTEGER; text: WebCellElems.Text; curC: WebCellElems.Elem; BEGIN IF (x < 0) OR (y < 0) OR (newColSpan < 0) OR (newRowSpan < 0) THEN RETURN END; IF newColSpan = 0 THEN newColSpan := table.contents[x, y].colSpan END; IF newRowSpan = 0 THEN newRowSpan := table.contents[x, y].rowSpan END; IF (x + newColSpan > table.columns) THEN newColSpan := table.columns - x END; IF (y + newRowSpan > table.rows) THEN newRowSpan := table.rows - y END; IF (x >= table.columns) OR (y >= table.rows) OR (table.contents[x, y] = NIL) OR ((table.contents[x, y].colSpan = newColSpan) & (table.contents[x, y].rowSpan = newRowSpan)) THEN RETURN END; im := Max(newColSpan - 1, table.contents[x, y].colSpan - 1); jm := Max(newRowSpan - 1, table.contents[x, y].rowSpan - 1); FOR i := 0 TO im DO FOR j := 0 TO jm DO IF ((i # 0) OR (j # 0)) & (x + i < table.columns) & (y + j < table.rows) THEN IF (i < newColSpan) & (j < newRowSpan) THEN (* overlapped cell, set to NIL *) IF (table.contents[x + i, y + j] # NIL) & ((table.contents[x + i, y + j].colSpan > 1) OR (table.contents[x + i, y + j].rowSpan > 1)) THEN ChangeSpan(table, x + i, y + j, 1, 1) END; IF table.contents[x + i, y + j] = NIL THEN GetOverlapCell(table, x + i, y + j, p, q); IF (p # -1) & (q # -1) THEN ChangeSpan(table, p, q, 1, 1) END END; table.contents[x + i, y + j] := NIL ELSIF ((i > newColSpan - 1) OR (j > newRowSpan - 1)) & (table.contents[x + i, y + j] = NIL) THEN NEW(table.contents[x + i, y + j]); (* create empty cell (i, j) *) InitCell(table.contents[x + i, y + j]); text := WebCellElems.NewText(""); table.contents[x + i, y + j].first := WebCellElems.Create(table.border, text); table.contents[x + i, y + j].width := table.colWidth[x + i]; table.contents[x + i, y + j].height := table.rowHeight[y + j]; table.contents[x + i, y + j].first.W := LONG(table.colWidth[x + i]) * TextFrames.Unit; table.contents[x + i, y + j].first.H := LONG(table.rowHeight[y + j]) * TextFrames.Unit END END END END; table.contents[x, y].colSpan := newColSpan; table.contents[x, y].rowSpan := newRowSpan; curC := table.contents[x, y].first; WHILE curC # NIL DO curC.colSpan := newColSpan; curC.rowSpan := newRowSpan; curC := curC.next END END ChangeSpan;8=<?8#Syntax10.Scn.Fnt VAR x, y, i, j: INTEGER; newTable: Table; text: WebCellElems.Text; BEGIN IF col > table.columns THEN RETURN END; NEW(newTable); InitTable(newTable); newTable.columns := table.columns + num; newTable.rows := table.rows; newTable.rowHeight := table.rowHeight; NEW(newTable.colWidth, newTable.columns); NEW(newTable.contents, newTable.columns, newTable.rows); FOR x := 0 TO newTable.columns - 1 DO FOR y := 0 TO newTable.rows - 1 DO newTable.contents[x, y] := NIL END END; newTable.width := table.width + num * 50; newTable.height := table.height + num; FOR x := 0 TO table.columns - 1 DO IF x < col THEN newTable.colWidth[x] := table.colWidth[x] ELSE newTable.colWidth[x + num] := table.colWidth[x] END; FOR y := 0 TO table.rows - 1 DO IF x < col THEN newTable.contents[x, y] := table.contents[x, y] ELSE newTable.contents[x + num, y] := table.contents[x, y] END END END; FOR x := col TO col + num - 1 DO newTable.colWidth[x] := 50; FOR y := 0 TO table.rows - 1 DO IF newTable.contents[x, y] = NIL THEN GetOverlapCell(newTable, x, y, i, j); IF (i # -1) & (j # -1) THEN i := i + newTable.contents[i, j].colSpan ELSE i := x END ELSE i := x END; NEW(newTable.contents[i, y]); InitCell(newTable.contents[i, y]); text := WebCellElems.NewText(""); newTable.contents[i, y].first := WebCellElems.Create(table.border, text); newTable.contents[i, y].width := 50; newTable.contents[i, y].height := newTable.rowHeight[y]; newTable.contents[i, y].first.W := LONG(50) * TextFrames.Unit; newTable.contents[i, y].first.H := LONG(newTable.rowHeight[y]) * TextFrames.Unit END END; table := newTable END AddColumns;879I8#Syntax10.Scn.Fnt VAR x, y, i, j: INTEGER; newTable: Table; text: WebCellElems.Text; BEGIN IF row > table.rows THEN RETURN END; NEW(newTable); InitTable(newTable); newTable.columns := table.columns; newTable.rows := table.rows + num; newTable.colWidth := table.colWidth; NEW(newTable.rowHeight, newTable.rows); NEW(newTable.contents, newTable.columns, newTable.rows); FOR x := 0 TO newTable.columns - 1 DO FOR y := 0 TO newTable.rows - 1 DO newTable.contents[x, y] := NIL END END; newTable.width := table.width; newTable.height := table.height + num * 50; FOR y := 0 TO table.rows - 1 DO IF y < row THEN newTable.rowHeight[y] := table.rowHeight[y] ELSE newTable.rowHeight[y + num] := table.rowHeight[y] END; FOR x := 0 TO table.columns - 1 DO IF y < row THEN newTable.contents[x, y] := table.contents[x, y] ELSE newTable.contents[x, y + num] := table.contents[x, y] END END END; FOR y := row TO row + num - 1 DO newTable.rowHeight[y] := 50; FOR x := 0 TO table.columns - 1 DO IF newTable.contents[x, y] = NIL THEN GetOverlapCell(newTable, x, y, j, i); IF (i # -1) & (j # -1) THEN i := i + newTable.contents[j, i].rowSpan ELSE i := y END ELSE i := y END; NEW(newTable.contents[x, i]); InitCell(newTable.contents[x, i]); text := WebCellElems.NewText(""); newTable.contents[x, i].first := WebCellElems.Create(table.border, text); newTable.contents[x, i].width := newTable.colWidth[x]; newTable.contents[x, i].height := 50; newTable.contents[x, i].first.W := LONG(newTable.colWidth[x]) * TextFrames.Unit; newTable.contents[x, i].first.H := LONG(50) * TextFrames.Unit END END; table := newTable END AddRows;8C>8#Syntax10.Scn.Fnt9 9 VAR x, y, i, j: INTEGER; newTable: Table; text: WebCellElems.Text; filler: TableCell; BEGIN IF col >= table.columns THEN RETURN END; IF col + num > table.columns THEN num := table.columns - col END; IF table.columns - num <= 0 THEN RETURN END; NEW(newTable); InitTable(newTable); newTable.columns := table.columns - num; newTable.rows := table.rows; newTable.rowHeight := table.rowHeight; NEW(newTable.colWidth, newTable.columns); NEW(newTable.contents, newTable.columns, newTable.rows); FOR x := 0 TO newTable.columns - 1 DO FOR y := 0 TO newTable.rows - 1 DO newTable.contents[x, y] := NIL END END; newTable.width := 0; newTable.height := table.height; FOR x := 0 TO newTable.columns - 1 DO IF x < col THEN newTable.colWidth[x] := table.colWidth[x] ELSE newTable.colWidth[x] := table.colWidth[x + num] END; INC(newTable.width, newTable.colWidth[x]); FOR y := 0 TO newTable.rows - 1 DO IF x < col THEN newTable.contents[x, y] := table.contents[x, y] ELSE newTable.contents[x, y] := table.contents[x + num, y] END END END; NEW(filler); FOR x := 0 TO newTable.columns -1 DO FOR y := 0 TO newTable.rows - 1 DO IF newTable.contents[x, y] = NIL THEN newTable.contents[x, y] := filler END END END; FOR x := 0 TO newTable.columns - 1 DO FOR y := 0 TO newTable.rows - 1 DO IF (newTable.contents[x, y] # NIL) & (newTable.contents[x, y] # filler) & ((newTable.contents[x, y].colSpan > 1) OR (newTable.contents[x, y].rowSpan > 1)) THEN FOR i := 0 TO newTable.contents[x, y].colSpan - 1 DO FOR j := 0 TO newTable.contents[x, y].rowSpan - 1 DO IF ~((i = 0) & (j = 0)) & (x + i < newTable.columns) & (y + j < newTable.rows) THEN newTable.contents[x + i, y + j] := NIL END END END END END END; FOR x := 0 TO newTable.columns -1 DO FOR y := 0 TO newTable.rows - 1 DO IF newTable.contents[x, y] = filler THEN NEW(newTable.contents[x, y]); InitCell(newTable.contents[x, y]); text := WebCellElems.NewText(""); newTable.contents[x, y].first := WebCellElems.Create(table.border, text); newTable.contents[x, y].width := newTable.colWidth[x]; newTable.contents[x, y].height := newTable.rowHeight[y]; newTable.contents[x, y].first.W := LONG(newTable.colWidth[x]) * TextFrames.Unit; newTable.contents[x, y].first.H := LONG(newTable.rowHeight[y]) * TextFrames.Unit ELSIF (newTable.contents[x, y] # NIL) & (x + newTable.contents[x, y].colSpan > newTable.columns) THEN newTable.contents[x, y].colSpan := newTable.columns - x END END END; table := newTable END DeleteColumns;8=;8#Syntax10.Scn.Fnt VAR x, y, i, j: INTEGER; newTable: Table; text: WebCellElems.Text; filler: TableCell; BEGIN IF row >= table.rows THEN RETURN END; IF row + num > table.rows THEN num := table.rows - row END; IF table.rows - num <= 0 THEN RETURN END; NEW(newTable); InitTable(newTable); newTable.columns := table.columns; newTable.rows := table.rows - num; newTable.colWidth := table.colWidth; NEW(newTable.rowHeight, newTable.rows); NEW(newTable.contents, newTable.columns, newTable.rows); FOR x := 0 TO newTable.columns - 1 DO FOR y := 0 TO newTable.rows - 1 DO newTable.contents[x, y] := NIL END END; newTable.width := table.width; newTable.height := 0; FOR y := 0 TO newTable.rows - 1 DO IF y < row THEN newTable.rowHeight[y] := table.rowHeight[y] ELSE newTable.rowHeight[y] := table.rowHeight[y + num] END; INC(newTable.height, newTable.rowHeight[y]); FOR x := 0 TO newTable.columns - 1 DO IF y < row THEN newTable.contents[x, y] := table.contents[x, y] ELSE newTable.contents[x, y] := table.contents[x, y + num] END END END; NEW(filler); FOR x := 0 TO newTable.columns -1 DO FOR y := 0 TO newTable.rows - 1 DO IF newTable.contents[x, y] = NIL THEN newTable.contents[x, y] := filler END END END; FOR x := 0 TO newTable.columns -1 DO FOR y := 0 TO newTable.rows - 1 DO IF (newTable.contents[x, y] # NIL) & (newTable.contents[x, y] # filler) & ((newTable.contents[x, y].colSpan > 1) OR (newTable.contents[x, y].rowSpan > 1)) THEN FOR i := 0 TO newTable.contents[x, y].colSpan - 1 DO FOR j := 0 TO newTable.contents[x, y].rowSpan - 1 DO IF ~((i = 0) & (j = 0)) & (x + i < newTable.columns) & (y + j < newTable.rows) THEN newTable.contents[x + i, y + j] := NIL END END END END END END; FOR x := 0 TO newTable.columns -1 DO FOR y := 0 TO newTable.rows - 1 DO IF newTable.contents[x, y] = filler THEN NEW(newTable.contents[x, y]); InitCell(newTable.contents[x, y]); text := WebCellElems.NewText(""); newTable.contents[x, y].first := WebCellElems.Create(table.border, text); newTable.contents[x, y].width := newTable.colWidth[x]; newTable.contents[x, y].height := newTable.rowHeight[y]; newTable.contents[x, y].first.W := LONG(newTable.colWidth[x]) * TextFrames.Unit; newTable.contents[x, y].first.H := LONG(newTable.rowHeight[y]) * TextFrames.Unit ELSIF (newTable.contents[x, y] # NIL) & (y + newTable.contents[x, y].rowSpan > newTable.rows) THEN newTable.contents[x, y].rowSpan := newTable.rows - y END END END; table := newTable END DeleteRows;8?J8#Syntax10.Scn.Fnt VAR curC: WebCellElems.Elem; BEGIN IF (x < 0) OR (y < 0) OR (table.contents[x, y] = NIL) THEN RETURN END; curC := table.contents[x, y].first; WHILE curC # NIL DO curC.header := header; curC := curC.next END END SetHeader0;8= PROCEDURE FindCaret(VAR text: Texts.Text; VAR pos: LONGINT);  PROCEDURE SetCaret(pos: LONGINT);  PROCEDURE OpenParams(VAR s: Texts.Scanner);  PROCEDURE ^InsertTable*(text: Texts.Text; panel: WebPanelElems.Elem; table: Table; x0, y0: INTEGER; textPos: LONGINT); PROCEDURE InsertCell0(panel: WebPanelElems.Elem; cell: TableCell; x, y: INTEGER);  PROCEDURE InsertRows0(w: Texts.Writer; panel: WebPanelElems.Elem; table: Table; start, end: INTEGER; x0: INTEGER; VAR y0: INTEGER);  (* Inserts a table into a text or a panel *) PROCEDURE InsertTable*(text: Texts.Text; panel: WebPanelElems.Elem; table: Table; x0, y0: INTEGER; textPos: LONGINT);  (* returns the cell (x1, y1) in table, which overlaps (x, y) *) PROCEDURE GetOverlapCell(table: Table; x, y: INTEGER; VAR x1, y1: INTEGER);  (* changes the colspan and rowspan fields of a cell (x, y) in table *) PROCEDURE ChangeSpan(VAR table: Table; x, y, newColSpan, newRowSpan: INTEGER);  (* adds "num" columns, begining at column "col" to a table *) PROCEDURE AddColumns(VAR table: Table; col, num: INTEGER);  (* adds "num" rows, begining at row "row" to a table *) PROCEDURE AddRows(VAR table: Table; row, num: INTEGER);  (* removes "num" columns, begining at column "col" from a table *) PROCEDURE DeleteColumns(VAR table: Table; col, num: INTEGER);  (* removes "num" rows, begining at row "row" from a table *) PROCEDURE DeleteRows(VAR table: Table; row, num: INTEGER);  (* sets the header flag of the cell (x, y) in table to header *) PROCEDURE SetHeader0(VAR table: Table; x, y: INTEGER; header: BOOLEAN);  .8y81`Syntax10.Scn.FntSyntax10i.Scn.FntT Syntax10b.Scn.Fnt"`8FoldElemsNew#Syntax10.Scn.FntVAR switch : ARRAY 8 OF CHAR; BEGIN In.Open; In.Name(switch); IF In.Done THEN showTables := switch = "on" ELSE Web.LogStr("$Tables are "); IF showTables THEN Web.LogStr("on$") ELSE Web.LogStr("off$") END END END Tables;8@ +U+8{Syntax10.Scn.Fnt1Syntax10i.Scn.Fnt%T;@[ VAR s: Texts.Scanner; x, y: INTEGER; table, oldTable, father, curT: Table; text: WebCellElems.Text; text2, outmostText: Texts.Text; pos, tablePos, tableEnd: LONGINT; curC, prev, elem: WebCellElems.Elem; msg: Texts.CopyMsg; BEGIN NEW(table); InitTable(table); table.border := 0; x := 0; y := 0; Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); IF s.eot THEN Web.LogStr("Not enough parameters!$"); RETURN END; IF s.class = Texts.Int THEN x := SHORT(s.i) END; Texts.Scan(s); IF s.class = Texts.Int THEN y := SHORT(s.i) END; Texts.Scan(s); IF s.class = Texts.Int THEN table.border := SHORT(s.i) ELSIF (s.class = Texts.String) & (s.s = "TRUE") THEN table.border := 1 END; IF (x = 0) OR (y = 0) THEN Web.LogStr("parameters may not be 0!$"); RETURN END; table.columns := x; table.rows := y; NEW(table.contents, x, y); NEW(table.colWidth, table.columns); NEW(table.rowHeight, table.rows); table.width := table.columns * 50; table.height := table.rows * 50; FOR x := 0 TO table.columns - 1 DO table.colWidth[x] := 50; FOR y := 0 TO table.rows - 1 DO table.rowHeight[y] := 50; NEW(table.contents[x, y]); InitCell(table.contents[x, y]); text := WebCellElems.NewText(""); table.contents[x, y].first := WebCellElems.Create(table.border, text); table.contents[x, y].width := 50; table.contents[x, y].height := 50; table.contents[x, y].first.W := LONG(50) * TextFrames.Unit; table.contents[x, y].first.H := table.contents[x, y].first.W END END; FindCaret(text2, pos); IF text2 = NIL THEN RETURN END; IF text2 IS WebCellElems.Text THEN (* nested table *) outmostText := WebCellElems.FindOutmostText(text2(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text2(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, oldTable); FindCell(oldTable, text2(WebCellElems.Text).elem, father, x, y); (* split the cell (x, y) in father into two parts and insert the new nested table *) curC := father.contents[x, y].first; prev := NIL; curT := father.contents[x, y].table; WHILE curC.txt # text2 DO prev := curC; curC := curC.next; IF (curC.txt # text2) & (curT # NIL) THEN curT := curT.next END END; msg.e := NIL; curC.handle(curC, msg); elem := msg.e(WebCellElems.Elem); IF pos < elem.txt.len THEN Texts.Delete(elem.txt, pos, elem.txt.len) END; IF prev = NIL THEN father.contents[x, y].first := elem ELSE prev.next := elem END; prev := elem; msg.e := NIL; curC.handle(curC, msg); elem := msg.e(WebCellElems.Elem); IF 0 < pos THEN Texts.Delete(elem.txt, 0, pos) END; prev.next := elem; elem.next := curC.next; IF curT = NIL THEN table.next := NIL; father.contents[x, y].table := table ELSE table.next := curT.next; curT.next := table END; MeasureTable(oldTable, maxWidth); CopyTable(oldTable); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, oldTable, 0, 0, tablePos) (* and re-insert it *) ELSE InsertTable(text2, NIL, table, 0, 0, pos) END END Insert;8c 8mSyntax10.Scn.FntSyntax10i.Scn.Fnt.9RTVAR x, y, xF, yF: INTEGER; outmost, table, father, prevT, curT: Table; text, outmostText: Texts.Text; pos, tablePos, tableEnd: LONGINT; curC, prevC: WebCellElems.Elem; buf: Texts.Buffer; BEGIN FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF outmost # table THEN FindTable(outmost, table, prevT, father, xF, yF) ELSE father := NIL END; IF father # NIL THEN prevC := father.contents[xF, yF].first; curC := father.contents[xF, yF].first.next; curT := father.contents[xF, yF].table; WHILE curT # table DO prevC := curC; curC := curC.next; curT := curT.next END; (* assemble the cell fragments prevC & curC *) NEW(buf); Texts.OpenBuf(buf); Texts.Save(curC.txt, 0, curC.txt.len, buf); Texts.Append(prevC.txt, buf); prevC.next := curC.next; IF prevT = NIL THEN father.contents[xF, yF].table := table.next ELSE prevT.next := prevT.next.next END; IF father.contents[xF, yF].table = NIL THEN (* no more nested tables *) prevC.border := ABS(prevC.border) END END; MeasureTable(outmost, maxWidth); CopyTable(outmost); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) IF father # NIL THEN InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) END ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END Remove;8j .8#Syntax10.Scn.FntVAR table: Table; text, outmostText: Texts.Text; pos, tablePos: LONGINT; viewer: Viewers.Viewer; BEGIN warning := FALSE; FindCaret(text, pos); IF (text # NIL) & (text IS WebCellElems.Text) THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tablePos := RebuildTable(outmostText, tablePos, table); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); ReformatTable(outmostText, table, maxWidth, TRUE) ELSE IF text = NIL THEN viewer := Oberon.MarkedViewer(); IF (viewer # NIL) & (viewer.dsc # NIL) & (viewer.dsc.next # NIL) & (viewer.dsc.next IS TextFrames.Frame) THEN text := viewer.dsc.next(TextFrames.Frame).text ELSE RETURN END END; pos := 0; WHILE pos # -1 DO pos := RebuildTable(text, pos, table); IF table # NIL THEN ReformatTable(text, table, maxWidth, TRUE) END END END END Refresh;8+   S"8_Syntax10.Scn.FntSyntax10i.Scn.Fnt:QVAR s: Texts.Scanner; border, x, y: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table: Table; curC: WebCellElems.Elem; BEGIN border := 0; Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); IF s.class = Texts.Int THEN border := SHORT(s.i) END; IF border < 0 THEN Web.LogStr('parameter "border" may not be less than 0!$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); (* Set border information *) FOR x := 0 TO table.columns - 1 DO FOR y := 0 TO table.rows - 1 DO IF table.contents[x, y] # NIL THEN curC := table.contents[x, y].first; WHILE curC # NIL DO curC.border := border; curC := curC.next END END END END; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END SetBorder;8? D}8_Syntax10.Scn.FntSyntax10i.Scn.Fnt:UVAR s: Texts.Scanner; x, y, num, add, xF, yF: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table, father, prev: Table; BEGIN num := MIN(INTEGER); Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); add := -1; IF s.class = Texts.Name THEN IF (s.s[0] = "l") OR (s.s[0] = "L") THEN add := 0 ELSIF (s.s[0] = "r") OR (s.s[0] = "R") THEN add := 1 ELSE Web.LogStr('You must specify "l" for left or "r" for right.$'); RETURN END; ELSE Web.LogStr('You must specify "l" for left or "r" for right.$'); RETURN END; IF add = -1 THEN Web.LogStr("Not enough parameters!$") END; Texts.Scan(s); IF s.class = Texts.Int THEN num := SHORT(s.i) END; IF num = MIN(INTEGER) THEN num := 1 END; IF num <= 0 THEN Web.LogStr('parameter "num" may not be less or equal 0!$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF outmost # table THEN FindTable(outmost, table, prev, father, xF, yF) ELSE father := NIL END; FillDimensions(table); IF add = 1 THEN add := table.contents[x, y].colSpan END; AddColumns(table, x + add, num); IF father # NIL THEN IF prev = NIL THEN table.next := father.contents[xF, yF].table.next; father.contents[xF, yF].table := table ELSE table.next := prev.next.next; prev.next := table END ELSE outmost := table END; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END InsertColumns;8<  >x8_Syntax10.Scn.FntSyntax10i.Scn.Fnt:RVAR s: Texts.Scanner; x, y, num, add, xF, yF: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table, father, prev: Table; BEGIN num := MIN(INTEGER); Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); add := -1; IF s.class = Texts.Name THEN IF (s.s[0] = "a") OR (s.s[0] = "A") THEN add := 0 ELSIF (s.s[0] = "b") OR (s.s[0] = "B") THEN add := 1 ELSE Web.LogStr('You must specify "a" for above or "b" for below.$'); RETURN END; ELSE Web.LogStr('You must specify "a" for above or "b" for below.$'); RETURN END; IF add = -1 THEN Web.LogStr("Not enough parameters!$") END; Texts.Scan(s); IF s.class = Texts.Int THEN num := SHORT(s.i) END; IF num = MIN(INTEGER) THEN num := 1 END; IF num <= 0 THEN Web.LogStr('parameter "num" may not be less or equal 0!$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF outmost # table THEN FindTable(outmost, table, prev, father, xF, yF) ELSE father := NIL END; FillDimensions(table); IF add = 1 THEN add := table.contents[x, y].rowSpan END; AddRows(table, y + add, num); IF father # NIL THEN IF prev = NIL THEN table.next := father.contents[xF, yF].table.next; father.contents[xF, yF].table := table ELSE table.next := prev.next.next; prev.next := table END ELSE outmost := table END; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END InsertRows;8@ <#t8_Syntax10.Scn.FntSyntax10i.Scn.Fnt:U.VAR s: Texts.Scanner; x, y, num, xF, yF: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table, father, prev: Table; BEGIN num := MIN(INTEGER); Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); IF s.class = Texts.Int THEN num := SHORT(s.i) END; IF num = MIN(INTEGER) THEN num := 1 END; IF num <= 0 THEN Web.LogStr('parameter "num" may not be less or equal 0!$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF outmost # table THEN FindTable(outmost, table, prev, father, xF, yF) ELSE father := NIL END; FillDimensions(table); DeleteColumns(table, x, num); IF father # NIL THEN IF prev = NIL THEN table.next := father.contents[xF, yF].table.next; father.contents[xF, yF].table := table ELSE table.next := prev.next.next; prev.next := table END ELSE outmost := table END; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END RemoveColumns;8=  6#z8_Syntax10.Scn.FntSyntax10i.Scn.Fnt:R(VAR s: Texts.Scanner; x, y, num, xF, yF: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table, father, prev: Table; BEGIN num := MIN(INTEGER); Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); IF s.class = Texts.Int THEN num := SHORT(s.i) END; IF num = MIN(INTEGER) THEN num := 1 END; IF num <= 0 THEN Web.LogStr('parameter "num" may not be less or equal 0!$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF outmost # table THEN FindTable(outmost, table, prev, father, xF, yF) ELSE father := NIL END; FillDimensions(table); DeleteRows(table, y, num); IF father # NIL THEN IF prev = NIL THEN table.next := father.contents[xF, yF].table.next; father.contents[xF, yF].table := table ELSE table.next := prev.next.next; prev.next := table END ELSE outmost := table END; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END RemoveRows;8M  )0-8_Syntax10.Scn.FntSyntax10i.Scn.Fnt:PuVAR s: Texts.Scanner; x, y, colSpan, rowSpan, xF, yF: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table, father, prev: Table; BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); IF s.class = Texts.Int THEN colSpan := SHORT(s.i) END; Texts.Scan(s); IF s.class = Texts.Int THEN rowSpan := SHORT(s.i) END; IF (colSpan < 0) OR (rowSpan < 0) THEN Web.LogStr('parameter "colSpan" or "rowSpan" may not be less than 0!$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF outmost # table THEN FindTable(outmost, table, prev, father, xF, yF) ELSE father := NIL END; FillDimensions(table); ChangeSpan(table, x, y, colSpan, rowSpan); IF father # NIL THEN IF prev = NIL THEN table.next := father.contents[xF, yF].table.next; father.contents[xF, yF].table := table ELSE table.next := prev.next.next; prev.next := table END ELSE outmost := table END; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END CellSpan;8+   J8QSyntax10.Scn.FntSyntax10i.Scn.FntOxVAR s: Texts.Scanner; header: BOOLEAN; x, y: INTEGER; pos, tablePos: LONGINT; text, outmostText: Texts.Text; outmost, table: Table; BEGIN header := FALSE; Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); OpenParams(s); text := Elems.CmdContext; IF s.class = Texts.Name THEN IF (s.s[0] = "t") OR (s.s[0] = "T") THEN header := TRUE ELSIF (s.s[0] = "f") OR (s.s[0] = "F") THEN header := FALSE; ELSE Web.LogStr('You must specify "t" for true or "f" for false.$'); RETURN END; ELSIF (s.class = Texts.Char) & (s.c = 0X) & (text # NIL) THEN (* called from the panel *) Elems.GetBoolean(Elems.NamedElem("header", text), "Value", header) ELSE Web.LogStr('You must specify "t" for true or "f" for false.$'); RETURN END; FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tablePos := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); SetHeader0(table, x, y, header) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END SetHeader;8(  8mSyntax10.Scn.FntISyntax10i.Scn.Fnt((:RVAR x, y, x0, y0: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table: Table; BEGIN FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); FillDimensions(table); IF (x + table.contents[x, y].colSpan >= table.columns) THEN RETURN (* new cell would be out of the table *) END; GetOverlapCell(table, x + table.contents[x, y].colSpan, y, x0, y0); IF (x0 # -1) OR (y0 # -1) THEN RETURN END; x := x + table.contents[x, y].colSpan; IF (x < 0) OR (x >= table.columns) THEN RETURN (* new cell would be out of the table *) END; IF table.contents[x, y] # NIL THEN RETURN END; NEW(table.contents[x, y]); InitCell(table.contents[x, y]); table.contents[x, y].first := WebCellElems.Create(table.border, WebCellElems.NewText("")); table.contents[x, y].width := table.colWidth[x]; table.contents[x, y].height := table.rowHeight[y]; table.contents[x, y].first.W := LONG(table.colWidth[x]) * TextFrames.Unit; table.contents[x, y].first.H := LONG(table.rowHeight[y]) * TextFrames.Unit; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END InsertCell;84  8QSyntax10.Scn.FntSyntax10i.Scn.Fnt;VAR x, y: INTEGER; pos, tablePos, tableEnd: LONGINT; text, outmostText: Texts.Text; outmost, table: Table; BEGIN FindCaret(text, pos); IF text = NIL THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tableEnd := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); IF x + table.contents[x, y].colSpan = table.columns THEN table.contents[x, y] := NIL; MeasureTable(outmost, maxWidth); IF tablePos > 0 THEN DEC(tablePos) END; SetCaret(tablePos); CopyTable(outmost); Texts.Delete(outmostText, tablePos, tableEnd); (* remove the table *) InsertTable(outmostText, NIL, outmost, 0, 0, tablePos) (* and re-insert it *) ELSE Web.LogStr("Cannot remove this cell!$") END ELSE Web.LogStr("Caret is not in a table cell!$"); RETURN END END RemoveCell;8+%  8CSyntax10.Scn.FntpSyntax10i.Scn.Fnt VAR ct, outmostText, text: Texts.Text; pos, tablePos: LONGINT; outmost, table: Table; x, y: INTEGER; BEGIN ct := Elems.CmdContext; FindCaret(text, pos); IF (ct = NIL) OR (text = NIL) THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tablePos := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); Elems.SetInteger(Elems.NamedElem("columns", ct), "Value", table.columns); Elems.SetInteger(Elems.NamedElem("rows", ct), "Value", table.rows); Elems.SetInteger(Elems.NamedElem("border", ct), "Value", table.border) END END InspectTable;8%  R8CSyntax10.Scn.FntpSyntax10i.Scn.Fntl VAR ct, outmostText, text: Texts.Text; pos, tablePos: LONGINT; outmost, table: Table; x, y: INTEGER; BEGIN ct := Elems.CmdContext; FindCaret(text, pos); IF (ct = NIL) OR (text = NIL) THEN RETURN END; IF text IS WebCellElems.Text THEN outmostText := WebCellElems.FindOutmostText(text(WebCellElems.Text)); tablePos := LocateTable(outmostText, NIL, text(WebCellElems.Text).elem); tablePos := RebuildTable(outmostText, tablePos, outmost); FindCell(outmost, text(WebCellElems.Text).elem, table, x, y); Elems.SetInteger(Elems.NamedElem("colspan", ct), "Value", table.contents[x, y].first.colSpan); Elems.SetInteger(Elems.NamedElem("rowspan", ct), "Value", table.contents[x, y].first.rowSpan); Elems.SetBoolean(Elems.NamedElem("header", ct), "Value", table.contents[x, y].first.header); Elems.UpdateElem(Elems.NamedElem("header", ct)) END END InspectCell;8W (* Uses the new table implementation ("on") or the default implementation ("off") *) PROCEDURE Tables*; (** WebTables.Tables "on"|"off" *) (** data types: switch: STRING, "on" for new implementation, "off" for old implementation *)  (* Insert a table into the text at the position of the carret *) PROCEDURE Insert*; (** WebTables.Insert columns rows border *) (** data types: columns: INTEGER rows: INTEGER border: BOOLEAN OR INTEGER *)  (* Removes the table, in which cell the caret is located (should only be used for nested tables!)*) PROCEDURE Remove*; (** WebTables.Remove *)  (* Refreshes the table with the cursor or all tables in a text, if the cursor is not located in a table *) PROCEDURE Refresh*; (** WebTables.Refresh *)  (* Changes the border width of the table *) PROCEDURE SetBorder*; (** WebTables.SetBorder border*) (** data types: border: INTEGER, border = 0 results in a table without border *)  (* Insert columns into a table at the position of the carret *) PROCEDURE InsertColumns*; (** WebTables.InsertColumns "l | r" "number of columns to insert" *) (** data types: where: CHAR; l = left of the cell with the caret, r = right of the cell with the caret num: INTEGER *)  (* Insert rows into a table at the position of the carret *) PROCEDURE InsertRows*; (** WebTables.InsertRows "a | b" "number of rows to insert" *) (** data types: where: CHAR; a = above the cell with the caret, b = below the cell with the caret num: INTEGER *)  (* Removes columns into a table at the position of the carret *) PROCEDURE RemoveColumns*; (** WebTables.RemoveColumns "number of columns to remove" *) (** data types: num: INTEGER *)  (* Removes rows into a table at the position of the carret *) PROCEDURE RemoveRows*; (** WebTables.RemoveRows "number of rows to remove" *) (** data types: num: INTEGER *)  (* Changes the colSpan and/or the rowSpan of the cell containing the caret *) PROCEDURE CellSpan*; (** WebTables.CellSpan colSpan rowSpan *) (** data types: colSpan, rowSpan: INTEGER *)  (* Changes the border width of the table *) PROCEDURE SetHeader*; (** WebTables.SetHeader header*) (** data types: header: BOOLEAN, TRUE for , FALSE for -cells *)  (* Inserts a single cell into a table *) PROCEDURE InsertCell*; (** WebTables.InsertCell *)  (* Removes the cell with the caret from its table *) PROCEDURE RemoveCell*; (** WebTables.RemoveCell *)  (* "Inspector" procedures for the table *) (* Inspect the cell with the caret *) PROCEDURE InspectTable*;  (* Inspect the cell with the caret *) PROCEDURE InspectCell*;  $8+8#Syntax10.Scn.Fnt maxWidth := SHORT(TextFrames.defParc.width DIV TextFrames.Unit) - 1; showTables := TRUE; warning := FALSE; table := NIL; cache.table := NIL; WebCellElems.notify := MyNotify8|MODULE WebTables; (* Andreas Helm,  *)   (* Auxiliary module for tables *) IMPORT Display, Elems, Fonts, In, LineElems, Oberon, PanelElems, TextFrames, Texts, Viewers, Web, WebCellElems, WebPanelElems; CONST  TYPE  VAR  PROCEDURE Max(a, b: INTEGER): INTEGER;  (* Set the default values for the types *) PROCEDURE InitCell*(cell: TableCell);  PROCEDURE InitRow*(row: TableRow);  PROCEDURE InitTable*(table: Table);  (* copies all the cellfragments of a table, generates "new" WebCellElems, but keeps the old text *) PROCEDURE CopyTable(table: Table);   --- Procedures for measuring tables, cells, rows and texts ---   --- various Helper Procedures ---   --- Rebuild a table from a text ---   --- Procedures for reformatting tables ---   --- Helper procedures for editing tables ---   --- Commands for editing tables ---  BEGIN  END WebTables.