Hi guys, attached a patch + icon the the toolbar that implements decimal alignment in tables.
I wouldn't mind some feedback on the code, especially where i do some chopping with insets (function splitCell()), but also on the general approach. Note that this does not use dcolumn, but rather splits the column in 2 in the latex output. This produces the best results (tight tables) without the necessity of tweaking by the user. It also works nicely with tabular* things left to be done are lyx2lyx and bumping the file format. I think that we'd also like a default decimal alignment symbol in the general preferences (for now i hardcoded a .) i was sort of hoping to sneak this in before 2.0. Opinions? Ed. Ps. Patch is not against latest trunk because i cannot connect to the internet with my laptop atm...
Index: lib/ui/stdtoolbars.inc
===================================================================
--- lib/ui/stdtoolbars.inc (revision 34413)
+++ lib/ui/stdtoolbars.inc (working copy)
@@ -153,6 +153,7 @@
Item "Align left" "command-alternatives inset-modify tabular
m-align-left;inset-modify tabular align-left"
Item "Align center" "command-alternatives inset-modify tabular
m-align-center;inset-modify tabular align-center"
Item "Align right" "command-alternatives inset-modify tabular
m-align-right;inset-modify tabular align-right"
+ Item "Align on decimal" "inset-modify tabular set-align-decimal
toggle"
Separator
Item "Align top" "command-alternatives inset-modify tabular
m-valign-top;inset-modify tabular valign-top"
Item "Align middle" "command-alternatives inset-modify tabular
m-valign-middle;inset-modify tabular valign-middle"
Index: src/frontends/qt4/GuiTabular.cpp
===================================================================
--- src/frontends/qt4/GuiTabular.cpp (revision 34413)
+++ src/frontends/qt4/GuiTabular.cpp (working copy)
@@ -376,6 +376,15 @@
setParam(param_str, Tabular::UNSET_BOOKTABS);
//
+ string decimalsymbol = "none";
+ if (decimalCB->isChecked()) {
+ decimalsymbol = fromqstr(decimalSymbolLE->text());
+ if (decimalsymbol.empty())
+ decimalsymbol = "."; // default for now
+ }
+ setParam(param_str, Tabular::SET_ALIGN_DECIMAL, decimalsymbol);
+
+ //
switch (topspaceCO->currentIndex()) {
case 0:
setParam(param_str, Tabular::SET_TOP_SPACE, "none");
@@ -753,6 +762,10 @@
}
TableAlignCB->setCurrentIndex(tableValign);
+ QString const align_d = toqstr(tabular.column_info[col].align_decimal);
+ decimalCB->setChecked(!align_d.isEmpty());
+ decimalSymbolLE->setText(align_d);
+
if (!tabular.is_long_tabular) {
headerStatusCB->setChecked(false);
headerBorderAboveCB->setChecked(false);
Index: src/frontends/qt4/ui/TabularUi.ui
===================================================================
--- src/frontends/qt4/ui/TabularUi.ui (revision 34413)
+++ src/frontends/qt4/ui/TabularUi.ui (working copy)
@@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>374</width>
- <height>389</height>
+ <width>378</width>
+ <height>400</height>
</rect>
</property>
<property name="windowTitle">
@@ -15,7 +15,7 @@
<property name="sizeGripEnabled" stdset="0">
<bool>true</bool>
</property>
- <layout class="QGridLayout" name="_2">
+ <layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0">
<widget class="QTabWidget" name="TabWidget">
<property name="toolTip">
@@ -75,7 +75,7 @@
</item>
</widget>
</item>
- <item row="0" column="2" colspan="2">
+ <item row="0" column="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -149,7 +149,7 @@
</item>
</widget>
</item>
- <item row="2" column="2" colspan="2">
+ <item row="2" column="2">
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -165,15 +165,58 @@
</property>
</spacer>
</item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="multicolumnCB">
- <property name="toolTip">
- <string>Merge cells of different columns</string>
- </property>
- <property name="text">
- <string>&Multicolumn</string>
- </property>
- </widget>
+ <item row="3" column="0" colspan="4">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QCheckBox" name="multicolumnCB">
+ <property name="toolTip">
+ <string>Merge cells of different columns</string>
+ </property>
+ <property name="text">
+ <string>&Multicolumn</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>13</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="decimalCB">
+ <property name="text">
+ <string>Align on decimal:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="decimalSymbolLE">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="maxLength">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</widget>
@@ -309,19 +352,6 @@
</property>
</widget>
</item>
- <item row="4" column="1">
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>4</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
<widget class="QWidget" name="Borders">
@@ -1462,5 +1492,38 @@
<include location="local">qt_i18n.h</include>
</includes>
<resources/>
- <connections/>
+ <connections>
+ <connection>
+ <sender>decimalCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>decimalSymbolLE</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>133</x>
+ <y>146</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>260</x>
+ <y>147</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>decimalCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>decimalSymbolLE</receiver>
+ <slot>setFocus()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>181</x>
+ <y>153</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>262</x>
+ <y>159</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
</ui>
Index: src/insets/InsetTabular.cpp
===================================================================
--- src/insets/InsetTabular.cpp (revision 34413)
+++ src/insets/InsetTabular.cpp (working copy)
@@ -174,6 +174,7 @@
{ Tabular::LONGTABULAR_ALIGN_LEFT, "longtabular-align-left", false },
{ Tabular::LONGTABULAR_ALIGN_CENTER, "longtabular-align-center", false
},
{ Tabular::LONGTABULAR_ALIGN_RIGHT, "longtabular-align-right", false },
+ { Tabular::SET_ALIGN_DECIMAL, "set-align-decimal", true },
{ Tabular::LAST_ACTION, "", false }
};
@@ -502,7 +503,35 @@
}
+InsetTableCell splitCell(InsetTableCell & head, docstring const align_d, bool
& hassep)
+{
+ InsetTableCell tail = InsetTableCell(head);
+ tail.getText(0)->setMacrocontextPosition(
+ head.getText(0)->macrocontextPosition());
+ tail.setBuffer(head.buffer());
+ DocIterator dit = doc_iterator_begin(&head.buffer(), &head);
+ for (; dit; dit.forwardChar()) {
+ if (dit.inTexted() && dit.depth()==1
+ && dit.paragraph().find(align_d, false, false,
dit.pos())) {
+ break;
+ }
+ }
+
+ pit_type const psize = head.paragraphs().front().size();
+
+ if (dit) {
+ head.paragraphs().front().eraseChars(dit.pos(), psize, false);
+ hassep = true;
+ }
+
+ tail.paragraphs().front().eraseChars(0,
+ dit.pos() < psize ? dit.pos() + 1 : psize, false);
+
+ return tail;
+}
+
+
/////////////////////////////////////////////////////////////////////
//
// Tabular
@@ -517,6 +546,8 @@
multirow(Tabular::CELL_NORMAL),
alignment(LYX_ALIGN_CENTER),
valignment(LYX_VALIGN_TOP),
+ decimal_hoffset(0),
+ decimal_width(0),
voffset(0),
top_line(false),
bottom_line(false),
@@ -537,6 +568,8 @@
multirow(cs.multirow),
alignment(cs.alignment),
valignment(cs.valignment),
+ decimal_hoffset(cs.decimal_hoffset),
+ decimal_width(cs.decimal_width),
voffset(cs.voffset),
top_line(cs.top_line),
bottom_line(cs.bottom_line),
@@ -564,6 +597,8 @@
std::swap(multirow, rhs.multirow);
std::swap(alignment, rhs.alignment);
std::swap(valignment, rhs.valignment);
+ std::swap(decimal_hoffset, rhs.decimal_hoffset);
+ std::swap(decimal_width, rhs.decimal_width);
std::swap(voffset, rhs.voffset);
std::swap(top_line, rhs.top_line);
std::swap(bottom_line, rhs.bottom_line);
@@ -910,6 +945,11 @@
bool Tabular::updateColumnWidths()
{
+ vector<int> max_dwidth(ncols(), 0);
+ for(col_type c = 0; c < ncols(); ++c)
+ for(row_type r = 0; r < nrows(); ++r)
+ max_dwidth[c] = max(max_dwidth[c],
cell_info[r][c].decimal_width);
+
bool update = false;
// for each col get max of single col cells
for(col_type c = 0; c < ncols(); ++c) {
@@ -917,7 +957,8 @@
for(row_type r = 0; r < nrows(); ++r) {
idx_type const i = cellIndex(r, c);
if (columnSpan(i) == 1)
- new_width = max(new_width, cellInfo(i).width);
+ new_width = max(new_width, cellInfo(i).width
+ + max_dwidth[c] -
cellInfo(i).decimal_width);
}
if (column_info[c].width != new_width) {
@@ -1217,6 +1258,16 @@
break;
}
+ col_type const c = cellColumn(cell);
+ if (!isMultiColumn(cell)
+ && !column_info[c].align_decimal.empty()
+ && cellInfo(cell).decimal_width!=0) {
+ int max_dhoffset = 0;
+ for(row_type r = 0; r < row_info.size() ; ++r)
+ max_dhoffset = max(max_dhoffset,
cell_info[r][c].decimal_hoffset);
+ x = WIDTH_OF_LINE + max_dhoffset -
cellInfo(cell).decimal_hoffset;
+ }
+
return x;
}
@@ -1307,6 +1358,7 @@
<< write_attribute("valignment", column_info[c].valignment)
<< write_attribute("width",
column_info[c].p_width.asString())
<< write_attribute("special", column_info[c].align_special)
+ << write_attribute("align_decimal",
column_info[c].align_decimal)
<< ">\n";
}
for (row_type r = 0; r < nrows(); ++r) {
@@ -1413,6 +1465,7 @@
getTokenValue(line, "valignment", column_info[c].valignment);
getTokenValue(line, "width", column_info[c].p_width);
getTokenValue(line, "special", column_info[c].align_special);
+ getTokenValue(line, "align_decimal",
column_info[c].align_decimal);
}
for (row_type i = 0; i < nrows(); ++i) {
@@ -1563,11 +1616,12 @@
Tabular::idx_type Tabular::columnSpan(idx_type cell) const
{
row_type const row = cellRow(cell);
- col_type column = cellColumn(cell) + 1;
- while (column < ncols() && isPartOfMultiColumn(row, column))
- ++column;
+ col_type const col = cellColumn(cell);
+ int span = 1;
+ while (col + span < ncols() && isPartOfMultiColumn(row, col + span))
+ ++span;
- return column - cellColumn(cell);
+ return span;
}
@@ -2048,8 +2102,18 @@
|| ((colright || nextcolleft) && !rightLine(cell) &&
!nextcellleft)
|| (!colright && !nextcolleft && (rightLine(cell) ||
nextcellleft))
|| (coldouble != celldouble);
+
+ ismulticol |= !column_info[c].align_decimal.empty()
+ && cellInfo(cell).decimal_width==0;
+
+ // since decimally aligned cols = 2 latex cols
+ int latexcolspan = columnSpan(cell);
+ for(col_type col = c; col < c + columnSpan(cell); ++col)
+ if (!column_info[col].align_decimal.empty())
+ ++latexcolspan;
+
if (ismulticol) {
- os << "\\multicolumn{" << columnSpan(cell) << "}{";
+ os << "\\multicolumn{" << latexcolspan << "}{";
if (c ==0 && leftLine(cell))
os << '|';
if (!cellInfo(cell).align_special.empty()) {
@@ -2360,7 +2424,22 @@
newrp.inTableCell = (getAlignment(cell) == LYX_ALIGN_BLOCK)
? OutputParams::PLAIN
: OutputParams::ALIGNED;
- ret += inset->latex(os, newrp);
+
+ if (!isMultiColumn(cell) &&
!column_info[c].align_decimal.empty()
+ && cellInfo(cell).decimal_width!=0) {
+ // copy cell and split in 2
+ InsetTableCell head =
InsetTableCell(*cellInset(cell).get());
+ head.getText(0)->setMacrocontextPosition(
+
cellInset(cell)->getText(0)->macrocontextPosition());
+ head.setBuffer(buffer());
+ bool hassep = false;
+ InsetTableCell tail = splitCell(head,
column_info[c].align_decimal, hassep);
+ head.latex(os, newrp);
+ os << '&';
+ ret += tail.latex(os, newrp);
+ } else
+ ret += inset->latex(os, newrp);
+
runparams.encoding = newrp.encoding;
if (rtl)
os << '}';
@@ -2461,6 +2540,9 @@
os << '|';
if (!column_info[c].align_special.empty()) {
os << column_info[c].align_special;
+ } else if (!column_info[c].align_decimal.empty()) {
+ os << "r...@{\\extracolsep{0pt}"
+ << column_info[c].align_decimal << "}l";
} else {
if (!column_info[c].p_width.zero()) {
switch (column_info[c].alignment) {
@@ -3235,6 +3317,32 @@
// FIXME(?): do we need a second metrics call?
TextMetrics const & tm =
mi.base.bv->textMetrics(tabular.cellInset(cell)->getText(0));
+
+ // determine horiz offset because of decimal align (if
necessary)
+ int decimal_hoffset = 0;
+ int decimal_width = 0;
+ if (!tabular.column_info[c].align_decimal.empty()
+ && !tabular.isMultiColumn(cell)) {
+ // make a copy which we will split in 2
+ InsetTableCell head =
InsetTableCell(*tabular.cellInset(cell).get());
+ head.getText(0)->setMacrocontextPosition(
+
tabular.cellInset(cell)->getText(0)->macrocontextPosition());
+ head.setBuffer(tabular.buffer());
+ // split in 2 and calculate width of each part
+ bool hassep = false;
+ InsetTableCell tail =
+ splitCell(head,
tabular.column_info[c].align_decimal, hassep);
+ Dimension dim1;
+ head.metrics(m, dim1);
+ decimal_hoffset = dim1.width();
+ if (hassep) {
+ tail.metrics(m, dim1);
+ decimal_width = dim1.width();
+ }
+ }
+ tabular.cell_info[r][c].decimal_hoffset =
decimal_hoffset;
+ tabular.cell_info[r][c].decimal_width = decimal_width;
+
// with LYX_VALIGN_BOTTOM the descent is relative to
the last par
// = descent of text in last par + TEXT_TO_INSET_OFFSET:
int const lastpardes = tm.last().second->descent()
@@ -4040,6 +4148,11 @@
status.clear();
return true;
+ case Tabular::SET_ALIGN_DECIMAL: {
+ Tabular::col_type col = tabular.cellColumn(cur.idx());
+
status.setOnOff(!tabular.column_info[col].align_decimal.empty());
+ }
+
case Tabular::MULTICOLUMN:
// If a row is set as longtable caption, it must not be
allowed
// to unset that this row is a multicolumn.
@@ -5245,6 +5358,20 @@
break;
}
+ case Tabular::SET_ALIGN_DECIMAL: {
+ for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
+ string ds = value;
+ if (ds=="toggle") {
+ if
(tabular.column_info[c].align_decimal.empty())
+ ds = "."; // default for now
+ else
+ ds = "";
+ }
+ tabular.column_info[c].align_decimal = from_ascii(ds);
+ }
+ break;
+ }
+
// dummy stuff just to avoid warnings
case Tabular::LAST_ACTION:
break;
Index: src/insets/InsetTabular.h
===================================================================
--- src/insets/InsetTabular.h (revision 34413)
+++ src/insets/InsetTabular.h (working copy)
@@ -190,6 +190,8 @@
///
LONGTABULAR_ALIGN_RIGHT,
///
+ SET_ALIGN_DECIMAL,
+ ///
LAST_ACTION
};
///
@@ -490,6 +492,10 @@
LyXAlignment alignment;
///
VAlignment valignment;
+ /// width of the part before the decimal
+ int decimal_hoffset;
+ /// width of the decimal part
+ int decimal_width;
///
int voffset;
///
@@ -570,6 +576,8 @@
Length p_width;
///
docstring align_special;
+ ///
+ docstring align_decimal;
};
///
typedef std::vector<ColumnData> column_vector;
@@ -944,6 +952,9 @@
std::string const featureAsString(Tabular::Feature feature);
+/// Split cell on decimal symbol
+InsetTableCell splitCell(InsetTableCell & head, docstring const decimal_sym,
bool & hassep);
+
} // namespace lyx
#endif // INSET_TABULAR_H
<<attachment: tabular-feature_set-align-decimal_toggle.png>>
