commit 1b82fc4d8eb50dac7eef743a0ac95002f5709589
Author: Juergen Spitzmueller <[email protected]>
Date:   Sun Oct 12 13:47:06 2025 +0200

    Implement LFUN_REFERENCE_TO_PARAGRAPH for mathed (#13206)
    
    This allows to enter references to numbered math formulae directly
    from the outliner (or the list of existing equations in the cross
    ref dialog), whether they already have a label or not (as for text
    elements).
---
 lib/ui/stdcontext.inc        |  9 ++++++
 src/BufferView.cpp           | 72 ++++++++++++++++++++++++++++++--------------
 src/TocBuilder.cpp           | 28 +++++++++++++++--
 src/TocBuilder.h             |  4 ++-
 src/frontends/qt/GuiRef.cpp  |  5 ++-
 src/mathed/InsetMathHull.cpp | 19 ++++++------
 src/mathed/InsetMathHull.h   |  2 +-
 7 files changed, 101 insertions(+), 38 deletions(-)

diff --git a/lib/ui/stdcontext.inc b/lib/ui/stdcontext.inc
index ad8571d0ed..316be4031b 100644
--- a/lib/ui/stdcontext.inc
+++ b/lib/ui/stdcontext.inc
@@ -593,6 +593,15 @@ Menuset
        End
 
 
+#
+# Toc Equation context menu
+#
+
+       Menu "context-toc-equation"
+               SubMenu "Insert Cross-Reference to this Item|C" 
"context-toc-ref-to-par"
+       End
+
+
 #
 # Toc Footnotes context menu
 #
diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index 20d7b0bbfe..eec356939e 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -53,6 +53,7 @@
 #include "insets/InsetRef.h"
 #include "insets/InsetText.h"
 
+#include "mathed/InsetMathHull.h"
 #include "mathed/InsetMathNest.h"
 #include "mathed/InsetMathRef.h"
 #include "mathed/MathData.h"
@@ -1723,6 +1724,7 @@ void BufferView::dispatch(FuncRequest const & cmd, 
DispatchResult & dr)
                vector<string> const pids = getVectorFromString(cmd.getArg(0));
                string const type = cmd.getArg(1);
                int id = convert<int>(pids.back());
+               row_type row = -1;
                inserted_label_.clear();
                if (id < 0)
                        break;
@@ -1735,43 +1737,69 @@ void BufferView::dispatch(FuncRequest const & cmd, 
DispatchResult & dr)
                                ++i;
                                continue;
                        }
-                       string label = dit.innerParagraph().getLabelForXRef();
+                       string label;
+                       if (dit.nextInset() && dit.nextInset()->asInsetMath() 
&& dit.nextInset()->asInsetMath()->asHullInset()) {
+                               row = convert<row_type>(pids.front());
+                               label = 
to_utf8(dit.nextInset()->asInsetMath()->asHullInset()->label(row));
+                       } else
+                               label = dit.innerParagraph().getLabelForXRef();
                        if (!label.empty()) {
                                // if the paragraph has a label, we use this
                                if (type == "forrefdialog")
+                                       // only store the label for the dialog 
to take it
                                        inserted_label_ = label;
                                else {
+                                       // insert a reference to the label 
(e.g., from outliner)
                                        string const arg = (type.empty()) ? 
label : label + " " + type;
                                        
lyx::dispatch(FuncRequest(LFUN_REFERENCE_INSERT, arg));
                                        cur.forceBufferUpdate();
                                }
                                break;
                        } else {
-                               // if there is not a label yet
-                               // go to the paragraph (including nested 
insets) ...
-                               lyx::dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, 
"0"));
-                               for (string const & s : pids) {
-                                       id = convert<int>(s);
-                                       if (id < 0)
-                                               break;
-                                       dit = b->getParFromID(id);
-                                       
lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, s));
+                               // if there is not a label yet, insert one
+                               // this categorically differes in texted and 
mathed
+                               docstring new_label;
+                               if (dit.nextInset() && 
dit.nextInset()->asInsetMath() && 
dit.nextInset()->asInsetMath()->asHullInset()) {
+                                       // in mathed, we set the label via 
InsetMathHull::label()
+                                       // but we need to assure it is unique
+                                       new_label = from_ascii("eq:equation");
+                                       int j = 1;
+                                       while (buffer().activeLabel(new_label)) 
{
+                                               new_label = new_label + '-' + 
convert<docstring>(j);
+                                               ++j;
+                                       }
+                                       // record undo for the math inset next 
to dit
+                                       
cur.buffer()->undo().recordUndoInset(CursorData(dit), dit.nextInset());
+                                       // insert the label to the desired row 
of the hull inset
+                                       
dit.nextInset()->asInsetMath()->asHullInset()->label(row, new_label, true);
+                                       // needed to get the new label into the 
buffer list
+                                       cur.buffer()->updateBuffer();
+                               } else {
+                                       // in texted, go to the paragraph 
(including nested insets) ...
+                                       
lyx::dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
+                                       for (string const & s : pids) {
+                                               id = convert<int>(s);
+                                               if (id < 0)
+                                                       break;
+                                               dit = b->getParFromID(id);
+                                               
lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, s));
+                                       }
+                                       // insert a new label
+                                       // we do not want to open the dialog, 
hence we
+                                       // do not employ LFUN_LABEL_INSERT
+                                       InsetCommandParams p(LABEL_CODE);
+                                       new_label = dit.getPossibleLabel();
+                                       p["name"] = new_label;
+                                       string const data = 
InsetCommand::params2string(p);
+                                       
lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, data));
+                                       // ... go back to the original position
+                                       
lyx::dispatch(FuncRequest(LFUN_BOOKMARK_GOTO, "0"));
                                }
-                               // ... if not, insert a new label
-                               // we do not want to open the dialog, hence we
-                               // do not employ LFUN_LABEL_INSERT
-                               InsetCommandParams p(LABEL_CODE);
-                               docstring const new_label = 
dit.getPossibleLabel();
-                               p["name"] = new_label;
-                               string const data = 
InsetCommand::params2string(p);
-                               lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, 
data));
-                               // ... go back to the original position
-                               lyx::dispatch(FuncRequest(LFUN_BOOKMARK_GOTO, 
"0"));
                                if (type == "forrefdialog")
-                                       // ... and save for the ref dialog to 
insert
+                                       // save for the ref dialog to insert
                                        inserted_label_ = to_utf8(new_label);
                                else {
-                                       // ... or insert the ref directly (from 
outliner)
+                                       // or insert the ref directly (from 
outliner)
                                        string const arg = (type.empty()) ? 
to_utf8(new_label)
                                                                          : 
to_utf8(new_label) + " " + type;
                                        
lyx::dispatch(FuncRequest(LFUN_REFERENCE_INSERT, arg));
diff --git a/src/TocBuilder.cpp b/src/TocBuilder.cpp
index fb688bd58e..458a95db66 100644
--- a/src/TocBuilder.cpp
+++ b/src/TocBuilder.cpp
@@ -15,6 +15,7 @@
 #include "DocIterator.h"
 #include "TocBackend.h"
 
+#include "support/convert.h"
 #include "support/lassert.h"
 
 using namespace std;
@@ -32,9 +33,16 @@ TocBuilder::TocBuilder(shared_ptr<Toc> const & toc)
 }
 
 void TocBuilder::pushItem(DocIterator const & dit, docstring const & s,
-                          bool output_active, bool is_captioned)
+                          bool output_active, bool is_captioned, int row)
 {
-       toc_->push_back(TocItem(dit, stack_.size(), s, output_active));
+       TocItem item(dit, stack_.size(), s, output_active);
+       if (row != -1) {
+               docstring parID = item.parIDs();
+               if (!stack_.empty())
+                       parID = 
(*toc_)[stack_.top().pos].dit().paragraphGotoArgument(true);
+               item.setParIDs(convert<docstring>(row) + ',' + parID);
+       }
+       toc_->push_back(item);
        frame f = {
                toc_->size() - 1, //pos
                is_captioned, //is_captioned
@@ -87,6 +95,22 @@ void TocBuilder::argumentItem(docstring const & arg_str)
        stack_.top().is_captioned = true;
 }
 
+void TocBuilder::mathMultilineItem(docstring const & arg_str, Toc::size_type 
row)
+{
+       if (stack_.empty() || arg_str.empty())
+               return;
+       TocItem & item = (*toc_)[stack_.top().pos];
+       docstring const & str = item.str();
+       string const & delim =
+               (str.empty() || !stack_.top().is_captioned) ? "" :  ", ";
+       item.str(str + from_ascii(delim) + arg_str);
+       docstring parID = item.parIDs();
+       if (!stack_.empty())
+               parID = 
(*toc_)[stack_.top().pos].dit().paragraphGotoArgument(true);
+       item.setParIDs(convert<docstring>(row) + ',' + parID);
+       stack_.top().is_captioned = true;
+}
+
 void TocBuilder::pop()
 {
        if (!stack_.empty())
diff --git a/src/TocBuilder.h b/src/TocBuilder.h
index af9c41cb24..23d5fe8329 100644
--- a/src/TocBuilder.h
+++ b/src/TocBuilder.h
@@ -31,7 +31,7 @@ public:
        /// Open a level.
        /// When entering a float or flex or paragraph (with AddToToc)
        void pushItem(DocIterator const & dit, docstring const & s,
-                     bool output_active, bool is_captioned = false);
+                     bool output_active, bool is_captioned = false, int row = 
-1);
        /// Edit entry at current level. Add new entry if already captioned.
        /// When encountering a float caption
        void captionItem(DocIterator const & dit, docstring const & s,
@@ -39,6 +39,8 @@ public:
        /// Edit entry at current level (always).
        /// When encountering an argument (with isTocCaption) for flex or 
paragraph
        void argumentItem(docstring const & arg_str);
+       ///
+       void mathMultilineItem(docstring const & arg_str, Toc::size_type row);
        /// Close a level.
        /// When exiting a float or flex or paragraph
        void pop();
diff --git a/src/frontends/qt/GuiRef.cpp b/src/frontends/qt/GuiRef.cpp
index 295294ae51..23e4ddbae4 100644
--- a/src/frontends/qt/GuiRef.cpp
+++ b/src/frontends/qt/GuiRef.cpp
@@ -554,9 +554,8 @@ void GuiRef::updateTargets()
                                
targetCO->addItem(toqstr(translateIfPossible(name.second)), toqstr(name.first));
                }
        }
-// FIXME This does not work yet with REFERENCE_TO_PARAGRAPH
-//     if (isTargetAvailable("equation"))
-//             targetCO->addItem(qt_("Equations"), "equation");
+       if (isTargetAvailable("equation"))
+               targetCO->addItem(qt_("Equations"), "equation");
        // restore previous setting
        int const i = targetCO->findData(target);
        if (i != -1)
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index b9d76279af..5d8e3b4abd 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -354,8 +354,8 @@ void InsetMathHull::addToToc(DocIterator const & pit, bool 
output_active,
        TocBuilder & b = backend.builder("equation");
        b.pushItem(pit, docstring(), output_active);
        if (first != last)
-               b.argumentItem(bformat(from_ascii("(%1$s-%2$s)"),
-                                      numbers_[first], numbers_[last]));
+               b.mathMultilineItem(bformat(from_ascii("(%1$s-%2$s)"),
+                                      numbers_[first], numbers_[last]), 0);
 
        odocstringstream ods;
        Encoding const * enc = encodings.fromLyXName("utf8");
@@ -373,7 +373,7 @@ void InsetMathHull::addToToc(DocIterator const & pit, bool 
output_active,
                if (first == last) {
                        // this is the only equation
                        plaintext(ods, ops, max_length);
-                       b.argumentItem(label + " " + ods.str());
+                       b.mathMultilineItem(label + " " + ods.str(), row);
                } else {
                        // insert as sub-items
                        otexrowstream ots(ods);
@@ -387,7 +387,7 @@ void InsetMathHull::addToToc(DocIterator const & pit, bool 
output_active,
                                        break;
                                }
                        }
-                       b.pushItem(pit, label+ " " + d, output_active);
+                       b.pushItem(pit, label+ " " + d, output_active, false, 
row);
                        // clear the stringstream
                        odocstringstream().swap(ods);
                        b.pop();
@@ -1002,7 +1002,7 @@ docstring InsetMathHull::label(row_type row) const
 }
 
 
-void InsetMathHull::label(row_type row, docstring const & label)
+void InsetMathHull::label(row_type row, docstring const & label, bool const 
init)
 {
        //lyxerr << "setting label '" << label << "' for row " << row << endl;
        if (labels_[row]) {
@@ -1022,6 +1022,9 @@ void InsetMathHull::label(row_type row, docstring const & 
label)
        labels_[row] = new InsetLabel(buffer_, p);
        if (buffer_)
                labels_[row]->setBuffer(buffer());
+       if (init)
+               // Newly created inset so initialize it.
+               labels_[row]->initView();
 }
 
 
@@ -2101,11 +2104,9 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest 
& cmd)
                                if (labels_[r])
                                        // The label will take care of the 
reference update.
                                        label(r, str);
-                               else {
-                                       label(r, str);
+                               else
                                        // Newly created inset so initialize it.
-                                       labels_[r]->initView();
-                               }
+                                       label(r, str, true);
                        }
                        cur.forceBufferUpdate();
                        break;
diff --git a/src/mathed/InsetMathHull.h b/src/mathed/InsetMathHull.h
index e25e516e0c..b5cfe92f03 100644
--- a/src/mathed/InsetMathHull.h
+++ b/src/mathed/InsetMathHull.h
@@ -71,7 +71,7 @@ public:
        ///
        docstring label(row_type row) const;
        ///
-       void label(row_type row, docstring const & label);
+       void label(row_type row, docstring const & label, bool const init = 
false);
        ///
        std::vector<InsetLabel *> const & getLabels() { return labels_; }
        ///
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to