From 68109443f3738b89574cfc82bc30b14e0c31a8be Mon Sep 17 00:00:00 2001 From: Guillaume Munch Date: Sun, 8 Jan 2017 21:57:02 +0100 Subject: [PATCH] Implement AddToToc for paragraph layouts Enables table of Theorems & Definitions --- src/Paragraph.cpp | 6 ++--- src/Paragraph.h | 4 +-- src/Text.cpp | 47 +++++++++++++++++++++++++++------ src/Text.h | 13 +++++++--- src/TocBackend.cpp | 2 +- src/TocBackend.h | 9 ++++--- src/insets/InsetFlex.cpp | 3 ++- src/insets/InsetText.cpp | 56 +++++++++++++++++++++++++++++++++++++--- src/insets/InsetText.h | 7 +++++ 9 files changed, 122 insertions(+), 25 deletions(-) diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index ba101e4eb2..6d50f278c4 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -3333,11 +3333,11 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options, const Out void Paragraph::forOutliner(docstring & os, size_t const maxlen, - bool const shorten) const + bool const shorten, bool const label) const { size_t tmplen = shorten ? maxlen + 1 : maxlen; - if (!d->params_.labelString().empty()) - os += d->params_.labelString() + ' '; + if (label && !labelString().empty()) + os += labelString() + ' '; for (pos_type i = 0; i < size() && os.length() < tmplen; ++i) { if (isDeleted(i)) continue; diff --git a/src/Paragraph.h b/src/Paragraph.h index 2133d8ce74..813640e548 100644 --- a/src/Paragraph.h +++ b/src/Paragraph.h @@ -181,8 +181,8 @@ public: int options = AS_STR_NONE, const OutputParams *runparams = 0) const; /// - void forOutliner(docstring &, size_t const maxlen, - bool const shorten = true) const; + void forOutliner(docstring &, size_t maxlen, bool shorten = true, + bool label = true) const; /// void write(std::ostream &, BufferParams const &, diff --git a/src/Text.cpp b/src/Text.cpp index 8e0d1beefd..8d08baa473 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -240,6 +240,21 @@ bool Text::isFirstInSequence(pit_type par_offset) const } +pit_type Text::lastInSequence(pit_type pit) const +{ + depth_type const depth = pars_[pit].getDepth(); + pit_type newpit = pit; + + while (size_t(newpit + 1) < pars_.size() && + (pars_[newpit + 1].getDepth() > depth || + (pars_[newpit + 1].getDepth() == depth && + pars_[newpit + 1].layout() == pars_[pit].layout()))) + ++newpit; + + return newpit; +} + + int Text::getTocLevel(pit_type par_offset) const { Paragraph const & par = pars_[par_offset]; @@ -2042,20 +2057,36 @@ docstring Text::asString(pit_type beg, pit_type end, int options) const void Text::shortenForOutliner(docstring & str, size_t const maxlen) { support::truncateWithEllipsis(str, maxlen); - docstring::iterator it = str.begin(); - docstring::iterator end = str.end(); - for (; it != end; ++it) - if ((*it) == L'\n' || (*it) == L'\t') - (*it) = L' '; + for (char_type & c : str) + if (c == L'\n' || c == L'\t') + c = L' '; } void Text::forOutliner(docstring & os, size_t const maxlen, - bool const shorten) const + bool const shorten) const +{ + pit_type end = pars_.size() - 1; + if (0 <= end && !pars_[0].labelString().empty()) + os += pars_[0].labelString() + ' '; + forOutliner(os, maxlen, 0, end, shorten); +} + + +void Text::forOutliner(docstring & os, size_t const maxlen, + pit_type pit_start, pit_type pit_end, + bool const shorten) const { size_t tmplen = shorten ? maxlen + 1 : maxlen; - for (size_t i = 0; i != pars_.size() && os.length() < tmplen; ++i) - pars_[i].forOutliner(os, tmplen, false); + pit_type end = min(size_t(pit_end), pars_.size() - 1); + bool first = true; + for (pit_type i = pit_start; i <= end && os.length() < tmplen; ++i) { + if (!first) + os += ' '; + // This function lets the first label be treated separately + pars_[i].forOutliner(os, tmplen, false, !first); + first = false; + } if (shorten) shortenForOutliner(os, maxlen); } diff --git a/src/Text.h b/src/Text.h index bdf2169fab..f49c8e2d91 100644 --- a/src/Text.h +++ b/src/Text.h @@ -130,9 +130,13 @@ public: /// Appends a possibly abbreviated representation of our text to \param os, /// where \param maxlen defines the maximum size of \param os. If \param - /// shorten is true, then os is shortened as above - void forOutliner(docstring & os, size_t const maxlen, - bool const shorten = true) const; + /// shorten is true, then os is shortened as above. + void forOutliner(docstring & os, size_t maxlen, bool shorten = true) const; + /// Appends a possibly abbreviated representation of our text, from + /// start to end, to \param os, where \param maxlen defines the + /// maximum size of \param os. Omits the label of the first paragraph. + void forOutliner(docstring & os, size_t maxlen, pit_type start, pit_type end, + bool shorten = true) const; /// insert a character at cursor position /// FIXME: replace Cursor with DocIterator. @@ -329,6 +333,9 @@ public: pit_type outerHook(pit_type pit) const; /// Is it the first par with same depth and layout? bool isFirstInSequence(pit_type pit) const; + /// Return the last paragraph with same depth and layout, or a strictly + /// greater depth + pit_type lastInSequence(pit_type pit) const; /// Is this paragraph in the table of contents? int getTocLevel(pit_type pit) const; /// Get the font of the "environment" of paragraph \p par_offset in \p pars. diff --git a/src/TocBackend.cpp b/src/TocBackend.cpp index f490c3534a..6c7c1f906d 100644 --- a/src/TocBackend.cpp +++ b/src/TocBackend.cpp @@ -200,7 +200,7 @@ void TocBuilder::argumentItem(docstring const & arg_str) TocItem & item = (*toc_)[stack_.top().pos]; docstring const & str = item.str(); string const & delim = - str.empty() ? "" : stack_.top().is_captioned ? ", " : ": "; + (str.empty() || !stack_.top().is_captioned) ? "" : ", "; item.str(str + from_ascii(delim) + arg_str); stack_.top().is_captioned = true; } diff --git a/src/TocBackend.h b/src/TocBackend.h index b851d68546..08754b90b0 100644 --- a/src/TocBackend.h +++ b/src/TocBackend.h @@ -56,6 +56,7 @@ enum TocType { MATH_MACRO,//"math-macro" EXTERNAL,//"external" SENSELESS,//"senseless" + USER_DEFINED,//any value defined in the layouts TOC_TYPE_COUNT } */ @@ -119,16 +120,16 @@ private: class TocBuilder { public: - TocBuilder(std::shared_ptr const toc); - /// When entering a float or flex (AddToToc) + TocBuilder(std::shared_ptr toc); + /// When entering a float or flex or paragraph (with AddToToc) void pushItem(DocIterator const & dit, docstring const & s, bool output_active, bool is_captioned = false); /// When encountering a float caption void captionItem(DocIterator const & dit, docstring const & s, bool output_active); - /// When encountering an argument (isTocCaption) + /// When encountering an argument (with isTocCaption) for flex or paragraph void argumentItem(docstring const & arg_str); - /// When exiting a float or flex + /// When exiting a float or flex or paragraph void pop(); private: TocBuilder(){} diff --git a/src/insets/InsetFlex.cpp b/src/insets/InsetFlex.cpp index 05cbb298b2..ee3b7c89c4 100644 --- a/src/insets/InsetFlex.cpp +++ b/src/insets/InsetFlex.cpp @@ -175,7 +175,8 @@ void InsetFlex::addToToc(DocIterator const & cpit, bool output_active, // Cursor inside the inset DocIterator pit = cpit; pit.push_back(CursorSlice(const_cast(*this))); - b.pushItem(pit, getLabel(), output_active); + docstring const label = getLabel(); + b.pushItem(pit, label + (label.empty() ? "" : ": "), output_active); // Proceed with the rest of the inset. InsetCollapsable::addToToc(cpit, output_active, utype); if (layout.isTocCaption()) { diff --git a/src/insets/InsetText.cpp b/src/insets/InsetText.cpp index 607f7f577a..db6b744deb 100644 --- a/src/insets/InsetText.cpp +++ b/src/insets/InsetText.cpp @@ -851,13 +851,29 @@ void InsetText::iterateForToc(DocIterator const & cdit, bool output_active, // can't hurt too much to do it again bool const doing_output = output_active && producesOutput(); - // For each paragraph, traverse its insets and let them add - // their toc items + // For each paragraph, + // * Add a toc item for the paragraph if it is AddToToc--merging adjacent + // paragraphs as needed. + // * Traverse its insets and let them add their toc items + // * Compute the main table of contents (this is hardcoded) + // * Add the list of changes ParagraphList const & pars = paragraphs(); pit_type pend = paragraphs().size(); + // Record pairs {start,end} of where a toc item was opened for a paragraph + // and where it must be closed + stack> addtotoc_stack; + for (pit_type pit = 0; pit != pend; ++pit) { Paragraph const & par = pars[pit]; dit.pit() = pit; + dit.pos() = 0; + + // Custom AddToToc in paragraph layouts (i.e. theorems) + if (par.layout().addToToc() && text().isFirstInSequence(pit)) { + pit_type end = openAddToTocForParagraph(pit, dit, output_active); + addtotoc_stack.push({pit, end}); + } + // if we find an optarg, we'll save it for use later. InsetText const * arginset = 0; InsetList::const_iterator it = par.insetList().begin(); @@ -870,7 +886,16 @@ void InsetText::iterateForToc(DocIterator const & cdit, bool output_active, if (inset.lyxCode() == ARG_CODE) arginset = inset.asInsetText(); } - // now the toc entry for the paragraph + + // End custom AddToToc in paragraph layouts + while (!addtotoc_stack.empty() && addtotoc_stack.top().second == pit) { + // execute the closing function + closeAddToTocForParagraph(addtotoc_stack.top().first, + addtotoc_stack.top().second); + addtotoc_stack.pop(); + } + + // now the toc entry for the paragraph in the main table of contents int const toclevel = text().getTocLevel(pit); if (toclevel != Layout::NOT_IN_TOC && toclevel >= min_toclevel) { // insert this into the table of contents @@ -895,6 +920,31 @@ void InsetText::iterateForToc(DocIterator const & cdit, bool output_active, } +pit_type InsetText::openAddToTocForParagraph(pit_type pit, + DocIterator const & dit, + bool output_active) const +{ + Paragraph const & par = paragraphs()[pit]; + TocBuilder & b = buffer().tocBackend().builder(par.layout().tocType()); + docstring const label = par.labelString(); + b.pushItem(dit, label + (label.empty() ? "" : " "), output_active); + return text().lastInSequence(pit); +} + + +void InsetText::closeAddToTocForParagraph(pit_type start, pit_type end) const +{ + Paragraph const & par = paragraphs()[start]; + TocBuilder & b = buffer().tocBackend().builder(par.layout().tocType()); + if (par.layout().isTocCaption()) { + docstring str; + text().forOutliner(str, TOC_ENTRY_LENGTH, start, end); + b.argumentItem(str); + } + b.pop(); +} + + bool InsetText::notifyCursorLeaves(Cursor const & old, Cursor & cur) { if (buffer().isClean()) diff --git a/src/insets/InsetText.h b/src/insets/InsetText.h index 5bc7c523eb..8c647fbff9 100644 --- a/src/insets/InsetText.h +++ b/src/insets/InsetText.h @@ -223,6 +223,13 @@ protected: void iterateForToc(DocIterator const & cdit, bool output_active, UpdateType utype) const; private: + /// Open the toc item for paragraph pit. Returns the paragraph index where + /// it should end. + pit_type openAddToTocForParagraph(pit_type pit, + DocIterator const & dit, + bool output_active) const; + /// Close a toc item opened in start and closed in end + void closeAddToTocForParagraph(pit_type start, pit_type end) const; /// bool drawFrame_; ///