Skip to content

Commit db1469f

Browse files
committed
Add dialog for link insertion when selection buffer is empty
1 parent 1665dec commit db1469f

5 files changed

Lines changed: 306 additions & 40 deletions

File tree

src/Gui/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ SET(Gui_UIC_SRCS
490490
InputVector.ui
491491
Placement.ui
492492
TaskTransform.ui
493+
TaskCommandLink.ui
493494
TextureMapping.ui
494495
TaskView/TaskAppearance.ui
495496
TaskView/TaskOrientation.ui
@@ -578,6 +579,7 @@ SET(Dialog_CPP_SRCS
578579
TaskDlgRelocation.cpp
579580
Dialogs/DlgCheckableMessageBox.cpp
580581
TaskTransform.cpp
582+
TaskCommandLink.cpp
581583
Dialogs/DlgUndoRedo.cpp
582584
InputVector.cpp
583585
Placement.cpp
@@ -620,6 +622,7 @@ SET(Dialog_HPP_SRCS
620622
Dialogs/DlgVersionMigrator.h
621623
TaskDlgRelocation.h
622624
TaskTransform.h
625+
TaskCommandLink.h
623626
Dialogs/DlgUndoRedo.h
624627
InputVector.h
625628
Placement.h

src/Gui/CommandLink.cpp

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
#include "Action.h"
3535
#include "Application.h"
3636
#include "Command.h"
37+
#include "Control.h"
3738
#include "Document.h"
3839
#include "MainWindow.h"
3940
#include "Selection.h"
41+
#include "TaskCommandLink.h"
4042
#include "Tree.h"
4143
#include "ViewProviderDocumentObject.h"
4244
#include "WaitCursor.h"
@@ -280,55 +282,65 @@ void StdCmdLinkMake::activated(int)
280282
return;
281283
}
282284

283-
std::set<App::DocumentObject*> objs;
284-
for (auto& sel : Selection().getCompleteSelection()) {
285-
if (sel.pObject && sel.pObject->isAttachedToDocument()) {
286-
objs.insert(sel.pObject);
287-
}
288-
}
289-
290-
Selection().selStackPush();
291-
Selection().clearCompleteSelection();
292-
293-
Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make link"));
294-
try {
295-
if (objs.empty()) {
296-
std::string name = doc->getUniqueObjectName("Link");
297-
Command::doCommand(
298-
Command::Doc,
299-
"App.getDocument('%s').addObject('App::Link','%s')",
300-
doc->getName(),
301-
name.c_str()
302-
);
303-
Selection().addSelection(doc->getName(), name.c_str());
304-
}
305-
else {
306-
for (auto obj : objs) {
285+
auto exec = [=](std::vector<App::DocumentObject*> objs) {
286+
doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Make link"));
287+
try {
288+
if (objs.empty()) {
307289
std::string name = doc->getUniqueObjectName("Link");
308290
Command::doCommand(
309291
Command::Doc,
310-
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument('%s'"
311-
").%s)",
292+
"App.getDocument('%s').addObject('App::Link','%s')",
312293
doc->getName(),
313-
name.c_str(),
314-
obj->getDocument()->getName(),
315-
obj->getNameInDocument()
294+
name.c_str()
316295
);
317-
setLinkLabel(obj, doc->getName(), name.c_str());
318296
Selection().addSelection(doc->getName(), name.c_str());
319297
}
298+
else {
299+
for (auto obj : objs) {
300+
std::string name = doc->getUniqueObjectName("Link");
301+
Command::doCommand(
302+
Command::Doc,
303+
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument("
304+
"'%s'"
305+
").%s)",
306+
doc->getName(),
307+
name.c_str(),
308+
obj->getDocument()->getName(),
309+
obj->getNameInDocument()
310+
);
311+
setLinkLabel(obj, doc->getName(), name.c_str());
312+
Selection().addSelection(doc->getName(), name.c_str());
313+
}
314+
}
315+
Selection().selStackPush();
316+
doc->commitTransaction();
317+
}
318+
catch (const Base::Exception& e) {
319+
doc->abortTransaction();
320+
QMessageBox::critical(
321+
getMainWindow(),
322+
QObject::tr("Create link failed"),
323+
QString::fromLatin1(e.what())
324+
);
325+
e.reportException();
326+
}
327+
};
328+
329+
330+
std::set<App::DocumentObject*> objs;
331+
for (auto& sel : Selection().getCompleteSelection()) {
332+
if (sel.pObject && sel.pObject->isAttachedToDocument()) {
333+
objs.insert(sel.pObject);
320334
}
321-
Selection().selStackPush();
322-
Command::commitCommand();
323335
}
324-
catch (const Base::Exception& e) {
325-
Command::abortCommand();
326-
QMessageBox::critical(
327-
getMainWindow(),
328-
QObject::tr("Create link failed"),
329-
QString::fromLatin1(e.what())
330-
);
331-
e.reportException();
336+
337+
if (objs.empty()) {
338+
Gui::Control().showDialog(new TaskCommandLinkDialog(exec));
339+
}
340+
else {
341+
Selection().selStackPush();
342+
Selection().clearCompleteSelection();
343+
exec(std::vector<App::DocumentObject*>(objs.begin(), objs.end()));
332344
}
333345
}
334346

src/Gui/TaskCommandLink.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/***************************************************************************
2+
* Copyright (c) 2026 Théo Veilleux-Trinh <theo.veilleux.trinh@proton.me>*
3+
* *
4+
* This file is part of the FreeCAD CAx development system. *
5+
* *
6+
* This library is free software; you can redistribute it and/or *
7+
* modify it under the terms of the GNU Library General Public *
8+
* License as published by the Free Software Foundation; either *
9+
* version 2 of the License, or (at your option) any later version. *
10+
* *
11+
* This library is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU Library General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU Library General Public *
17+
* License along with this library; see the file COPYING.LIB. If not, *
18+
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19+
* Suite 330, Boston, MA 02111-1307, USA *
20+
* *
21+
***************************************************************************/
22+
23+
#include "TaskCommandLink.h"
24+
25+
#include "ui_TaskCommandLink.h"
26+
27+
#include "Application.h"
28+
#include "Document.h"
29+
#include "ViewProvider.h"
30+
31+
#include <App/Application.h>
32+
#include <App/DocumentObject.h>
33+
#include <App/Document.h>
34+
35+
namespace Gui
36+
{
37+
TaskCommandLink::TaskCommandLink()
38+
: ui(new Ui_TaskCommandLinkDialog())
39+
{
40+
proxy = new QWidget(this);
41+
ui->setupUi(proxy);
42+
ui->objectsList->header()->hide();
43+
ui->objectsList->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
44+
45+
this->groupLayout()->addWidget(proxy);
46+
47+
buildObjectsList();
48+
}
49+
TaskCommandLink::~TaskCommandLink()
50+
{
51+
delete proxy;
52+
delete ui;
53+
}
54+
std::vector<App::DocumentObject*> TaskCommandLink::selectedObjects()
55+
{
56+
auto selected = ui->objectsList->selectedItems();
57+
std::vector<App::DocumentObject*> dst;
58+
dst.reserve(selected.size());
59+
60+
for (auto sel : selected) {
61+
dst.push_back(sel->data(0, Qt::UserRole).value<App::DocumentObject*>());
62+
}
63+
return dst;
64+
}
65+
void processObjectsHelper(std::vector<App::DocumentObject*> objs, QTreeWidgetItem* item)
66+
{
67+
for (auto obj : objs) {
68+
auto objItem = new QTreeWidgetItem(item);
69+
70+
objItem->setText(0, obj->Label.getValue());
71+
objItem->setData(0, Qt::UserRole, QVariant::fromValue(obj));
72+
73+
Gui::ViewProvider* vp = nullptr;
74+
if (auto doc = Application::Instance->getDocument(obj->getDocument())) {
75+
vp = doc->getViewProvider(obj);
76+
}
77+
if (vp) {
78+
objItem->setIcon(0, vp->getIcon());
79+
processObjectsHelper(vp->claimChildren(), objItem);
80+
}
81+
else {
82+
objItem->setIcon(0, QIcon());
83+
}
84+
}
85+
}
86+
void TaskCommandLink::buildObjectsList()
87+
{
88+
ui->objectsList->clear();
89+
90+
auto allDocuments = App::GetApplication().getDocuments();
91+
bool collapse = true;
92+
std::map<QTreeWidgetItem*, App::Document*> docItemMap;
93+
94+
for (auto doc : allDocuments) {
95+
auto docItem = new QTreeWidgetItem();
96+
std::string itemName = doc->Label.getValue();
97+
98+
docItem->setText(0, QString::fromStdString(itemName));
99+
docItem->setIcon(0, QIcon(QStringLiteral(":/icons/Document.svg")));
100+
docItem->setFlags(docItem->flags() & ~Qt::ItemIsSelectable); // Can't link a whole document
101+
102+
docItemMap[docItem] = doc;
103+
104+
ui->objectsList->addTopLevelItem(docItem);
105+
106+
processObjectsHelper(Application::Instance->getDocument(doc)->getTreeRootObjects(), docItem);
107+
108+
if (collapse) {
109+
ui->objectsList->collapseAll();
110+
}
111+
else {
112+
ui->objectsList->expandToDepth(0);
113+
}
114+
}
115+
ui->objectsList->selectedItems();
116+
}
117+
118+
// dialog
119+
120+
TaskCommandLinkDialog::TaskCommandLinkDialog(
121+
std::function<void(std::vector<App::DocumentObject*>)> executor_
122+
)
123+
: executor(executor_)
124+
{
125+
commandLink = new TaskCommandLink();
126+
Content.push_back(commandLink);
127+
}
128+
void TaskCommandLinkDialog::open()
129+
{}
130+
bool TaskCommandLinkDialog::accept()
131+
{
132+
executor(commandLink->selectedObjects());
133+
return true;
134+
}
135+
} // namespace Gui

src/Gui/TaskCommandLink.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/***************************************************************************
2+
* Copyright (c) 2026 Théo Veilleux-Trinh <theo.veilleux.trinh@proton.me>*
3+
* *
4+
* This file is part of the FreeCAD CAx development system. *
5+
* *
6+
* This library is free software; you can redistribute it and/or *
7+
* modify it under the terms of the GNU Library General Public *
8+
* License as published by the Free Software Foundation; either *
9+
* version 2 of the License, or (at your option) any later version. *
10+
* *
11+
* This library is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU Library General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU Library General Public *
17+
* License along with this library; see the file COPYING.LIB. If not, *
18+
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19+
* Suite 330, Boston, MA 02111-1307, USA *
20+
* *
21+
***************************************************************************/
22+
23+
#pragma once
24+
25+
#include "TaskView/TaskDialog.h"
26+
#include "TaskView/TaskView.h"
27+
28+
#include <QTreeWidgetItem>
29+
30+
#include <functional>
31+
#include <vector>
32+
33+
namespace App
34+
{
35+
class DocumentObject;
36+
}
37+
38+
namespace Gui
39+
{
40+
class Document;
41+
class Ui_TaskCommandLinkDialog;
42+
43+
class TaskCommandLink: public Gui::TaskView::TaskBox
44+
{
45+
public:
46+
TaskCommandLink();
47+
~TaskCommandLink();
48+
49+
std::vector<App::DocumentObject*> selectedObjects();
50+
51+
private:
52+
void buildObjectsList();
53+
54+
private:
55+
Ui_TaskCommandLinkDialog* ui {nullptr};
56+
QWidget* proxy {nullptr};
57+
};
58+
59+
class TaskCommandLinkDialog: public Gui::TaskView::TaskDialog
60+
{
61+
Q_OBJECT
62+
63+
public:
64+
TaskCommandLinkDialog(std::function<void(std::vector<App::DocumentObject*>)> executor_);
65+
~TaskCommandLinkDialog() override = default;
66+
67+
QDialogButtonBox::StandardButtons getStandardButtons() const override
68+
{
69+
return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
70+
}
71+
72+
void open() override;
73+
bool accept() override;
74+
75+
private:
76+
TaskCommandLink* commandLink {nullptr};
77+
Gui::Document* document {nullptr};
78+
std::function<void(std::vector<App::DocumentObject*>)> executor;
79+
};
80+
} // namespace Gui

src/Gui/TaskCommandLink.ui

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>Gui::TaskCommandLinkDialog</class>
4+
<widget class="QWidget" name="Gui::TaskCommandLinkDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>340</width>
10+
<height>212</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Insert</string>
15+
</property>
16+
<layout class="QGridLayout" name="gridLayout">
17+
<item row="0" column="0">
18+
<widget class="QTreeWidget" name="objectsList">
19+
<property name="sizePolicy">
20+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
21+
<horstretch>0</horstretch>
22+
<verstretch>0</verstretch>
23+
</sizepolicy>
24+
</property>
25+
<column>
26+
<property name="text">
27+
<string notr="true">1</string>
28+
</property>
29+
</column>
30+
</widget>
31+
</item>
32+
</layout>
33+
</widget>
34+
<resources/>
35+
<connections/>
36+
</ui>

0 commit comments

Comments
 (0)