namespace LAview { /** * Tables and its components in the document. */ namespace Table { /** * Any error at ``ATable`` splitting. */ public errordomain SplitError { /** * ``ATable`` isn't a child of the {@link Glob}. */ ISNT_CHILD, /** * Any errors in the split indexes. */ INDEX_ERROR, /** * Any other error. */ OTHER, } /** * Any Table in the LaTeX document. */ public abstract class ATable : ADoc { /** * Align of the table. * * Possible values: 't', 'b'. */ public char align { get; set; } /** * Style of the {@link AddSpace}/{@link Subtable}. */ public AddSpaces.Style style { get; set; } /** * Parameters of columns. */ public ColParams params { get; set; default = new ColParams (); } /** * Main subtable. */ public Subtable table { get; set; default = new Subtable (); } /** * First Header. */ public Subtable first_header { get; set; default = new Subtable (); } /** * Header. */ public Subtable header { get; set; default = new Subtable (); } /** * Footer. */ public Subtable footer { get; set; default = new Subtable (); } /** * Last Footer. */ public Subtable last_footer { get; set; default = new Subtable (); } protected ATable () {} /** * Gets a copy of the ``ATable``. */ public override IDoc copy () { var clone = Object.new (get_type ()) as ATable; clone.align = align; clone.style = style; clone.params = params.copy () as ColParams; clone.table = table.copy () as Subtable; clone.first_header = first_header.copy () as Subtable; clone.header = header.copy () as Subtable; clone.footer = footer.copy () as Subtable; clone.last_footer = last_footer.copy () as Subtable; return clone; } /** * Generates LaTeX string for the ``ATable``. */ public override string generate () { assert (false); return ""; } /** * Removes {@link Cell}-s in the column by specified index. * * @param index index of column to remove. * @param line_style {@link Row.OpLineStyle} of the operation. */ public void remove_col (int index, Row.OpLineStyle line_style = Row.OpLineStyle.BORDER_DBLLINES) { if (index >= params.size) return; var param = params[index]; if ((line_style & Row.OpLineStyle.VBORDER) != 0 && param.align != "") { if (params.size > 1) { if (index == 0) params[1].nllines = param.nllines; else if (index == params.size - 1) params[params.size - 2].nrlines = param.nrlines; } } if ((line_style & Row.OpLineStyle.VDBLLINES) != 0) { if (index > 0 && index < params.size - 1) { var prev = params[index - 1], next = params[index + 1]; next.nllines = prev.nrlines != 0 || next.nllines != 0 ? 1 : 0; prev.nrlines = 0; } } params.remove_at (index); first_header.remove_col (index, line_style); header.remove_col (index, line_style); footer.remove_col (index, line_style); last_footer.remove_col (index, line_style); table.remove_col (index, line_style); } /** * Clones column of {@link Cell}-s by specified indexes. * * @param src_index source position of the column. * @param dest_index destination to clone the column. * @param multicol preserve multicolumn property or not. * @param line_style {@link Row.OpLineStyle} of the operation. */ public void clone_col (int src_index, int dest_index, bool multicol, Row.OpLineStyle line_style = Row.OpLineStyle.BORDER_DBLLINES) { if (src_index >= params.size || dest_index > params.size) return; var param = params[src_index].copy () as ColParam; if ((Row.OpLineStyle.VBORDER & line_style) != 0) { if (dest_index >= params.size) { var last_param = params[params.size - 1]; if (last_param.align != "") param.nrlines = last_param.nrlines; } else { var first_param = params[0]; if (dest_index == 0 && first_param.align != "") param.nllines = first_param.nllines; } } if ((Row.OpLineStyle.VDBLLINES & line_style) != 0) { int prev_index; bool prev_edit = false; if (dest_index < params.size) { prev_index = dest_index > 0 ? dest_index - 1 : 0; if (prev_index > 0) prev_edit = true; var dest_param = params[dest_index]; dest_param.nllines = param.nrlines != 0 || dest_param.nllines != 0 ? 1 : 0; param.nrlines = 0; } else { prev_edit = true; prev_index = params.size - 1; } if (prev_edit) { var prev_param = params[prev_index]; param.nllines = prev_param.nrlines != 0 || param.nllines != 0 ? 1 : 0; prev_param.nrlines = 0; } } params.insert (dest_index, param); first_header.clone_col (src_index, dest_index, multicol, line_style); header.clone_col (src_index, dest_index, multicol, line_style); footer.clone_col (src_index, dest_index, multicol, line_style); last_footer.clone_col (src_index, dest_index, multicol, line_style); table.clone_col (src_index, dest_index, multicol, line_style); } /** * Bounds of the ``ATable`` to split. */ public struct SplitLimit { /** * First column index [0; last]. */ uint first; /** * Last column index [first; ncols - 1]. */ uint last; /** * Maximum of columns per page [1; ncols]. */ uint max_cols; } bool check_limits (List sorted_limits) { /* check nearby limits */ for (var i = 1; i < sorted_limits.length(); ++i) if (sorted_limits.nth_data(i - 1).last >= sorted_limits.nth_data(i).first || sorted_limits.nth_data(i).first > sorted_limits.nth_data(i).last) return false; /* check limits of the first and last elements */ if (sorted_limits.nth_data(0).first > sorted_limits.nth_data(0).last || params.size <= sorted_limits.nth_data(sorted_limits.length() - 1).last) return false; return true; } uint [] get_indexes (List sorted_limits) { var lim_indexes = new uint[sorted_limits.length()]; for (var i = 0; i < sorted_limits.length(); ++i) lim_indexes[i] = sorted_limits.nth_data(i).first; return lim_indexes; } ATable? split_table (List sorted_limits, uint [] lim_indexes, Row.OpLineStyle line_style) { var return_table = copy () as ATable; bool split_finish = true; /* removing spare columns */ for (uint i = sorted_limits.length() - 1; i < sorted_limits.length(); --i) { // group for (uint j = sorted_limits.nth_data(i).last; j >= lim_indexes[i] + sorted_limits.nth_data(i).max_cols && j <= sorted_limits.nth_data(i).last; --j) return_table.remove_col ((int)j, line_style); for (uint j = lim_indexes[i] - 1; j >= sorted_limits.nth_data(i).first && j < lim_indexes[i]; --j) return_table.remove_col ((int)j, line_style); /* count indexes */ if (lim_indexes[i] <= sorted_limits.nth_data(i).last) { split_finish = false; lim_indexes[i] += sorted_limits.nth_data(i).max_cols; } } /* did any indexes updated */ if (split_finish) return null; return return_table; } /** * Split an ``ATable`` into several ``ATable``s by columns according to the limits. * * For example: table<
> * ``[fix1 fix2 colA1 colA2 colA3 colA4 colA5 fix3 fix4 colB1 colB2 colB3 colB4 fix5 fix6]``<
> * with limits { {2, 6, 2}, {9, 12, 3} }<
> * will be splitted into 3 tables<
> * [fix1 fix2 colA1 colA2 fix3 fix4 colB1 colB2 colB3 fix5 fix6]<
> * [fix1 fix2 colA3 colA4 fix3 fix4 colB4 fix6]<
> * [fix1 fix2 colA5 fix3 fix4 fix6]<
> * 3rd param 'limits'. For all elements following conditions should be satisfied. * last[i] < first[i+1], 0 <= first[i] <= last[i] <= ncols-1, 1 <= max_cols <= ncols. * * @param glob {@link Glob} document with a ``ATable``. * @param limits array of {@link SplitLimit}s. * @param delimiter any LaTeX code to separate new tables, '\\', for ex * @param line_style {@link Row.OpLineStyle} of the operation. * * @return number of ``ATable``s the table splitted to. */ public uint split (Glob glob, List limits, string delimiter = "", Row.OpLineStyle line_style = Row.OpLineStyle.BORDER_DBLLINES) throws SplitError { /* is table a child of glob */ var glob_index = glob.index_of (this); if (glob_index == -1) throw new SplitError.ISNT_CHILD (_("2nd param (ATable) isn't a child of the 1st (Glob).")); /* sorting limits */ var sorted_limits = limits.copy (); sorted_limits.sort ((a, b) => { if (a.first < b.first) return -1; if (a.first > b.first) return 1; return 0; }); /* checking limits for intersections */ if (!check_limits (sorted_limits)) throw new SplitError.INDEX_ERROR (_("3rd param (limits) is incorrect. Read the manual.")); /* split the table on several tables inserting them before glob_index + 1 */ var lim_indexes = get_indexes (sorted_limits); ATable temp_table; uint result = 0; var part_idx = glob_index + 1; while (null != (temp_table = split_table (sorted_limits, lim_indexes, line_style))) { if (delimiter != "" && part_idx != glob_index + 1) { glob.insert (part_idx++, new Text(delimiter)); ++result; } glob.insert (part_idx++, temp_table); ++result; } /* remove table from the doc */ if (result != 0) glob.remove_at (glob_index); else throw new SplitError.OTHER (_("Cann't split the table. Read the manual.")); return result; } } } }