X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=0266c2e6ff9cf19a5b7875a12ebee99a7fceed7e;hb=ce2e071cdfde88864c1524818d66cbae52273502;hp=61ca78ae1364bca4afcdfad468aa1f5990ad2858;hpb=6a8219c190315a63426e6798290cc3a04dbc0f09;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 61ca78ae13..0266c2e6ff 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -4,13 +4,14 @@ * Licence details can be found in the file COPYING. * * \author Asger Alstrup - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes + * \author Richard Heck (XHTML output) * \author Jean-Marc Lasgouttes * \author Angus Leeming * \author John Levon - * \author André Pönitz + * \author André Pönitz * \author Dekel Tsur - * \author Jürgen Vigna + * \author Jürgen Vigna * * Full author contact details are available in file CREDITS. */ @@ -35,13 +36,15 @@ #include "LyXRC.h" #include "OutputParams.h" #include "output_latex.h" -#include "paragraph_funcs.h" +#include "output_xhtml.h" #include "ParagraphParameters.h" +#include "SpellChecker.h" #include "sgml.h" #include "TextClass.h" #include "TexRow.h" #include "Text.h" #include "VSpace.h" +#include "WordLangTuple.h" #include "WordList.h" #include "frontends/alert.h" @@ -49,13 +52,12 @@ #include "insets/InsetBibitem.h" #include "insets/InsetLabel.h" -#include "support/lassert.h" -#include "support/convert.h" #include "support/debug.h" +#include "support/docstring_list.h" #include "support/ExceptionMessage.h" #include "support/gettext.h" +#include "support/lassert.h" #include "support/lstrings.h" -#include "support/Messages.h" #include "support/textutils.h" #include @@ -93,7 +95,7 @@ public: /// Output the surrogate pair formed by \p c and \p next to \p os. /// \return the number of characters written. int latexSurrogatePair(odocstream & os, char_type c, char_type next, - Encoding const &); + OutputParams const &); /// Output a space in appropriate formatting (or a surrogate pair /// if the next character is a combining character). @@ -162,8 +164,7 @@ public: OutputParams const & runparams); /// - void validate(LaTeXFeatures & features, - Layout const & layout) const; + void validate(LaTeXFeatures & features) const; /// Checks if the paragraph contains only text and no inset or font change. bool onlyText(Buffer const & buf, Font const & outerfont, @@ -171,6 +172,12 @@ public: /// match a string against a particular point in the paragraph bool isTextAt(string const & str, pos_type pos) const; + + + InsetCode ownerCode() const + { + return inset_owner_ ? inset_owner_->lyxCode() : NO_CODE; + } /// Which Paragraph owns us? Paragraph * owner_; @@ -286,6 +293,15 @@ void Paragraph::addChangesToToc(DocIterator const & cdit, } +bool Paragraph::isDeleted(pos_type start, pos_type end) const +{ + LASSERT(start >= 0 && start <= size(), /**/); + LASSERT(end > start && end <= size() + 1, /**/); + + return d->changes_.isDeleted(start, end); +} + + bool Paragraph::isChanged(pos_type start, pos_type end) const { LASSERT(start >= 0 && start <= size(), /**/); @@ -302,7 +318,7 @@ bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges) const return true; Change const change = d->changes_.lookup(size()); - return change.type == Change::INSERTED && change.author == 0; + return change.inserted() && change.currentAuthor(); } @@ -323,7 +339,7 @@ void Paragraph::setChange(Change const & change) * Conclusion: An inset's content should remain untouched if you delete it */ - if (change.type != Change::DELETED) { + if (!change.deleted()) { for (pos_type pos = 0; pos < size(); ++pos) { if (Inset * inset = getInset(pos)) inset->setChange(change); @@ -338,7 +354,7 @@ void Paragraph::setChange(pos_type pos, Change const & change) d->changes_.set(change, pos); // see comment in setChange(Change const &) above - if (change.type != Change::DELETED && pos < size()) + if (!change.deleted() && pos < size()) if (Inset * inset = getInset(pos)) inset->setChange(change); } @@ -351,8 +367,7 @@ Change const & Paragraph::lookupChange(pos_type pos) const } -void Paragraph::acceptChanges(BufferParams const & bparams, pos_type start, - pos_type end) +void Paragraph::acceptChanges(pos_type start, pos_type end) { LASSERT(start >= 0 && start <= size(), /**/); LASSERT(end > start && end <= size() + 1, /**/); @@ -362,14 +377,14 @@ void Paragraph::acceptChanges(BufferParams const & bparams, pos_type start, case Change::UNCHANGED: // accept changes in nested inset if (Inset * inset = getInset(pos)) - inset->acceptChanges(bparams); + inset->acceptChanges(); break; case Change::INSERTED: d->changes_.set(Change(Change::UNCHANGED), pos); // also accept changes in nested inset if (Inset * inset = getInset(pos)) - inset->acceptChanges(bparams); + inset->acceptChanges(); break; case Change::DELETED: @@ -387,8 +402,7 @@ void Paragraph::acceptChanges(BufferParams const & bparams, pos_type start, } -void Paragraph::rejectChanges(BufferParams const & bparams, - pos_type start, pos_type end) +void Paragraph::rejectChanges(pos_type start, pos_type end) { LASSERT(start >= 0 && start <= size(), /**/); LASSERT(end > start && end <= size() + 1, /**/); @@ -398,7 +412,7 @@ void Paragraph::rejectChanges(BufferParams const & bparams, case Change::UNCHANGED: // reject changes in nested inset if (Inset * inset = getInset(pos)) - inset->rejectChanges(bparams); + inset->rejectChanges(); break; case Change::INSERTED: @@ -482,13 +496,13 @@ bool Paragraph::eraseChar(pos_type pos, bool trackChanges) // a) it was previously unchanged or // b) it was inserted by a co-author - if (change.type == Change::UNCHANGED || - (change.type == Change::INSERTED && change.author != 0)) { + if (!change.changed() || + (change.inserted() && !change.currentAuthor())) { setChange(pos, Change(Change::DELETED)); return false; } - if (change.type == Change::DELETED) + if (change.deleted()) return false; } @@ -533,7 +547,7 @@ int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges) int Paragraph::Private::latexSurrogatePair(odocstream & os, char_type c, - char_type next, Encoding const & encoding) + char_type next, OutputParams const & runparams) { // Writing next here may circumvent a possible font change between // c and next. Since next is only output if it forms a surrogate pair @@ -542,12 +556,18 @@ int Paragraph::Private::latexSurrogatePair(odocstream & os, char_type c, // hopefully impossible to input. // FIXME: change tracking // Is this correct WRT change tracking? + Encoding const & encoding = *(runparams.encoding); docstring const latex1 = encoding.latexChar(next); docstring const latex2 = encoding.latexChar(c); if (docstring(1, next) == latex1) { // the encoding supports the combination os << latex2 << latex1; return latex1.length() + latex2.length(); + } else if (runparams.local_font && + runparams.local_font->language()->lang() == "polutonikogreek") { + // polutonikogreek only works without the brackets + os << latex1 << latex2; + return latex1.length() + latex2.length(); } else os << latex1 << '{' << latex2 << '}'; return latex1.length() + latex2.length() + 2; @@ -567,15 +587,14 @@ bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, if (i + 1 < int(text_.size())) { char_type next = text_[i + 1]; if (Encodings::isCombiningChar(next)) { - Encoding const & encoding = *(runparams.encoding); // This space has an accent, so we must always output it. - column += latexSurrogatePair(os, ' ', next, encoding) - 1; + column += latexSurrogatePair(os, ' ', next, runparams) - 1; return true; } } - if (lyxrc.plaintext_linelen > 0 - && column > lyxrc.plaintext_linelen + if (runparams.linelen > 0 + && column > runparams.linelen && i && text_[i - 1] != ' ' && (i + 1 < int(text_.size())) @@ -745,24 +764,25 @@ void Paragraph::Private::latexInset( column = 0; } - if (owner_->lookupChange(i).type == Change::DELETED) { + if (owner_->isDeleted(i)) { if( ++runparams.inDeletedInset == 1) runparams.changeOfDeletedInset = owner_->lookupChange(i); } if (inset->canTrackChanges()) { column += Changes::latexMarkChange(os, bparams, running_change, - Change(Change::UNCHANGED)); + Change(Change::UNCHANGED), runparams); running_change = Change(Change::UNCHANGED); } bool close = false; odocstream::pos_type const len = os.tellp(); - if (inset->forceLTR() + if (inset->forceLTR() && running_font.isRightToLeft() - // ERT is an exception, it should be output with no decorations at all - && inset->lyxCode() != ERT_CODE) { + // ERT is an exception, it should be output with no + // decorations at all + && inset->lyxCode() != ERT_CODE) { if (running_font.language()->lang() == "farsi") os << "\\beginL{}"; else @@ -817,10 +837,10 @@ void Paragraph::Private::latexInset( } if (close) { - if (running_font.language()->lang() == "farsi") - os << "\\endL{}"; - else - os << '}'; + if (running_font.language()->lang() == "farsi") + os << "\\endL{}"; + else + os << '}'; } if (tmp) { @@ -833,7 +853,7 @@ void Paragraph::Private::latexInset( column += os.tellp() - len; } - if (owner_->lookupChange(i).type == Change::DELETED) + if (owner_->isDeleted(i)) --runparams.inDeletedInset; } @@ -864,9 +884,15 @@ void Paragraph::Private::latexSpecialChar( return; } - if (lyxrc.fontenc == "T1" && latexSpecialT1(c, os, i, column)) + // If T1 font encoding is used, use the special + // characters it provides. + // NOTE: some languages reset the font encoding + // internally + if (!running_font.language()->internalFontEncoding() + && lyxrc.fontenc == "T1" && latexSpecialT1(c, os, i, column)) return; + // \tt font needs special treatment if (running_font.fontInfo().family() == TYPEWRITER_FAMILY && latexSpecialTypewriter(c, os, i, column)) return; @@ -915,7 +941,9 @@ void Paragraph::Private::latexSpecialChar( column += 17; break; - case '*': case '[': + case '*': + case '[': + case ']': // avoid being mistaken for optional arguments os << '{'; os.put(c); @@ -943,7 +971,7 @@ void Paragraph::Private::latexSpecialChar( if (i + 1 < int(text_.size())) { char_type next = text_[i + 1]; if (Encodings::isCombiningChar(next)) { - column += latexSurrogatePair(os, c, next, encoding) - 1; + column += latexSurrogatePair(os, c, next, runparams) - 1; ++i; break; } @@ -1042,15 +1070,26 @@ bool Paragraph::Private::latexSpecialPhrase(odocstream & os, pos_type & i, } -void Paragraph::Private::validate(LaTeXFeatures & features, - Layout const & layout) const +void Paragraph::Private::validate(LaTeXFeatures & features) const { + if (layout_->inpreamble && inset_owner_) { + Buffer const & buf = inset_owner_->buffer(); + BufferParams const & bp = buf.params(); + Font f; + TexRow tr; + odocstringstream ods; + owner_->latex(bp, f, ods, tr, features.runparams()); + docstring d = ods.str(); + if (!d.empty()) + features.addPreambleSnippet(to_utf8(d)); + } + // check the params. if (!params_.spacing().isDefault()) features.require("setspace"); // then the layouts - features.useLayout(layout.name()); + features.useLayout(layout_->name()); // then the fonts fontlist_.validate(features); @@ -1065,7 +1104,7 @@ void Paragraph::Private::validate(LaTeXFeatures & features, for (; icit != iend; ++icit) { if (icit->inset) { icit->inset->validate(features); - if (layout.needprotect && + if (layout_->needprotect && icit->inset->lyxCode() == FOOT_CODE) features.require("NeedLyXFootnoteCode"); } @@ -1140,6 +1179,18 @@ Paragraph::~Paragraph() } +namespace { + +// this shall be called just before every "os << ..." action. +void flushString(ostream & os, docstring & s) +{ + os << to_utf8(s); + s.erase(); +} + +} + + void Paragraph::write(ostream & os, BufferParams const & bparams, depth_type & dth) const { @@ -1167,19 +1218,27 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, Change running_change = Change(Change::UNCHANGED); + // this string is used as a buffer to avoid repetitive calls + // to to_utf8(), which turn out to be expensive (JMarc) + docstring write_buffer; + int column = 0; for (pos_type i = 0; i <= size(); ++i) { - Change change = lookupChange(i); - Changes::lyxMarkChange(os, column, running_change, change); + Change const change = lookupChange(i); + if (change != running_change) + flushString(os, write_buffer); + Changes::lyxMarkChange(os, bparams, column, running_change, change); running_change = change; if (i == size()) break; - // Write font changes + // Write font changes (ignore spelling markers) Font font2 = getFontSettings(bparams, i); + font2.setMisspelled(false); if (font2 != font1) { + flushString(os, write_buffer); font2.lyxWriteChanges(font1, os); column = 0; font1 = font2; @@ -1189,6 +1248,7 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, switch (c) { case META_INSET: if (Inset const * inset = getInset(i)) { + flushString(os, write_buffer); if (inset->directWrite()) { // international char, let it write // code directly so it's shorter in @@ -1205,10 +1265,12 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, } break; case '\\': + flushString(os, write_buffer); os << "\n\\backslash\n"; column = 0; break; case '.': + flushString(os, write_buffer); if (i + 1 < size() && d->text_[i + 1] == ' ') { os << ".\n"; column = 0; @@ -1218,13 +1280,14 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, default: if ((column > 70 && c == ' ') || column > 79) { + flushString(os, write_buffer); os << '\n'; column = 0; } // this check is to amend a bug. LyX sometimes // inserts '\0' this could cause problems. if (c != '\0') - os << to_utf8(docstring(1, c)); + write_buffer.push_back(c); else LYXERR0("NUL char in structure."); ++column; @@ -1232,13 +1295,14 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, } } + flushString(os, write_buffer); os << "\n\\end_layout\n"; } void Paragraph::validate(LaTeXFeatures & features) const { - d->validate(features, *d->layout_); + d->validate(features); } @@ -1275,7 +1339,7 @@ void Paragraph::appendString(docstring const & s, Font const & font, d->text_.append(s); // FIXME: Optimize this! - for (pos_type i = oldsize; i != newsize; ++i) { + for (size_t i = oldsize; i != newsize; ++i) { // track change d->changes_.insert(change, i); } @@ -1413,13 +1477,14 @@ Font const Paragraph::getFont(BufferParams const & bparams, pos_type pos, Font font = getFontSettings(bparams, pos); pos_type const body_pos = beginOfBody(); + FontInfo & fi = font.fontInfo(); if (pos < body_pos) - font.fontInfo().realize(d->layout_->labelfont); + fi.realize(d->layout_->labelfont); else - font.fontInfo().realize(d->layout_->font); + fi.realize(d->layout_->font); - font.fontInfo().realize(outerfont.fontInfo()); - font.fontInfo().realize(bparams.getFont().fontInfo()); + fi.realize(outerfont.fontInfo()); + fi.realize(bparams.getFont().fontInfo()); return font; } @@ -1570,7 +1635,8 @@ docstring const & Paragraph::labelString() const // the next two functions are for the manual labels docstring const Paragraph::getLabelWidthString() const { - if (d->layout_->margintype == MARGIN_MANUAL) + if (d->layout_->margintype == MARGIN_MANUAL + || d->layout_->latextype == LATEX_BIB_ENVIRONMENT) return d->params_.labelWidthString(); else return _("Senseless with this layout!"); @@ -1583,35 +1649,17 @@ void Paragraph::setLabelWidthString(docstring const & s) } -docstring const Paragraph::translateIfPossible(docstring const & s, - BufferParams const & bparams) const -{ - if (!isAscii(s) || s.empty()) { - // This must be a user defined layout. We cannot translate - // this, since gettext accepts only ascii keys. - return s; - } - // Probably standard layout, try to translate - Messages & m = getMessages(getParLanguage(bparams)->code()); - return m.get(to_ascii(s)); -} - - docstring Paragraph::expandLabel(Layout const & layout, BufferParams const & bparams, bool process_appendix) const { DocumentClass const & tclass = bparams.documentClass(); - - docstring fmt; - if (process_appendix && d->params_.appendix()) - fmt = translateIfPossible(layout.labelstring_appendix(), - bparams); - else - fmt = translateIfPossible(layout.labelstring(), bparams); + string const & lang = getParLanguage(bparams)->code(); + bool const in_appendix = process_appendix && d->params_.appendix(); + docstring fmt = translateIfPossible(layout.labelstring(in_appendix), lang); if (fmt.empty() && layout.labeltype == LABEL_COUNTER && !layout.counter.empty()) - fmt = "\\the" + layout.counter; + return tclass.counters().theCounter(layout.counter, lang); // handle 'inherited level parts' in 'fmt', // i.e. the stuff between '@' in '@Section@.\arabic{subsection}' @@ -1629,7 +1677,7 @@ docstring Paragraph::expandLabel(Layout const & layout, } } - return tclass.counters().counterLabel(fmt); + return tclass.counters().counterLabel(fmt, lang); } @@ -1687,12 +1735,6 @@ void Paragraph::setBeginOfBody() } -bool Paragraph::forcePlainLayout() const -{ - return inInset().forcePlainLayout(); -} - - bool Paragraph::allowParagraphCustomization() const { return inInset().allowParagraphCustomization(); @@ -1799,7 +1841,7 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, } string const begin_tag = "\\begin"; - InsetCode code = owner_->ownerCode(); + InsetCode code = ownerCode(); bool const lastpar = runparams.isLastPar; switch (curAlign) { @@ -1845,7 +1887,12 @@ int Paragraph::Private::endTeXParParams(BufferParams const & bparams, { int column = 0; - switch (params_.align()) { + LyXAlignment const curAlign = params_.align(); + + if (curAlign == layout_->align) + return column; + + switch (curAlign) { case LYX_ALIGN_NONE: case LYX_ALIGN_BLOCK: case LYX_ALIGN_LAYOUT: @@ -1862,10 +1909,10 @@ int Paragraph::Private::endTeXParParams(BufferParams const & bparams, } string const end_tag = "\n\\par\\end"; - InsetCode code = owner_->ownerCode(); + InsetCode code = ownerCode(); bool const lastpar = runparams.isLastPar; - switch (params_.align()) { + switch (curAlign) { case LYX_ALIGN_NONE: case LYX_ALIGN_BLOCK: case LYX_ALIGN_LAYOUT: @@ -1904,19 +1951,24 @@ int Paragraph::Private::endTeXParParams(BufferParams const & bparams, // This one spits out the text of the paragraph bool Paragraph::latex(BufferParams const & bparams, - Font const & outerfont, - odocstream & os, TexRow & texrow, - OutputParams const & runparams) const + Font const & outerfont, + odocstream & os, TexRow & texrow, + OutputParams const & runparams, + int start_pos, int end_pos) const { LYXERR(Debug::LATEX, "Paragraph::latex... " << this); + if (layout().inpreamble) + return true; + bool return_value = false; - bool asdefault = forcePlainLayout(); + bool const allowcust = allowParagraphCustomization(); - Layout const & style = asdefault ? - bparams.documentClass().plainLayout() : - *d->layout_; + // FIXME This check should not be needed. Perhaps issue an + // error if it triggers. + Layout const & style = inInset().forcePlainLayout() ? + bparams.documentClass().plainLayout() : *d->layout_; // Current base font for all inherited font changes, without any // change caused by an individual character, except for the language: @@ -1931,10 +1983,8 @@ bool Paragraph::latex(BufferParams const & bparams, unsigned int column = 0; if (body_pos > 0) { - // the optional argument is kept in curly brackets in - // case it contains a ']' - os << "[{"; - column += 2; + os << '['; + column += 1; basefont = getLabelFont(bparams, outerfont); } else { basefont = getLayoutFont(bparams, outerfont); @@ -1947,6 +1997,8 @@ bool Paragraph::latex(BufferParams const & bparams, Change runningChange = Change(Change::UNCHANGED); + Encoding const * const prev_encoding = runparams.encoding; + texrow.start(id(), 0); // if the paragraph is empty, the loop will not be entered at all @@ -1955,7 +2007,7 @@ bool Paragraph::latex(BufferParams const & bparams, os << '{'; ++column; } - if (!asdefault) + if (allowcust) column += d->startTeXParParams(bparams, os, texrow, runparams); } @@ -1974,18 +2026,19 @@ bool Paragraph::latex(BufferParams const & bparams, running_font = basefont; column += Changes::latexMarkChange(os, bparams, - runningChange, Change(Change::UNCHANGED)); + runningChange, Change(Change::UNCHANGED), + runparams); runningChange = Change(Change::UNCHANGED); - os << "}] "; - column +=3; + os << "] "; + column +=2; } if (style.isCommand()) { os << '{'; ++column; } - if (!asdefault) + if (allowcust) column += d->startTeXParParams(bparams, os, texrow, runparams); @@ -2003,13 +2056,14 @@ bool Paragraph::latex(BufferParams const & bparams, basefont = getLayoutFont(bparams, outerfont); running_font = basefont; - column += Changes::latexMarkChange(os, bparams, runningChange, change); + column += Changes::latexMarkChange(os, bparams, runningChange, + change, runparams); runningChange = change; } // do not output text which is marked deleted // if change tracking output is disabled - if (!bparams.outputChanges && change.type == Change::DELETED) { + if (!bparams.outputChanges && change.deleted()) { continue; } @@ -2068,16 +2122,22 @@ bool Paragraph::latex(BufferParams const & bparams, running_font = font; open_font = true; docstring fontchange = ods.str(); + // check whether the fontchange ends with a \\textcolor + // modifier and the text starts with a space (bug 4473) + docstring const last_modifier = rsplit(fontchange, '\\'); + if (prefixIs(last_modifier, from_ascii("textcolor")) && c == ' ') + os << fontchange << from_ascii("{}"); // check if the fontchange ends with a trailing blank // (like "\small " (see bug 3382) - if (suffixIs(fontchange, ' ') && c == ' ') + else if (suffixIs(fontchange, ' ') && c == ' ') os << fontchange.substr(0, fontchange.size() - 1) << from_ascii("{}"); else os << fontchange; } - if (c == ' ') { + // FIXME: think about end_pos implementation... + if (c == ' ' && i >= start_pos && (end_pos == -1 || i < end_pos)) { // FIXME: integrate this case in latexSpecialChar // Do not print the separation of the optional argument // if style.pass_thru is false. This works because @@ -2105,16 +2165,19 @@ bool Paragraph::latex(BufferParams const & bparams, // Two major modes: LaTeX or plain // Handle here those cases common to both modes // and then split to handle the two modes separately. - if (c == META_INSET) - d->latexInset(bparams, os, - texrow, rp, running_font, - basefont, outerfont, open_font, - runningChange, style, i, column); - else { - try { - d->latexSpecialChar(os, rp, running_font, runningChange, - style, i, column); - } catch (EncodingException & e) { + if (c == META_INSET) { + if (i >= start_pos && (end_pos == -1 || i < end_pos)) { + d->latexInset(bparams, os, + texrow, rp, running_font, + basefont, outerfont, open_font, + runningChange, style, i, column); + } + } else { + if (i >= start_pos && (end_pos == -1 || i < end_pos)) { + try { + d->latexSpecialChar(os, rp, running_font, runningChange, + style, i, column); + } catch (EncodingException & e) { if (runparams.dryrun) { os << "<" << _("LyX Warning: ") << _("uncodable character") << " '"; @@ -2128,6 +2191,7 @@ bool Paragraph::latex(BufferParams const & bparams, } } } + } // Set the encoding to that returned from latexSpecialChar (see // comment for encoding member in OutputParams.h) @@ -2155,17 +2219,20 @@ bool Paragraph::latex(BufferParams const & bparams, #endif } - column += Changes::latexMarkChange(os, bparams, runningChange, Change(Change::UNCHANGED)); + column += Changes::latexMarkChange(os, bparams, runningChange, + Change(Change::UNCHANGED), runparams); // Needed if there is an optional argument but no contents. if (body_pos > 0 && body_pos == size()) { - os << "}]~"; + os << "]~"; return_value = false; } - if (!asdefault) { - column += d->endTeXParParams(bparams, os, texrow, - runparams); + if (allowcust && d->endTeXParParams(bparams, os, texrow, runparams) + && runparams.encoding != prev_encoding) { + runparams.encoding = prev_encoding; + if (!bparams.useXetex) + os << setEncoding(prev_encoding->iconvName()); } LYXERR(Debug::LATEX, "Paragraph::latex... done " << this); @@ -2178,6 +2245,8 @@ bool Paragraph::emptyTag() const for (pos_type i = 0; i < size(); ++i) { if (Inset const * inset = getInset(i)) { InsetCode lyx_code = inset->lyxCode(); + // FIXME testing like that is wrong. What is + // the intent? if (lyx_code != TOC_CODE && lyx_code != INCLUDE_CODE && lyx_code != GRAPHICS_CODE && @@ -2214,7 +2283,7 @@ string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) } -pos_type Paragraph::firstWord(odocstream & os, OutputParams const & runparams) +pos_type Paragraph::firstWordDocBook(odocstream & os, OutputParams const & runparams) const { pos_type i; @@ -2232,6 +2301,24 @@ pos_type Paragraph::firstWord(odocstream & os, OutputParams const & runparams) } +pos_type Paragraph::firstWordLyXHTML(XHTMLStream & xs, OutputParams const & runparams) + const +{ + pos_type i; + for (i = 0; i < size(); ++i) { + if (Inset const * inset = getInset(i)) { + inset->xhtml(xs, runparams); + } else { + char_type c = d->text_[i]; + if (c == ' ') + break; + xs << c; + } + } + return i; +} + + bool Paragraph::Private::onlyText(Buffer const & buf, Font const & outerfont, pos_type initial) const { Font font_old; @@ -2303,6 +2390,102 @@ void Paragraph::simpleDocBookOnePar(Buffer const & buf, } +docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, + XHTMLStream & xs, + OutputParams const & runparams, + Font const & outerfont, + bool fortoc, + pos_type initial) const +{ + docstring retval; + + bool emph_flag = false; + bool bold_flag = false; + std::string closing_tag; + + Layout const & style = *d->layout_; + + if (!fortoc) { + // generate a magic label for this paragraph + string const attr = "id='" + magicLabel() + "'"; + xs << CompTag("a", attr); + } + + FontInfo font_old = + style.labeltype == LABEL_MANUAL ? style.labelfont : style.font; + + // parsing main loop + for (pos_type i = initial; i < size(); ++i) { + Font font = getFont(buf.params(), i, outerfont); + + // emphasis + if (font_old.emph() != font.fontInfo().emph()) { + if (font.fontInfo().emph() == FONT_ON) { + xs << StartTag("em"); + emph_flag = true; + } else if (emph_flag && i != initial) { + xs << EndTag("em"); + emph_flag = false; + } + } + // bold + if (font_old.series() != font.fontInfo().series()) { + if (font.fontInfo().series() == BOLD_SERIES) { + xs << StartTag("strong"); + bold_flag = true; + } else if (bold_flag && i != initial) { + xs << EndTag("strong"); + bold_flag = false; + } + } + // FIXME XHTML + // Other such tags? What about the other text ranges? + + Inset const * inset = getInset(i); + if (inset) { + InsetCommand const * ic = inset->asInsetCommand(); + InsetLayout const & il = inset->getLayout(); + if (!fortoc || il.isInToc() || (ic && ic->isInToc())) { + OutputParams np = runparams; + if (!il.htmlisblock()) + np.html_in_par = true; + retval += inset->xhtml(xs, np); + } + } else { + char_type c = d->text_[i]; + + if (style.pass_thru) + xs << c; + else if (c == '-') { + docstring str; + int j = i + 1; + if (j < size() && d->text_[j] == '-') { + j += 1; + if (j < size() && d->text_[j] == '-') { + str += from_ascii("—"); + i += 2; + } else { + str += from_ascii("–"); + i += 1; + } + } + else + str += c; + // We don't want to escape the entities. Note that + // it is safe to do this, since str can otherwise + // only be "-". E.g., it can't be "<". + xs << XHTMLStream::NextRaw() << str; + } else + xs << c; + } + font_old = font.fontInfo(); + } + + xs.closeFontTags(); + return retval; +} + + bool Paragraph::isHfill(pos_type pos) const { Inset const * inset = getInset(pos); @@ -2328,13 +2511,15 @@ bool Paragraph::isLineSeparator(pos_type pos) const } -/// Used by the spellchecker -bool Paragraph::isLetter(pos_type pos) const +bool Paragraph::isWordSeparator(pos_type pos) const { if (Inset const * inset = getInset(pos)) - return inset->isLetter(); + return !inset->isLetter(); char_type const c = d->text_[pos]; - return isLetterChar(c) || isDigit(c); + // We want to pass the ' and escape chars to the spellchecker + static docstring const quote = from_utf8(lyxrc.spellchecker_esc_chars + '\''); + return (!isLetterChar(c) && !isDigit(c) && !contains(quote, c)) + || pos == size(); } @@ -2370,8 +2555,7 @@ bool Paragraph::isRTL(BufferParams const & bparams) const { return lyxrc.rtl_support && getParLanguage(bparams)->rightToLeft() - && ownerCode() != ERT_CODE - && ownerCode() != LISTINGS_CODE; + && !inInset().getLayout().forceLTR(); } @@ -2421,10 +2605,37 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options) const for (pos_type i = beg; i < end; ++i) { char_type const c = d->text_[i]; - if (isPrintable(c) || c == '\t') + if (isPrintable(c) || c == '\t' + || (c == '\n' && (options & AS_STR_NEWLINES))) os.put(c); - else if (c == META_INSET && options & AS_STR_INSETS) - getInset(i)->textString(os); + else if (c == META_INSET && (options & AS_STR_INSETS)) { + getInset(i)->tocString(os); + if (getInset(i)->asInsetMath()) + os << " "; + } + } + + return os.str(); +} + + +docstring Paragraph::stringify(pos_type beg, pos_type end, int options, OutputParams & runparams) const +{ + odocstringstream os; + + if (beg == 0 + && options & AS_STR_LABEL + && !d->params_.labelString().empty()) + os << d->params_.labelString() << ' '; + + for (pos_type i = beg; i < end; ++i) { + char_type const c = d->text_[i]; + if (isPrintable(c) || c == '\t' + || (c == '\n' && (options & AS_STR_NEWLINES))) + os.put(c); + else if (c == META_INSET && (options & AS_STR_INSETS)) { + getInset(i)->plaintext(os, runparams); + } } return os.str(); @@ -2455,29 +2666,35 @@ void Paragraph::setLayout(Layout const & layout) } +void Paragraph::setDefaultLayout(DocumentClass const & tc) +{ + setLayout(tc.defaultLayout()); +} + + +void Paragraph::setPlainLayout(DocumentClass const & tc) +{ + setLayout(tc.plainLayout()); +} + + void Paragraph::setPlainOrDefaultLayout(DocumentClass const & tclass) { if (usePlainLayout()) - setLayout(tclass.plainLayout()); + setPlainLayout(tclass); else - setLayout(tclass.defaultLayout()); + setDefaultLayout(tclass); } Inset const & Paragraph::inInset() const { LASSERT(d->inset_owner_, throw ExceptionMessage(BufferException, - _("Memory problem"), _("Paragraph not properly initiliazed"))); + _("Memory problem"), _("Paragraph not properly initialized"))); return *d->inset_owner_; } -InsetCode Paragraph::ownerCode() const -{ - return d->inset_owner_ ? d->inset_owner_->lyxCode() : NO_CODE; -} - - ParagraphParameters & Paragraph::params() { return d->params_; @@ -2604,7 +2821,7 @@ int Paragraph::checkBiblio(Buffer const & buffer) // There was no inset at the beginning, so we need to create one with // the key and label of the one we erased. InsetBibitem * inset = - new InsetBibitem(buffer, InsetCommandParams(BIBITEM_CODE)); + new InsetBibitem(const_cast(&buffer), InsetCommandParams(BIBITEM_CODE)); // restore values of previously deleted item in this par. if (!oldkey.empty()) inset->setParam("key", oldkey); @@ -2622,21 +2839,21 @@ void Paragraph::checkAuthors(AuthorList const & authorList) } -bool Paragraph::isUnchanged(pos_type pos) const +bool Paragraph::isChanged(pos_type pos) const { - return lookupChange(pos).type == Change::UNCHANGED; + return lookupChange(pos).changed(); } bool Paragraph::isInserted(pos_type pos) const { - return lookupChange(pos).type == Change::INSERTED; + return lookupChange(pos).inserted(); } bool Paragraph::isDeleted(pos_type pos) const { - return lookupChange(pos).type == Change::DELETED; + return lookupChange(pos).deleted(); } @@ -2709,33 +2926,32 @@ void Paragraph::changeCase(BufferParams const & bparams, pos_type pos, } } - if (!isLetter(pos) || isDeleted(pos)) { + if (isWordSeparator(pos) || isDeleted(pos)) { // permit capitalization again capitalize = true; } - if (oldChar != newChar) + if (oldChar != newChar) { changes += newChar; + if (pos != right - 1) + continue; + // step behind the changing area + pos++; + } - if (oldChar == newChar || pos == right - 1) { - if (oldChar != newChar) { - // step behind the changing area - pos++; - } - int erasePos = pos - changes.size(); - for (size_t i = 0; i < changes.size(); i++) { - insertChar(pos, changes[i], - getFontSettings(bparams, - erasePos), - trackChanges); - if (!eraseChar(erasePos, trackChanges)) { - ++erasePos; - ++pos; // advance - ++right; // expand selection - } + int erasePos = pos - changes.size(); + for (size_t i = 0; i < changes.size(); i++) { + insertChar(pos, changes[i], + getFontSettings(bparams, + erasePos), + trackChanges); + if (!eraseChar(erasePos, trackChanges)) { + ++erasePos; + ++pos; // advance + ++right; // expand selection } - changes.clear(); } + changes.clear(); } } @@ -2762,10 +2978,10 @@ bool Paragraph::find(docstring const & str, bool cs, bool mw, // if necessary, check whether string matches word if (mw) { - if (pos > 0 && isLetter(pos - 1)) + if (pos > 0 && !isWordSeparator(pos - 1)) return false; if (pos + strsize < parsize - && isLetter(pos + strsize)) + && !isWordSeparator(pos + strsize)) return false; } @@ -2814,38 +3030,56 @@ void Paragraph::deregisterWords() } -void Paragraph::collectWords(CursorSlice const & sl) +void Paragraph::locateWord(pos_type & from, pos_type & to, + word_location const loc) const { - // find new words - bool inword = false; + switch (loc) { + case WHOLE_WORD_STRICT: + if (from == 0 || from == size() + || isWordSeparator(from) + || isWordSeparator(from - 1)) { + to = from; + return; + } + // no break here, we go to the next - //lyxerr << "Words: "; - pos_type n = size(); - for (pos_type pos = 0; pos != n; ++pos) { - if (isDeleted(pos)) - continue; + case WHOLE_WORD: + // If we are already at the beginning of a word, do nothing + if (!from || isWordSeparator(from - 1)) + break; + // no break here, we go to the next - if (!isLetter(pos)) { - inword = false; - continue; - } + case PREVIOUS_WORD: + // always move the cursor to the beginning of previous word + while (from && !isWordSeparator(from - 1)) + --from; + break; + case NEXT_WORD: + LYXERR0("Paragraph::locateWord: NEXT_WORD not implemented yet"); + break; + case PARTIAL_WORD: + // no need to move the 'from' cursor + break; + } + to = from; + while (to < size() && !isWordSeparator(to)) + ++to; +} - if (inword) - continue; - inword = true; - CursorSlice from = sl; - CursorSlice to = sl; - from.pos() = pos; - to.pos() = pos; - from.text()->getWord(from, to, WHOLE_WORD); - if (to.pos() - from.pos() < 6) +void Paragraph::collectWords() +{ + pos_type n = size(); + for (pos_type pos = 0; pos < n; ++pos) { + if (isWordSeparator(pos)) continue; - docstring word = asString(from.pos(), to.pos(), false); - d->words_.insert(word); - //lyxerr << word << " "; + pos_type from = pos; + locateWord(from, pos, WHOLE_WORD); + if (pos - from >= 6) { + docstring word = asString(from, pos, AS_STR_NONE); + d->words_.insert(word); + } } - //lyxerr << std::endl; } @@ -2858,12 +3092,68 @@ void Paragraph::registerWords() } -void Paragraph::updateWords(CursorSlice const & sl) +void Paragraph::updateWords() { - LASSERT(&sl.paragraph() == this, /**/); deregisterWords(); - collectWords(sl); + collectWords(); registerWords(); } + +bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl, + docstring_list & suggestions, bool do_suggestion) const +{ + SpellChecker * speller = theSpellChecker(); + if (!speller) + return false; + + locateWord(from, to, WHOLE_WORD); + if (from == to || from >= pos_type(d->text_.size())) + return false; + + docstring word = asString(from, to, AS_STR_INSETS); + string const lang_code = lyxrc.spellchecker_alt_lang.empty() + ? getFontSettings(d->inset_owner_->buffer().params(), from).language()->code() + : lyxrc.spellchecker_alt_lang; + wl = WordLangTuple(word, lang_code); + SpellChecker::Result res = speller->check(wl); + // Just ignore any error that the spellchecker reports. + // FIXME: we should through out an exception and catch it in the GUI to + // display the error. + if (!speller->error().empty()) + return false; + + bool const misspelled = res != SpellChecker::OK + && res != SpellChecker::IGNORED_WORD; + + if (lyxrc.spellcheck_continuously) + d->fontlist_.setMisspelled(from, to, misspelled); + + if (misspelled && do_suggestion) + speller->suggest(wl, suggestions); + else + suggestions.clear(); + + return misspelled; +} + + +bool Paragraph::isMisspelled(pos_type pos) const +{ + pos_type from = pos; + pos_type to = pos; + WordLangTuple wl; + docstring_list suggestions; + return spellCheck(from, to, wl, suggestions, false); +} + + +string Paragraph::magicLabel() const +{ + stringstream ss; + ss << "magicparlabel-" << id(); + return ss.str(); +} + + } // namespace lyx