LAview.LaTeX-Struct/src/latex-struct/Table.vala

340 lines
9.8 KiB
Vala

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<weak SplitLimit?> 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<weak SplitLimit?> 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<weak SplitLimit?> 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<<BR>>
* ``[fix1 fix2 colA1 colA2 colA3 colA4 colA5 fix3 fix4 colB1 colB2 colB3 colB4 fix5 fix6]``<<BR>>
* with limits { {2, 6, 2}, {9, 12, 3} }<<BR>>
* will be splitted into 3 tables<<BR>>
* [fix1 fix2 colA1 colA2 fix3 fix4 colB1 colB2 colB3 fix5 fix6]<<BR>>
* [fix1 fix2 colA3 colA4 fix3 fix4 colB4 fix6]<<BR>>
* [fix1 fix2 colA5 fix3 fix4 fix6]<<BR>>
* 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<SplitLimit?> 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;
}
}
}
}