]> git.lyx.org Git - features.git/blobdiff - src/insets/InsetIndex.cpp
Allow removing words from the personal dictionary, that weren't previously added.
[features.git] / src / insets / InsetIndex.cpp
index 94bfa13315f133ef0d500e99473c4d07777ac6a0..f40ba031fcca8bf7b88f223e34eecc7f129a3c90 100644 (file)
@@ -18,6 +18,7 @@
 #include "BufferView.h"
 #include "ColorSet.h"
 #include "Cursor.h"
+#include "CutAndPaste.h"
 #include "DispatchResult.h"
 #include "Encoding.h"
 #include "ErrorList.h"
@@ -29,6 +30,7 @@
 #include "LaTeX.h"
 #include "LaTeXFeatures.h"
 #include "Lexer.h"
+#include "LyX.h"
 #include "output_latex.h"
 #include "output_xhtml.h"
 #include "xml.h"
@@ -54,6 +56,9 @@
 using namespace std;
 using namespace lyx::support;
 
+// Uncomment to enable InsetIndex-specific debugging mode: the tree for the index will be printed to std::cout.
+// #define LYX_INSET_INDEX_DEBUG
+
 namespace lyx {
 
 namespace {
@@ -128,6 +133,9 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
 {
        OutputParams runparams(runparams_in);
        runparams.inIndexEntry = true;
+       if (runparams_in.postpone_fragile_stuff)
+               // This is not needed and would impact sorting
+               runparams.moving_arg = false;
 
        otexstringstream os;
 
@@ -156,11 +164,18 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
                getSortkey(os, runparams);
                os << "@";
                os << ourlatex.str();
-               getSubentries(os, runparams);
+               getSubentries(os, runparams, ourlatex.str());
                if (hasSeeRef()) {
                        os << "|";
                        os << insetindexpagerangetranslator_latex().find(params_.range);
                        getSeeRefs(os, runparams);
+               } else if (!params_.pagefmt.empty() && params_.pagefmt != "default") {
+                       os << "|";
+                       os << insetindexpagerangetranslator_latex().find(params_.range);
+                       os << from_utf8(params_.pagefmt);
+               } else if (params_.range != InsetIndexParams::PageRange::None) {
+                       os << "|";
+                       os << insetindexpagerangetranslator_latex().find(params_.range);
                }
        } else {
                // We check whether we need a sort key.
@@ -206,7 +221,7 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
 
                odocstringstream subentries;
                otexstream otsub(subentries);
-               getSubentries(otsub, runparams);
+               getSubentries(otsub, runparams, ourlatex.str());
                if (subentries.str().empty()) {
                        // Separate the entries and subentries, i.e., split on "!".
                        // This goes wrong on an escaped "!", but as the escape
@@ -222,6 +237,12 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
                        vector<docstring>::const_iterator it2 = levels_plain.begin();
                        bool first = true;
                        for (; it != end; ++it) {
+                               if ((*it).empty()) {
+                                       emptySubentriesWarning(ourlatex.str());
+                                       if (it2 < levels_plain.end())
+                                               ++it2;
+                                       continue;
+                               }
                                // The separator needs to be put back when
                                // writing the levels, except for the first level
                                if (!first)
@@ -253,6 +274,9 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
                        os << "|"
                           << insetindexpagerangetranslator_latex().find(params_.range)
                           << cmd;
+               } else if (params_.range != InsetIndexParams::PageRange::None) {
+                       os << "|";
+                       os << insetindexpagerangetranslator_latex().find(params_.range);
                }
        }
        os << '}';
@@ -349,7 +373,8 @@ void InsetIndex::docbook(XMLStream & xs, OutputParams const & runparams) const
 
        // Handle primary, secondary, and tertiary terms (entries, subentries, and subsubentries, for LaTeX).
        vector<docstring> terms;
-       if (const vector<docstring> potential_terms = getSubentriesAsText(runparams); !potential_terms.empty()) {
+       const vector<docstring> potential_terms = getSubentriesAsText(runparams);
+       if (!potential_terms.empty()) {
                terms = potential_terms;
                // The main term is not present in the vector, as it's not a subentry. The main index term is inserted raw in
                // the index inset. Considering that the user either uses the new or the legacy mechanism, the main term is the
@@ -429,7 +454,7 @@ void InsetIndex::docbook(XMLStream & xs, OutputParams const & runparams) const
                xs << XMLStream::ESCAPE_NONE << (from_utf8("<!-- Output Error: ") + error + from_utf8(" -->\n"));
        }
 
-    // Write all of this down.
+       // Write all of this down.
        if (terms.empty() && !hasEndRange) {
                docstring error = from_utf8("No index term found! Complete entry: \"") + latexString + from_utf8("\"");
                LYXERR0(error);
@@ -559,6 +584,21 @@ void InsetIndex::doDispatch(Cursor & cur, FuncRequest & cmd)
                        params_.index = from_utf8(cmd.getArg(1));
                        break;
                }
+               if (cmd.getArg(0) == "changeparam") {
+                       string const p = cmd.getArg(1);
+                       string const v = cmd.getArg(2);
+                       cur.recordUndoInset(this);
+                       if (p == "range")
+                               params_.range = insetindexpagerangetranslator().find(v);
+                       if (p == "pagefmt") {
+                               if (v == "default" || v == "textbf"
+                                   || v == "textit" || v == "emph")
+                                       params_.pagefmt = v;
+                               else
+                                       lyx::dispatch(FuncRequest(LFUN_INSET_SETTINGS, "index"));
+                       }
+                       break;
+               }
                InsetIndexParams params;
                InsetIndex::string2params(to_utf8(cmd.argument()), params);
                cur.recordUndoInset(this);
@@ -575,6 +615,24 @@ void InsetIndex::doDispatch(Cursor & cur, FuncRequest & cmd)
                cur.bv().updateDialog("index", params2string(params_));
                break;
 
+       case LFUN_PARAGRAPH_BREAK: {
+               // Since this inset in single-par anyway, let's use
+               // return to enter subentries
+               FuncRequest fr(LFUN_INDEXMACRO_INSERT, "subentry");
+               lyx::dispatch(fr);
+               break;
+       }
+
+       case LFUN_INSET_INSERT_COPY: {
+               Cursor & bvcur = cur.bv().cursor();
+               if (cmd.origin() == FuncRequest::TOC && bvcur.inTexted()) {
+                       cap::copyInsetToTemp(cur, clone());
+                       cap::pasteFromTemp(bvcur, bvcur.buffer()->errorList("Paste"));
+               } else
+                       cur.undispatched();
+               break;
+       }
+
        default:
                InsetCollapsible::doDispatch(cur, cmd);
                break;
@@ -598,6 +656,23 @@ bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
                                from_utf8(cmd.getArg(1)) == params_.index);
                        return true;
                }
+               if (cmd.getArg(0) == "changeparam") {
+                       string const p = cmd.getArg(1);
+                       string const v = cmd.getArg(2);
+                       if (p == "range") {
+                               flag.setEnabled(v == "none" || v == "start" || v == "end");
+                               flag.setOnOff(params_.range == insetindexpagerangetranslator().find(v));
+                       }
+                       if (p == "pagefmt") {
+                               flag.setEnabled(!v.empty());
+                               if (params_.pagefmt == "default" || params_.pagefmt == "textbf"
+                                   || params_.pagefmt == "textit" || params_.pagefmt == "emph")
+                                       flag.setOnOff(params_.pagefmt == v);
+                               else
+                                       flag.setOnOff(v == "custom");
+                       }
+                       return true;
+               }
                return InsetCollapsible::getStatus(cur, cmd, flag);
 
        case LFUN_INSET_DIALOG_UPDATE: {
@@ -605,10 +680,22 @@ bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
                flag.setEnabled(realbuffer.params().use_indices);
                return true;
        }
-       
+
+       case LFUN_INSET_INSERT_COPY:
+               // This can only be invoked by ToC widget
+               flag.setEnabled(cmd.origin() == FuncRequest::TOC
+                               && cur.bv().cursor().inset().insetAllowed(lyxCode()));
+               return true;
+
+       case LFUN_PARAGRAPH_BREAK:
+               return macrosPossible("subentry");
+
        case LFUN_INDEXMACRO_INSERT:
                return macrosPossible(cmd.getArg(0));
 
+       case LFUN_INDEX_TAG_ALL:
+               return true;
+
        default:
                return InsetCollapsible::getStatus(cur, cmd, flag);
        }
@@ -649,7 +736,22 @@ docstring InsetIndex::getSortkeyAsText(OutputParams const & runparams) const
 }
 
 
-void InsetIndex::getSubentries(otexstream & os, OutputParams const & runparams) const
+void InsetIndex::emptySubentriesWarning(docstring const & mainentry) const
+{
+       // Empty subentries crash makeindex. So warn and ignore this.
+       TeXErrors terr;
+       ErrorList & errorList = buffer().errorList("Export");
+       docstring const s = bformat(_("There is an empty index subentry in the entry '%1$s'.\n"
+                                     "It will be ignored in the output."), mainentry);
+       Paragraph const & par = buffer().paragraphs().front();
+       errorList.push_back(ErrorItem(_("Empty index subentry!"), s,
+                                     {par.id(), 0}, {par.id(), -1}));
+       buffer().bufferErrors(terr, errorList);
+}
+
+
+void InsetIndex::getSubentries(otexstream & os, OutputParams const & runparams,
+                              docstring const & mainentry) const
 {
        Paragraph const & par = paragraphs().front();
        InsetList::const_iterator it = par.insetList().begin();
@@ -659,7 +761,11 @@ void InsetIndex::getSubentries(otexstream & os, OutputParams const & runparams)
                if (inset.lyxCode() == INDEXMACRO_CODE) {
                        InsetIndexMacro const & iim =
                                static_cast<InsetIndexMacro const &>(inset);
-                       if (iim.params().type == InsetIndexMacroParams::Subindex) {
+                       if (iim.params().type == InsetIndexMacroParams::Subentry) {
+                               if (iim.hasNoContent()) {
+                                       emptySubentriesWarning(mainentry);
+                                       continue;
+                               }
                                ++i;
                                if (i > 2)
                                        return;
@@ -671,7 +777,8 @@ void InsetIndex::getSubentries(otexstream & os, OutputParams const & runparams)
 }
 
 
-std::vector<docstring> InsetIndex::getSubentriesAsText(OutputParams const & runparams) const
+std::vector<docstring> InsetIndex::getSubentriesAsText(OutputParams const & runparams,
+                                                      bool const asLabel) const
 {
        std::vector<docstring> subentries;
 
@@ -683,14 +790,19 @@ std::vector<docstring> InsetIndex::getSubentriesAsText(OutputParams const & runp
                if (inset.lyxCode() == INDEXMACRO_CODE) {
                        InsetIndexMacro const & iim =
                                static_cast<InsetIndexMacro const &>(inset);
-                       if (iim.params().type == InsetIndexMacroParams::Subindex) {
+                       if (iim.params().type == InsetIndexMacroParams::Subentry) {
                                ++i;
                                if (i > 2)
                                        break;
-
-                               otexstringstream os;
-                               iim.getLatex(os, runparams);
-                               subentries.emplace_back(os.str());
+                               if (asLabel) {
+                                       docstring const l;
+                                       docstring const sl = iim.getNewLabel(l);
+                                       subentries.emplace_back(sl);
+                               } else {
+                                       otexstringstream os;
+                                       iim.getLatex(os, runparams);
+                                       subentries.emplace_back(os.str());
+                               }
                        }
                }
        }
@@ -726,7 +838,8 @@ void InsetIndex::getSeeRefs(otexstream & os, OutputParams const & runparams) con
 }
 
 
-docstring InsetIndex::getSeeAsText(OutputParams const & runparams) const
+docstring InsetIndex::getSeeAsText(OutputParams const & runparams,
+                                  bool const asLabel) const
 {
        Paragraph const & par = paragraphs().front();
        InsetList::const_iterator it = par.insetList().begin();
@@ -736,9 +849,14 @@ docstring InsetIndex::getSeeAsText(OutputParams const & runparams) const
                        InsetIndexMacro const & iim =
                                static_cast<InsetIndexMacro const &>(inset);
                        if (iim.params().type == InsetIndexMacroParams::See) {
-                               otexstringstream os;
-                               iim.getLatex(os, runparams);
-                               return os.str();
+                               if (asLabel) {
+                                       docstring const l;
+                                       return iim.getNewLabel(l);
+                               } else {
+                                       otexstringstream os;
+                                       iim.getLatex(os, runparams);
+                                       return os.str();
+                               }
                        }
                }
        }
@@ -746,7 +864,8 @@ docstring InsetIndex::getSeeAsText(OutputParams const & runparams) const
 }
 
 
-std::vector<docstring> InsetIndex::getSeeAlsoesAsText(OutputParams const & runparams) const
+std::vector<docstring> InsetIndex::getSeeAlsoesAsText(OutputParams const & runparams,
+                                                     bool const asLabel) const
 {
        std::vector<docstring> seeAlsoes;
 
@@ -758,9 +877,14 @@ std::vector<docstring> InsetIndex::getSeeAlsoesAsText(OutputParams const & runpa
                        InsetIndexMacro const & iim =
                                static_cast<InsetIndexMacro const &>(inset);
                        if (iim.params().type == InsetIndexMacroParams::Seealso) {
-                               otexstringstream os;
-                               iim.getLatex(os, runparams);
-                               seeAlsoes.emplace_back(os.str());
+                               if (asLabel) {
+                                       docstring const l;
+                                       seeAlsoes.emplace_back(iim.getNewLabel(l));
+                               } else {
+                                       otexstringstream os;
+                                       iim.getLatex(os, runparams);
+                                       seeAlsoes.emplace_back(os.str());
+                               }
                        }
                }
        }
@@ -797,7 +921,7 @@ bool hasInsetWithCode(const InsetIndex * const inset_index, const InsetCode code
 
 bool InsetIndex::hasSubentries() const
 {
-       return hasInsetWithCode(this, INDEXMACRO_CODE, {InsetIndexMacroParams::Subindex});
+       return hasInsetWithCode(this, INDEXMACRO_CODE, {InsetIndexMacroParams::Subentry});
 }
 
 
@@ -816,7 +940,7 @@ bool InsetIndex::hasSortKey() const
 bool InsetIndex::macrosPossible(string const type) const
 {
        if (type != "see" && type != "seealso"
-           && type != "sortkey" && type != "subindex")
+           && type != "sortkey" && type != "subentry")
                return false;
 
        Paragraph const & par = paragraphs().front();
@@ -832,8 +956,8 @@ bool InsetIndex::macrosPossible(string const type) const
                             && (iim.params().type == InsetIndexMacroParams::See
                                 || iim.params().type == InsetIndexMacroParams::Seealso))
                                return false;
-                       if (type == "subindex"
-                            && iim.params().type == InsetIndexMacroParams::Subindex) {
+                       if (type == "subentry"
+                            && iim.params().type == InsetIndexMacroParams::Subentry) {
                                ++subidxs;
                                if (subidxs > 1)
                                        return false;
@@ -910,8 +1034,22 @@ docstring const InsetIndex::buttonLabel(BufferView const & bv) const
        docstring res;
        if (!il.contentaslabel() || geometry(bv) != ButtonOnly)
                res = label;
-       else
+       else {
                res = getNewLabel(label);
+               OutputParams const rp(0);
+               vector<docstring> sublbls = getSubentriesAsText(rp, true);
+               for (auto const & sublbl : sublbls) {
+                       res += " " + docstring(1, char_type(0x2023));// TRIANGULAR BULLET
+                       res += " " + sublbl;
+               }
+               docstring see = getSeeAsText(rp, true);
+               if (see.empty() && !getSeeAlsoesAsText(rp, true).empty())
+                       see = getSeeAlsoesAsText(rp, true).front();
+               if (!see.empty()) {
+                       res += " " + docstring(1, char_type(0x261e));// WHITE RIGHT POINTING INDEX
+                       res += " " + see;
+               }
+       }
        if (!insetindexpagerangetranslator_latex().find(params_.range).empty())
                res += " " + from_ascii(insetindexpagerangetranslator_latex().find(params_.range));
        return res;
@@ -963,11 +1101,29 @@ void InsetIndex::addToToc(DocIterator const & cpit, bool output_active,
        DocIterator pit = cpit;
        pit.push_back(CursorSlice(const_cast<InsetIndex &>(*this)));
        docstring str;
+       InsetLayout const & il = getLayout();
+       docstring label = translateIfPossible(il.labelstring());
+       if (!il.contentaslabel())
+               str = label;
+       else {
+               str = getNewLabel(label);
+               OutputParams const rp(0);
+               vector<docstring> sublbls = getSubentriesAsText(rp, true);
+               for (auto const & sublbl : sublbls) {
+                       str += " " + docstring(1, char_type(0x2023));// TRIANGULAR BULLET
+                       str += " " + sublbl;
+               }
+               docstring see = getSeeAsText(rp, true);
+               if (see.empty() && !getSeeAlsoesAsText(rp, true).empty())
+                       see = getSeeAlsoesAsText(rp, true).front();
+               if (!see.empty()) {
+                       str += " " + docstring(1, char_type(0x261e));// WHITE RIGHT POINTING INDEX
+                       str += " " + see;
+               }
+       }
        string type = "index";
        if (buffer().masterBuffer()->params().use_indices)
                type += ":" + to_utf8(params_.index);
-       // this is unlikely to be terribly long
-       text().forOutliner(str, INT_MAX);
        TocBuilder & b = backend.builder(type);
        b.pushItem(pit, str, output_active);
        // Proceed with the rest of the inset.
@@ -1293,7 +1449,9 @@ public:
 private:
        bool isModern()
        {
+#ifdef LYX_INSET_INDEX_DEBUG
                std::cout << to_utf8(entry_) << std::endl;
+#endif // LYX_INSET_INDEX_DEBUG
 
                // If a modern parameter is present, this is definitely a modern index inset. Similarly, if it contains the
                // usual LaTeX symbols (!|@), then it is definitely a legacy index inset. Otherwise, if it has features of
@@ -1521,12 +1679,17 @@ docstring termAtLevel(const IndexNode* node, unsigned depth)
 
 void insertIntoNode(const IndexEntry& entry, IndexNode* node, unsigned depth = 0)
 {
+       // Do not insert empty entries.
+       if (entry.terms().empty())
+               return;
+
        // depth == 0 is for the root, not yet the index, hence the increase when going to vector size.
        for (IndexNode* child : node->children) {
                if (entry.terms()[depth] == termAtLevel(child, depth)) {
                        if (depth + 1 == entry.terms().size()) { // == child.entries.begin()->terms().size()
                                // All term entries match: it's an entry.
-                               child->entries.emplace_back(entry);
+                               if (!entry.terms()[depth].empty())
+                                       child->entries.emplace_back(entry);
                                return;
                        } else {
                                insertIntoNode(entry, child, depth + 1);
@@ -1572,7 +1735,7 @@ void outputIndexPage(XMLStream & xs, const IndexNode* root_node, unsigned depth
 
        xs << xml::StartTag("li", "class='" + generateCssClassAtDepth(depth) + "'");
        xs << xml::CR();
-       xs << XMLStream::ESCAPE_NONE << termAtLevel(root_node, depth);
+       xs << termAtLevel(root_node, depth);
        // By tree assumption, all the entries at this node have the same set of terms.
 
        if (!root_node->entries.empty()) {
@@ -1625,6 +1788,7 @@ void outputIndexPage(XMLStream & xs, const IndexNode* root_node, unsigned depth
                        }
                        entry_number += 1;
                }
+               xs << xml::CR();
        }
 
        if (!root_node->entries.empty() && !root_node->children.empty()) {
@@ -1647,7 +1811,7 @@ void outputIndexPage(XMLStream & xs, const IndexNode* root_node, unsigned depth
        xs << xml::CR();
 }
 
-// Only useful for debugging.
+#ifdef LYX_INSET_INDEX_DEBUG
 void printTree(const IndexNode* root_node, unsigned depth = 0)
 {
        static const std::string pattern = "    ";
@@ -1682,6 +1846,7 @@ void printTree(const IndexNode* root_node, unsigned depth = 0)
                printTree(node, depth + 1);
        }
 }
+#endif // LYX_INSET_INDEX_DEBUG
 }
 
 
@@ -1707,7 +1872,7 @@ docstring InsetPrintIndex::xhtml(XMLStream &, OutputParams const & op) const
                return docstring();
 
        const IndexNode* index_root = buildIndexTree(entries);
-#if 0
+#ifdef LYX_INSET_INDEX_DEBUG
        printTree(index_root);
 #endif