This commit is contained in:
Kellermorph
2026-04-26 10:48:47 +02:00
parent ee65142b65
commit 0118d94d4e
15 changed files with 669 additions and 90 deletions

View File

@@ -254,6 +254,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/diagramevent/diagrameventaddtext.h
${QET_DIR}/sources/diagramevent/diagrameventinterface.cpp
${QET_DIR}/sources/diagramevent/diagrameventinterface.h
${QET_DIR}/sources/diagramevent/diagrameventaddmacro.cpp
${QET_DIR}/sources/diagramevent/diagrameventaddmacro.h
${QET_DIR}/sources/dvevent/dveventinterface.cpp
${QET_DIR}/sources/dvevent/dveventinterface.h

View File

@@ -119,8 +119,8 @@ int ElementCollectionItem::rowForInsertItem(const QString &name)
return -1;
QList <ElementCollectionItem *> child;
//The item to insert is an element we search from element child
if (name.endsWith(".elmt"))
//The item to insert is an element/template we search from element child
if (name.endsWith(".elmt") || name.endsWith(".qetmak"))
{
child = elementsDirectChild();
//There isn't element, we insert at last position

View File

@@ -93,10 +93,7 @@ void ElementsCollectionWidget::addProject(QETProject *project)
{
if (m_model)
{
m_progress_bar->show();
m_tree_view->setDisabled(true);
QList <QETProject *> prj; prj.append(project);
m_model->loadCollections(false, false, false, prj);
m_model->addProject(project, true);
}
else {
m_waiting_project.append(project);
@@ -194,7 +191,7 @@ void ElementsCollectionWidget::setUpWidget()
m_tree_view->setMouseTracking(true);
m_tree_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
//Setup the macros tree view (DEV)
//Setup the macros tree view
m_macros_tree_view = new ElementsTreeView(this);
m_macros_tree_view->setHeaderHidden(true);
m_macros_tree_view->setIconSize(QSize(50, 50));
@@ -209,7 +206,7 @@ void ElementsCollectionWidget::setUpWidget()
m_tab_widget->setDocumentMode(true);
m_tab_widget->setTabPosition(QTabWidget::North);
m_tab_widget->addTab(m_tree_view, tr("Collections"));
m_tab_widget->addTab(m_macros_tree_view, tr("Modèles (DEV)"));
m_tab_widget->addTab(m_macros_tree_view, tr("Modèles"));
m_main_vlayout->addWidget(m_search_field);
m_main_vlayout->addWidget(m_tab_widget);
@@ -258,11 +255,15 @@ void ElementsCollectionWidget::setUpConnection()
this, &ElementsCollectionWidget::dirProperties);
connect(m_tree_view, &QTreeView::doubleClicked,
[this](const QModelIndex &index)
{
this->m_index_at_context_menu = index ;
this->editElement();
});
[this](const QModelIndex &index)
{
this->m_index_at_context_menu = index ;
ElementCollectionItem *eci = elementCollectionItemForIndex(index);
if (eci && eci->collectionPath().endsWith(".qetmak")) {
return; // Do nothing on double click for macros
}
this->editElement();
});
connect(m_tree_view, &QTreeView::entered,
[this] (const QModelIndex &index) {
@@ -276,11 +277,15 @@ void ElementsCollectionWidget::setUpConnection()
this, &ElementsCollectionWidget::customContextMenu);
connect(m_macros_tree_view, &QTreeView::doubleClicked,
[this](const QModelIndex &index)
{
this->m_index_at_context_menu = index ;
this->editElement();
});
[this](const QModelIndex &index)
{
this->m_index_at_context_menu = index ;
ElementCollectionItem *eci = elementCollectionItemForIndex(index);
if (eci && eci->collectionPath().endsWith(".qetmak")) {
return; // Do nothing on double click for macros
}
this->editElement();
});
connect(m_macros_tree_view, &QTreeView::entered,
[this] (const QModelIndex &index) {
@@ -310,7 +315,7 @@ void ElementsCollectionWidget::customContextMenu(const QPoint &point)
m_index_at_context_menu);
bool add_open_dir = false;
if (eci->isElement())
if (eci->isElement() && !eci->collectionPath().endsWith(".qetmak"))
m_context_menu->addAction(m_edit_element);
if (eci->type() == FileElementCollectionItem::Type)
@@ -398,6 +403,9 @@ void ElementsCollectionWidget::editElement()
if ( !(eci && eci->isElement()) ) return;
// Prevent the element editor from opening for macros
if (eci->collectionPath().endsWith(".qetmak")) return;
ElementsLocation location(eci->collectionPath());
QETApp *app = QETApp::instance();
@@ -422,11 +430,15 @@ void ElementsCollectionWidget::deleteElement()
if (!eci) return;
ElementsLocation loc(eci->collectionPath());
if (! (loc.isElement()
&& loc.exist()
&& loc.isFileSystem()
&& (loc.collectionPath().startsWith("company://")
|| loc.collectionPath().startsWith("custom://"))) ) return;
bool isDeletableFile = loc.isElement() || eci->collectionPath().endsWith(".qetmak");
if (! (isDeletableFile
&& loc.exist()
&& loc.isFileSystem()
&& (loc.collectionPath().startsWith("company://")
|| loc.collectionPath().startsWith("custom://")
|| loc.collectionPath().startsWith("macros://"))) ) return;
if (QET::QetMessageBox::question(
this,
@@ -468,10 +480,11 @@ void ElementsCollectionWidget::deleteDirectory()
ElementsLocation loc (eci->collectionPath());
if (! (loc.isDirectory()
&& loc.exist()
&& loc.isFileSystem()
&& (loc.collectionPath().startsWith("company://")
|| loc.collectionPath().startsWith("custom://"))) ) return;
&& loc.exist()
&& loc.isFileSystem()
&& (loc.collectionPath().startsWith("company://")
|| loc.collectionPath().startsWith("custom://")
|| loc.collectionPath().startsWith("macros://"))) ) return;
if (QET::QetMessageBox::question(
this,

View File

@@ -297,7 +297,7 @@ void ElementsLocation::setPath(const QString &path)
else
{
QString path_ = path;
if(path_.endsWith(".elmt"))
if(path_.endsWith(".elmt") || path_.endsWith(".qetmak"))
{
m_file_system_path = path_;
if (path_.startsWith(QETApp::commonElementsDirN()))
@@ -366,10 +366,11 @@ void ElementsLocation::setPath(const QString &path)
*/
bool ElementsLocation::addToPath(const QString &string)
{
if (m_collection_path.endsWith(".elmt", Qt::CaseInsensitive))
if (m_collection_path.endsWith(".elmt", Qt::CaseInsensitive) ||
m_collection_path.endsWith(".qetmak", Qt::CaseInsensitive))
{
qDebug() << "ElementsLocation::addToPath :"
" Can't add string to the path of an element";
" Can't add string to the path of an element or template";
return(false);
}
@@ -472,7 +473,7 @@ QString ElementsLocation::toString() const
*/
bool ElementsLocation::isElement() const
{
return m_collection_path.endsWith(".elmt");
return m_collection_path.endsWith(".elmt") || m_collection_path.endsWith(".qetmak");
}
/**

View File

@@ -22,7 +22,12 @@
#include "../qeticons.h"
#include "elementcollectionitem.h"
#include "elementslocation.h"
#include "../qetproject.h"
#include "../diagram.h"
#include "xmlelementcollection.h"
#include "../nameslist.h"
#include <QPainter>
#include <QScopedPointer>
#include <QDrag>
#include <QStandardItemModel>
@@ -95,9 +100,107 @@ void ElementsTreeView::startElementDrag(const ElementsLocation &location)
if (location.isDirectory())
{
mime_data->setData("application/x-qet-category-uri",
location_str.toLatin1());
location_str.toLatin1());
drag->setPixmap(QET::Icons::Folder.pixmap(22, 22));
}
else if (location.fileName().endsWith(".qetmak"))
{
mime_data->setData("application/x-qet-element-uri", location_str.toLatin1());
QPixmap macro_pixmap;
// --- MINI-RENDERER FÜR DAS MAKRO-VORSCHAUBILD ---
QFile file(location.fileSystemPath());
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QDomDocument macro_doc;
if (macro_doc.setContent(&file)) {
QDomElement root = macro_doc.documentElement();
if (root.tagName() == "qet_macro") {
// 1. Unsichtbares Dummy-Projekt erstellen
QScopedPointer<QETProject> dummy_project(new QETProject());
// 2. Bauteile in das Dummy-Projekt laden (wie beim echten Drop)
QDomElement collection_node = root.firstChildElement("collection");
if (!collection_node.isNull()) {
QDomNodeList elements = collection_node.elementsByTagName("element");
for (int i = 0; i < elements.count(); ++i) {
QDomElement elmt_node = elements.at(i).toElement();
QString path = elmt_node.attribute("path");
QDomElement definition = elmt_node.firstChildElement("definition");
if (!path.isEmpty() && !definition.isNull()) {
int last_slash = path.lastIndexOf('/');
QString dir_path = (last_slash != -1) ? path.left(last_slash) : "";
QString file_name = (last_slash != -1) ? path.mid(last_slash + 1) : path;
if (!dir_path.isEmpty()) {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QStringList parts = dir_path.split('/', QString::SkipEmptyParts);
#else
QStringList parts = dir_path.split('/', Qt::SkipEmptyParts);
#endif
QString current_path = "";
for (const QString &part : parts) {
QString parent_path = current_path;
if (!current_path.isEmpty()) current_path += "/";
current_path += part;
if (current_path == "import") continue;
NamesList empty_names;
dummy_project->embeddedElementCollection()->createDir(parent_path, part, empty_names);
}
}
dummy_project->embeddedElementCollection()->addElementDefinition(dir_path, file_name, definition);
}
}
}
Diagram *dummy_diagram = dummy_project->addNewDiagram();
// 4. Makro auf dem unsichtbaren Blatt zeichnen
QDomElement diagram_content_node = root.firstChildElement("diagram_content");
QDomElement diagram_node = diagram_content_node.firstChildElement("diagram");
if (!diagram_node.isNull()) {
QDomNodeList instances = diagram_node.elementsByTagName("element");
for (int i = 0; i < instances.count(); ++i) {
QDomElement inst = instances.at(i).toElement();
QString type = inst.attribute("type");
if (type.startsWith("macro://")) {
inst.setAttribute("type", type.replace("macro://", "embed://"));
}
}
dummy_diagram->fromXml(diagram_node, QPointF(0, 0), false, nullptr);
dummy_diagram->clearSelection();
// 5. "Screenshot" (Pixmap) von den gezeichneten Elementen machen
QRectF scene_rect = dummy_diagram->itemsBoundingRect();
if (!scene_rect.isEmpty()) {
scene_rect.adjust(-5, -5, 5, 5); // Kleiner Rand
macro_pixmap = QPixmap(scene_rect.size().toSize());
macro_pixmap.fill(Qt::transparent); // Transparenter Hintergrund
QPainter painter(&macro_pixmap);
painter.setRenderHint(QPainter::Antialiasing);
dummy_diagram->render(&painter, macro_pixmap.rect(), scene_rect);
}
}
}
}
}
if (macro_pixmap.isNull()) {
macro_pixmap = QET::Icons::Project.pixmap(32, 32);
}
// Bild verkleinern, falls das Makro gigantisch groß ist
if (macro_pixmap.width() > MAX_DND_PIXMAP_WIDTH || macro_pixmap.height() > MAX_DND_PIXMAP_HEIGHT) {
macro_pixmap = macro_pixmap.scaled(MAX_DND_PIXMAP_WIDTH, MAX_DND_PIXMAP_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
drag->setPixmap(macro_pixmap);
// Bild zentriert an die Maus hängen
drag->setHotSpot(QPoint(macro_pixmap.width() / 2, macro_pixmap.height() / 2));
}
else if (location.isElement())
{
mime_data->setData("application/x-qet-element-uri",

View File

@@ -93,7 +93,7 @@ QString FileElementCollectionItem::dirPath() const
*/
bool FileElementCollectionItem::isDir() const
{
if (m_path.endsWith(".elmt"))
if (m_path.endsWith(".elmt") || m_path.endsWith(".qetmak"))
return false;
else
return true;
@@ -120,7 +120,6 @@ QString FileElementCollectionItem::localName()
else if (isDir()) {
if (isCollectionRoot()) {
// --- NEU: Makro-Pfad laden ---
QString macrosPath = QETApp::userMacrosDir();
if (macrosPath.endsWith("/")) macrosPath.remove(macrosPath.length() - 1, 1);
@@ -130,7 +129,7 @@ QString FileElementCollectionItem::localName()
setText(QObject::tr("Collection Company"));
else if (m_path == QETApp::customElementsDirN())
setText(QObject::tr("Collection utilisateur"));
else if (m_path == macrosPath) // <-- NEU: Name des Ordners zuweisen
else if (m_path == macrosPath)
setText(QObject::tr("Makros"));
else
setText(QObject::tr("Collection inconnue"));
@@ -153,7 +152,11 @@ QString FileElementCollectionItem::localName()
}
else if (isElement()) {
ElementsLocation loc(collectionPath());
setText(loc.name());
QString display_name = loc.name();
if (display_name.endsWith(".qetmak")) {
display_name.remove(".qetmak");
}
setText(display_name);
}
return text();
@@ -175,7 +178,12 @@ QString FileElementCollectionItem::localName(const ElementsLocation &location)
localName();
}
else if (isElement()) {
setText(location.name());
QString display_name = location.name();
// Schneide die Endung .qetmak für die Anzeige ab
if (display_name.endsWith(".qetmak")) {
display_name.remove(".qetmak");
}
setText(display_name);
}
return text();
@@ -237,7 +245,7 @@ bool FileElementCollectionItem::isCollectionRoot() const
if (m_path == QETApp::commonElementsDirN()
|| m_path == QETApp::companyElementsDirN()
|| m_path == QETApp::customElementsDirN()
|| m_path == macrosPath) // <-- NEU: Makros sind jetzt ein echtes Root-Verzeichnis
|| m_path == macrosPath)
return true;
else
return false;
@@ -367,13 +375,13 @@ void FileElementCollectionItem::setUpIcon()
@param hide_element
*/
void FileElementCollectionItem::setPathName(const QString& path_name,
bool set_data,
bool hide_element)
bool set_data,
bool hide_element)
{
m_path = path_name;
//This isn't an element, we create the childs
if (!path_name.endsWith(".elmt"))
//This isn't an element or template, we create the childs
if (!path_name.endsWith(".elmt") && !path_name.endsWith(".qetmak"))
populate(set_data, hide_element);
}
@@ -402,9 +410,9 @@ void FileElementCollectionItem::populate(bool set_data, bool hide_element)
return;
//Get all elmt file in this directory
dir.setNameFilters(QStringList() << "*.elmt");
dir.setNameFilters(QStringList() << "*.elmt" << "*.qetmak");
for (auto& str :
dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name))
dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name))
{
FileElementCollectionItem *feci = new FileElementCollectionItem();
appendRow(feci);

View File

@@ -0,0 +1,260 @@
/*
* Copyright 2006-2026 The QElectroTech Team
* This file is part of QElectroTech.
*/
#include "diagrameventaddmacro.h"
#include "../diagram.h"
#include "../qetapp.h"
#include "../qetdiagrameditor.h"
#include "../qetproject.h"
#include "../ElementsCollection/xmlelementcollection.h"
#include "../nameslist.h"
#include "../diagramcommands.h"
#include "../diagramcontent.h"
#include <QFile>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QStatusBar>
#include <QPainter>
DiagramEventAddMacro::DiagramEventAddMacro(const ElementsLocation &location, Diagram *diagram, QPointF pos) :
DiagramEventInterface(diagram),
m_location(location),
m_preview_item(nullptr)
{
if (loadMacro()) {
init();
QScopedPointer<QETProject> dummy_project(new QETProject());
QDomElement root = m_macro_doc.documentElement();
QDomElement collection_node = root.firstChildElement("collection");
if (!collection_node.isNull()) {
QDomNodeList elements = collection_node.elementsByTagName("element");
for (int i = 0; i < elements.count(); ++i) {
QDomElement elmt_node = elements.at(i).toElement();
QString path = elmt_node.attribute("path");
QDomElement definition = elmt_node.firstChildElement("definition");
if (!path.isEmpty() && !definition.isNull()) {
int last_slash = path.lastIndexOf('/');
QString dir_path = (last_slash != -1) ? path.left(last_slash) : "";
QString file_name = (last_slash != -1) ? path.mid(last_slash + 1) : path;
if (!dir_path.isEmpty()) {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QStringList parts = dir_path.split('/', QString::SkipEmptyParts);
#else
QStringList parts = dir_path.split('/', Qt::SkipEmptyParts);
#endif
QString current_path = "";
for (const QString &part : parts) {
QString parent_path = current_path;
if (!current_path.isEmpty()) current_path += "/";
current_path += part;
if (current_path == "import") continue;
NamesList empty_names;
dummy_project->embeddedElementCollection()->createDir(parent_path, part, empty_names);
}
}
dummy_project->embeddedElementCollection()->addElementDefinition(dir_path, file_name, definition);
}
}
}
Diagram *dummy_diagram = dummy_project->addNewDiagram();
QDomElement diagram_node = root.firstChildElement("diagram_content").firstChildElement("diagram");
if (!diagram_node.isNull()) {
dummy_diagram->fromXml(diagram_node, QPointF(0, 0), false, nullptr);
QRectF scene_rect = dummy_diagram->itemsBoundingRect();
if (!scene_rect.isEmpty()) {
QPixmap pixmap(scene_rect.toAlignedRect().size());
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing);
dummy_diagram->render(&painter, QRectF(QPointF(0,0), scene_rect.size()), scene_rect);
m_preview_item = new QGraphicsPixmapItem(pixmap);
m_preview_item->setOffset(scene_rect.topLeft());
}
}
if (m_preview_item) {
m_preview_item->setPos(Diagram::snapToGrid(pos));
m_preview_item->setOpacity(0.6);
m_diagram->addItem(m_preview_item);
m_running = true;
}
if (!diagram->views().isEmpty()) {
const auto qde = QETApp::diagramEditorAncestorOf(diagram->views().at(0));
if (qde) {
m_status_bar = qde->statusBar();
}
} else {
m_status_bar.clear();
}
}
}
DiagramEventAddMacro::~DiagramEventAddMacro()
{
if (m_preview_item) {
m_diagram->removeItem(m_preview_item);
delete m_preview_item;
}
if (m_status_bar) {
m_status_bar->clearMessage();
}
for (auto view : m_diagram->views())
view->setContextMenuPolicy(Qt::DefaultContextMenu);
}
void DiagramEventAddMacro::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (m_preview_item) {
const auto pos_{Diagram::snapToGrid(event->scenePos())};
m_preview_item->setPos(pos_);
if (m_status_bar) {
m_status_bar->showMessage(QString("x %1 : y %2 (Makro-Anker)").arg(QString::number(pos_.x()), QString::number(pos_.y())));
}
}
event->setAccepted(true);
}
void DiagramEventAddMacro::mousePressEvent(QGraphicsSceneMouseEvent *event) {
event->setAccepted(true);
}
void DiagramEventAddMacro::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (m_preview_item) {
if (event->button() == Qt::RightButton) {
m_diagram->removeItem(m_preview_item);
delete m_preview_item;
m_preview_item = nullptr;
m_running = false;
emit finish();
}
else if (event->button() == Qt::LeftButton) {
addMacro(Diagram::snapToGrid(event->scenePos()));
}
}
event->setAccepted(true);
}
void DiagramEventAddMacro::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (m_preview_item && (event->button() == Qt::LeftButton)) {
m_diagram->removeItem(m_preview_item);
delete m_preview_item;
m_preview_item = nullptr;
m_running = false;
emit finish();
}
event->setAccepted(true);
}
void DiagramEventAddMacro::keyPressEvent(QKeyEvent *event)
{
DiagramEventInterface::keyPressEvent(event);
}
void DiagramEventAddMacro::init()
{
foreach(QGraphicsView *view, m_diagram->views())
view->setContextMenuPolicy(Qt::NoContextMenu);
}
bool DiagramEventAddMacro::loadMacro()
{
QFile file(m_location.fileSystemPath());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Error: Macro file could not be read:" << m_location.fileSystemPath();
return false;
}
if (!m_macro_doc.setContent(&file)) {
qDebug() << "Error: Invalid XML in macro.";
return false;
}
QDomElement root = m_macro_doc.documentElement();
if (root.tagName() != "qet_macro") return false;
QDomElement collection_node = root.firstChildElement("collection");
if (!collection_node.isNull()) {
QDomNodeList elements = collection_node.elementsByTagName("element");
for (int i = 0; i < elements.count(); ++i) {
QDomElement elmt_node = elements.at(i).toElement();
QString path = elmt_node.attribute("path");
QDomElement definition = elmt_node.firstChildElement("definition");
if (!path.isEmpty() && !definition.isNull()) {
int last_slash = path.lastIndexOf('/');
QString dir_path = (last_slash != -1) ? path.left(last_slash) : "";
QString file_name = (last_slash != -1) ? path.mid(last_slash + 1) : path;
if (!dir_path.isEmpty()) {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QStringList parts = dir_path.split('/', QString::SkipEmptyParts);
#else
QStringList parts = dir_path.split('/', Qt::SkipEmptyParts);
#endif
QString current_path = "";
for (const QString &part : parts) {
QString parent_path = current_path;
if (!current_path.isEmpty()) current_path += "/";
current_path += part;
if (current_path == "import") continue;
NamesList empty_names;
m_diagram->project()->embeddedElementCollection()->createDir(parent_path, part, empty_names);
}
}
m_diagram->project()->embeddedElementCollection()->addElementDefinition(dir_path, file_name, definition);
}
}
}
QDomElement diagram_node = root.firstChildElement("diagram_content").firstChildElement("diagram");
if (!diagram_node.isNull()) {
QDomNodeList instances = diagram_node.elementsByTagName("element");
for (int i = 0; i < instances.count(); ++i) {
QDomElement inst = instances.at(i).toElement();
QString type = inst.attribute("type");
if (type.startsWith("macro://")) {
inst.setAttribute("type", type.replace("macro://", "embed://"));
}
}
}
return true;
}
void DiagramEventAddMacro::addMacro(QPointF final_pos)
{
QDomElement root = m_macro_doc.documentElement();
QDomElement diagram_node = root.firstChildElement("diagram_content").firstChildElement("diagram");
if (!diagram_node.isNull()) {
QDomElement cloned_node = diagram_node.cloneNode(true).toElement();
QPointF target_pos = final_pos;
if (m_preview_item) {
target_pos += m_preview_item->offset();
}
DiagramContent pasted_content;
m_diagram->fromXml(cloned_node, target_pos, false, &pasted_content);
m_diagram->refreshContents();
m_diagram->undoStack().push(new PasteDiagramCommand(m_diagram, pasted_content));
}
}

View File

@@ -0,0 +1,45 @@
/*
C opyright 2006-2026 The QEle*ctroTech Team
This file is part of QElectroTech.
*/
#ifndef DIAGRAMEVENTADDMACRO_H
#define DIAGRAMEVENTADDMACRO_H
#include "../ElementsCollection/elementslocation.h"
#include "diagrameventinterface.h"
#include <QDomDocument>
#include <QGraphicsPixmapItem>
class QStatusBar;
/**
* @brief The DiagramEventAddMacro class
*/
class DiagramEventAddMacro : public DiagramEventInterface
{
Q_OBJECT
public:
DiagramEventAddMacro(const ElementsLocation &location, Diagram *diagram, QPointF pos = QPointF(0,0));
~DiagramEventAddMacro() override;
void mouseMoveEvent (QGraphicsSceneMouseEvent *event) override;
void mousePressEvent (QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent (QGraphicsSceneMouseEvent *event) override;
void mouseDoubleClickEvent (QGraphicsSceneMouseEvent *event) override;
void keyPressEvent (QKeyEvent *event) override;
void init() override;
private:
bool loadMacro();
void addMacro(QPointF final_pos);
private:
ElementsLocation m_location;
QDomDocument m_macro_doc;
QGraphicsPixmapItem *m_preview_item;
QPointer<QStatusBar> m_status_bar;
};
#endif // DIAGRAMEVENTADDMACRO_H

View File

@@ -20,6 +20,7 @@
#include "QPropertyUndoCommand/qpropertyundocommand.h"
#include "diagramcommands.h"
#include "diagramevent/diagrameventaddelement.h"
#include "diagramevent/diagrameventaddmacro.h"
#include "dvevent/dveventinterface.h"
#include "projectview.h"
#include "qetdiagrameditor.h"
@@ -34,7 +35,9 @@
#include "utils/conductorcreator.h"
#include "undocommand/addgraphicsobjectcommand.h"
#include "diagram.h"
#include "ElementsCollection/xmlelementcollection.h"
#include "nameslist.h"
#include "elementdialog.h"
#include <QDropEvent>
/**
@@ -84,6 +87,10 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
d.exec();
});
// Setup the action to create a template
m_create_template = new QAction(tr("Créer un template", "context menu action"), this);
connect(m_create_template, SIGNAL(triggered()), this, SLOT(createTemplateFromSelection()));
//setup three separators, to be use in context menu
for(int i=0 ; i<3 ; ++i)
{
@@ -188,13 +195,13 @@ void DiagramView::dropEvent(QDropEvent *e) {
}
/**
@brief DiagramView::handleElementDrop
Handle the drop of an element.
@param event the QDropEvent describing the current drag'n drop
*/
* @brief DiagramView::handleElementDrop
* Handle the drop of an element.
* @param event the QDropEvent describing the current drag'n drop
*/
void DiagramView::handleElementDrop(QDropEvent *event)
{
//Build an element from the text of the mime data
//Build an element from the text of the mime data
ElementsLocation location(event->mimeData()->text());
if ( !(location.isElement() && location.exist()) )
@@ -203,20 +210,20 @@ void DiagramView::handleElementDrop(QDropEvent *event)
return;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
diagram()->setEventInterface(
new DiagramEventAddElement(
location, diagram(), mapToScene(event->pos())));
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
diagram()->setEventInterface(
new DiagramEventAddElement(
location, diagram(), event->position()));
#endif
QPointF drop_pos;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
drop_pos = mapToScene(event->pos());
#else
drop_pos = event->position();
#endif
//Set focus to the view to get event
if (location.path().endsWith(".qetmak")) {
diagram()->setEventInterface(new DiagramEventAddMacro(location, diagram(), drop_pos));
} else {
diagram()->setEventInterface(new DiagramEventAddElement(location, diagram(), drop_pos));
}
//Set focus to the view to get event
this->setFocus();
}
@@ -1202,6 +1209,7 @@ QList<QAction *> DiagramView::contextMenuActions() const
list << qde->m_copy;
list << m_multi_paste;
list << m_separators.at(0);
list << m_create_template; // Add the create template action
list << qde->m_conductor_reset;
list << m_separators.at(1);
list << qde->m_selection_actions_group.actions();
@@ -1265,6 +1273,101 @@ void DiagramView::contextMenuEvent(QContextMenuEvent *e)
}
}
/**
* @brief DiagramView::createTemplateFromSelection
* Triggered from the context menu to create a new template (macro) from the current selection.
*/
void DiagramView::createTemplateFromSelection()
{
QList<QGraphicsItem *> selected_elements = m_diagram->selectedItems();
if (selected_elements.isEmpty()) {
return;
}
qDebug() << "Ready to create a template from" << selected_elements.size() << "elements!";
// Open the dialog to let the user select where to save the .qetmak file
ElementsLocation template_location = ElementDialog::getSaveTemplateLocation(this);
// Check if the user clicked 'Cancel' or closed the window
if (template_location.isNull()) {
qDebug() << "User canceled template creation.";
return;
}
qDebug() << "Will save template to:" << template_location.path();
QDomDocument content_xml = m_diagram->toXml(false, true);
QDomDocument macro_doc;
QDomElement root = macro_doc.createElement("qet_macro");
macro_doc.appendChild(root);
QDomElement collection_node = macro_doc.createElement("collection");
root.appendChild(collection_node);
QSet<QString> processed_types;
QDomNodeList element_nodes = content_xml.elementsByTagName("element");
for (int i = 0; i < element_nodes.count(); ++i) {
QDomElement elmt_node = element_nodes.at(i).toElement();
QString old_type = elmt_node.attribute("type");
if (old_type.isEmpty()) continue;
ElementsLocation loc(old_type, m_diagram->project());
QString clean_path = loc.collectionPath(false);
QString new_type = "macro://" + clean_path;
elmt_node.setAttribute("type", new_type);
if (!processed_types.contains(clean_path)) {
processed_types.insert(clean_path);
QDomElement definition_node = loc.xml();
if (!definition_node.isNull()) {
QDomElement collection_elmt = macro_doc.createElement("element");
collection_elmt.setAttribute("path", clean_path);
QDomNode imported_def = macro_doc.importNode(definition_node, true);
collection_elmt.appendChild(imported_def);
collection_node.appendChild(collection_elmt);
} else {
qDebug() << "Warnung: Konnte XML-Definition für" << old_type << "nicht laden.";
}
}
}
QDomElement content_container = macro_doc.createElement("diagram_content");
root.appendChild(content_container);
QDomNode imported_node = macro_doc.importNode(content_xml.documentElement(), true);
content_container.appendChild(imported_node);
QString full_path = template_location.fileSystemPath();
QDir().mkpath(QFileInfo(full_path).absolutePath());
QFile file(full_path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out.setCodec("UTF-8");
out << macro_doc.toString(4);
file.close();
qDebug() << "Template successfully saved to:" << full_path;
QMessageBox::information(this, tr("Modèle enregistré"),
tr("Le modèle a été enregistré avec succès sous :\n%1").arg(full_path));
} else {
qDebug() << "Error: Could not open file for writing:" << full_path;
QMessageBox::critical(this, tr("Erreur"), tr("Le fichier n'a pas pu être écrit."));
}
}
/**
@return l'editeur de schemas parent ou 0
*/

View File

@@ -18,7 +18,7 @@
#ifndef DIAGRAMVIEW_H
#define DIAGRAMVIEW_H
#include "ElementsCollection/elementslocation.h"
#include "../ElementsCollection/elementslocation.h"
#include "titleblock/templatelocation.h"
#include <QClipboard>
@@ -53,6 +53,7 @@ class DiagramView : public QGraphicsView
DVEventInterface *m_event_interface = nullptr;
QAction *m_paste_here = nullptr;
QAction *m_multi_paste = nullptr;
QAction *m_create_template = nullptr;
QPoint m_paste_here_pos;
QPointF m_drag_last_pos;
bool m_fresh_focus_in,
@@ -133,5 +134,6 @@ class DiagramView : public QGraphicsView
private slots:
void adjustGridToZoom();
void applyReadOnly();
void createTemplateFromSelection();
};
#endif

View File

@@ -74,6 +74,10 @@ void ElementDialog::setUpWidget()
title_ = tr("Enregistrer une catégorie", "dialog title");
label_ = tr("Choisissez une catégorie.", "dialog content");
break;
case SaveTemplate:
title_ = tr("Enregistrer un template", "dialog title");
label_ = tr("Choisissez l'emplacement dans lequel vous souhaitez enregistrer votre template.", "dialog content");
break;
default:
title_ = tr("Titre");
label_ = tr("Label");
@@ -92,10 +96,14 @@ void ElementDialog::setUpWidget()
foreach(QETProject *prj, QETApp::registeredProjects())
prjs.append(prj);
if (m_mode == OpenElement)
if (m_mode == OpenElement) {
m_model->loadCollections(true, true, true, prjs);
else
} else if (m_mode == SaveTemplate) {
// Load only the templates/macros collection for the template save dialog
m_model->loadMacrosCollection();
} else {
m_model->loadCollections(false, true, true, prjs);
}
m_tree_view->setModel(m_model);
m_tree_view->setHeaderHidden(true);
@@ -103,14 +111,21 @@ void ElementDialog::setUpWidget()
m_buttons_box = new QDialogButtonBox(this);
if (m_mode == SaveCategory || m_mode == SaveElement)
if (m_mode == SaveCategory || m_mode == SaveElement || m_mode == SaveTemplate)
{
m_buttons_box->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
m_buttons_box->button(QDialogButtonBox::Save)->setDisabled(true);
m_text_field = new QFileNameEdit();
m_text_field->setDisabled(true);
m_text_field->setPlaceholderText(m_mode == SaveCategory? tr("Nom du nouveau dossier") : tr("Nom du nouvel élément"));
if (m_mode == SaveCategory) {
m_text_field->setPlaceholderText(tr("Nom du nouveau dossier"));
} else if (m_mode == SaveTemplate) {
m_text_field->setPlaceholderText(tr("Nom du nouveau template"));
} else {
m_text_field->setPlaceholderText(tr("Nom du nouvel élément"));
}
layout->addWidget(m_text_field);
}
@@ -156,14 +171,17 @@ void ElementDialog::checkCurrentLocation()
if (m_mode == OpenElement) {
m_buttons_box->button(QDialogButtonBox::Open)->setEnabled(m_location.isElement() && m_location.exist());
}
else if (m_mode == SaveElement)
else if (m_mode == SaveElement || m_mode == SaveTemplate)
{
m_buttons_box->button(QDialogButtonBox::Save)->setDisabled(true);
//Location doesn't exist
//Location doesn't exist
if (!m_location.exist()) { return; }
if (m_location.isElement())
// Accept .elmt for elements, and .qetmak for templates
bool is_valid_file = m_location.isElement() || (m_mode == SaveTemplate && m_location.path().endsWith(".qetmak"));
if (is_valid_file)
{
m_text_field->setDisabled(true);
m_buttons_box->button(QDialogButtonBox::Save)->setEnabled(true);
@@ -174,10 +192,10 @@ void ElementDialog::checkCurrentLocation()
if (m_text_field->text().isEmpty()) { return; }
//Only enable save button if the location at path :
//m_location.collectionPath + m_text_filed.text doesn't exist.
//Only enable save button if the location at path doesn't exist.
QString new_path = m_text_field->text();
if (!new_path.endsWith(".elmt")) new_path += ".elmt";
QString extension = (m_mode == SaveTemplate) ? ".qetmak" : ".elmt";
if (!new_path.endsWith(extension)) new_path += extension;
ElementsLocation loc = m_location;
loc.addToPath(new_path);
@@ -209,15 +227,19 @@ void ElementDialog::checkAccept()
return;
}
}
else if (m_mode == SaveElement)
else if (m_mode == SaveElement || m_mode == SaveTemplate)
{
if (loc.isElement())
bool is_valid_file = loc.isElement() || (m_mode == SaveTemplate && loc.path().endsWith(".qetmak"));
if (is_valid_file)
{
if (loc.exist())
{
QString msgTitle = (m_mode == SaveTemplate) ? tr("Écraser le template ?", "message box title") : tr("Écraser l'élément ?", "message box title");
QString msgContent = (m_mode == SaveTemplate) ? tr("Le template existe déjà. Voulez-vous l'écraser ?", "message box content") : tr("L'élément existe déjà. Voulez-vous l'écraser ?", "message box content");
QMessageBox::StandardButton answer = QET::QetMessageBox::question(this,
tr("Écraser l'élément ?", "message box title"),
tr("L'élément existe déjà. Voulez-vous l'écraser ?", "message box content"),
msgTitle,
msgContent,
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (answer == QMessageBox::Yes) {accept();}
@@ -248,19 +270,21 @@ ElementsLocation ElementDialog::location() const
else {return ElementsLocation(); }
}
else if (m_mode == SaveElement)
else if (m_mode == SaveElement || m_mode == SaveTemplate)
{
//Current selected location is element, we return this location
if (m_location.isElement()) { return m_location; }
//Current selected location is element or template, we return this location
bool is_valid_file = m_location.isElement() || (m_mode == SaveTemplate && m_location.path().endsWith(".qetmak"));
if (is_valid_file) { return m_location; }
//Current selected location is directory, we return a location at path :
//m_location->collectionPath + m_text_field->text
//Current selected location is directory, we return a location at path :
//m_location->collectionPath + m_text_field->text
else if (m_location.isDirectory())
{
QString new_path = m_text_field->text();
if (new_path.isEmpty()) { return ElementsLocation(); }
if (!new_path.endsWith(".elmt")) { new_path += ".elmt"; }
QString extension = (m_mode == SaveTemplate) ? ".qetmak" : ".elmt";
if (!new_path.endsWith(extension)) { new_path += extension; }
ElementsLocation loc = m_location;
loc.addToPath(new_path);
@@ -291,6 +315,16 @@ ElementsLocation ElementDialog::getSaveElementLocation(QWidget *parentWidget) {
return(ElementDialog::execConfiguredDialog(ElementDialog::SaveElement, parentWidget));
}
/**
* @brief ElementDialog::getSaveTemplateLocation
* Display a dialog that allow to user to select a location for saving a template
* @param parentWidget
* @return The location where the template must be save
*/
ElementsLocation ElementDialog::getSaveTemplateLocation(QWidget *parentWidget) {
return(ElementDialog::execConfiguredDialog(ElementDialog::SaveTemplate, parentWidget));
}
/**
@brief ElementDialog::execConfiguredDialog
launch a dialog with the chosen mode

View File

@@ -42,7 +42,8 @@ class ElementDialog : public QDialog
OpenElement = 0, ///< The dialog should open an element
SaveElement = 1, ///< The dialog should select an element for saving
OpenCategory = 2, ///< The dialog should open a category
SaveCategory = 3 ///< The dialog should select a category for saving
SaveCategory = 3, ///< The dialog should select a category for saving
SaveTemplate = 4 ///< The dialog should select a template for saving
};
// constructors, destructor
@@ -74,6 +75,7 @@ class ElementDialog : public QDialog
public:
static ElementsLocation getOpenElementLocation(QWidget *parent = nullptr);
static ElementsLocation getSaveElementLocation(QWidget *parent = nullptr);
static ElementsLocation getSaveTemplateLocation(QWidget *parent = nullptr);
private:
static ElementsLocation execConfiguredDialog(int, QWidget *parent = nullptr);

View File

@@ -24,6 +24,7 @@
#include "qeticons.h"
#include "qetproject.h"
#include "titleblock/templatedeleter.h"
#include <QFileInfo>
/*
When the ENABLE_PANEL_WIDGET_DND_CHECKS flag is set, the panel
@@ -152,6 +153,11 @@ void ElementsPanelWidget::openDirectoryForSelectedItem()
if (QTreeWidgetItem *qtwi = elements_panel -> currentItem()) {
QString dir_path = elements_panel -> dirPathForItem(qtwi);
if (!dir_path.isEmpty()) {
QFileInfo fileInfo(dir_path);
// Wenn der Pfad auf eine Datei (z.B. Makro) zeigt, isoliere den Ordnerpfad
if (fileInfo.isFile()) {
dir_path = fileInfo.absolutePath();
}
QDesktopServices::openUrl(QUrl::fromLocalFile(dir_path));
}
}

View File

@@ -540,7 +540,7 @@ void GeneralConfigurationPage::on_m_user_macros_path_cb_currentIndexChanged(int
{
if (index == 1)
{
QString path = QFileDialog::getExistingDirectory(this, tr("Chemin des macros utilisateur (DEV)"), QETApp::documentDir());
QString path = QFileDialog::getExistingDirectory(this, tr("Chemin des macros utilisateur"), QETApp::documentDir());
if (!path.isEmpty()) {
ui->m_user_macros_path_cb->setItemData(1, path, Qt::DisplayRole);
}

View File

@@ -17,7 +17,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="tab_3">
<attribute name="title">
@@ -352,7 +352,7 @@
<item row="7" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Macros utilisateur (DEV)</string>
<string>Répertoire des Macros utilisateur</string>
</property>
</widget>
</item>