Add dialog for link insertion when selection buffer is empty

This commit is contained in:
theo-vt
2026-03-08 11:16:01 -04:00
parent 1665dec4e4
commit db1469f219
5 changed files with 307 additions and 41 deletions
+3
View File
@@ -490,6 +490,7 @@ SET(Gui_UIC_SRCS
InputVector.ui
Placement.ui
TaskTransform.ui
TaskCommandLink.ui
TextureMapping.ui
TaskView/TaskAppearance.ui
TaskView/TaskOrientation.ui
@@ -578,6 +579,7 @@ SET(Dialog_CPP_SRCS
TaskDlgRelocation.cpp
Dialogs/DlgCheckableMessageBox.cpp
TaskTransform.cpp
TaskCommandLink.cpp
Dialogs/DlgUndoRedo.cpp
InputVector.cpp
Placement.cpp
@@ -620,6 +622,7 @@ SET(Dialog_HPP_SRCS
Dialogs/DlgVersionMigrator.h
TaskDlgRelocation.h
TaskTransform.h
TaskCommandLink.h
Dialogs/DlgUndoRedo.h
InputVector.h
Placement.h
+53 -41
View File
@@ -34,9 +34,11 @@
#include "Action.h"
#include "Application.h"
#include "Command.h"
#include "Control.h"
#include "Document.h"
#include "MainWindow.h"
#include "Selection.h"
#include "TaskCommandLink.h"
#include "Tree.h"
#include "ViewProviderDocumentObject.h"
#include "WaitCursor.h"
@@ -280,6 +282,51 @@ void StdCmdLinkMake::activated(int)
return;
}
auto exec = [=](std::vector<App::DocumentObject*> objs) {
doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Make link"));
try {
if (objs.empty()) {
std::string name = doc->getUniqueObjectName("Link");
Command::doCommand(
Command::Doc,
"App.getDocument('%s').addObject('App::Link','%s')",
doc->getName(),
name.c_str()
);
Selection().addSelection(doc->getName(), name.c_str());
}
else {
for (auto obj : objs) {
std::string name = doc->getUniqueObjectName("Link");
Command::doCommand(
Command::Doc,
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument("
"'%s'"
").%s)",
doc->getName(),
name.c_str(),
obj->getDocument()->getName(),
obj->getNameInDocument()
);
setLinkLabel(obj, doc->getName(), name.c_str());
Selection().addSelection(doc->getName(), name.c_str());
}
}
Selection().selStackPush();
doc->commitTransaction();
}
catch (const Base::Exception& e) {
doc->abortTransaction();
QMessageBox::critical(
getMainWindow(),
QObject::tr("Create link failed"),
QString::fromLatin1(e.what())
);
e.reportException();
}
};
std::set<App::DocumentObject*> objs;
for (auto& sel : Selection().getCompleteSelection()) {
if (sel.pObject && sel.pObject->isAttachedToDocument()) {
@@ -287,48 +334,13 @@ void StdCmdLinkMake::activated(int)
}
}
Selection().selStackPush();
Selection().clearCompleteSelection();
Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make link"));
try {
if (objs.empty()) {
std::string name = doc->getUniqueObjectName("Link");
Command::doCommand(
Command::Doc,
"App.getDocument('%s').addObject('App::Link','%s')",
doc->getName(),
name.c_str()
);
Selection().addSelection(doc->getName(), name.c_str());
}
else {
for (auto obj : objs) {
std::string name = doc->getUniqueObjectName("Link");
Command::doCommand(
Command::Doc,
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument('%s'"
").%s)",
doc->getName(),
name.c_str(),
obj->getDocument()->getName(),
obj->getNameInDocument()
);
setLinkLabel(obj, doc->getName(), name.c_str());
Selection().addSelection(doc->getName(), name.c_str());
}
}
Selection().selStackPush();
Command::commitCommand();
if (objs.empty()) {
Gui::Control().showDialog(new TaskCommandLinkDialog(exec));
}
catch (const Base::Exception& e) {
Command::abortCommand();
QMessageBox::critical(
getMainWindow(),
QObject::tr("Create link failed"),
QString::fromLatin1(e.what())
);
e.reportException();
else {
Selection().selStackPush();
Selection().clearCompleteSelection();
exec(std::vector<App::DocumentObject*>(objs.begin(), objs.end()));
}
}
+135
View File
@@ -0,0 +1,135 @@
/***************************************************************************
* Copyright (c) 2026 Théo Veilleux-Trinh <theo.veilleux.trinh@proton.me>*
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "TaskCommandLink.h"
#include "ui_TaskCommandLink.h"
#include "Application.h"
#include "Document.h"
#include "ViewProvider.h"
#include <App/Application.h>
#include <App/DocumentObject.h>
#include <App/Document.h>
namespace Gui
{
TaskCommandLink::TaskCommandLink()
: ui(new Ui_TaskCommandLinkDialog())
{
proxy = new QWidget(this);
ui->setupUi(proxy);
ui->objectsList->header()->hide();
ui->objectsList->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
this->groupLayout()->addWidget(proxy);
buildObjectsList();
}
TaskCommandLink::~TaskCommandLink()
{
delete proxy;
delete ui;
}
std::vector<App::DocumentObject*> TaskCommandLink::selectedObjects()
{
auto selected = ui->objectsList->selectedItems();
std::vector<App::DocumentObject*> dst;
dst.reserve(selected.size());
for (auto sel : selected) {
dst.push_back(sel->data(0, Qt::UserRole).value<App::DocumentObject*>());
}
return dst;
}
void processObjectsHelper(std::vector<App::DocumentObject*> objs, QTreeWidgetItem* item)
{
for (auto obj : objs) {
auto objItem = new QTreeWidgetItem(item);
objItem->setText(0, obj->Label.getValue());
objItem->setData(0, Qt::UserRole, QVariant::fromValue(obj));
Gui::ViewProvider* vp = nullptr;
if (auto doc = Application::Instance->getDocument(obj->getDocument())) {
vp = doc->getViewProvider(obj);
}
if (vp) {
objItem->setIcon(0, vp->getIcon());
processObjectsHelper(vp->claimChildren(), objItem);
}
else {
objItem->setIcon(0, QIcon());
}
}
}
void TaskCommandLink::buildObjectsList()
{
ui->objectsList->clear();
auto allDocuments = App::GetApplication().getDocuments();
bool collapse = true;
std::map<QTreeWidgetItem*, App::Document*> docItemMap;
for (auto doc : allDocuments) {
auto docItem = new QTreeWidgetItem();
std::string itemName = doc->Label.getValue();
docItem->setText(0, QString::fromStdString(itemName));
docItem->setIcon(0, QIcon(QStringLiteral(":/icons/Document.svg")));
docItem->setFlags(docItem->flags() & ~Qt::ItemIsSelectable); // Can't link a whole document
docItemMap[docItem] = doc;
ui->objectsList->addTopLevelItem(docItem);
processObjectsHelper(Application::Instance->getDocument(doc)->getTreeRootObjects(), docItem);
if (collapse) {
ui->objectsList->collapseAll();
}
else {
ui->objectsList->expandToDepth(0);
}
}
ui->objectsList->selectedItems();
}
// dialog
TaskCommandLinkDialog::TaskCommandLinkDialog(
std::function<void(std::vector<App::DocumentObject*>)> executor_
)
: executor(executor_)
{
commandLink = new TaskCommandLink();
Content.push_back(commandLink);
}
void TaskCommandLinkDialog::open()
{}
bool TaskCommandLinkDialog::accept()
{
executor(commandLink->selectedObjects());
return true;
}
} // namespace Gui
+80
View File
@@ -0,0 +1,80 @@
/***************************************************************************
* Copyright (c) 2026 Théo Veilleux-Trinh <theo.veilleux.trinh@proton.me>*
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#pragma once
#include "TaskView/TaskDialog.h"
#include "TaskView/TaskView.h"
#include <QTreeWidgetItem>
#include <functional>
#include <vector>
namespace App
{
class DocumentObject;
}
namespace Gui
{
class Document;
class Ui_TaskCommandLinkDialog;
class TaskCommandLink: public Gui::TaskView::TaskBox
{
public:
TaskCommandLink();
~TaskCommandLink();
std::vector<App::DocumentObject*> selectedObjects();
private:
void buildObjectsList();
private:
Ui_TaskCommandLinkDialog* ui {nullptr};
QWidget* proxy {nullptr};
};
class TaskCommandLinkDialog: public Gui::TaskView::TaskDialog
{
Q_OBJECT
public:
TaskCommandLinkDialog(std::function<void(std::vector<App::DocumentObject*>)> executor_);
~TaskCommandLinkDialog() override = default;
QDialogButtonBox::StandardButtons getStandardButtons() const override
{
return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
}
void open() override;
bool accept() override;
private:
TaskCommandLink* commandLink {nullptr};
Gui::Document* document {nullptr};
std::function<void(std::vector<App::DocumentObject*>)> executor;
};
} // namespace Gui
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::TaskCommandLinkDialog</class>
<widget class="QWidget" name="Gui::TaskCommandLinkDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>340</width>
<height>212</height>
</rect>
</property>
<property name="windowTitle">
<string>Insert</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTreeWidget" name="objectsList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>