commit 3f3a92f0c1979777169da3c33b62e54abc6d5a5a
Author: Juergen Spitzmueller <[email protected]>
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>&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>&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&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>&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&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 &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
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs