]> git.lyx.org Git - lyx.git/commitdiff
Merge branch 'master' into features/indexmacros
authorJuergen Spitzmueller <spitz@lyx.org>
Wed, 20 Apr 2022 06:38:12 +0000 (08:38 +0200)
committerJuergen Spitzmueller <spitz@lyx.org>
Wed, 20 Apr 2022 06:38:12 +0000 (08:38 +0200)
21 files changed:
lib/layouts/stdinsets.inc
lib/ui/stdcontext.inc
lib/ui/stdmenus.inc
src/FuncCode.h
src/LyXAction.cpp
src/Makefile.am
src/Text3.cpp
src/factory.cpp
src/frontends/qt/GuiIndex.cpp
src/frontends/qt/GuiIndex.h
src/frontends/qt/ui/IndexUi.ui
src/insets/InsetCode.h
src/insets/InsetIndex.cpp
src/insets/InsetIndex.h
src/insets/InsetIndexMacro.cpp [new file with mode: 0644]
src/insets/InsetIndexMacro.h [new file with mode: 0644]
src/insets/InsetText.cpp
src/tex2lyx/Parser.cpp
src/tex2lyx/Parser.h
src/tex2lyx/tex2lyx.h
src/tex2lyx/text.cpp

index 074b2748d52b11ad441b554a195e007d7416d22c..4063c630d702732ba225c715253ab7ec7d01db99 100644 (file)
@@ -431,6 +431,36 @@ InsetLayout Index
        PassThruChars         @|!
 End
 
+InsetLayout IndexMacro:see
+       LabelString           See
+       Decoration            classic
+       Font
+         Size                Small
+       EndFont
+       LabelFont
+         Color               indexlabel
+         Size                Small
+       EndFont
+       MultiPar              false
+       CustomPars            false
+       ForcePlain            true
+End
+
+InsetLayout IndexMacro:seealso
+       CopyStyle             IndexMacro:see
+       LabelString           "See also"
+End
+
+InsetLayout IndexMacro:sortkey
+       CopyStyle             IndexMacro:see
+       LabelString           "Sort as"
+End
+
+InsetLayout IndexMacro:subindex
+       CopyStyle             IndexMacro:see
+       LabelString           "Subindex"
+End
+
 InsetLayout Box
        LabelFont
          Color               foreground
index ed9a9da9face17ffaa44758e777f45b1743e8126..5929dbf2176fa9aa28077f883e32f4a4f80cc30d 100644 (file)
@@ -631,6 +631,23 @@ Menuset
        Menu "context-index"
                IndicesContext
        End
+       
+       Menu "context-edit-index"
+               OptItem "Insert Subentry|b" "indexmacro-insert subindex"
+               OptItem "Insert Sortkey|k" "indexmacro-insert sortkey"
+               OptItem "Insert See Reference|e" "indexmacro-insert see"
+               OptItem "Insert See also Reference|a" "indexmacro-insert seealso"
+       End
+
+#
+# IndexMacro context menu
+#
+
+       Menu "context-indexmacro"
+               OptItem "See|e" "inset-modify changetype see"
+               OptItem "See also|a" "inset-modify changetype seealso"
+       End
+
 
 #
 # Index Lists context menu
index ec5c5c0ed64cead5fa4358e2ad3aee54308b4894..2954b39c1df1fc0e12a4572756f347b7808641e7 100644 (file)
@@ -391,6 +391,7 @@ Menuset
                Item "Label...|L" "label-insert"
                Captions
                Indices
+               OptSubmenu "Index Properties" "index_properties"
                Item "Nomenclature Entry...|y" "nomencl-insert"
                Separator
                Item "Table...|T" "tabular-insert"
@@ -532,6 +533,13 @@ Menuset
                Item "Double Frame|u" "box-insert Doublebox"
        End
 
+       Menu "index_properties"
+               OptItem "Subentry|b" "indexmacro-insert subindex"
+               OptItem "Sortkey|k" "indexmacro-insert sortkey"
+               OptItem "See|e" "indexmacro-insert see"
+               OptItem "See also|a" "indexmacro-insert seealso"
+       End
+
        Menu "insert_note"
                Item "LyX Note|N" "note-insert Note"
                Item "Comment|C" "note-insert Comment"
index 8337c1f9210e277b3ca2ac578a8a4bda7caf863b..6d7ed85a2521e05cebc8eb55bf56b0ac74e521d5 100644 (file)
@@ -503,6 +503,8 @@ enum FuncCode
        LFUN_FINISHED_DOWN,             // lasgouttes 20210629
        LFUN_FINISHED_UP,               // lasgouttes 20210629
        LFUN_BRANCH_SYNC_ALL,           // sanda 20220415
+       LFUN_INDEXMACRO_INSERT,         // spitz 20220220
+       // 395
        LFUN_LASTACTION                 // end of the table
 };
 
index 559d9501e3e12508b44a1bdbdab62a81d31ead6c..399b3c0c8cbabc1e45a4450fc48779f552606375 100644 (file)
@@ -2323,6 +2323,16 @@ void LyXAction::init()
  */
                { LFUN_IN_MATHMACROTEMPLATE, "in-mathmacrotemplate", Noop, Math },
 
+/*!
+ * \var lyx::FuncCode lyx::LFUN_INDEXMACRO_INSERT
+ * \li Action: Inserts special Index macros into the document.
+ * \li Syntax: indexmacro-insert <type>
+ * \li Params: <type>: see, seealso, subindex, sortkey.
+ * \li Origin: spitz, 20 Feb 2022
+ * \endvar
+ */
+               { LFUN_INDEXMACRO_INSERT, "indexmacro-insert", Noop, Edit },
+
 
 /*!
  * \var lyx::FuncCode lyx::LFUN_IPAMACRO_INSERT
index 99a0f98ceb16895bb81e23a84b78bf27198a7ddc..9a642d83f7199025767262e6dd5dd57f8dc3d851 100644 (file)
@@ -565,6 +565,7 @@ SOURCEFILESINSETS = \
        insets/InsetHyperlink.cpp \
        insets/InsetInclude.cpp \
        insets/InsetIndex.cpp \
+       insets/InsetIndexMacro.cpp \
        insets/InsetInfo.cpp \
        insets/InsetIPA.cpp \
        insets/InsetIPAMacro.cpp \
index 185008b7255197995b61405a148aa2f4bf27b251..bacc808b06518185be97141e35dd2157ed4fc7be 100644 (file)
@@ -58,6 +58,7 @@
 #include "insets/InsetGraphics.h"
 #include "insets/InsetGraphicsParams.h"
 #include "insets/InsetInfo.h"
+#include "insets/InsetIndexMacro.h"
 #include "insets/InsetIPAMacro.h"
 #include "insets/InsetNewline.h"
 #include "insets/InsetQuotes.h"
@@ -2107,6 +2108,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        case LFUN_BRANCH_INSERT:
        case LFUN_PHANTOM_INSERT:
        case LFUN_ERT_INSERT:
+       case LFUN_INDEXMACRO_INSERT:
        case LFUN_LISTING_INSERT:
        case LFUN_MARGINALNOTE_INSERT:
        case LFUN_ARGUMENT_INSERT:
@@ -3285,6 +3287,14 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                }
                code = HYPERLINK_CODE;
                break;
+       case LFUN_INDEXMACRO_INSERT: {
+               string const arg = cmd.getArg(0);
+               if (arg == "sortkey")
+                       code = INDEXMACRO_SORTKEY_CODE;
+               else
+                       code = INDEXMACRO_CODE;
+               break;
+       }
        case LFUN_IPAMACRO_INSERT: {
                string const arg = cmd.getArg(0);
                if (arg == "deco")
index 6f17797ada95b88c45961f23f7a6b9ca5cf212ad..4ab74dad4c59093c54c8b3146989e758151a2355 100644 (file)
@@ -34,6 +34,7 @@
 #include "insets/InsetHyperlink.h"
 #include "insets/InsetInclude.h"
 #include "insets/InsetIndex.h"
+#include "insets/InsetIndexMacro.cpp"
 #include "insets/InsetInfo.h"
 #include "insets/InsetIPA.h"
 #include "insets/InsetIPAMacro.h"
@@ -159,6 +160,16 @@ Inset * createInsetHelper(Buffer * buf, FuncRequest const & cmd)
                        return new InsetIPADeco(buf, arg2);
                }
 
+               case LFUN_INDEXMACRO_INSERT: {
+                       string const arg = cmd.getArg(0);
+                       if (arg != "see" && arg != "seealso"
+                           && arg != "subindex" && arg != "sortkey") {
+                               LYXERR0("LFUN_IPAMACRO_INSERT: wrong argument");
+                               return nullptr;
+                       }
+                       return new InsetIndexMacro(buf, arg);
+               }
+
                case LFUN_ERT_INSERT:
                        return new InsetERT(buf);
 
@@ -667,6 +678,9 @@ Inset * readInset(Lexer & lex, Buffer * buf)
                        inset.reset(new InsetCaption(buf, s));
                } else if (tmptok == "Index") {
                        inset.reset(new InsetIndex(buf, InsetIndexParams()));
+               } else if (tmptok == "IndexMacro") {
+                       string s = lex.getString();
+                       inset.reset(new InsetIndexMacro(buf, s));
                } else if (tmptok == "FloatList") {
                        inset.reset(new InsetFloatList(buf));
                } else if (tmptok == "Info") {
index 6264d6a1012b24ba405675481cab6c29d689acf3..5e145808e6d0091970d7423665452518ab3e5f66 100644 (file)
@@ -20,6 +20,7 @@
 #include "BufferParams.h"
 #include "FuncRequest.h"
 #include "IndicesList.h"
+#include "insets/InsetIndex.h"
 
 #include <QPushButton>
 
@@ -37,6 +38,18 @@ GuiIndex::GuiIndex(GuiView & lv)
                this, SLOT(slotButtonBox(QAbstractButton *)));
        connect(indicesCO, SIGNAL(activated(int)), this, SLOT(change_adaptor()));
 
+       rangeCO->addItem(qt_("None"), InsetIndexParams::None);
+       rangeCO->addItem(qt_("Start"), InsetIndexParams::Start);
+       rangeCO->addItem(qt_("End"), InsetIndexParams::End);
+       connect(rangeCO, SIGNAL(activated(int)), this, SLOT(change_adaptor()));
+
+       pageFormatCO->addItem(qt_("Default"), toqstr("default"));
+       pageFormatCO->addItem(qt_("Bold"), toqstr("textbf"));
+       pageFormatCO->addItem(qt_("Italic"), toqstr("textit"));
+       pageFormatCO->addItem(qt_("Emphasized"), toqstr("emph"));
+       pageFormatCO->addItem(qt_("Custom"), toqstr("custom"));
+       connect(pageFormatCO, SIGNAL(activated(int)), this, SLOT(pageFormatChanged(int)));
+
        bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
        bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
        bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
@@ -49,11 +62,26 @@ void GuiIndex::change_adaptor()
 }
 
 
+void GuiIndex::pageFormatChanged(int i)
+{
+       QString const pf = pageFormatCO->itemData(i).toString();
+       pageFormatLE->setEnabled(pf == "custom");
+       change_adaptor();
+}
+
+
 void GuiIndex::updateContents()
 {
        typedef IndicesList::const_iterator const_iterator;
 
-       IndicesList const & indiceslist = buffer().params().indiceslist();
+       BufferParams const & bp = buffer().masterBuffer()->params();
+       indicesGB->setEnabled(bp.use_indices);
+
+       QString const pf = pageFormatCO->itemData(
+               pageFormatCO->currentIndex()).toString();
+       pageFormatLE->setEnabled(pf == "custom");
+
+       IndicesList const & indiceslist = bp.indiceslist();
        docstring const cur_index = params_.index;
 
        indicesCO->clear();
@@ -64,8 +92,19 @@ void GuiIndex::updateContents()
                indicesCO->addItem(toqstr(it->index()),
                        QVariant(toqstr(it->shortcut())));
 
-       int const pos = indicesCO->findData(toqstr(cur_index));
+       int pos = indicesCO->findData(toqstr(cur_index));
        indicesCO->setCurrentIndex(pos);
+
+       pos = pageFormatCO->findData(toqstr(params_.pagefmt));
+       if (pos == -1) {
+               pos = pageFormatCO->findData("custom");
+               pageFormatLE->setText(toqstr(params_.pagefmt));
+       } else
+               pageFormatLE->clear();
+       pageFormatCO->setCurrentIndex(pos);
+
+       pos = rangeCO->findData(params_.range);
+       rangeCO->setCurrentIndex(pos);
 }
 
 
@@ -73,7 +112,15 @@ void GuiIndex::applyView()
 {
        QString const index = indicesCO->itemData(
                indicesCO->currentIndex()).toString();
+       int const range = rangeCO->itemData(
+               rangeCO->currentIndex()).toInt();
+       QString const pagefmt = pageFormatCO->itemData(
+               pageFormatCO->currentIndex()).toString();
        params_.index = qstring_to_ucs4(index);
+       params_.range = InsetIndexParams::PageRange(range);
+       params_.pagefmt = (pagefmt == "custom") 
+                       ? fromqstr(pageFormatLE->text())
+                       : fromqstr(pagefmt);
 }
 
 
index bb99a22efdda4962e32bcd6ee6659c1d4b2ee65d..0644644ca3e1f4ba9c156c60efe6fb446a03922b 100644 (file)
@@ -31,6 +31,7 @@ public:
 
 private Q_SLOTS:
        void change_adaptor();
+       void pageFormatChanged(int);
 
 private:
        ///  Apply changes
index 95c2a710146170c8af95fa83c758880df7071a82..a314489e054909e6aa1a3d1055c7c4dc5a7aa87e 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>262</width>
-    <height>121</height>
+    <width>384</width>
+    <height>288</height>
    </rect>
   </property>
   <property name="windowTitle">
   <property name="sizeGripEnabled">
    <bool>true</bool>
   </property>
-  <layout class="QGridLayout" name="gridLayout">
+  <layout class="QGridLayout" name="gridLayout_3">
    <item row="0" column="0">
-    <layout class="QVBoxLayout">
-     <property name="spacing">
-      <number>6</number>
+    <widget class="QGroupBox" name="indicesGB">
+     <property name="title">
+      <string>Available I&amp;ndexes</string>
      </property>
-     <property name="leftMargin">
-      <number>0</number>
-     </property>
-     <property name="topMargin">
-      <number>0</number>
-     </property>
-     <property name="rightMargin">
-      <number>0</number>
-     </property>
-     <property name="bottomMargin">
-      <number>0</number>
-     </property>
-     <item>
-      <widget class="QLabel" name="indicesLA">
-       <property name="text">
-        <string>Available I&amp;ndexes:</string>
-       </property>
-       <property name="buddy">
-        <cstring>indicesCO</cstring>
-       </property>
-      </widget>
-     </item>
-     <item>
-      <widget class="QComboBox" name="indicesCO">
-       <property name="toolTip">
-        <string>Select the index this entry should be listed in.</string>
-       </property>
-      </widget>
-     </item>
-    </layout>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QComboBox" name="indicesCO">
+        <property name="toolTip">
+         <string>Select the index this entry should be listed in.</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
    </item>
    <item row="1" column="0">
+    <widget class="QGroupBox" name="paginationGB">
+     <property name="title">
+      <string>&amp;Pagination</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="rangeLA">
+        <property name="text">
+         <string>Page &amp;Range:</string>
+        </property>
+        <property name="buddy">
+         <cstring>rangeCO</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="rangeCO">
+        <property name="toolTip">
+         <string>If the entry spans multiple pages, you can start or end the range here</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>112</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="pageFormatLA">
+        <property name="text">
+         <string>&amp;Format:</string>
+        </property>
+        <property name="buddy">
+         <cstring>pageFormatCO</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QComboBox" name="pageFormatCO">
+        <property name="toolTip">
+         <string>Customize the format of the page number here. Note that the format is not used with &quot;See&quot; and &quot;See also&quot; references.</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="2">
+       <widget class="QLineEdit" name="pageFormatLE">
+        <property name="toolTip">
+         <string>Enter custom command here (without leading backslash).</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="3" column="0">
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="standardButtons">
       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
      </property>
     </widget>
    </item>
+   <item row="2" column="0">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
   </layout>
  </widget>
  <tabstops>
index 43ed4b5d0af2ec0ecf6cd20f47b07d513503072f..0b60ebfb8fc34f365478ba9af0418c76255e8303 100644 (file)
@@ -239,6 +239,10 @@ enum InsetCode {
        ///
        COUNTER_CODE,
        ///
+       INDEXMACRO_CODE, // 110
+       ///
+       INDEXMACRO_SORTKEY_CODE,
+       ///
        INSET_CODE_SIZE
 };
 
index 86222635e35296ead049294f582a5fc8ba6f41d4..8b69b6f3280d8d34065b5b0efeaa9020c5ee7ba8 100644 (file)
@@ -11,6 +11,7 @@
 #include <config.h>
 
 #include "InsetIndex.h"
+#include "InsetIndexMacro.h"
 
 #include "Buffer.h"
 #include "BufferParams.h"
 #include "Cursor.h"
 #include "DispatchResult.h"
 #include "Encoding.h"
+#include "ErrorList.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
 #include "IndicesList.h"
+#include "InsetList.h"
 #include "Language.h"
+#include "LaTeX.h"
 #include "LaTeXFeatures.h"
 #include "Lexer.h"
 #include "output_latex.h"
@@ -37,6 +41,7 @@
 #include "support/FileName.h"
 #include "support/gettext.h"
 #include "support/lstrings.h"
+#include "support/Translator.h"
 
 #include "frontends/alert.h"
 
@@ -51,6 +56,62 @@ using namespace lyx::support;
 
 namespace lyx {
 
+namespace {
+
+typedef Translator<string, InsetIndexParams::PageRange> PageRangeTranslator;
+typedef Translator<docstring, InsetIndexParams::PageRange> PageRangeTranslatorLoc;
+
+PageRangeTranslator const init_insetindexpagerangetranslator()
+{
+       PageRangeTranslator translator("none", InsetIndexParams::None);
+       translator.addPair("start", InsetIndexParams::Start);
+       translator.addPair("end", InsetIndexParams::End);
+       return translator;
+}
+
+PageRangeTranslator const init_insetindexpagerangetranslator_latex()
+{
+       PageRangeTranslator translator("", InsetIndexParams::None);
+       translator.addPair("(", InsetIndexParams::Start);
+       translator.addPair(")", InsetIndexParams::End);
+       return translator;
+}
+
+
+PageRangeTranslatorLoc const init_insetindexpagerangetranslator_loc()
+{
+       PageRangeTranslatorLoc translator(docstring(), InsetIndexParams::None);
+       translator.addPair(_("Starts page range"), InsetIndexParams::Start);
+       translator.addPair(_("Ends page range"), InsetIndexParams::End);
+       return translator;
+}
+
+
+PageRangeTranslator const & insetindexpagerangetranslator()
+{
+       static PageRangeTranslator const prtranslator =
+                       init_insetindexpagerangetranslator();
+       return prtranslator;
+}
+
+
+PageRangeTranslatorLoc const & insetindexpagerangetranslator_loc()
+{
+       static PageRangeTranslatorLoc const translator =
+                       init_insetindexpagerangetranslator_loc();
+       return translator;
+}
+
+
+PageRangeTranslator const & insetindexpagerangetranslator_latex()
+{
+       static PageRangeTranslator const lttranslator =
+                       init_insetindexpagerangetranslator_latex();
+       return lttranslator;
+}
+
+} // namespace anon
+
 /////////////////////////////////////////////////////////////////////
 //
 // InsetIndex
@@ -59,7 +120,7 @@ namespace lyx {
 
 
 InsetIndex::InsetIndex(Buffer * buf, InsetIndexParams const & params)
-        : InsetCollapsible(buf), params_(params)
+       : InsetCollapsible(buf), params_(params)
 {}
 
 
@@ -91,106 +152,108 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
                return;
        }
 
-       // For the sorting key, we use the plaintext version
-       odocstringstream ourplain;
-       InsetText::plaintext(ourplain, runparams);
-
-       // These are the LaTeX and plaintext representations
-       docstring latexstr = ourlatex.str();
-       docstring plainstr = ourplain.str();
-
-       // This will get what follows | if anything does,
-       // the command (e.g., see, textbf) for pagination
-       // formatting
-       docstring cmd;
-
-       // Check for the | separator to strip the cmd.
-       // This goes wrong on an escaped "|", but as the escape
-       // character can be changed in style files, we cannot
-       // prevent that.
-       size_t pos = latexstr.find(from_ascii("|"));
-       if (pos != docstring::npos) {
-               // Put the bit after "|" into cmd...
-               cmd = latexstr.substr(pos + 1);
-               // ...and erase that stuff from latexstr
-               latexstr = latexstr.erase(pos);
-               // ...as well as from plainstr
-               size_t ppos = plainstr.find(from_ascii("|"));
-               if (ppos < plainstr.size())
-                       plainstr.erase(ppos);
-               else
-                       LYXERR0("The `|' separator was not found in the plaintext version!");
-       }
+       if (hasSortKey()) {
+               getSortkey(os, runparams);
+               os << "@";
+               os << ourlatex.str();
+               getSubentries(os, runparams);
+               if (hasSeeRef()) {
+                       os << "|";
+                       os << insetindexpagerangetranslator_latex().find(params_.range);
+                       getSeeRefs(os, runparams);
+               }
+       } else {
+               // We check whether we need a sort key.
+               // If so, we use the plaintext version
+               odocstringstream ourplain;
+               InsetText::plaintext(ourplain, runparams);
+
+               // These are the LaTeX and plaintext representations
+               docstring latexstr = ourlatex.str();
+               docstring plainstr = ourplain.str();
+       
+               // This will get what follows | if anything does,
+               // the command (e.g., see, textbf) for pagination
+               // formatting
+               docstring cmd;
+
+               if (hasSeeRef()) {
+                       odocstringstream seeref;
+                       otexstream otsee(seeref);
+                       getSeeRefs(otsee, runparams);
+                       cmd = seeref.str();
+               } else if (!params_.pagefmt.empty() && params_.pagefmt != "default") {
+                       cmd = from_utf8(params_.pagefmt);
+               } else {
+                       // Check for the | separator to strip the cmd.
+                       // This goes wrong on an escaped "|", but as the escape
+                       // character can be changed in style files, we cannot
+                       // prevent that.
+                       size_t pos = latexstr.find(from_ascii("|"));
+                       if (pos != docstring::npos) {
+                               // Put the bit after "|" into cmd...
+                               cmd = latexstr.substr(pos + 1);
+                               // ...and erase that stuff from latexstr
+                               latexstr = latexstr.erase(pos);
+                               // ...as well as from plainstr
+                               size_t ppos = plainstr.find(from_ascii("|"));
+                               if (ppos < plainstr.size())
+                                       plainstr.erase(ppos);
+                               else
+                                       LYXERR0("The `|' separator was not found in the plaintext version!");
+                       }
+               }
 
-       // Separate the entries and subentries, i.e., split on "!".
-       // This goes wrong on an escaped "!", but as the escape
-       // character can be changed in style files, we cannot
-       // prevent that.
-       std::vector<docstring> const levels =
-                       getVectorFromString(latexstr, from_ascii("!"), true);
-       std::vector<docstring> const levels_plain =
-                       getVectorFromString(plainstr, from_ascii("!"), true);
-
-       vector<docstring>::const_iterator it = levels.begin();
-       vector<docstring>::const_iterator end = levels.end();
-       vector<docstring>::const_iterator it2 = levels_plain.begin();
-       bool first = true;
-       for (; it != end; ++it) {
-               // The separator needs to be put back when
-               // writing the levels, except for the first level
-               if (!first)
-                       os << '!';
-               else
-                       first = false;
-
-               // Now here comes the reason for this whole procedure:
-               // We try to correctly sort macros and formatted strings.
-               // If we find a command, prepend a plain text
-               // version of the content to get sorting right,
-               // e.g. \index{LyX@\LyX}, \index{text@\textbf{text}}.
-               // We do this on all levels.
-               // We don't do it if the level already contains a '@', though.
-               if (contains(*it, '\\') && !contains(*it, '@')) {
-                       // Plaintext might return nothing (e.g. for ERTs).
-                       // In that case, we use LaTeX.
-                       docstring const spart =
-                                       (it2 < levels_plain.end() && !(*it2).empty())
-                                       ? *it2 : *it;
-                       // Now we need to validate that all characters in
-                       // the sorting part are representable in the current
-                       // encoding. If not try the LaTeX macro which might
-                       // or might not be a good choice, and issue a warning.
-                       pair<docstring, docstring> spart_latexed =
-                                       runparams.encoding->latexString(spart, runparams.dryrun);
-                       if (!spart_latexed.second.empty())
-                               LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
-                       if (spart != spart_latexed.first && !runparams.dryrun) {
-                               // FIXME: warning should be passed to the error dialog
-                               frontend::Alert::warning(_("Index sorting failed"),
-                                                        bformat(_("LyX's automatic index sorting algorithm faced\n"
-                                                                  "problems with the entry '%1$s'.\n"
-                                                                  "Please specify the sorting of this entry manually, as\n"
-                                                                  "explained in the User Guide."), spart));
+               odocstringstream subentries;
+               otexstream otsub(subentries);
+               getSubentries(otsub, runparams);
+               if (subentries.str().empty()) {
+                       // Separate the entries and subentries, i.e., split on "!".
+                       // This goes wrong on an escaped "!", but as the escape
+                       // character can be changed in style files, we cannot
+                       // prevent that.
+                       std::vector<docstring> const levels =
+                                       getVectorFromString(latexstr, from_ascii("!"), true);
+                       std::vector<docstring> const levels_plain =
+                                       getVectorFromString(plainstr, from_ascii("!"), true);
+               
+                       vector<docstring>::const_iterator it = levels.begin();
+                       vector<docstring>::const_iterator end = levels.end();
+                       vector<docstring>::const_iterator it2 = levels_plain.begin();
+                       bool first = true;
+                       for (; it != end; ++it) {
+                               // The separator needs to be put back when
+                               // writing the levels, except for the first level
+                               if (!first)
+                                       os << '!';
+                               else
+                                       first = false;
+               
+                               // Now here comes the reason for this whole procedure:
+                               // We try to correctly sort macros and formatted strings.
+                               // If we find a command, prepend a plain text
+                               // version of the content to get sorting right,
+                               // e.g. \index{LyX@\LyX}, \index{text@\textbf{text}}.
+                               // We do this on all levels.
+                               // We don't do it if the level already contains a '@', though.
+                               // Plaintext might return nothing (e.g. for ERTs).
+                               // In that case, we use LaTeX.
+                               docstring const spart = (levels_plain.empty() || (*it2).empty()) ? *it : *it2;
+                               processLatexSorting(os, runparams, *it, spart);
+                               if (it2 < levels_plain.end())
+                                       ++it2;
                        }
-                       // Remove remaining \'s from the sort key
-                       docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
-                       // Plain quotes need to be escaped, however (#10649), as this
-                       // is the default escape character
-                       ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
-
-                       // Now insert the sortkey, separated by '@'.
-                       os << ppart;
-                       os << '@';
+               } else {
+                       processLatexSorting(os, runparams, latexstr, plainstr);
+                       os << subentries.str();
+               }
+
+               // At last, re-insert the command, separated by "|"
+               if (!cmd.empty()) {
+                       os << "|"
+                          << insetindexpagerangetranslator_latex().find(params_.range)
+                          << cmd;
                }
-               // Insert the actual level text
-               docstring const tpart = *it;
-               os << tpart;
-               if (it2 < levels_plain.end())
-                       ++it2;
-       }
-       // At last, re-insert the command, separated by "|"
-       if (!cmd.empty()) {
-               os << "|" << cmd;
        }
        os << '}';
 
@@ -203,6 +266,45 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
 }
 
 
+void InsetIndex::processLatexSorting(otexstream & os, OutputParams const & runparams,
+                               docstring const latex, docstring const spart) const
+{
+       if (contains(latex, '\\') && !contains(latex, '@')) {
+               // Now we need to validate that all characters in
+               // the sorting part are representable in the current
+               // encoding. If not try the LaTeX macro which might
+               // or might not be a good choice, and issue a warning.
+               pair<docstring, docstring> spart_latexed =
+                               runparams.encoding->latexString(spart, runparams.dryrun);
+               if (!spart_latexed.second.empty())
+                       LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
+               if (spart != spart_latexed.first && !runparams.dryrun) {
+                       TeXErrors terr;
+                       ErrorList & errorList = buffer().errorList("Export");
+                       docstring const s = bformat(_("LyX's automatic index sorting algorithm faced "
+                                                     "problems with the entry '%1$s'.\n"
+                                                     "Please specify the sorting of this entry manually, as "
+                                                     "explained in the User Guide."), spart);
+                       Paragraph const & par = buffer().paragraphs().front();
+                       errorList.push_back(ErrorItem(_("Index sorting failed"), s,
+                                                     {par.id(), 0}, {par.id(), -1}));
+                       buffer().bufferErrors(terr, errorList);
+               }
+               // Remove remaining \'s from the sort key
+               docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
+               // Plain quotes need to be escaped, however (#10649), as this
+               // is the default escape character
+               ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
+
+               // Now insert the sortkey, separated by '@'.
+               os << ppart;
+               os << '@';
+       }
+       // Insert the actual level text
+       os << latex;
+}
+
+
 void InsetIndex::docbook(XMLStream & xs, OutputParams const & runparams) const
 {
        // Get the content of the inset as LaTeX, as some things may be encoded as ERT (like {}).
@@ -445,6 +547,8 @@ void InsetIndex::doDispatch(Cursor & cur, FuncRequest & cmd)
                InsetIndex::string2params(to_utf8(cmd.argument()), params);
                cur.recordUndoInset(this);
                params_.index = params.index;
+               params_.range = params.range;
+               params_.pagefmt = params.pagefmt;
                // what we really want here is a TOC update, but that means
                // a full buffer update
                cur.forceBufferUpdate();
@@ -485,6 +589,9 @@ bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
                flag.setEnabled(realbuffer.params().use_indices);
                return true;
        }
+       
+       case LFUN_INDEXMACRO_INSERT:
+               return macrosPossible(cmd.getArg(0));
 
        default:
                return InsetCollapsible::getStatus(cur, cmd, flag);
@@ -492,6 +599,125 @@ bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
 }
 
 
+void InsetIndex::getSortkey(otexstream & os, OutputParams const & runparams) const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE) {
+                       InsetIndexMacro const & iim =
+                               static_cast<InsetIndexMacro const &>(inset);
+                       iim.getLatex(os, runparams);
+                       return;
+               }
+       }
+}
+
+
+void InsetIndex::getSubentries(otexstream & os, OutputParams const & runparams) const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       int i = 0;
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_CODE) {
+                       InsetIndexMacro const & iim =
+                               static_cast<InsetIndexMacro const &>(inset);
+                       if (iim.params().type == InsetIndexMacroParams::Subindex) {
+                               ++i;
+                               if (i > 2)
+                                       return;
+                               os << "!";
+                               iim.getLatex(os, runparams);
+                       }
+               }
+       }
+}
+
+
+void InsetIndex::getSeeRefs(otexstream & os, OutputParams const & runparams) const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_CODE) {
+                       InsetIndexMacro const & iim =
+                               static_cast<InsetIndexMacro const &>(inset);
+                       if (iim.params().type == InsetIndexMacroParams::See
+                           || iim.params().type == InsetIndexMacroParams::Seealso) {
+                               iim.getLatex(os, runparams);
+                               return;
+                       }
+               }
+       }
+}
+
+
+bool InsetIndex::hasSeeRef() const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_CODE) {
+                       InsetIndexMacro const & iim =
+                               static_cast<InsetIndexMacro const &>(inset);
+                       if (iim.params().type == InsetIndexMacroParams::See
+                           || iim.params().type == InsetIndexMacroParams::Seealso)
+                               return true;
+               }
+       }
+       return false;
+}
+
+
+bool InsetIndex::hasSortKey() const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
+                       return true;
+       }
+       return false;
+}
+
+
+bool InsetIndex::macrosPossible(string const type) const
+{
+       if (type != "see" && type != "seealso"
+           && type != "sortkey" && type != "subindex")
+               return false;
+
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       int subidxs = 0;
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (type == "sortkey" && inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
+                       return false;
+               if (inset.lyxCode() == INDEXMACRO_CODE) {
+                       InsetIndexMacro const & iim = static_cast<InsetIndexMacro const &>(inset);
+                       if ((type == "see" || type == "seealso")
+                            && (iim.params().type == InsetIndexMacroParams::See
+                                || iim.params().type == InsetIndexMacroParams::Seealso))
+                               return false;
+                       if (type == "subindex"
+                            && iim.params().type == InsetIndexMacroParams::Subindex) {
+                               ++subidxs;
+                               if (subidxs > 1)
+                                       return false;
+                       }
+               }
+       }
+       return true;
+}
+
+
 ColorCode InsetIndex::labelColor() const
 {
        if (params_.index.empty() || params_.index == from_ascii("idx"))
@@ -520,7 +746,21 @@ docstring InsetIndex::toolTip(BufferView const &, int, int) const
                tip += ")";
        }
        tip += ": ";
-       return toolTipText(tip);
+       docstring res = toolTipText(tip);
+       if (!insetindexpagerangetranslator_loc().find(params_.range).empty())
+               res += "\n" + insetindexpagerangetranslator_loc().find(params_.range);
+       if (!params_.pagefmt.empty() && params_.pagefmt != "default") {
+               res += "\n" + _("Pagination format:") + " ";
+               if (params_.pagefmt == "textbf")
+                       res += _("bold");
+               else if (params_.pagefmt == "textit")
+                       res += _("italic");
+               else if (params_.pagefmt == "emph")
+                       res += _("emphasized");
+               else
+                       res += from_utf8(params_.pagefmt);
+       }
+       return res;
 }
 
 
@@ -541,9 +781,14 @@ docstring const InsetIndex::buttonLabel(BufferView const & bv) const
                label += ")";
        }
 
+       docstring res;
        if (!il.contentaslabel() || geometry(bv) != ButtonOnly)
-               return label;
-       return getNewLabel(label);
+               res = label;
+       else
+               res = getNewLabel(label);
+       if (!insetindexpagerangetranslator_latex().find(params_.range).empty())
+               res += " " + from_ascii(insetindexpagerangetranslator_latex().find(params_.range));
+       return res;
 }
 
 
@@ -621,12 +866,33 @@ string InsetIndex::contextMenuName() const
 }
 
 
+string InsetIndex::contextMenu(BufferView const & bv, int x, int y) const
+{
+       // We override the implementation of InsetCollapsible,
+       // because we have eytra entries.
+       string owncm = "context-edit-index;";
+       return owncm + InsetCollapsible::contextMenu(bv, x, y);
+}
+
+
 bool InsetIndex::hasSettings() const
 {
-       return buffer().masterBuffer()->params().use_indices;
+       return true;
 }
 
 
+bool InsetIndex::insetAllowed(InsetCode code) const
+{
+       switch (code) {
+       case INDEXMACRO_CODE:
+       case INDEXMACRO_SORTKEY_CODE:
+               return true;
+       case INDEX_CODE:
+               return false;
+       default:
+               return InsetCollapsible::insetAllowed(code);
+       }
+}
 
 
 /////////////////////////////////////////////////////////////////////
@@ -644,6 +910,12 @@ void InsetIndexParams::write(ostream & os) const
        else
                os << "idx";
        os << '\n';
+       os << "range "
+          << insetindexpagerangetranslator().find(range)
+          << '\n';
+       os << "pageformat "
+          << pagefmt
+          << '\n';
 }
 
 
@@ -653,6 +925,16 @@ void InsetIndexParams::read(Lexer & lex)
                index = lex.getDocString();
        else
                index = from_ascii("idx");
+       if (lex.checkFor("range")) {
+               string st = lex.getString();
+               if (lex.eatLine()) {
+                       st = lex.getString();
+                       range = insetindexpagerangetranslator().find(lex.getString());
+               }
+       }
+       if (lex.checkFor("pageformat") && lex.eatLine()) {
+               pagefmt = lex.getString();
+       }
 }
 
 
index b064cc700180a769ebfd1182794c8cd6399925e9..97a82babf575fe8547ea1b657b8fd38860db32be 100644 (file)
@@ -21,15 +21,24 @@ namespace lyx {
 
 class InsetIndexParams {
 public:
+       enum PageRange {
+               None,
+               Start,
+               End
+       };
        ///
        explicit InsetIndexParams(docstring const & b = docstring())
-               : index(b) {}
+               : index(b), range(None), pagefmt("default") {}
        ///
        void write(std::ostream & os) const;
        ///
        void read(Lexer & lex);
        ///
        docstring index;
+       ///
+       PageRange range;
+       ///
+       std::string pagefmt;
 };
 
 
@@ -63,6 +72,9 @@ private:
        ///
        void latex(otexstream &, OutputParams const &) const override;
        ///
+       void processLatexSorting(otexstream &, OutputParams const &,
+                                docstring const, docstring const) const;
+       ///
        bool showInsetDialog(BufferView *) const override;
        ///
        bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const override;
@@ -80,11 +92,27 @@ private:
        /// Updates needed features for this inset.
        void validate(LaTeXFeatures & features) const override;
        ///
+       void getSortkey(otexstream &, OutputParams const &) const;
+       ///
+       void getSubentries(otexstream &, OutputParams const &) const;
+       ///
+       void getSeeRefs(otexstream &, OutputParams const &) const;
+       ///
+       bool hasSeeRef() const;
+       ///
+       bool hasSortKey() const;
+       ///
+       bool macrosPossible(std::string const type) const;
+       ///
        std::string contextMenuName() const override;
        ///
+       std::string contextMenu(BufferView const &, int, int) const override;
+       ///
        Inset * clone() const override { return new InsetIndex(*this); }
        /// Is the content of this inset part of the immediate text sequence?
        bool isPartOfTextSequence() const override { return false; }
+       ///
+       bool insetAllowed(InsetCode code) const override;
 
        ///
        friend class InsetIndexParams;
diff --git a/src/insets/InsetIndexMacro.cpp b/src/insets/InsetIndexMacro.cpp
new file mode 100644 (file)
index 0000000..2bfbc91
--- /dev/null
@@ -0,0 +1,384 @@
+/**
+ * \file InsetIndexMacro.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jürgen Spitzmüller
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "InsetIndexMacro.h"
+
+#include "Buffer.h"
+#include "BufferParams.h"
+#include "Cursor.h"
+#include "Dimension.h"
+#include "Encoding.h"
+#include "ErrorList.h"
+#include "FontInfo.h"
+#include "FuncRequest.h"
+#include "FuncStatus.h"
+#include "InsetLayout.h"
+#include "InsetList.h"
+#include "LaTeX.h"
+#include "LaTeXFeatures.h"
+#include "Lexer.h"
+#include "MetricsInfo.h"
+#include "xml.h"
+#include "texstream.h"
+
+#include "frontends/alert.h"
+
+#include "support/debug.h"
+#include "support/docstream.h"
+#include "support/gettext.h"
+#include "support/lstrings.h"
+#include "support/Translator.h"
+
+using namespace std;
+using namespace lyx::support;
+
+namespace lyx {
+
+namespace {
+
+typedef Translator<string, InsetIndexMacroParams::Type> InsetIndexMacroTranslator;
+typedef Translator<docstring, InsetIndexMacroParams::Type> InsetIndexMacroTranslatorLoc;
+
+InsetIndexMacroTranslator const init_insetindexmacrotranslator()
+{
+       InsetIndexMacroTranslator translator("see", InsetIndexMacroParams::See);
+       translator.addPair("seealso", InsetIndexMacroParams::Seealso);
+       translator.addPair("subindex", InsetIndexMacroParams::Subindex);
+       translator.addPair("sortkey", InsetIndexMacroParams::Sortkey);
+       return translator;
+}
+
+
+InsetIndexMacroTranslatorLoc const init_insetindexmacrotranslator_loc()
+{
+       InsetIndexMacroTranslatorLoc translator(_("See"), InsetIndexMacroParams::See);
+       translator.addPair(_("See also"), InsetIndexMacroParams::Seealso);
+       translator.addPair(_("Subindex"), InsetIndexMacroParams::Subindex);
+       translator.addPair(_("Sort as"), InsetIndexMacroParams::Sortkey);
+       return translator;
+}
+
+
+InsetIndexMacroTranslator const & insetindexmacrotranslator()
+{
+       static InsetIndexMacroTranslator const macrotranslator =
+                       init_insetindexmacrotranslator();
+       return macrotranslator;
+}
+
+
+InsetIndexMacroTranslatorLoc const & insetindexmacrotranslator_loc()
+{
+       static InsetIndexMacroTranslatorLoc const translator =
+                       init_insetindexmacrotranslator_loc();
+       return translator;
+}
+
+} // namespace
+
+
+InsetIndexMacroParams::InsetIndexMacroParams()
+       : type(See)
+{}
+
+
+void InsetIndexMacroParams::write(ostream & os) const
+{
+       string const label = insetindexmacrotranslator().find(type);
+       os << "IndexMacro " << label << "\n";
+}
+
+
+void InsetIndexMacroParams::read(Lexer & lex)
+{
+       string label;
+       lex >> label;
+       if (lex)
+               type = insetindexmacrotranslator().find(label);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// InsetIndexMacro
+//
+/////////////////////////////////////////////////////////////////////
+
+InsetIndexMacro::InsetIndexMacro(Buffer * buf, string const & label)
+       : InsetCollapsible(buf)
+{
+       setDrawFrame(true);
+       setFrameColor(Color_insetframe);
+       params_.type = insetindexmacrotranslator().find(label);
+}
+
+
+InsetIndexMacro::~InsetIndexMacro()
+{}
+
+
+docstring InsetIndexMacro::layoutName() const
+{
+       return from_ascii("IndexMacro:" + insetindexmacrotranslator().find(params_.type));
+}
+
+InsetCode InsetIndexMacro::lyxCode() const
+{
+       return params_.type == InsetIndexMacroParams::Sortkey
+                       ? INDEXMACRO_SORTKEY_CODE
+                       : INDEXMACRO_CODE;
+}
+
+
+void InsetIndexMacro::write(ostream & os) const
+{
+       params_.write(os);
+       InsetCollapsible::write(os);
+}
+
+
+void InsetIndexMacro::read(Lexer & lex)
+{
+       params_.read(lex);
+       InsetCollapsible::read(lex);
+}
+
+
+void InsetIndexMacro::getLatex(otexstream & os, OutputParams const & runparams) const
+{
+       if (params_.type == InsetIndexMacroParams::Subindex) {
+               if (hasSortKey()) {
+                       getSortkey(os, runparams);
+                       os << "@";
+               } else {
+                       odocstringstream ourlatex;
+                       otexstream ots(ourlatex);
+                       InsetText::latex(ots, runparams);
+                       odocstringstream ourplain;
+                       InsetText::plaintext(ourplain, runparams);
+                       // These are the LaTeX and plaintext representations
+                       docstring latexstr = ourlatex.str();
+                       docstring plainstr = ourplain.str();
+                       processLatexSorting(os, runparams, latexstr, plainstr);
+               }
+               return;
+       }
+
+       if (params_.type == InsetIndexMacroParams::See)
+               os << "see{";
+       else if (params_.type == InsetIndexMacroParams::Seealso)
+               os << "seealso{";
+
+       InsetCollapsible::latex(os, runparams);
+
+       if (params_.type == InsetIndexMacroParams::See
+            || params_.type == InsetIndexMacroParams::Seealso)
+               os << "}";
+}
+
+
+int InsetIndexMacro::getPlaintext(odocstringstream & os,
+                           OutputParams const & runparams, size_t max_length) const
+{
+       return InsetText::plaintext(os, runparams, max_length);
+}
+
+
+void InsetIndexMacro::getDocbook(XMLStream & xs, OutputParams const & runparams) const
+{
+       InsetText::docbook(xs, runparams);
+}
+
+
+docstring InsetIndexMacro::getXhtml(XMLStream & xs, OutputParams const & runparams) const
+{
+       return InsetText::xhtml(xs, runparams);
+}
+
+
+void InsetIndexMacro::doDispatch(Cursor & cur, FuncRequest & cmd)
+{
+       switch (cmd.action()) {
+
+       case LFUN_INSET_MODIFY: {
+               if (cmd.getArg(0) == "changetype") {
+                       cur.recordUndoInset(this);
+                       params_.type = insetindexmacrotranslator().find(cmd.getArg(1));
+                       break;
+               }
+               InsetCollapsible::doDispatch(cur, cmd);
+               break;
+       }
+
+       default:
+               InsetCollapsible::doDispatch(cur, cmd);
+               break;
+       }
+}
+
+
+bool InsetIndexMacro::getStatus(Cursor & cur, FuncRequest const & cmd,
+                               FuncStatus & flag) const
+{
+       switch (cmd.action()) {
+
+       case LFUN_INSET_MODIFY:
+               if (cmd.getArg(0) == "changetype") {
+                       docstring const newtype = from_utf8(cmd.getArg(1));
+                       bool const enabled = (params_.type == InsetIndexMacroParams::See
+                                             || params_.type == InsetIndexMacroParams::Seealso)
+                                           && (newtype == "see" || newtype == "seealso");
+                       flag.setEnabled(enabled);
+                       flag.setOnOff(
+                               newtype == from_ascii(insetindexmacrotranslator().find(params_.type)));
+                       return true;
+               }
+               return InsetCollapsible::getStatus(cur, cmd, flag);
+
+       default:
+               return InsetCollapsible::getStatus(cur, cmd, flag);
+       }
+}
+
+
+void InsetIndexMacro::processLatexSorting(otexstream & os, OutputParams const & runparams,
+                               docstring const latex, docstring const plain) const
+{
+       if (contains(latex, '\\') && !contains(latex, '@')) {
+               // Plaintext might return nothing (e.g. for ERTs).
+               // In that case, we use LaTeX.
+               docstring const spart = (plain.empty()) ? latex : plain;
+               // Now we need to validate that all characters in
+               // the sorting part are representable in the current
+               // encoding. If not try the LaTeX macro which might
+               // or might not be a good choice, and issue a warning.
+               pair<docstring, docstring> spart_latexed =
+                               runparams.encoding->latexString(spart, runparams.dryrun);
+               if (!spart_latexed.second.empty())
+                       LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
+               if (spart != spart_latexed.first && !runparams.dryrun) {
+                       TeXErrors terr;
+                       ErrorList & errorList = buffer().errorList("Export");
+                       docstring const s = bformat(_("LyX's automatic index sorting algorithm faced "
+                                                     "problems with the sub-entry '%1$s'.\n"
+                                                     "Please specify the sorting of this entry manually, as "
+                                                     "explained in the User Guide."), spart);
+                       Paragraph const & par = buffer().paragraphs().front();
+                       errorList.push_back(ErrorItem(_("Index sorting failed"), s,
+                                                     {par.id(), 0}, {par.id(), -1}));
+                       buffer().bufferErrors(terr, errorList);
+               }
+               // Remove remaining \'s from the sort key
+               docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
+               // Plain quotes need to be escaped, however (#10649), as this
+               // is the default escape character
+               ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
+
+               // Now insert the sortkey, separated by '@'.
+               os << ppart;
+               os << '@';
+       }
+       // Insert the actual level text
+       os << latex;
+}
+
+
+docstring InsetIndexMacro::toolTip(BufferView const &, int, int) const
+{
+       return insetindexmacrotranslator_loc().find(params_.type);
+}
+
+
+string InsetIndexMacro::params2string(InsetIndexMacroParams const & params)
+{
+       ostringstream data;
+       data << "IndexMacro" << ' ';
+       params.write(data);
+       return data.str();
+}
+
+
+void InsetIndexMacro::string2params(string const & in, InsetIndexMacroParams & params)
+{
+       params = InsetIndexMacroParams();
+
+       if (in.empty())
+               return;
+
+       istringstream data(in);
+       Lexer lex;
+       lex.setStream(data);
+       lex.setContext("InsetIndexMacro::string2params");
+       lex >> "IndexMacro" >> "see";
+
+       params.read(lex);
+}
+
+
+bool InsetIndexMacro::hasSortKey() const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
+                       return true;
+       }
+       return false;
+}
+
+
+void InsetIndexMacro::getSortkey(otexstream & os, OutputParams const & runparams) const
+{
+       Paragraph const & par = paragraphs().front();
+       InsetList::const_iterator it = par.insetList().begin();
+       for (; it != par.insetList().end(); ++it) {
+               Inset & inset = *it->inset;
+               if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE) {
+                       InsetIndexMacro const & iim =
+                               static_cast<InsetIndexMacro const &>(inset);
+                       iim.getLatex(os, runparams);
+                       return;
+               }
+       }
+}
+
+
+string InsetIndexMacro::contextMenuName() const
+{
+       return "context-indexmacro";
+}
+
+
+string InsetIndexMacro::contextMenu(BufferView const & bv, int x, int y) const
+{
+       // We override the implementation of InsetCollapsible,
+       // because we have eytra entries.
+       string owncm = "context-edit-index;";
+       return owncm + InsetCollapsible::contextMenu(bv, x, y);
+}
+
+
+bool InsetIndexMacro::insetAllowed(InsetCode code) const
+{
+       switch (code) {
+       case INDEX_CODE:
+               return false;
+       case INDEXMACRO_SORTKEY_CODE:
+               return (params_.type == InsetIndexMacroParams::Subindex
+                       && !hasSortKey());
+       default:
+               return InsetCollapsible::insetAllowed(code);
+       }
+}
+
+} // namespace lyx
diff --git a/src/insets/InsetIndexMacro.h b/src/insets/InsetIndexMacro.h
new file mode 100644 (file)
index 0000000..4122621
--- /dev/null
@@ -0,0 +1,125 @@
+// -*- C++ -*-
+/**
+ * \file InsetIndexMacro.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jürgen Spitzmüller
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef INSET_INSETMACRO_H
+#define INSET_INSETMACRO_H
+
+
+#include "Inset.h"
+#include "InsetCollapsible.h"
+
+
+namespace lyx {
+
+class LaTeXFeatures;
+
+class InsetIndexMacroParams
+{
+public:
+       enum Type {
+               See,
+               Seealso,
+               Subindex,
+               Sortkey
+       };
+       ///
+       InsetIndexMacroParams();
+       ///
+       void write(std::ostream & os) const;
+       ///
+       void read(Lexer & lex);
+       ///
+       Type type;
+};
+
+/////////////////////////////////////////////////////////////////////////
+//
+// InsetIndexMacro
+//
+/////////////////////////////////////////////////////////////////////////
+
+/// Used to insert index references
+class InsetIndexMacro : public InsetCollapsible
+{
+public:
+       ///
+       InsetIndexMacro(Buffer *, std::string const &);
+       ///
+       ~InsetIndexMacro();
+       ///
+       static std::string params2string(InsetIndexMacroParams const &);
+       ///
+       static void string2params(std::string const &, InsetIndexMacroParams &);
+       ///
+       InsetIndexMacroParams const & params() const { return params_; }
+       ///
+       void getLatex(otexstream &, OutputParams const &) const;
+       ///
+       int getPlaintext(odocstringstream &, OutputParams const &, size_t) const;
+       ///
+       void getDocbook(XMLStream &, OutputParams const &) const;
+private:
+       ///
+       InsetCode lyxCode() const override;
+       ///
+       docstring layoutName() const override;
+       ///
+       void write(std::ostream &) const override;
+       ///
+       void read(Lexer & lex) override;
+       ///
+       bool neverIndent() const override { return true; }
+       /// We do not output anything directly to the stream
+       void latex(otexstream &, OutputParams const &) const override {};
+       /// We do not output anything directly to the stream
+       int plaintext(odocstringstream &, OutputParams const &, size_t) const override { return 0; };
+       /// We do not output anything directly to the stream
+       void docbook(XMLStream &, OutputParams const &) const override {};
+       /// We do not output anything directly to the stream
+       docstring xhtml(XMLStream &, OutputParams const &) const override { return docstring(); };
+       ///
+       docstring getXhtml(XMLStream &, OutputParams const &) const;
+       ///
+       bool allowSpellCheck() const override { return false; }
+       ///
+       bool insetAllowed(InsetCode code) const override;
+       ///
+       bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const override;
+       ///
+       void doDispatch(Cursor & cur, FuncRequest & cmd) override;
+       ///
+       docstring toolTip(BufferView const & bv, int x, int y) const override;
+       ///
+       void processLatexSorting(otexstream &, OutputParams const &,
+                           docstring const, docstring const) const;
+       ///
+       bool hasSortKey() const;
+       ///
+       void getSortkey(otexstream &, OutputParams const &) const;
+       ///
+       std::string contextMenuName() const override;
+       ///
+       std::string contextMenu(BufferView const &, int, int) const override;
+       ///
+       Inset * clone() const override { return new InsetIndexMacro(*this); }
+       /// used by the constructors
+       void init();
+       ///
+       friend class InsetIndexMacroParams;
+
+       ///
+       InsetIndexMacroParams params_;
+};
+
+
+} // namespace lyx
+
+#endif
index 1767f2c31f2591f1f7dd9d41f62309e799b4a411..c67e934addcd6ed6fd520b32546b39a3e4a2ad26 100644 (file)
@@ -1040,6 +1040,10 @@ bool InsetText::insetAllowed(InsetCode code) const
        case QUOTE_CODE:
        case COUNTER_CODE:
                return true;
+       // These are only allowed in index insets
+       case INDEXMACRO_CODE:
+       case INDEXMACRO_SORTKEY_CODE:
+               return false;
        default:
                return !isPassThru();
        }
index e4eb9b40ba42235e1de19e7bb760d43930255139..2cfac87e55fa2d30af60afc137e11369f2cf9836 100644 (file)
@@ -486,7 +486,40 @@ bool Parser::hasOpt(string const & l)
 }
 
 
-Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
+bool Parser::hasIdxMacros(string const & c, string const & e)
+{
+       // Check for index entry separator (! or @),
+       // consider escaping via "
+       // \p e marks a terminating delimiter¸
+
+       // remember current position
+       unsigned int oldpos = pos_;
+       // skip spaces and comments
+       bool retval = false;
+       while (good()) {
+               get_token();
+               if (isParagraph()) {
+                       putback();
+                       break;
+               }
+               if (curr_token().cat() == catEnd)
+                       break;
+               if (!e.empty() && curr_token().asInput() == e
+                   && prev_token().asInput() != "\"")
+                       break;
+               if (curr_token().asInput() == c
+                   && prev_token().asInput() != "\"") {
+                       retval = true;
+                       break;
+               }
+               continue;
+       }
+       pos_ = oldpos;
+       return retval;
+}
+
+
+Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping, char e)
 {
        skip_spaces(true);
 
@@ -495,25 +528,28 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
        if (! good())
                return make_pair(false, string());
 
-       int group_level = 0;
+       int group_level = (left == '{') ? 1 : 0;
        string result;
        Token t = get_token();
 
-       if (t.cat() == catComment || t.cat() == catEscape ||
-           t.character() != left) {
+       if (left != char()
+           && (t.cat() == catComment || t.cat() == catEscape
+               || t.character() != left)) {
                putback();
                return make_pair(false, string());
        } else {
                while (good()) {
                        t = get_token();
                        // honor grouping
-                       if (left != '{' && t.cat() == catBegin) {
+                       if (t.cat() == catBegin) {
                                ++group_level;
-                               continue;
+                               if (left != '{')
+                                       continue;
                        }
-                       if (left != '{' && t.cat() == catEnd) {
+                       if (group_level > 0 && t.cat() == catEnd) {
                                --group_level;
-                               continue;
+                               if (left != '{')
+                                       continue;
                        }
                        // Ignore comments
                        if (t.cat() == catComment) {
@@ -525,6 +561,10 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
                                if (t.cat() != catEscape && t.character() == right
                                    && group_level == 0)
                                        break;
+                       } else if (e != char()) {
+                               if (prev_token().character() != e && t.character() == right
+                                   && group_level == 0)
+                                       break;
                        } else {
                                if (t.character() == right) {
                                        if (t.cat() == catEscape)
@@ -540,9 +580,9 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
 }
 
 
-string Parser::getArg(char left, char right, bool allow_escaping)
+string Parser::getArg(char left, char right, bool allow_escaping, char e)
 {
-       return getFullArg(left, right, allow_escaping).second;
+       return getFullArg(left, right, allow_escaping, e).second;
 }
 
 
index a99b40f5dcf35d655a84dbc8fd27e5dcf53d6ccd..ec2fc3c355f3cde2ccc4ed5f9c0f5b23cd3d186b 100644 (file)
@@ -215,17 +215,22 @@ public:
 
        /// Does an optional argument follow after the current token?
        bool hasOpt(std::string const & l = "[");
+       /// Does this index entry has levels?
+       bool hasIdxMacros(std::string const & c,
+                         std::string const & e = std::string());
        ///
        typedef std::pair<bool, std::string> Arg;
        /*!
         * Get an argument enclosed by \p left and \p right.
         * If \p allow_escaping is true, a right delimiter escaped by a
         * backslash does not count as delimiter, but is included in the
-        * argument.
+        * argument. The \p e allows for a different escape character
+        * (used in index insets)
         * \returns whether an argument was found in \p Arg.first and the
         * argument in \p Arg.second. \see getArg().
         */
-       Arg getFullArg(char left, char right, bool allow_escaping = true);
+       Arg getFullArg(char left, char right, bool allow_escaping = true,
+                      char e = char());
        /*!
         * Get an argument enclosed by \p left and \p right.
         * If \p allow_escaping is true, a right delimiter escaped by a
@@ -236,7 +241,8 @@ public:
         * getFullArg() if you need to know whether there was an empty
         * argument or no argument at all.
         */
-       std::string getArg(char left, char right, bool allow_escaping = true);
+       std::string getArg(char left, char right, bool allow_escaping = true,
+                          char e = char());
        /*!
         * Like getOpt(), but distinguishes between a missing argument ""
         * and an empty argument "[]".
index f720cf1ee6ebb7d9184d666a18f9e666a83a7c6b..4152911bf3186c0246bb2aa427d97c5832757192 100644 (file)
@@ -48,7 +48,8 @@ extern std::string rgbcolor2code(std::string const & name);
 std::string translate_len(std::string const &);
 
 void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer,
-               Context & context, std::string const & rdelim = "");
+               Context & context, std::string const & rdelim = "",
+               std::string const & rdelimesc = "");
 void check_comment_bib(std::ostream & os, Context & context);
 
 void fix_child_filename(std::string & name);
@@ -68,7 +69,8 @@ std::string find_file(std::string const & name, std::string const & path,
 void parse_text_in_inset(Parser & p, std::ostream & os, unsigned flags,
                          bool outer, Context & context,
                          InsetLayout const * layout = nullptr,
-                         std::string const & rdelim = "");
+                         std::string const & rdelim = "",
+                        std::string const & rdelimesc = "");
 
 /// Guess document language from \p p if CJK is used.
 /// \p lang is used for all non-CJK contents.
index 545515fd618472d34f5c0bdd36ce55b3cc429744..b661e6fc85cbefc8c2716c08a8005eccb453c33e 100644 (file)
@@ -55,7 +55,7 @@ void output_arguments(ostream &, Parser &, bool, bool, const string &, Context &
 
 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
                Context & context, InsetLayout const * layout,
-               string const & rdelim)
+               string const & rdelim, string const & rdelimesc)
 {
        bool const forcePlainLayout =
                layout ? layout->forcePlainLayout() : false;
@@ -78,7 +78,7 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
                parse_text(p, oss, FLAG_RDELIM, outer, dummy,
                           string(1, context.latexparam.back()));
        }
-       parse_text(p, os, flags, outer, newcontext, rdelim);
+       parse_text(p, os, flags, outer, newcontext, rdelim, rdelimesc);
        if (layout)
                output_arguments(os, p, outer, false, "post", newcontext,
                                 layout->postcommandargs());
@@ -91,7 +91,8 @@ namespace {
 
 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
                Context const & context, string const & name,
-               string const & rdelim = string())
+               string const & rdelim = string(),
+               string const & rdelimesc = string())
 {
        InsetLayout const * layout = 0;
        DocumentClass::InsetLayouts::const_iterator it =
@@ -99,17 +100,18 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
        if (it != context.textclass.insetLayouts().end())
                layout = &(it->second);
        Context newcontext = context;
-       parse_text_in_inset(p, os, flags, outer, newcontext, layout, rdelim);
+       parse_text_in_inset(p, os, flags, outer, newcontext, layout, rdelim, rdelimesc);
 }
 
 /// parses a paragraph snippet, useful for example for \\emph{...}
 void parse_text_snippet(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context & context)
+                       Context & context, string const & rdelim = string(),
+                       string const & rdelimesc = string())
 {
        Context newcontext(context);
        // Don't inherit the paragraph-level extra stuff
        newcontext.par_extra_stuff.clear();
-       parse_text(p, os, flags, outer, newcontext);
+       parse_text(p, os, flags, outer, newcontext, rdelim, rdelimesc);
        // Make sure that we don't create invalid .lyx files
        context.need_layout = newcontext.need_layout;
        context.need_end_layout = newcontext.need_end_layout;
@@ -1506,6 +1508,218 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
 }
 
 
+void parse_index_entry(Parser & p, ostream & os, Context & context, string const & kind)
+{
+       // write inset header
+       begin_inset(os, "Index ");
+       os << kind;
+
+       // Parse for post argument (|...)
+       p.pushPosition();
+       string const marg = p.getArg('{', '}');
+       p.popPosition();
+       char lc = char();
+       bool inpost = false;
+       bool startrange = false;
+       bool endrange = false;
+       string post;
+       for (string::const_iterator it = marg.begin(), et = marg.end(); it != et; ++it) {
+               char c = *it;
+               if (inpost) {
+                       if (post.empty() && c == '(')
+                               startrange = true;
+                       else if (post.empty() && c == ')')
+                               endrange = true;
+                       else
+                               post += c;
+               }
+               if (!inpost && (c == '|' && lc != '"'))
+                       inpost = true;
+               lc = c;
+       }
+       if (startrange)
+               os << "\nrange start";
+       else if (endrange)
+               os << "\nrange end";
+       else
+               os << "\nrange none";
+       bool const see = prefixIs(post, "see{");
+       bool const seealso = prefixIs(post, "seealso{");
+       if (!post.empty() && !see && !seealso)
+               os << "\npageformat " << post;
+       else
+               os << "\npageformat default";
+       os << "\nstatus collapsed\n";
+
+       bool main = true;
+       // save position
+       p.pushPosition();
+       // Check for levels
+       if (p.hasIdxMacros("!")) {
+               // Index entry with levels
+               while (p.hasIdxMacros("!")) {
+                       if (main) {
+                               // swallow brace
+                               p.get_token();
+                               os << "\\begin_layout Plain Layout\n";
+                       } else {
+                               begin_inset(os, "IndexMacro subindex");
+                               os << "\nstatus collapsed\n";
+                       }
+                       // Check for (level-specific) sortkey
+                       if (p.hasIdxMacros("@", "!")) {
+                               if (!main)
+                                       os << "\\begin_layout Plain Layout\n";
+                               begin_inset(os, "IndexMacro sortkey");
+                               os << "\nstatus collapsed\n";
+                               parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "IndexMacro sortkey", "@", "\"");
+                               end_inset(os);
+                       }
+                       parse_text_snippet(p, os, FLAG_RDELIM, false, context, "!", "\"");
+                       if (!main) {
+                               os << "\n\\end_layout\n";
+                               end_inset(os);
+                       }
+                       main = false;
+               }
+               if (!main) {
+                       begin_inset(os, "IndexMacro subindex");
+                       os << "\nstatus collapsed\n";
+               }
+               // Final level
+               // Check for (level-specific) sortkey
+               if (p.hasIdxMacros("@", "!")) {
+                       if (main) {
+                               // swallow brace
+                               p.get_token();
+                       }
+                       os << "\\begin_layout Plain Layout\n";
+                       begin_inset(os, "IndexMacro sortkey");
+                       os << "\nstatus collapsed\n";
+                       parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "IndexMacro sortkey", "@", "\"");
+                       end_inset(os);
+                       if (post.empty() && !startrange && !endrange) {
+                               parse_text_snippet(p, os, FLAG_BRACE_LAST, false, context);
+                               p.dropPosition();
+                       } else {
+                               // Handle post-argument
+                               parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
+                               if (see || seealso) {
+                                       while (p.next_token().character() != '{' && p.good())
+                                               p.get_token();
+                                       // this ends the subinset, as the see[also] insets
+                                       // must come at main index inset
+                                       os << "\n\\end_layout\n";
+                                       end_inset(os);
+                                       if (see)
+                                               begin_inset(os, "IndexMacro see");
+                                       else
+                                               begin_inset(os, "IndexMacro seealso");
+                                       os << "\nstatus collapsed\n";
+                                       os << "\\begin_layout Plain Layout\n";
+                                       parse_text_snippet(p, os, FLAG_ITEM, false, context);
+                               }
+                               p.popPosition();
+                               // swallow argument
+                               p.getArg('{', '}');
+                       }
+                       os << "\n\\end_layout\n";
+               } else {
+                       if (post.empty() && !startrange && !endrange) {
+                               parse_text_in_inset(p, os, FLAG_BRACE_LAST, false, context, "IndexMacro subindex");
+                               p.dropPosition();
+                       } else {
+                               // Handle post-argument
+                               if (see || seealso) {
+                                       os << "\\begin_layout Plain Layout\n";
+                                       parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
+                                       while (p.next_token().character() != '{' && p.good())
+                                               p.get_token();
+                                       // this ends the subinset, as the see[also] insets
+                                       // must come at main index inset
+                                       os << "\n\\end_layout\n";
+                                       end_inset(os);
+                                       if (see)
+                                               begin_inset(os, "IndexMacro see");
+                                       else
+                                               begin_inset(os, "IndexMacro seealso");
+                                       os << "\nstatus collapsed\n";
+                                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, "IndexMacro see");
+                               } else
+                                       parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "Index", "|", "\"");
+                               p.popPosition();
+                               // swallow argument
+                               p.getArg('{', '}');
+                       }
+               }
+               if (!main)
+                       end_inset(os);
+               os << "\n\\end_layout\n";
+       } else {
+               // Index without any levels
+               // Check for sortkey
+               if (p.hasIdxMacros("@", "!")) {
+                       // swallow brace
+                       p.get_token();
+                       os << "\\begin_layout Plain Layout\n";
+                       begin_inset(os, "IndexMacro sortkey");
+                       os << "\nstatus collapsed\n";
+                       parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "IndexMacro sortkey", "@", "\"");
+                       end_inset(os);
+                       if (post.empty() && !startrange && !endrange) {
+                               parse_text_snippet(p, os, FLAG_BRACE_LAST, false, context);
+                               p.dropPosition();
+                       } else {
+                               parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
+                               if (see || seealso) {
+                                       while (p.next_token().character() != '{' && p.good())
+                                               p.get_token();
+                                       if (see)
+                                               begin_inset(os, "IndexMacro see");
+                                       else
+                                               begin_inset(os, "IndexMacro seealso");
+                                       os << "\nstatus collapsed\n";
+                                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, "IndexMacro see");
+                                       end_inset(os);
+                               }
+                               p.popPosition();
+                               // swallow argument
+                               p.getArg('{', '}');
+                       }
+                       os << "\n\\end_layout\n";
+               } else {
+                       if (post.empty() && !startrange && !endrange) {
+                               parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
+                               p.dropPosition();
+                       } else {
+                               // Handle post-argument
+                               // swallow brace
+                               p.get_token();
+                               if (see || seealso) {
+                                       os << "\\begin_layout Plain Layout\n";
+                                       parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
+                                       while (p.next_token().character() != '{' && p.good())
+                                               p.get_token();
+                                       if (see)
+                                               begin_inset(os, "IndexMacro see");
+                                       else
+                                               begin_inset(os, "IndexMacro seealso");
+                                       os << "\nstatus collapsed\n";
+                                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, "IndexMacro see");
+                                       end_inset(os);
+                                       os << "\n\\end_layout\n";
+                               } else
+                                       parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "Index", "|", "\"");
+                               p.popPosition();
+                               // swallow argument
+                               p.getArg('{', '}');
+                       }
+               }
+       }
+       end_inset(os);
+}
+
+
 void parse_listings(Parser & p, ostream & os, Context & parent_context,
                    bool in_line, bool use_minted)
 {
@@ -2901,7 +3115,7 @@ void fix_child_filename(string & name)
 
 
 void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context & context, string const & rdelim)
+               Context & context, string const & rdelim, string const & rdelimesc)
 {
        Layout const * newlayout = 0;
        InsetLayout const * newinsetlayout = 0;
@@ -2990,7 +3204,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                if (rdelim.size() > 1)
                        tok += p.next_token().asInput();
                if (t.cat() != catEscape && !rdelim.empty()
-                   && tok == rdelim && (flags & FLAG_RDELIM)) {
+                   && tok == rdelim && (flags & FLAG_RDELIM)
+                   && (rdelimesc.empty() || p.prev_token().asInput() != rdelimesc)) {
                        if (rdelim.size() > 1)
                                p.get_token(); // eat rdelim
                        return;
@@ -4712,10 +4927,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        string const arg = (t.cs() == "sindex" && p.hasOpt()) ?
                                p.getArg('[', ']') : "";
                        string const kind = arg.empty() ? "idx" : arg;
-                       begin_inset(os, "Index ");
-                       os << kind << "\nstatus collapsed\n";
-                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
-                       end_inset(os);
+                       parse_index_entry(p, os, context, kind);
                        if (kind != "idx")
                                preamble.registerAutomaticallyLoadedPackage("splitidx");
                        continue;