X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=c47718c3d5a8eedf9d14d8040b1d10769600465a;hb=f3711d8a651445a61e1a759ac7493a9c5c7800e2;hp=7681cbdbbb4a5e2452b0d8996dee0aedb9f64a64;hpb=3b6931719aa7e1269997d2e950b3ce436d5bd34d;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 7681cbdbbb..c47718c3d5 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -5,6 +5,7 @@ * * \author Asger Alstrup * \author Lars Gullik Bjønnes + * \author Richard Heck (XHTML output) * \author Jean-Marc Lasgouttes * \author Angus Leeming * \author John Levon @@ -163,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, @@ -189,9 +189,8 @@ public: FontList fontlist_; /// - unsigned int id_; - /// - static unsigned int paragraph_id; + int id_; + /// ParagraphParameters params_; @@ -208,17 +207,14 @@ public: /// TextContainer text_; - typedef std::set Words; + typedef set Words; /// - Words words_; + map words_; /// Layout const * layout_; }; -// Initialization of the counter for the paragraph id's, -unsigned int Paragraph::Private::paragraph_id = 0; - namespace { struct special_phrase { @@ -240,20 +236,26 @@ size_t const phrases_nr = sizeof(special_phrases)/sizeof(special_phrase); Paragraph::Private::Private(Paragraph * owner, Layout const & layout) - : owner_(owner), inset_owner_(0), begin_of_body_(0), layout_(&layout) + : owner_(owner), inset_owner_(0), id_(-1), begin_of_body_(0), layout_(&layout) { - id_ = paragraph_id++; text_.reserve(100); } +// Initialization of the counter for the paragraph id's, +// +// FIXME: There should be a more intelligent way to generate and use the +// paragraph ids per buffer instead a global static counter for all InsetText +// in the running program. +static int paragraph_id = -1; + Paragraph::Private::Private(Private const & p, Paragraph * owner) : owner_(owner), inset_owner_(p.inset_owner_), fontlist_(p.fontlist_), params_(p.params_), changes_(p.changes_), insetlist_(p.insetlist_), begin_of_body_(p.begin_of_body_), text_(p.text_), words_(p.words_), layout_(p.layout_) { - id_ = paragraph_id++; + id_ = ++paragraph_id; } @@ -265,7 +267,7 @@ Paragraph::Private::Private(Private const & p, Paragraph * owner, begin_of_body_(p.begin_of_body_), words_(p.words_), layout_(p.layout_) { - id_ = paragraph_id++; + id_ = ++paragraph_id; if (beg >= pos_type(p.text_.size())) return; text_ = p.text_.substr(beg, end - beg); @@ -1070,15 +1072,31 @@ 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)); + } + + if (features.runparams().flavor == OutputParams::HTML + && layout_->htmltitle()) { + features.setHTMLTitle(owner_->asString(AS_STR_INSETS)); + } + // 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); @@ -1093,7 +1111,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"); } @@ -1168,6 +1186,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 { @@ -1195,10 +1225,16 @@ 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 const change = lookupChange(i); + if (change != running_change) + flushString(os, write_buffer); Changes::lyxMarkChange(os, bparams, column, running_change, change); running_change = change; @@ -1209,6 +1245,7 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, Font font2 = getFontSettings(bparams, i); font2.setMisspelled(false); if (font2 != font1) { + flushString(os, write_buffer); font2.lyxWriteChanges(font1, os); column = 0; font1 = font2; @@ -1218,6 +1255,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 @@ -1234,10 +1272,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; @@ -1247,13 +1287,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; @@ -1261,13 +1302,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); } @@ -1614,7 +1656,21 @@ void Paragraph::setLabelWidthString(docstring const & s) } -docstring Paragraph::expandLabel(Layout const & layout, +docstring Paragraph::expandLabel(Layout const & layout, + BufferParams const & bparams) const +{ + return expandParagraphLabel(layout, bparams, true); +} + + +docstring Paragraph::expandDocBookLabel(Layout const & layout, + BufferParams const & bparams) const +{ + return expandParagraphLabel(layout, bparams, false); +} + + +docstring Paragraph::expandParagraphLabel(Layout const & layout, BufferParams const & bparams, bool process_appendix) const { DocumentClass const & tclass = bparams.documentClass(); @@ -1635,7 +1691,7 @@ docstring Paragraph::expandLabel(Layout const & layout, docstring parent(fmt, i + 1, j - i - 1); docstring label = from_ascii("??"); if (tclass.hasLayout(parent)) - docstring label = expandLabel(tclass[parent], bparams, + docstring label = expandParagraphLabel(tclass[parent], bparams, process_appendix); fmt = docstring(fmt, 0, i) + label + docstring(fmt, j + 1, docstring::npos); @@ -1923,6 +1979,9 @@ bool Paragraph::latex(BufferParams const & bparams, { LYXERR(Debug::LATEX, "Paragraph::latex... " << this); + if (layout().inpreamble) + return true; + bool return_value = false; bool const allowcust = allowParagraphCustomization(); @@ -1945,8 +2004,10 @@ bool Paragraph::latex(BufferParams const & bparams, unsigned int column = 0; if (body_pos > 0) { - os << '['; - column += 1; + // the optional argument is kept in curly brackets in + // case it contains a ']' + os << "[{"; + column += 2; basefont = getLabelFont(bparams, outerfont); } else { basefont = getLayoutFont(bparams, outerfont); @@ -1992,8 +2053,8 @@ bool Paragraph::latex(BufferParams const & bparams, runparams); runningChange = Change(Change::UNCHANGED); - os << "] "; - column +=2; + os << "}] "; + column +=3; } if (style.isCommand()) { os << '{'; @@ -2186,7 +2247,7 @@ bool Paragraph::latex(BufferParams const & bparams, // Needed if there is an optional argument but no contents. if (body_pos > 0 && body_pos == size()) { - os << "]~"; + os << "}]~"; return_value = false; } @@ -2263,18 +2324,18 @@ pos_type Paragraph::firstWordDocBook(odocstream & os, OutputParams const & runpa } -pos_type Paragraph::firstWordLyXHTML(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(os, runparams); + inset->xhtml(xs, runparams); } else { char_type c = d->text_[i]; if (c == ' ') break; - os << html::escapeChar(c); + xs << c; } } return i; @@ -2353,59 +2414,76 @@ void Paragraph::simpleDocBookOnePar(Buffer const & buf, docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, - odocstream & os, + XHTMLStream & xs, OutputParams const & runparams, Font const & outerfont, pos_type initial) const { docstring retval; - // FIXME We really need to manage the tag nesting here. - // Probably in the same sort of way as in output_xhtml. bool emph_flag = false; bool bold_flag = false; - std::string closing_tag; + string closing_tag; Layout const & style = *d->layout_; + + if (!runparams.for_toc && runparams.html_make_pars) { + // generate a magic label for this paragraph + string const attr = "id='" + magicLabel() + "'"; + xs << html::CompTag("a", attr); + } + FontInfo font_old = style.labeltype == LABEL_MANUAL ? style.labelfont : style.font; - //if (style.pass_thru && !d->onlyText(buf, outerfont, initial)) - // os << "]]>"; - // parsing main loop for (pos_type i = initial; i < size(); ++i) { + // let's not show deleted material in the output + if (isDeleted(i)) + continue; + Font font = getFont(buf.params(), i, outerfont); // emphasis if (font_old.emph() != font.fontInfo().emph()) { if (font.fontInfo().emph() == FONT_ON) { - os << ""; + xs << html::StartTag("em"); emph_flag = true; } else if (emph_flag && i != initial) { - os << ""; + xs << html::EndTag("em"); emph_flag = false; } } // bold if (font_old.series() != font.fontInfo().series()) { if (font.fontInfo().series() == BOLD_SERIES) { - os << ""; + xs << html::StartTag("strong"); bold_flag = true; } else if (bold_flag && i != initial) { - os << ""; + xs << html::EndTag("strong"); bold_flag = false; } } - // FIXME Other such tags? - - if (Inset const * inset = getInset(i)) { - retval += inset->xhtml(os, runparams); + // 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(); + InsetMath const * im = inset->asInsetMath(); + if (!runparams.for_toc + || im || 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) - os.put(c); + xs << c; else if (c == '-') { docstring str; int j = i + 1; @@ -2421,19 +2499,17 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, } else str += c; - os << str; + // 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 - os << html::escapeChar(c); + xs << c; } font_old = font.fontInfo(); } - // FIXME This could be out of order. See above. - if (emph_flag) - os << ""; - if (bold_flag) - os << ""; - + xs.closeFontTags(); return retval; } @@ -2527,7 +2603,7 @@ void Paragraph::changeLanguage(BufferParams const & bparams, bool Paragraph::isMultiLingual(BufferParams const & bparams) const { - Language const * doc_language = bparams.language; + Language const * doc_language = bparams.language; FontList::const_iterator cit = d->fontlist_.begin(); FontList::const_iterator end = d->fontlist_.end(); @@ -2540,6 +2616,20 @@ bool Paragraph::isMultiLingual(BufferParams const & bparams) const } +void Paragraph::getLanguages(std::set & languages) const +{ + FontList::const_iterator cit = d->fontlist_.begin(); + FontList::const_iterator end = d->fontlist_.end(); + + for (; cit != end; ++cit) { + Language const * lang = cit->font().language(); + if (lang != ignore_language && + lang != latex_language) + languages.insert(lang); + } +} + + docstring Paragraph::asString(int options) const { return asString(0, size(), options); @@ -2551,8 +2641,8 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options) const odocstringstream os; if (beg == 0 - && options & AS_STR_LABEL - && !d->params_.labelString().empty()) + && options & AS_STR_LABEL + && !d->params_.labelString().empty()) os << d->params_.labelString() << ' '; for (pos_type i = beg; i < end; ++i) { @@ -2606,6 +2696,12 @@ int Paragraph::id() const } +void Paragraph::setId(int id) +{ + d->id_ = id; +} + + Layout const & Paragraph::layout() const { return *d->layout_; @@ -2773,7 +2869,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); @@ -2974,10 +3070,13 @@ bool Paragraph::isSeparator(pos_type pos) const void Paragraph::deregisterWords() { + map::const_iterator itl; Private::Words::const_iterator it; - WordList & wl = theWordList(); - for (it = d->words_.begin(); it != d->words_.end(); ++it) - wl.remove(*it); + for (itl = d->words_.begin(); itl != d->words_.end(); ++itl) { + WordList * wl = theWordList(itl->first); + for (it = (itl->second).begin(); it != (itl->second).end(); ++it) + wl->remove(*it); + } d->words_.clear(); } @@ -3029,7 +3128,11 @@ void Paragraph::collectWords() locateWord(from, pos, WHOLE_WORD); if (pos - from >= 6) { docstring word = asString(from, pos, AS_STR_NONE); - d->words_.insert(word); + FontList::const_iterator cit = d->fontlist_.fontIterator(pos); + if (cit == d->fontlist_.end()) + return; + Language const * lang = cit->font().language(); + d->words_[*lang].insert(word); } } } @@ -3037,10 +3140,13 @@ void Paragraph::collectWords() void Paragraph::registerWords() { + map::const_iterator itl; Private::Words::const_iterator it; - WordList & wl = theWordList(); - for (it = d->words_.begin(); it != d->words_.end(); ++it) - wl.insert(*it); + for (itl = d->words_.begin(); itl != d->words_.end(); ++itl) { + WordList * wl = theWordList(itl->first); + for (it = (itl->second).begin(); it != (itl->second).end(); ++it) + wl->insert(*it); + } } @@ -3059,21 +3165,40 @@ bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl, if (!speller) return false; + if (!d->layout_->spellcheck || !inInset().allowSpellCheck()) + 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); + // Ignore words with digits + // FIXME: make this customizable + // (note that hunspell ignores words with digits by default) + bool const ignored = hasDigit(word); + Language * lang = const_cast(getFontSettings( + d->inset_owner_->buffer().params(), from).language()); + if (lang == d->inset_owner_->buffer().params().language + && !lyxrc.spellchecker_alt_lang.empty()) { + string lang_code; + string const lang_variety = + split(lyxrc.spellchecker_alt_lang, lang_code, '-'); + lang->setCode(lang_code); + lang->setVariety(lang_variety); + } + wl = WordLangTuple(word, lang); + SpellChecker::Result res = ignored ? + SpellChecker::OK : speller->check(wl); +#if 0 +// FIXME: the code below makes aspell abort if a word in an unknown +// language is checked. // 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; +#endif bool const misspelled = res != SpellChecker::OK && res != SpellChecker::IGNORED_WORD; @@ -3100,4 +3225,12 @@ bool Paragraph::isMisspelled(pos_type pos) const } +string Paragraph::magicLabel() const +{ + stringstream ss; + ss << "magicparlabel-" << id(); + return ss.str(); +} + + } // namespace lyx