Merge pull request #1170 from ispyisail/revive/641-cabinet-thumbnails

Add cabinet thumbnails generated from a device's manufacturer and reference
This commit is contained in:
ispyisail
2026-09-30 11:37:23 +13:00
committed by GitHub
2 changed files with 184 additions and 1 deletions
+182 -1
View File
@@ -45,13 +45,20 @@
#include "ElementsCollection/xmlelementcollection.h"
#include "NameList/nameslist.h"
#include "elementdialog.h"
#include "qetapp.h"
#include "qetgraphicsitem/element.h"
#include "qetinformation.h"
#include "qetversion.h"
#include <QApplication>
#include <QDropEvent>
#include <QPainter>
#include <QPointer>
#include <QSet>
#include <QUuid>
#include <algorithm>
static bool hasCabinetInfo(const DiagramContext &infos);
/**
Constructeur
@param diagram Schema a afficher ; si diagram vaut 0, un nouveau Diagram est utilise
@@ -103,6 +110,10 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
m_create_template = new QAction(tr("Créer un template", "context menu action"), this);
connect(m_create_template, &QAction::triggered, this, &DiagramView::createTemplateFromSelection);
//Setup the action to generate cabinet placement thumbnails (discussion #602)
m_generate_cabinet_thumbnail = new QAction(tr("Générer une vignette d'armoire", "context menu action"), this);
connect(m_generate_cabinet_thumbnail, &QAction::triggered, this, &DiagramView::generateCabinetThumbnails);
//Filled each time the context menu opens, see updateFolioReportMenu()
m_folio_report_menu = new QMenu(tr("Renvoi de folio"), this);
@@ -1765,7 +1776,8 @@ QList<QAction *> DiagramView::contextMenuActions() const
QList<QAction *> list;
if (QETDiagramEditor *qde = diagramEditor())
{
if (m_diagram->selectedItems().isEmpty())
const QList<QGraphicsItem *> selected_items = m_diagram->selectedItems();
if (selected_items.isEmpty())
{
//Drawing comes first. The row and column actions change
//the folio's layout and are rarely wanted, so they sit one
@@ -1786,6 +1798,15 @@ QList<QAction *> DiagramView::contextMenuActions() const
list << m_multi_paste;
list << m_separators.at(0);
list << m_create_template; // Add the create template action
//Disabled, and so left out below, unless a selected element
//has both a manufacturer and a manufacturer reference.
m_generate_cabinet_thumbnail->setEnabled(std::any_of(
selected_items.cbegin(), selected_items.cend(),
[](QGraphicsItem *qgi) {
const Element *element = qgraphicsitem_cast<Element *>(qgi);
return element && hasCabinetInfo(element->elementInformations());
}));
list << m_generate_cabinet_thumbnail;
list << qde->m_conductor_reset;
list << m_separators.at(1);
list << qde->m_selection_actions_group.actions();
@@ -2073,6 +2094,166 @@ void DiagramView::createTemplateFromSelection()
}
}
/**
@brief hasCabinetInfo
@return true if @a infos has both a manufacturer and a manufacturer
reference, the two values a cabinet thumbnail shows.
*/
static bool hasCabinetInfo(const DiagramContext &infos)
{
return !infos[QETInformation::ELMT_MANUFACTURER].toString().trimmed().isEmpty()
&& !infos[QETInformation::ELMT_MANUFACTURER_REF].toString().trimmed().isEmpty();
}
/**
@brief cabinetThumbnailDefinition
@return the definition of a thumbnail element, a 120x30 frame showing
the manufacturer and the manufacturer reference in a dynamic text.
The two values are also stored in the definition's element
information, so the thumbnail shows them as soon as it is placed.
*/
static QDomElement cabinetThumbnailDefinition(QDomDocument &doc,
const QString &display_name,
const QString &manufacturer,
const QString &reference)
{
QDomElement definition = doc.createElement(QStringLiteral("definition"));
definition.setAttribute(QStringLiteral("version"), QetVersion::currentVersion().toString());
definition.setAttribute(QStringLiteral("type"), QStringLiteral("element"));
definition.setAttribute(QStringLiteral("link_type"), QStringLiteral("thumbnail"));
definition.setAttribute(QStringLiteral("width"), 120);
definition.setAttribute(QStringLiteral("height"), 30);
definition.setAttribute(QStringLiteral("hotspot_x"), 60);
definition.setAttribute(QStringLiteral("hotspot_y"), 15);
doc.appendChild(definition);
QDomElement uuid = doc.createElement(QStringLiteral("uuid"));
uuid.setAttribute(QStringLiteral("uuid"), QUuid::createUuid().toString());
definition.appendChild(uuid);
NamesList names;
names.addName(QStringLiteral("en"), display_name);
definition.appendChild(names.toXml(doc));
QDomElement informations = doc.createElement(QStringLiteral("elementInformations"));
DiagramContext infos;
infos.addValue(QETInformation::ELMT_MANUFACTURER, manufacturer);
infos.addValue(QETInformation::ELMT_MANUFACTURER_REF, reference);
infos.toXml(informations, QStringLiteral("elementInformation"));
definition.appendChild(informations);
QDomElement description = doc.createElement(QStringLiteral("description"));
QDomElement rect = doc.createElement(QStringLiteral("rect"));
rect.setAttribute(QStringLiteral("x"), -60);
rect.setAttribute(QStringLiteral("y"), -15);
rect.setAttribute(QStringLiteral("width"), 120);
rect.setAttribute(QStringLiteral("height"), 30);
rect.setAttribute(QStringLiteral("rx"), 0);
rect.setAttribute(QStringLiteral("ry"), 0);
rect.setAttribute(QStringLiteral("style"),
QStringLiteral("line-style:normal;line-weight:normal;filling:none;color:black"));
rect.setAttribute(QStringLiteral("antialias"), QStringLiteral("false"));
description.appendChild(rect);
QDomElement text = doc.createElement(QStringLiteral("dynamic_text"));
text.setAttribute(QStringLiteral("x"), -57);
text.setAttribute(QStringLiteral("y"), -14);
text.setAttribute(QStringLiteral("z"), 1);
text.setAttribute(QStringLiteral("text_width"), 114);
text.setAttribute(QStringLiteral("Halignment"), QStringLiteral("AlignLeft"));
text.setAttribute(QStringLiteral("Valignment"), QStringLiteral("AlignTop"));
text.setAttribute(QStringLiteral("frame"), QStringLiteral("false"));
text.setAttribute(QStringLiteral("rotation"), 0);
text.setAttribute(QStringLiteral("keep_visual_rotation"), QStringLiteral("false"));
text.setAttribute(QStringLiteral("text_from"), QStringLiteral("CompositeText"));
text.setAttribute(QStringLiteral("uuid"), QUuid::createUuid().toString());
text.setAttribute(QStringLiteral("font"), QETApp::dynamicTextsItemFont(7).toString());
QDomElement text_cache = doc.createElement(QStringLiteral("text"));
text_cache.appendChild(doc.createTextNode(manufacturer + QLatin1Char('\n') + reference));
text.appendChild(text_cache);
QDomElement composite_text = doc.createElement(QStringLiteral("composite_text"));
composite_text.appendChild(doc.createTextNode(QStringLiteral("%{manufacturer}\n%{manufacturer_reference}")));
text.appendChild(composite_text);
description.appendChild(text);
definition.appendChild(description);
return definition;
}
/**
@brief DiagramView::generateCabinetThumbnails
Context menu action (discussion #602). For every selected element with
both a manufacturer and a manufacturer reference, add a thumbnail
element showing them to the folder "Cabinet thumbnails" of this
project's embedded collection, from where it can be placed on a
cabinet layout folio like any other element. A device that already
has a thumbnail there is skipped, so placing it several times does
not pile up copies.
*/
void DiagramView::generateCabinetThumbnails()
{
QETProject *project = m_diagram->project();
if (!project || project->isReadOnly()) {
return;
}
XmlElementCollection *collection = project->embeddedElementCollection();
if (!collection) {
return;
}
const QString dir_name = QStringLiteral("Cabinet thumbnails");
const QString dir_path = QStringLiteral("import/") + dir_name;
if (!collection->exist(dir_path))
{
NamesList dir_names;
dir_names.addName(QStringLiteral("en"), dir_name);
//Stored per language, like an element's names: not tr(), which
//would store the interface's language under "fr"
dir_names.addName(QStringLiteral("fr"), QStringLiteral("Vignettes d'armoire"));
if (!collection->createDir(QStringLiteral("import"), dir_name, dir_names)) {
return;
}
}
QStringList existing = collection->elementsNames(collection->directory(dir_path));
int created = 0;
const QList<QGraphicsItem *> selected_items = m_diagram->selectedItems();
for (QGraphicsItem *qgi : selected_items)
{
const Element *element = qgraphicsitem_cast<Element *>(qgi);
if (!element || !hasCabinetInfo(element->elementInformations())) {
continue;
}
const DiagramContext infos = element->elementInformations();
const QString manufacturer = infos[QETInformation::ELMT_MANUFACTURER].toString().trimmed();
const QString reference = infos[QETInformation::ELMT_MANUFACTURER_REF].toString().trimmed();
//The display name keeps the values as typed; the file name
//cannot hold characters such as '/', which would be read as
//a folder.
const QString display_name = manufacturer + QLatin1Char(' ') + reference;
const QString file_name = QET::stringToFileName(display_name) + QStringLiteral(".elmt");
if (existing.contains(file_name)) {
continue;
}
QDomDocument doc;
const QDomElement definition = cabinetThumbnailDefinition(doc, display_name, manufacturer, reference);
if (collection->addElementDefinition(dir_path, file_name, definition))
{
existing << file_name;
++created;
}
}
if (created) {
project->setModified(true);
}
}
/**
@return l'editeur de schemas parent ou 0
*/
+2
View File
@@ -58,6 +58,7 @@ class DiagramView : public PaletteGraphicsView
QAction *m_paste_here = nullptr;
QAction *m_multi_paste = nullptr;
QAction *m_create_template = nullptr;
QAction *m_generate_cabinet_thumbnail = nullptr;
QMenu *m_folio_report_menu = nullptr;
QPoint m_paste_here_pos;
QPoint m_press_pos;
@@ -197,5 +198,6 @@ class DiagramView : public PaletteGraphicsView
void adjustGridToZoom();
void applyReadOnly();
void createTemplateFromSelection();
void generateCabinetThumbnails();
};
#endif