More sensible longtable caption handling (needed for bug #7412)

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@40522 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Georg Baum 2011-12-18 21:27:17 +00:00
parent b7fbcd0b30
commit efa0f19836
9 changed files with 254 additions and 108 deletions

View File

@ -11,6 +11,13 @@ adjustments are made to tex2lyx and bugs are fixed in lyx2lyx.
-----------------------
2011-12-18 Georg Baum <Georg.Baum@post.rwth-aachen.de>
* Format incremented to 421 (r40522)
The caption flag of longtable rows is no longer exclusive to the head
and foot flags, since captions can occur in any of the two heads and
two foots. Before, captions were implicitly in head or firsthead.
For Docbook and XHTML output the caption flag "wins" over head/foot.
2011-12-12 Julien Rioux <jrioux@lyx.org>
* Format incremented to 420 (r40484)
New buffer param \biblio_style to specify a document-wide

View File

@ -231,11 +231,12 @@ initials
\bullet 1 1 34 -1
\bullet 2 2 35 -1
\bullet 3 2 7 -1
\tracking_changes false
\tracking_changes true
\output_changes false
\html_math_output 0
\html_css_as_file 0
\html_be_strict false
\author -195340706 "Georg Baum"
\end_header
\begin_body
@ -6118,7 +6119,38 @@ reference "sec:Longtables"
\end_inset
.
Only one table row can contain the caption.
\change_inserted -195340706 1324242393
A caption must be put into one of
\family sans
First
\begin_inset space ~
\end_inset
header
\family default
,
\family sans
Header
\family default
,
\family sans
Footer
\family default
and
\family sans
Fast
\begin_inset space ~
\end_inset
footer
\family default
.
Each kind of footer and header may only contain one
\change_deleted -195340706 1324242398
Only one table row can contain the
\change_unchanged
caption.
\end_layout
\begin_layout Standard

View File

@ -983,7 +983,6 @@ def revert_multirow(document):
numrows = int(numrows)
numcols = int(numcols)
except:
document.warning(numrows)
document.warning("Unable to determine rows and columns!")
begin_table = end_table
continue
@ -2222,7 +2221,6 @@ def revert_multirowOffset(document):
numrows = int(numrows)
numcols = int(numcols)
except:
document.warning(numrows)
document.warning("Unable to determine rows and columns!")
begin_table = end_table
continue

View File

@ -25,8 +25,8 @@ import sys, os
# Uncomment only what you need to import, please.
from parser_tools import del_token, find_token, find_end_of_inset, get_value, \
get_quoted_value
from parser_tools import del_token, find_token, find_end_of, find_end_of_inset, \
get_option_value, get_value, get_quoted_value, set_option_value
#from parser_tools import find_token, find_end_of, find_tokens, \
#find_token_exact, find_end_of_inset, find_end_of_layout, \
@ -220,7 +220,7 @@ def revert_australian(document):
else:
document.body[j] = document.body[j].replace("\\lang australian", "\\lang english")
j += 1
def convert_biblio_style(document):
"Add a sensible default for \\biblio_style based on the citation engine."
@ -261,6 +261,77 @@ def revert_biblio_style(document):
i = j
def handle_longtable_captions(document, forward):
begin_table = 0
while True:
begin_table = find_token(document.body, '<lyxtabular version=', begin_table)
if begin_table == -1:
break
end_table = find_end_of(document.body, begin_table, '<lyxtabular', '</lyxtabular>')
if end_table == -1:
document.warning("Malformed LyX document: Could not find end of table.")
begin_table += 1
continue
fline = find_token(document.body, "<features", begin_table, end_table)
if fline == -1:
document.warning("Can't find features for inset at line " + str(begin_table))
begin_table += 1
continue
p = document.body[fline].find("islongtable")
if p == -1:
# no longtable
begin_table += 1
continue
numrows = get_option_value(document.body[begin_table], "rows")
try:
numrows = int(numrows)
except:
document.warning(document.body[begin_table])
document.warning("Unable to determine rows!")
begin_table = end_table
continue
begin_row = begin_table
for row in range(numrows):
begin_row = find_token(document.body, '<row', begin_row, end_table)
if begin_row == -1:
document.warning("Can't find row " + str(row + 1))
break
end_row = find_end_of(document.body, begin_row, '<row', '</row>')
if end_row == -1:
document.warning("Can't find end of row " + str(row + 1))
break
if forward:
if (get_option_value(document.body[begin_row], 'caption') == 'true' and
get_option_value(document.body[begin_row], 'endfirsthead') != 'true' and
get_option_value(document.body[begin_row], 'endhead') != 'true' and
get_option_value(document.body[begin_row], 'endfoot') != 'true' and
get_option_value(document.body[begin_row], 'endlastfoot') != 'true'):
document.body[begin_row] = set_option_value(document.body[begin_row], 'caption', 'true", endfirsthead="true')
elif get_option_value(document.body[begin_row], 'caption') == 'true':
if get_option_value(document.body[begin_row], 'endfirsthead') == 'true':
document.body[begin_row] = set_option_value(document.body[begin_row], 'endfirsthead', 'false')
if get_option_value(document.body[begin_row], 'endhead') == 'true':
document.body[begin_row] = set_option_value(document.body[begin_row], 'endhead', 'false')
if get_option_value(document.body[begin_row], 'endfoot') == 'true':
document.body[begin_row] = set_option_value(document.body[begin_row], 'endfoot', 'false')
if get_option_value(document.body[begin_row], 'endlastfoot') == 'true':
document.body[begin_row] = set_option_value(document.body[begin_row], 'endlastfoot', 'false')
begin_row = end_row
# since there could be a tabular inside this one, we
# cannot jump to end.
begin_table += 1
def convert_longtable_captions(document):
"Add a firsthead flag to caption rows"
handle_longtable_captions(document, True)
def revert_longtable_captions(document):
"remove head/foot flag from caption rows"
handle_longtable_captions(document, False)
##
# Conversion hub
#
@ -274,9 +345,11 @@ convert = [
[418, []],
[419, []],
[420, [convert_biblio_style]],
[421, [convert_longtable_captions]],
]
revert = [
[420, [revert_longtable_captions]],
[419, [revert_biblio_style]],
[418, [revert_australian]],
[417, [revert_justification]],

View File

@ -315,6 +315,15 @@ def get_option_value(line, option):
return m.group(1)
def set_option_value(line, option, value):
rx = '(' + option + '\s*=\s*")[^"]+"'
rx = re.compile(rx)
m = rx.search(line)
if not m:
return line
return re.sub(rx, '\g<1>' + value + '"', line)
def del_token(lines, token, start, end = 0):
""" del_token(lines, token, start, end) -> int

View File

@ -1906,45 +1906,49 @@ bool Tabular::getLTNewPage(row_type row) const
}
bool Tabular::haveLTHead() const
bool Tabular::haveLTHead(bool withcaptions) const
{
if (!is_long_tabular)
return false;
for (row_type i = 0; i < nrows(); ++i)
if (row_info[i].endhead)
if (row_info[i].endhead &&
(withcaptions || !row_info[i].caption))
return true;
return false;
}
bool Tabular::haveLTFirstHead() const
bool Tabular::haveLTFirstHead(bool withcaptions) const
{
if (!is_long_tabular || endfirsthead.empty)
return false;
for (row_type r = 0; r < nrows(); ++r)
if (row_info[r].endfirsthead)
if (row_info[r].endfirsthead &&
(withcaptions || !row_info[r].caption))
return true;
return false;
}
bool Tabular::haveLTFoot() const
bool Tabular::haveLTFoot(bool withcaptions) const
{
if (!is_long_tabular)
return false;
for (row_type r = 0; r < nrows(); ++r)
if (row_info[r].endfoot)
if (row_info[r].endfoot &&
(withcaptions || !row_info[r].caption))
return true;
return false;
}
bool Tabular::haveLTLastFoot() const
bool Tabular::haveLTLastFoot(bool withcaptions) const
{
if (!is_long_tabular || endlastfoot.empty)
return false;
for (row_type r = 0; r < nrows(); ++r)
if (row_info[r].endlastfoot)
if (row_info[r].endlastfoot &&
(withcaptions || !row_info[r].caption))
return true;
return false;
}
@ -1959,6 +1963,11 @@ Tabular::idx_type Tabular::setLTCaption(row_type row, bool what)
setBottomLine(i, false);
setLeftLine(i, false);
setRightLine(i, false);
if (!row_info[row].endfirsthead && !row_info[row].endhead &&
!row_info[row].endfoot && !row_info[row].endlastfoot) {
setLTHead(row, true, endfirsthead, true);
row_info[row].endfirsthead = true;
}
} else {
unsetMultiColumn(i);
// When unsetting a caption row, also all existing
@ -1975,13 +1984,34 @@ bool Tabular::ltCaption(row_type row) const
}
bool Tabular::haveLTCaption() const
bool Tabular::haveLTCaption(CaptionType captiontype) const
{
if (!is_long_tabular)
return false;
for (row_type r = 0; r < nrows(); ++r)
if (row_info[r].caption)
return true;
for (row_type r = 0; r < nrows(); ++r) {
if (row_info[r].caption) {
switch (captiontype) {
case CAPTION_FIRSTHEAD:
if (row_info[r].endfirsthead)
return true;
break;
case CAPTION_HEAD:
if (row_info[r].endhead)
return true;
break;
case CAPTION_FOOT:
if (row_info[r].endfoot)
return true;
break;
case CAPTION_LASTFOOT:
if (row_info[r].endlastfoot)
return true;
break;
case CAPTION_ANY:
return true;
}
}
}
return false;
}
@ -2355,17 +2385,7 @@ void Tabular::TeXLongtableHeaderFooter(otexstream & os,
if (!is_long_tabular)
return;
// caption handling
// the caption must be output before the headers
if (haveLTCaption()) {
for (row_type r = 0; r < nrows(); ++r) {
if (row_info[r].caption)
TeXRow(os, r, runparams);
}
}
// output first header info
// first header must be output before the header, otherwise the
// correct caption placement becomes really weird
if (haveLTFirstHead()) {
if (endfirsthead.topDL)
os << "\\hline\n";
@ -2487,7 +2507,7 @@ void Tabular::TeXRow(otexstream & os, row_type row,
os << "\\textFR{";
else if (lang == "arabic_arabi")
os << "\\textAR{";
// currently, remaning RTL languages are
// currently, remaining RTL languages are
// arabic_arabtex and hebrew
else
os << "\\R{";
@ -2537,13 +2557,7 @@ void Tabular::TeXRow(otexstream & os, row_type row,
os << " &\n";
}
}
if (row_info[row].caption && !endfirsthead.empty && !haveLTFirstHead())
// if no first header and no empty first header is used,
// the caption needs to be terminated by \endfirsthead
// (bug 6057)
os << "\\endfirsthead";
else
os << "\\tabularnewline";
os << "\\tabularnewline";
if (row_info[row].bottom_space_default) {
if (use_booktabs)
os << "\\addlinespace";
@ -2809,6 +2823,7 @@ int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
//+---------------------------------------------------------------------
// output caption info
// The caption flag wins over head/foot
if (haveLTCaption()) {
os << "<caption>\n";
++ret;
@ -2821,11 +2836,12 @@ int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
++ret;
}
// output header info
if (haveLTHead() || haveLTFirstHead()) {
if (haveLTHead(false) || haveLTFirstHead(false)) {
os << "<thead>\n";
++ret;
for (row_type r = 0; r < nrows(); ++r) {
if (row_info[r].endhead || row_info[r].endfirsthead) {
if ((row_info[r].endhead || row_info[r].endfirsthead) &&
!row_info[r].caption) {
ret += docbookRow(os, r, runparams);
}
}
@ -2833,11 +2849,12 @@ int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
++ret;
}
// output footer info
if (haveLTFoot() || haveLTLastFoot()) {
if (haveLTFoot(false) || haveLTLastFoot(false)) {
os << "<tfoot>\n";
++ret;
for (row_type r = 0; r < nrows(); ++r) {
if (row_info[r].endfoot || row_info[r].endlastfoot) {
if ((row_info[r].endfoot || row_info[r].endlastfoot) &&
!row_info[r].caption) {
ret += docbookRow(os, r, runparams);
}
}
@ -2943,6 +2960,7 @@ docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
}
xs << html::StartTag("div", "class='longtable' style='text-align: " + align + ";'")
<< html::CR();
// The caption flag wins over head/foot
if (haveLTCaption()) {
xs << html::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'")
<< html::CR();
@ -2956,30 +2974,32 @@ docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
xs << html::StartTag("table") << html::CR();
// output header info
bool const havefirsthead = haveLTFirstHead();
bool const havefirsthead = haveLTFirstHead(false);
// if we have a first head, then we are going to ignore the
// headers for the additional pages, since there aren't any
// in XHTML. this test accomplishes that.
bool const havehead = !havefirsthead && haveLTHead();
bool const havehead = !havefirsthead && haveLTHead(false);
if (havehead || havefirsthead) {
xs << html::StartTag("thead") << html::CR();
for (row_type r = 0; r < nrows(); ++r) {
if ((havefirsthead && row_info[r].endfirsthead)
|| (havehead && row_info[r].endhead)) {
if (((havefirsthead && row_info[r].endfirsthead) ||
(havehead && row_info[r].endhead)) &&
!row_info[r].caption) {
ret += xhtmlRow(xs, r, runparams, true);
}
}
xs << html::EndTag("thead") << html::CR();
}
// output footer info
bool const havelastfoot = haveLTLastFoot();
bool const havelastfoot = haveLTLastFoot(false);
// as before.
bool const havefoot = !havelastfoot && haveLTFoot();
bool const havefoot = !havelastfoot && haveLTFoot(false);
if (havefoot || havelastfoot) {
xs << html::StartTag("tfoot") << html::CR();
for (row_type r = 0; r < nrows(); ++r) {
if ((havelastfoot && row_info[r].endlastfoot)
|| (havefoot && row_info[r].endfoot)) {
if (((havelastfoot && row_info[r].endlastfoot) ||
(havefoot && row_info[r].endfoot)) &&
!row_info[r].caption) {
ret += xhtmlRow(xs, r, runparams);
}
}
@ -4583,10 +4603,9 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
break;
// every row can only be one thing:
// either a footer or header or caption
// either a footer or header
case Tabular::SET_LTFIRSTHEAD:
status.setEnabled(sel_row_start == sel_row_end
&& !tabular.ltCaption(sel_row_start));
status.setEnabled(sel_row_start == sel_row_end);
status.setOnOff(tabular.getRowOfLTFirstHead(sel_row_start, dummyltt));
break;
@ -4595,8 +4614,7 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
break;
case Tabular::SET_LTHEAD:
status.setEnabled(sel_row_start == sel_row_end
&& !tabular.ltCaption(sel_row_start));
status.setEnabled(sel_row_start == sel_row_end);
status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
break;
@ -4605,8 +4623,7 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
break;
case Tabular::SET_LTFOOT:
status.setEnabled(sel_row_start == sel_row_end
&& !tabular.ltCaption(sel_row_start));
status.setEnabled(sel_row_start == sel_row_end);
status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
break;
@ -4615,8 +4632,7 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
break;
case Tabular::SET_LTLASTFOOT:
status.setEnabled(sel_row_start == sel_row_end
&& !tabular.ltCaption(sel_row_start));
status.setEnabled(sel_row_start == sel_row_end);
status.setOnOff(tabular.getRowOfLTLastFoot(sel_row_start, dummyltt));
break;
@ -4628,22 +4644,39 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
status.setOnOff(tabular.getLTNewPage(sel_row_start));
break;
// only one row can be the caption
// only one row in head/firsthead/foot/lasthead can be the caption
// and a multirow cannot be set as caption
case Tabular::SET_LTCAPTION:
case Tabular::UNSET_LTCAPTION:
case Tabular::TOGGLE_LTCAPTION:
status.setEnabled(sel_row_start == sel_row_end
&& !tabular.getRowOfLTFirstHead(sel_row_start, dummyltt)
&& !tabular.getRowOfLTHead(sel_row_start, dummyltt)
&& !tabular.getRowOfLTFoot(sel_row_start, dummyltt)
&& !tabular.getRowOfLTLastFoot(sel_row_start, dummyltt)
&& (!tabular.haveLTCaption()
|| tabular.ltCaption(sel_row_start))
&& (!tabular.getRowOfLTFirstHead(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_FIRSTHEAD))
&& (!tabular.getRowOfLTHead(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_HEAD))
&& (!tabular.getRowOfLTFoot(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_FOOT))
&& (!tabular.getRowOfLTLastFoot(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_LASTFOOT))
&& !tabular.isMultiRow(sel_row_start));
status.setOnOff(tabular.ltCaption(sel_row_start));
break;
case Tabular::UNSET_LTCAPTION:
status.setEnabled(sel_row_start == sel_row_end && tabular.ltCaption(sel_row_start));
break;
case Tabular::TOGGLE_LTCAPTION:
status.setEnabled(sel_row_start == sel_row_end && (tabular.ltCaption(sel_row_start)
|| ((!tabular.getRowOfLTFirstHead(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_FIRSTHEAD))
&& (!tabular.getRowOfLTHead(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_HEAD))
&& (!tabular.getRowOfLTFoot(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_FOOT))
&& (!tabular.getRowOfLTLastFoot(sel_row_start, dummyltt)
|| !tabular.haveLTCaption(Tabular::CAPTION_LASTFOOT)))));
status.setOnOff(tabular.ltCaption(sel_row_start));
break;
case Tabular::SET_BOOKTABS:
status.setOnOff(tabular.use_booktabs);
break;

View File

@ -317,6 +317,19 @@ public:
BOX_MINIPAGE = 2
};
enum CaptionType {
///
CAPTION_FIRSTHEAD,
///
CAPTION_HEAD,
///
CAPTION_FOOT,
///
CAPTION_LASTFOOT,
///
CAPTION_ANY
};
class ltType {
public:
// constructor
@ -505,8 +518,6 @@ public:
//
// Long Tabular Options support functions
///
bool checkLTType(row_type row, ltType const &) const;
///
void setLTHead(row_type row, bool flag, ltType const &, bool first);
///
bool getRowOfLTHead(row_type row, ltType &) const;
@ -527,15 +538,15 @@ public:
///
bool ltCaption(row_type row) const;
///
bool haveLTHead() const;
bool haveLTHead(bool withcaptions = true) const;
///
bool haveLTFirstHead() const;
bool haveLTFirstHead(bool withcaptions = true) const;
///
bool haveLTFoot() const;
bool haveLTFoot(bool withcaptions = true) const;
///
bool haveLTLastFoot() const;
bool haveLTLastFoot(bool withcaptions = true) const;
///
bool haveLTCaption() const;
bool haveLTCaption(CaptionType captiontype = CAPTION_ANY) const;
///
// end longtable support
///

View File

@ -1171,45 +1171,28 @@ void handle_tabular(Parser & p, ostream & os, string const & name,
// one multicolumn cell. The contents of that
// cell must contain exactly one caption inset
// and nothing else.
// LyX outputs all caption rows as first head,
// so we must not set the caption flag for
// captions not in the first head.
// Fortunately, the caption flag is only needed
// for tables with more than one column.
bool usecaption = (rowinfo[row].type == LT_NORMAL ||
rowinfo[row].type == LT_FIRSTHEAD);
for (size_t r = 0; r < row && usecaption; ++r)
if (rowinfo[row].type != LT_NORMAL &&
rowinfo[row].type != LT_FIRSTHEAD)
usecaption = false;
if (usecaption) {
rowinfo[row].caption = true;
for (size_t c = 1; c < cells.size(); ++c) {
if (!cells[c].empty()) {
cerr << "Moving cell content '"
<< cells[c]
<< "' into the caption cell. "
"This will probably not work."
<< endl;
cells[0] += cells[c];
}
rowinfo[row].caption = true;
for (size_t c = 1; c < cells.size(); ++c) {
if (!cells[c].empty()) {
cerr << "Moving cell content '"
<< cells[c]
<< "' into the caption cell. "
"This will probably not work."
<< endl;
cells[0] += cells[c];
}
cells.resize(1);
cellinfo[row][col].align = colinfo[col].align;
cellinfo[row][col].multi = CELL_BEGIN_OF_MULTICOLUMN;
} else {
cellinfo[row][col].leftlines = colinfo[col].leftlines;
cellinfo[row][col].rightlines = colinfo[col].rightlines;
cellinfo[row][col].align = colinfo[col].align;
}
cells.resize(1);
cellinfo[row][col].align = colinfo[col].align;
cellinfo[row][col].multi = CELL_BEGIN_OF_MULTICOLUMN;
ostringstream os;
parse_text_in_inset(p, os, FLAG_CELL, false, context);
cellinfo[row][col].content += os.str();
if (usecaption) {
// add dummy multicolumn cells
for (size_t c = 1; c < colinfo.size(); ++c)
cellinfo[row][c].multi = CELL_PART_OF_MULTICOLUMN;
}
// add dummy multicolumn cells
for (size_t c = 1; c < colinfo.size(); ++c)
cellinfo[row][c].multi = CELL_PART_OF_MULTICOLUMN;
} else {
cellinfo[row][col].leftlines = colinfo[col].leftlines;
cellinfo[row][col].rightlines = colinfo[col].rightlines;

View File

@ -30,8 +30,8 @@ extern char const * const lyx_version_info;
// Do not remove the comment below, so we get merge conflict in
// independent branches. Instead add your own.
#define LYX_FORMAT_LYX 420 // jrioux : document-wide bibliography style
#define LYX_FORMAT_TEX2LYX 420 // jrioux : document-wide bibliography style
#define LYX_FORMAT_LYX 421 // baum : longtable captions
#define LYX_FORMAT_TEX2LYX 421
#if LYX_FORMAT_FOR_TEX2LYX != LYX_FORMAT_FOR_LYX
#warning "tex2lyx produces an out of date file format."