commit 3f3a92f0c1979777169da3c33b62e54abc6d5a5a
Author: Juergen Spitzmueller <sp...@lyx.org>
Date:   Fri Apr 4 16:37:48 2025 +0200

    Allow to insert a cross-reference via dialog directly to target (#1624)
    
    Next to the list of existing labels, the crossref dialog now also allows
    to select a target directly from the TOC, list of figures, etc.
    
    If this element already has a label, we use it. Otherwise, we auto-create
    a new one and use that (as already possible via the outliner).
---
 src/BufferView.cpp            |  23 +++--
 src/frontends/qt/GuiRef.cpp   | 213 ++++++++++++++++++++++++++++++++++++++----
 src/frontends/qt/GuiRef.h     |  14 ++-
 src/frontends/qt/TocModel.cpp |   6 ++
 src/frontends/qt/TocModel.h   |   2 +
 src/frontends/qt/ui/RefUi.ui  |  82 +++++++++-------
 6 files changed, 277 insertions(+), 63 deletions(-)

diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index 0344b608c3..e161029678 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -1713,9 +1713,13 @@ void BufferView::dispatch(FuncRequest const & cmd, 
DispatchResult & dr)
                        }
                        string label = dit.innerParagraph().getLabelForXRef();
                        if (!label.empty()) {
-                               // if the paragraph has a label, we refer to 
this
-                               string const arg = (type.empty()) ? label : 
label + " " + type;
-                               
lyx::dispatch(FuncRequest(LFUN_REFERENCE_INSERT, arg));
+                               // if the paragraph has a label, we use this
+                               if (type == "forrefdialog")
+                                       inserted_label_ = label;
+                               else {
+                                       string const arg = (type.empty()) ? 
label : label + " " + type;
+                                       
lyx::dispatch(FuncRequest(LFUN_REFERENCE_INSERT, arg));
+                               }
                                break;
                        } else {
                                // if there is not a label yet
@@ -1736,16 +1740,17 @@ void BufferView::dispatch(FuncRequest const & cmd, 
DispatchResult & dr)
                                p["name"] = new_label;
                                string const data = 
InsetCommand::params2string(p);
                                lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, 
data));
-                               string const arg = (type.empty()) ? 
to_utf8(new_label)
-                                                                 : 
to_utf8(new_label) + " " + type;
-                               // ... and go back to the original position
+                               // ... go back to the original position
                                lyx::dispatch(FuncRequest(LFUN_BOOKMARK_GOTO, 
"0"));
                                if (type == "forrefdialog")
-                                       // and save for the ref dialog to insert
-                                       inserted_label_ = arg;
-                               else
+                                       // ... and save for the ref dialog to 
insert
+                                       inserted_label_ = to_utf8(new_label);
+                               else {
                                        // ... 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));
+                               }
                                break;
                        }
                }
diff --git a/src/frontends/qt/GuiRef.cpp b/src/frontends/qt/GuiRef.cpp
index 7f38f24098..baa1d8c737 100644
--- a/src/frontends/qt/GuiRef.cpp
+++ b/src/frontends/qt/GuiRef.cpp
@@ -20,10 +20,16 @@
 #include "BufferList.h"
 #include "BufferView.h"
 #include "Cursor.h"
+#include "Paragraph.h"
+#include "TextClass.h"
+
 #include "FancyLineEdit.h"
 #include "FuncRequest.h"
+#include "GuiView.h"
 #include "PDFOptions.h"
 
+#include "TocModel.h"
+#include "TocBackend.h"
 #include "qt_helpers.h"
 
 #include "insets/InsetRef.h"
@@ -55,7 +61,7 @@ namespace frontend {
 
 GuiRef::GuiRef(GuiView & lv)
        : GuiDialog(lv, "ref", qt_("Cross-reference")),
-         params_(insetCode("ref"))
+         params_(insetCode("ref")), view_(&lv)
 {
        setupUi(this);
 
@@ -110,6 +116,8 @@ GuiRef::GuiRef(GuiView & lv)
                this, SLOT(gotoClicked()));
        connect(bufferCO, SIGNAL(activated(int)),
                this, SLOT(updateClicked()));
+       connect(targetCO, SIGNAL(activated(int)),
+               this, SLOT(updateClicked()));
        connect(pluralCB, SIGNAL(clicked()),
                this, SLOT(changed_adaptor()));
        connect(capsCB, SIGNAL(clicked()),
@@ -197,7 +205,30 @@ bool GuiRef::isSelected(const QModelIndex & idx)
 {
        if (!selectedLV->model() || selectedLV->model()->rowCount() == 0)
                return false;
-       QVariant const & str = refsTW->model()->data(idx, Qt::DisplayRole);
+       QVariant str = refsTW->model()->data(idx, Qt::DisplayRole);
+       if (targetCO->itemData(targetCO->currentIndex()).toString() != 
"labels") {
+               // for outliner-based items, we need to check whether the 
paragraph
+               // has a label and see if this is already selected
+               int const id = refsTW->currentItem()->data(0, 
Qt::UserRole).toInt();
+               if (id < 0)
+                       return false;
+               int const the_buffer = bufferCO->currentIndex();
+               if (the_buffer == -1)
+                       return false;
+               FileNameList const names(theBufferList().fileNames());
+               FileName const & name = names[the_buffer];
+               Buffer const * buf = theBufferList().getBuffer(name);
+               if (!buf)
+                       return false;
+               DocIterator dit = buf->getParFromID(id);
+               if (dit.empty())
+                       return false;
+               string label = dit.innerParagraph().getLabelForXRef();
+               if (label.empty())
+                       return false;
+               str = toqstr(label);
+       }
+       
        QModelIndexList qmil =
                        
selectedLV->model()->match(selectedLV->model()->index(0, 0),
                                             Qt::DisplayRole, str, 1,
@@ -229,9 +260,9 @@ void GuiRef::updateAddPB()
        QModelIndexList const availSels =
                refsTW->selectionModel()->selectedIndexes();
        addPB->setEnabled(arows > 0
-                &&!availSels.isEmpty()
+                && !availSels.isEmpty()
                 && !isSelected(availSels.first())
-                && ! threshold);
+                && !threshold);
 }
 
 
@@ -377,13 +408,13 @@ void GuiRef::refSelected(QTreeWidgetItem * sel)
 
 void GuiRef::sortToggled()
 {
-       redoRefs();
+       updateAvailableLabels();
 }
 
 
 void GuiRef::groupToggled()
 {
-       redoRefs();
+       updateAvailableLabels();
 }
 
 
@@ -418,6 +449,17 @@ void GuiRef::updateClicked()
 void GuiRef::addClicked()
 {
        QString text = refsTW->currentItem()->data(0, Qt::UserRole).toString();
+       if (targetCO->itemData(targetCO->currentIndex()).toString() != 
"labels") {
+               dispatch(FuncRequest(LFUN_REFERENCE_TO_PARAGRAPH,
+                               qstring_to_ucs4(text) + " " + "forrefdialog"));
+               if (bufferview()->insertedLabel().empty()) {
+                       frontend::Alert::error(_("Label creation error!"),
+                                              _("Could not auto-generate label 
for this target.\n"
+                                                "Please insert a label 
manually."));
+                       return;
+               }
+               text = toqstr(bufferview()->insertedLabel());
+       }
        QTreeWidgetItem * item = new QTreeWidgetItem(selectedLV);
        item->setText(0, text);
        item->setData(0, Qt::UserRole, text);
@@ -489,6 +531,31 @@ void GuiRef::closeEvent(QCloseEvent * e)
 }
 
 
+void GuiRef::updateTargets()
+{
+       QString const target = 
targetCO->itemData(targetCO->currentIndex()).toString();
+       targetCO->clear();
+       targetCO->addItem(qt_("Existing Labels"), "labels");
+       if (isTargetAvailable("tableofcontents"))
+               targetCO->addItem(qt_("Table of Contents"), "tableofcontents");
+       for (auto const & name : 
buffer().params().documentClass().outlinerNames()) {
+               // Use only items that make sense in this context
+               // FIXME: avoid hardcoding
+               if (name.first != "branch" && name.first != "index"
+                   && name.first != "marginalnote" && name.first != "note") {
+                       if (isTargetAvailable(toqstr(name.first)))
+                               
targetCO->addItem(toqstr(translateIfPossible(name.second)), toqstr(name.first));
+               }
+       }
+       if (isTargetAvailable("equation"))
+               targetCO->addItem(qt_("Equations"), "equation");
+       // restore previous setting
+       int const i = targetCO->findData(target);
+       if (i != -1)
+               targetCO->setCurrentIndex(i);
+}
+
+
 void GuiRef::updateContents()
 {
        QString const orig_type =
@@ -555,6 +622,8 @@ void GuiRef::updateContents()
        }
        active_buffer_ = thebuffer;
 
+       updateTargets();
+
        updateRefs();
        enableBoxes();
        // Activate OK/Apply buttons if the users inserts a new ref
@@ -641,7 +710,7 @@ inline bool caseInsensitiveLessThan(QString const & s1, 
QString const & s2)
 }
 
 
-void GuiRef::redoRefs()
+void GuiRef::updateAvailableLabels()
 {
        // Prevent these widgets from emitting any signals whilst
        // we modify their state.
@@ -754,20 +823,128 @@ void GuiRef::redoRefs()
 }
 
 
+void GuiRef::getTargetChildren(QModelIndex & index, QAbstractItemModel * model,
+                              QTreeWidgetItem * pitem, QString const & target)
+{
+       for (int r = 0; r != model->rowCount(index); ++r) {
+               QModelIndex mi = model->index(r, 0, index);
+               if (mi == index)
+                       continue;
+               TocItem const & ti = view_->tocModels().currentItem(target, mi);
+               docstring const id = (ti.parIDs().empty())
+                               ? ti.dit().paragraphGotoArgument(true)
+                               : ti.parIDs();
+               QString const ref = toqstr(id);
+               QString const val = model->data(mi, Qt::DisplayRole).toString();
+               QTreeWidgetItem * child = new QTreeWidgetItem(pitem);
+               child->setText(0, val);
+               child->setData(0, Qt::UserRole, ref);
+               // recursive call to get grandchildren
+               if (model->hasChildren(mi))
+                       getTargetChildren(mi, model, child, target);
+               pitem->addChild(child);
+       }
+}
+
+
+void GuiRef::updateAvailableTargets()
+{
+       // Prevent these widgets from emitting any signals whilst
+       // we modify their state.
+       refsTW->blockSignals(true);
+       refsTW->setUpdatesEnabled(false);
+
+       refsTW->clear();
+
+       QString const target = 
targetCO->itemData(targetCO->currentIndex()).toString();
+       QAbstractItemModel * toc_model = view_->tocModels().model(target);
+       if (!toc_model)
+               return;
+
+       bool has_children = false;
+       QList<QTreeWidgetItem *> refsItems;
+       for (int r = 0; r < toc_model->rowCount(); ++r) {
+               QModelIndex mi = toc_model->index(r, 0);
+               QTreeWidgetItem * item = new QTreeWidgetItem(refsTW);
+               TocItem const & ti = view_->tocModels().currentItem(target, mi);
+               docstring const id = (ti.parIDs().empty())
+                               ? ti.dit().paragraphGotoArgument(true)
+                               : ti.parIDs();
+               QString const ref = toqstr(id);
+               QString const val = toc_model->data(mi, 
Qt::DisplayRole).toString();
+               item->setText(0, val);
+               item->setData(0, Qt::UserRole, ref);
+               if (toc_model->hasChildren(mi)) {
+                       getTargetChildren(mi, toc_model, item, target);
+                       has_children = true;
+               }
+               refsItems.append(item);
+       }
+       refsTW->addTopLevelItems(refsItems);
+
+       refsTW->setUpdatesEnabled(true);
+       refsTW->update();
+       updateButtons();
+
+       // redo filter
+       filterLabels();
+
+       // Re-activate the emission of signals by these widgets.
+       refsTW->blockSignals(false);
+
+       bool const sel = selectedLV->currentItem()
+               && selectedLV->currentItem()->isSelected();
+
+       gotoPB->setEnabled(sel);
+       typeCO->setEnabled(sel);
+       typeLA->setEnabled(sel);
+
+       if (has_children)
+               refsTW->setIndentation(10);
+       else
+               refsTW->setIndentation(0);
+}
+
+
+bool GuiRef::isTargetAvailable(QString const & target)
+{
+       if (!view_->tocModels().hasModel(target))
+               return false;
+
+       QAbstractItemModel * toc_model = view_->tocModels().model(target);
+       return toc_model && toc_model->rowCount() > 0;
+}
+
+
 void GuiRef::updateRefs()
 {
-       refs_.clear();
-       int const the_buffer = bufferCO->currentIndex();
-       if (the_buffer != -1) {
-               FileNameList const names(theBufferList().fileNames());
-               FileName const & name = names[the_buffer];
-               Buffer const * buf = theBufferList().getBuffer(name);
-               buf->getLabelList(refs_);
+       QString const target = 
targetCO->itemData(targetCO->currentIndex()).toString();
+       bool const show_labels = target == "labels";
+       if (show_labels) {
+               refs_.clear();
+               int const the_buffer = bufferCO->currentIndex();
+               if (the_buffer != -1) {
+                       FileNameList const names(theBufferList().fileNames());
+                       FileName const & name = names[the_buffer];
+                       Buffer const * buf = theBufferList().getBuffer(name);
+                       buf->getLabelList(refs_);
+               }
+       }
+       bool const enable_tw = (show_labels) ? !refs_.empty()
+                                            : isTargetAvailable(target);
+       refsTW->setEnabled(enable_tw);
+       sortingCO->setEnabled(show_labels && !refs_.empty());
+       groupCB->setEnabled(show_labels && !refs_.empty());
+
+       if (show_labels) {
+               refsTW->header()->setVisible(true);
+               availableLA->setText(qt_("Available &Labels:"));
+               updateAvailableLabels();
+       } else {
+               refsTW->header()->setVisible(false);
+               availableLA->setText(qt_("Available &Targets:"));
+               updateAvailableTargets();
        }
-       sortingCO->setEnabled(!refs_.empty());
-       refsTW->setEnabled(!refs_.empty());
-       groupCB->setEnabled(!refs_.empty());
-       redoRefs();
 }
 
 
diff --git a/src/frontends/qt/GuiRef.h b/src/frontends/qt/GuiRef.h
index fc585f5bfa..fcd1822c98 100644
--- a/src/frontends/qt/GuiRef.h
+++ b/src/frontends/qt/GuiRef.h
@@ -86,11 +86,15 @@ private:
        void setGoBack();
        /// set goto ref button
        void setGotoRef();
-       /// re-enter references
-       void redoRefs();
+       /// re-enter available labels
+       void updateAvailableLabels();
+       /// re-enter available targets
+       void updateAvailableTargets();
        /// update references
        void updateRefs();
        ///
+       void updateTargets();
+       ///
        bool initialiseParams(std::string const & data) override;
        /// clean-up on hide.
        void clearParams() override { params_.clear(); }
@@ -110,6 +114,10 @@ private:
        virtual void updateUpPB();
        ///
        bool isSelected(const QModelIndex & idx);
+       ///
+       bool isTargetAvailable(QString const &);
+       ///
+       void getTargetChildren(QModelIndex &, QAbstractItemModel *, 
QTreeWidgetItem *, QString const &);
 
        /// contains the search box
        FancyLineEdit * filter_;
@@ -126,6 +134,8 @@ private:
        /// string, and pretty dereferenced name ("Lemma 3")
        /// FIXME: might be a good idea to use a custom struct
        std::vector<std::tuple<docstring, docstring, docstring>> refs_;
+       ///
+       GuiView * view_;
 };
 
 } // namespace frontend
diff --git a/src/frontends/qt/TocModel.cpp b/src/frontends/qt/TocModel.cpp
index ab37c987cd..70b5e785c8 100644
--- a/src/frontends/qt/TocModel.cpp
+++ b/src/frontends/qt/TocModel.cpp
@@ -287,6 +287,12 @@ QAbstractItemModel * TocModels::model(QString const & type)
 }
 
 
+bool TocModels::hasModel(QString const & type) const
+{
+       return (models_.find(type) != models_.end());
+}
+
+
 QAbstractItemModel * TocModels::nameModel()
 {
        return names_sorted_;
diff --git a/src/frontends/qt/TocModel.h b/src/frontends/qt/TocModel.h
index b37484fcc6..b64d6f0f70 100644
--- a/src/frontends/qt/TocModel.h
+++ b/src/frontends/qt/TocModel.h
@@ -122,6 +122,8 @@ public:
        ///
        QAbstractItemModel * model(QString const & type);
        ///
+       bool hasModel(QString const & type) const;
+       ///
        QAbstractItemModel * nameModel();
        ///
        QModelIndex currentIndex(QString const & type,
diff --git a/src/frontends/qt/ui/RefUi.ui b/src/frontends/qt/ui/RefUi.ui
index 462a12d77a..6d09894997 100644
--- a/src/frontends/qt/ui/RefUi.ui
+++ b/src/frontends/qt/ui/RefUi.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>622</width>
+    <width>654</width>
     <height>579</height>
    </rect>
   </property>
@@ -20,16 +20,19 @@
    <item row="0" column="0">
     <layout class="QGridLayout" name="gridLayout">
      <item row="0" column="0">
-      <layout class="QVBoxLayout" name="verticalLayout">
+      <widget class="QLabel" name="targetLA">
+       <property name="text">
+        <string>&amp;Select from:</string>
+       </property>
+       <property name="buddy">
+        <cstring>targetCO</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
-        <widget class="QLabel" name="findKeysLA">
-         <property name="text">
-          <string>&amp;Filter:</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-         </property>
-        </widget>
+        <widget class="QComboBox" name="targetCO"/>
        </item>
        <item>
         <widget class="QLabel" name="refsL">
@@ -41,35 +44,20 @@
          </property>
         </widget>
        </item>
-      </layout>
-     </item>
-     <item row="0" column="1">
-      <layout class="QVBoxLayout" name="verticalLayout_2">
-       <item>
-        <layout class="QHBoxLayout" name="horizontalLayout">
-         <item>
-          <layout class="QHBoxLayout" name="filterBarL"/>
-         </item>
-         <item>
-          <widget class="QCheckBox" name="csFindCB">
-           <property name="toolTip">
-            <string>Filter case-sensitively</string>
-           </property>
-           <property name="text">
-            <string>Case Sensiti&amp;ve</string>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
        <item>
         <widget class="QComboBox" name="bufferCO">
          <property name="sizePolicy">
-          <sizepolicy hsizetype="Ignored" vsizetype="Fixed">
+          <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
+         <property name="minimumSize">
+          <size>
+           <width>121</width>
+           <height>0</height>
+          </size>
+         </property>
          <property name="toolTip">
           <string>The (sub-)document from which the available labels are 
displayed</string>
          </property>
@@ -83,12 +71,39 @@
        </item>
       </layout>
      </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="findKeysLA">
+       <property name="text">
+        <string>&amp;Filter:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <layout class="QHBoxLayout" name="horizontalLayout_4">
+       <item>
+        <layout class="QHBoxLayout" name="filterBarL"/>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="csFindCB">
+         <property name="toolTip">
+          <string>Filter case-sensitively</string>
+         </property>
+         <property name="text">
+          <string>Case Sensiti&amp;ve</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
     </layout>
    </item>
    <item row="1" column="0">
     <layout class="QGridLayout" name="gridLayout_3">
      <item row="0" column="0">
-      <widget class="QLabel" name="label">
+      <widget class="QLabel" name="availableLA">
        <property name="text">
         <string>Available &amp;Labels:</string>
        </property>
@@ -504,7 +519,6 @@
  </widget>
  <tabstops>
   <tabstop>refsTW</tabstop>
-  <tabstop>csFindCB</tabstop>
   <tabstop>groupCB</tabstop>
   <tabstop>typeCO</tabstop>
  </tabstops>
-- 
lyx-cvs mailing list
lyx-cvs@lists.lyx.org
https://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to