mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-19 20:44:16 +02:00
Add editing the information of several elements at once (#1439)
Selecting several elements left the Selection properties panel blank and Edit (Ctrl+E) disabled, so giving many elements the same supplier or manufacturer meant editing them one by one. The panel now shows the Information fields for two or more elements, or for elements selected with other items (left unchanged, and counted in the panel's header). Ctrl+E opens the same editor as a dialog when only elements are selected. - A field shows the value every element shares, or "Multiple values". Left alone, each element keeps its own value; typed text goes to every element; the erase box clears the field on every element. - A restore button per field, and Reset, bring back the values the elements had. - One apply is one undo step. The keystrokes typed in the panel merge into one step but never into an earlier change of the same elements (a paste of element information, another edit). - Numbering scheme, label freezing and the number picker are left out: they are about one element. Custom properties are kept as they are. Also: - An element's own texts, selected with it by Select all, count as part of the element, not as other items. - ElementInfoWidget::reset(): Reset in the element dialog now brings back the Information tab's values; it did nothing. - The panel opens an element's editor on the tab picked last, instead of the first tab, after the selection went to something else and back. Test: tst_multielementinfo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+28
-2
@@ -39,7 +39,9 @@
|
||||
#include "qeticons.h"
|
||||
#include "qetpalette.h"
|
||||
#include "titleblock/integrationmovetemplateshandler.h"
|
||||
#include "PropertiesEditor/propertieseditordialog.h"
|
||||
#include "ui/diagrampropertiesdialog.h"
|
||||
#include "ui/multielementinfowidget.h"
|
||||
#include "ui/multipastedialog.h"
|
||||
#include "undocommand/changetitleblockcommand.h"
|
||||
#include "utils/conductorcreator.h"
|
||||
@@ -2080,9 +2082,33 @@ bool DiagramView::selectedItemHasFocus()
|
||||
*/
|
||||
void DiagramView::editSelection()
|
||||
{
|
||||
if (m_diagram -> isReadOnly() || m_diagram -> selectedItems().size() != 1 ) return;
|
||||
if (m_diagram -> isReadOnly()) return;
|
||||
|
||||
QGraphicsItem *item = m_diagram->selectedItems().first();
|
||||
//Several elements and nothing else: edit their information together (#1439)
|
||||
const QList<QGraphicsItem *> selection =
|
||||
MultiElementInfoWidget::withoutOwnTexts(m_diagram -> selectedItems());
|
||||
if (selection.size() > 1)
|
||||
{
|
||||
bool only_elements = true;
|
||||
for (QGraphicsItem *qgi : selection) {
|
||||
if (qgi->type() != Element::Type) {
|
||||
only_elements = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const QList<Element *> elements = MultiElementInfoWidget::informationElements(selection);
|
||||
if (only_elements && elements.size() > 1)
|
||||
{
|
||||
PropertiesEditorDialog dialog(
|
||||
new MultiElementInfoWidget(elements, selection.size() - elements.size()),
|
||||
this);
|
||||
dialog.exec();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (selection.size() != 1) return;
|
||||
|
||||
QGraphicsItem *item = selection.first();
|
||||
|
||||
//We use dynamic_cast instead of qgraphicsitem_cast for QetGraphicsItem
|
||||
//because they haven't got their own type().
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "../ui/conductorpropertieseditorwidget.h"
|
||||
#include "../ui/imagepropertieswidget.h"
|
||||
#include "../ui/inditextpropertieswidget.h"
|
||||
#include "../ui/multielementinfowidget.h"
|
||||
#include "../ui/shapegraphicsitempropertieswidget.h"
|
||||
|
||||
#include <QGraphicsItem>
|
||||
@@ -115,7 +116,7 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor(
|
||||
mapped << qgi;
|
||||
}
|
||||
}
|
||||
items = mapped;
|
||||
items = MultiElementInfoWidget::withoutOwnTexts(mapped);
|
||||
|
||||
const int count_ = items.size();
|
||||
if (count_ == 0) {
|
||||
@@ -124,19 +125,37 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor(
|
||||
QGraphicsItem *item = items.first();
|
||||
const int type_ = item->type();
|
||||
|
||||
//The editor widget can only edit one item
|
||||
//or several items of the same type
|
||||
for (auto qgi : items) {
|
||||
if (qgi->type() != type_) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QString class_name;
|
||||
if (editor) {
|
||||
class_name = editor->metaObject()->className();
|
||||
}
|
||||
|
||||
//The editor widget can only edit one item
|
||||
//or several items of the same type,
|
||||
//or the information of several elements (#1439)
|
||||
bool same_type = true;
|
||||
for (auto qgi : items) {
|
||||
if (qgi->type() != type_) {
|
||||
same_type = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!same_type || (type_ == Element::Type && count_ > 1))
|
||||
{
|
||||
const QList<Element *> elements =
|
||||
MultiElementInfoWidget::informationElements(items);
|
||||
if (elements.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
const int other_items = count_ - elements.size();
|
||||
if (class_name == MultiElementInfoWidget::staticMetaObject.className())
|
||||
{
|
||||
static_cast<MultiElementInfoWidget *>(editor)->setElements(elements, other_items);
|
||||
return editor;
|
||||
}
|
||||
return new MultiElementInfoWidget(elements, other_items, parent);
|
||||
}
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
case Conductor::Type: //1001
|
||||
@@ -162,9 +181,6 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor(
|
||||
}
|
||||
case Element::Type: //1000
|
||||
{
|
||||
if (count_ > 1) {
|
||||
return nullptr;
|
||||
}
|
||||
auto elmt = static_cast<Element*>(item);
|
||||
//auto created_editor = new ElementPropertiesWidget(elmt, parent);
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#include "factory/qetgraphicstablefactory.h"
|
||||
#include "print/projectprintwindow.h"
|
||||
#include "project/projectpropertieshandler.h"
|
||||
#include "ui/multielementinfowidget.h"
|
||||
#include "ui/projectpropertiesdialog.h"
|
||||
#include "projectview.h"
|
||||
#include "qetproject.h"
|
||||
@@ -3085,6 +3086,21 @@ void QETDiagramEditor::slot_updateUndoStack()
|
||||
undo_group.setActiveStack(currentProjectView()->project()->undoStack());
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
@return the elements of @p diagram's selection which take information
|
||||
*/
|
||||
QList<Element *> selectedInformationElements(Diagram *diagram)
|
||||
{
|
||||
QList<Element *> list;
|
||||
for (Element *element : DiagramContent(diagram).m_elements) {
|
||||
if (MultiElementInfoWidget::takesInformations(element))
|
||||
list << element;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::slot_updateComplexActions
|
||||
Manage the actions that need some conditions to be enabled or not.
|
||||
@@ -3211,7 +3227,16 @@ void QETDiagramEditor::slot_updateComplexActions()
|
||||
+ selected_conductors_count
|
||||
+ selected_cables;
|
||||
|
||||
if (selected_editable == 1)
|
||||
//Several elements and nothing else: edit their information together (#1439)
|
||||
if (!ro && selected_editable > 1 && selected_editable == selected_elements_count
|
||||
&& selectedInformationElements(diagram_).size() > 1)
|
||||
{
|
||||
m_edit_selection -> setEnabled(true);
|
||||
m_edit_selection -> setText(tr("Edit the information of the elements",
|
||||
"edit several elements"));
|
||||
m_edit_selection -> setIcon(QET::Icons::ElementEdit);
|
||||
}
|
||||
else if (selected_editable == 1)
|
||||
{
|
||||
m_edit_selection -> setEnabled(true);
|
||||
//edit element
|
||||
@@ -3323,39 +3348,6 @@ void QETDiagramEditor::slot_updateModeActions()
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
@return true if @p element has information the user can edit, the
|
||||
same element kinds ElementPropertiesWidget gives an ElementInfoWidget.
|
||||
Plain slaves and folio reports take theirs from what they are linked to.
|
||||
*/
|
||||
bool takesElementInformations(const Element *element)
|
||||
{
|
||||
switch (element->linkType()) {
|
||||
case Element::Simple:
|
||||
case Element::Thumbnail:
|
||||
case Element::Master:
|
||||
case Element::Terminale:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return the elements of @p diagram's selection which take information
|
||||
*/
|
||||
QList<Element *> selectedInformationElements(Diagram *diagram)
|
||||
{
|
||||
QList<Element *> list;
|
||||
for (Element *element : DiagramContent(diagram).m_elements) {
|
||||
if (takesElementInformations(element))
|
||||
list << element;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::slot_updatePasteAction
|
||||
Gere les actions ayant besoin du presse-papier
|
||||
|
||||
@@ -111,6 +111,18 @@ void ElementInfoWidget::apply()
|
||||
m_element -> diagram() -> undoStack().push(undo);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::reset
|
||||
Forget the edits not applied yet: show again the information
|
||||
the element has.
|
||||
*/
|
||||
void ElementInfoWidget::reset()
|
||||
{
|
||||
if (m_element) {
|
||||
updateUi();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::associatedUndo
|
||||
If the edited info is different of the actual element info,
|
||||
|
||||
@@ -53,6 +53,7 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
|
||||
|
||||
void setElement(Element *element) override;
|
||||
void apply() override;
|
||||
void reset() override;
|
||||
QUndoCommand *associatedUndo () const override;
|
||||
QString title() const override {return tr("Information");}
|
||||
bool setLiveEdit(bool live_edit) override;
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
#include <QUndoStack>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
//The tab last picked in the selection properties panel
|
||||
QString s_live_tab;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPropertiesWidget::ElementPropertiesWidget
|
||||
Default constructor
|
||||
@@ -149,6 +154,7 @@ void ElementPropertiesWidget::setDynamicText(DynamicElementTextItem *text)
|
||||
{
|
||||
DynamicElementTextItemEditor *detie = static_cast<DynamicElementTextItemEditor *>(aepew);
|
||||
m_tab->setCurrentWidget(detie);
|
||||
m_text_tab_wanted = true;
|
||||
detie->setCurrentText(text);
|
||||
}
|
||||
}
|
||||
@@ -175,6 +181,7 @@ void ElementPropertiesWidget::setTextsGroup(ElementTextItemGroup *group)
|
||||
{
|
||||
DynamicElementTextItemEditor *detie = static_cast<DynamicElementTextItemEditor *>(aepew);
|
||||
m_tab->setCurrentWidget(detie);
|
||||
m_text_tab_wanted = true;
|
||||
detie->setCurrentGroup(group);
|
||||
}
|
||||
}
|
||||
@@ -225,6 +232,18 @@ bool ElementPropertiesWidget::setLiveEdit(bool live_edit)
|
||||
foreach (AbstractElementPropertiesEditorWidget *aepew, m_list_editor)
|
||||
aepew->setLiveEdit(m_live_edit);
|
||||
|
||||
if (m_live_edit && !m_text_tab_wanted && !s_live_tab.isEmpty())
|
||||
{
|
||||
for (int i = 0 ; i < m_tab->count() ; ++i)
|
||||
{
|
||||
if (m_tab->tabText(i) == s_live_tab)
|
||||
{
|
||||
m_tab->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -262,6 +281,14 @@ void ElementPropertiesWidget::editElement()
|
||||
void ElementPropertiesWidget::buildGui()
|
||||
{
|
||||
m_tab = new QTabWidget(this);
|
||||
//The selection properties panel builds a new editor each time
|
||||
//the selection goes from one element to something else and back:
|
||||
//it opens on the tab last picked there (#1439)
|
||||
connect(m_tab, &QTabWidget::tabBarClicked, this, [this](int index) {
|
||||
if (m_live_edit && index >= 0) {
|
||||
s_live_tab = m_tab->tabText(index);
|
||||
}
|
||||
});
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout -> addWidget(m_tab);
|
||||
setLayout(main_layout);
|
||||
|
||||
@@ -61,6 +61,7 @@ class ElementPropertiesWidget : public AbstractElementPropertiesEditorWidget
|
||||
QTabWidget *m_tab;
|
||||
QList <AbstractElementPropertiesEditorWidget *> m_list_editor;
|
||||
QWidget *m_general_widget;
|
||||
bool m_text_tab_wanted = false;
|
||||
};
|
||||
|
||||
#endif // ELEMENTPROPERTIESWIDGET_H
|
||||
|
||||
@@ -0,0 +1,837 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "multielementinfowidget.h"
|
||||
|
||||
#include "../dataBase/projectdatabase.h"
|
||||
#include "../diagram.h"
|
||||
#include "../qet.h"
|
||||
#include "../qeticons.h"
|
||||
#include "../qetgraphicsitem/dynamicelementtextitem.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/elementtextitemgroup.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../undocommand/changeelementinformationcommand.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QCompleter>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QScrollArea>
|
||||
#include <QSet>
|
||||
#include <QSqlQuery>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
/**
|
||||
The change of one editing session: the keystrokes applied one by one in
|
||||
the selection panel make one undo step, but never join a change made
|
||||
before, by the same editor or another one, to the same elements.
|
||||
*/
|
||||
class MultiElementInfoCommand : public ChangeElementInformationCommand
|
||||
{
|
||||
public:
|
||||
MultiElementInfoCommand(
|
||||
const QMap<QPointer<Element>, QPair<DiagramContext, DiagramContext>> &map,
|
||||
quint64 session) :
|
||||
ChangeElementInformationCommand(map),
|
||||
m_session(session)
|
||||
{}
|
||||
|
||||
int id() const override {return 1439;}
|
||||
bool mergeWith(const QUndoCommand *other) override
|
||||
{
|
||||
if (other->id() != id()
|
||||
|| static_cast<const MultiElementInfoCommand *>(other)->m_session != m_session) {
|
||||
return false;
|
||||
}
|
||||
return ChangeElementInformationCommand::mergeWith(other);
|
||||
}
|
||||
|
||||
private:
|
||||
quint64 m_session;
|
||||
};
|
||||
|
||||
quint64 s_last_session = 0;
|
||||
|
||||
const QString EXCLUDE_FROM_BOM = QStringLiteral("exclude_from_bom");
|
||||
const QString AUTO_NUM_LOCKED = QStringLiteral("auto_num_locked");
|
||||
const QString POTENTIAL_ISOLATING = QStringLiteral("potential_isolating");
|
||||
|
||||
QString flagText(bool on)
|
||||
{
|
||||
return on ? QStringLiteral("true") : QStringLiteral("false");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::MultiElementInfoWidget
|
||||
@param elements : the elements to edit
|
||||
@param other_items : how many other selected items this editor leaves
|
||||
alone, only shown to the user
|
||||
@param parent
|
||||
*/
|
||||
MultiElementInfoWidget::MultiElementInfoWidget(const QList<Element *> &elements,
|
||||
int other_items,
|
||||
QWidget *parent) :
|
||||
PropertiesEditorWidget(parent)
|
||||
{
|
||||
buildInterface();
|
||||
setElements(elements, other_items);
|
||||
}
|
||||
|
||||
MultiElementInfoWidget::~MultiElementInfoWidget()
|
||||
{
|
||||
qDeleteAll(m_rows);
|
||||
qDeleteAll(m_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::takesInformations
|
||||
@return true if @p element has information the user can edit: the
|
||||
element kinds ElementPropertiesWidget gives an Information tab.
|
||||
Plain slaves and folio reports take theirs from what they are linked to.
|
||||
*/
|
||||
bool MultiElementInfoWidget::takesInformations(const Element *element)
|
||||
{
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
switch (element->linkType()) {
|
||||
case Element::Simple:
|
||||
case Element::Thumbnail:
|
||||
case Element::Master:
|
||||
case Element::Terminale:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::informationElements
|
||||
@return the elements of @p items which take information
|
||||
*/
|
||||
QList<Element *> MultiElementInfoWidget::informationElements(const QList<QGraphicsItem *> &items)
|
||||
{
|
||||
QList<Element *> list;
|
||||
for (QGraphicsItem *qgi : items) {
|
||||
if (qgi->type() != Element::Type) {
|
||||
continue;
|
||||
}
|
||||
auto element = static_cast<Element *>(qgi);
|
||||
if (takesInformations(element)) {
|
||||
list << element;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::withoutOwnTexts
|
||||
@return @p items without the texts and text groups of elements which
|
||||
are in @p items too: select all takes an element's texts with it, and
|
||||
they are part of the element rather than other items.
|
||||
*/
|
||||
QList<QGraphicsItem *> MultiElementInfoWidget::withoutOwnTexts(const QList<QGraphicsItem *> &items)
|
||||
{
|
||||
QSet<QGraphicsItem *> elements;
|
||||
for (QGraphicsItem *qgi : items) {
|
||||
if (qgi->type() == Element::Type) {
|
||||
elements.insert(qgi);
|
||||
}
|
||||
}
|
||||
if (elements.isEmpty()) {
|
||||
return items;
|
||||
}
|
||||
|
||||
QList<QGraphicsItem *> list;
|
||||
for (QGraphicsItem *qgi : items)
|
||||
{
|
||||
Element *owner = nullptr;
|
||||
if (qgi->type() == DynamicElementTextItem::Type) {
|
||||
owner = static_cast<DynamicElementTextItem *>(qgi)->parentElement();
|
||||
} else if (auto group = dynamic_cast<ElementTextItemGroup *>(qgi)) {
|
||||
owner = group->parentElement();
|
||||
}
|
||||
if (!owner || !elements.contains(owner)) {
|
||||
list << qgi;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::setElements
|
||||
Edit @p elements, forgetting any pending change.
|
||||
*/
|
||||
void MultiElementInfoWidget::setElements(const QList<Element *> &elements, int other_items)
|
||||
{
|
||||
for (const auto &element : std::as_const(m_elements)) {
|
||||
if (element) {
|
||||
disconnect(element.data(), &Element::elementInfoChange,
|
||||
this, &MultiElementInfoWidget::elementInfoChanged);
|
||||
}
|
||||
}
|
||||
|
||||
m_elements.clear();
|
||||
for (Element *element : elements) {
|
||||
m_elements << QPointer<Element>(element);
|
||||
connect(element, &Element::elementInfoChange,
|
||||
this, &MultiElementInfoWidget::elementInfoChanged);
|
||||
}
|
||||
m_other_items = other_items;
|
||||
|
||||
updateUi();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::elements
|
||||
@return the edited elements still alive
|
||||
*/
|
||||
QList<Element *> MultiElementInfoWidget::elements() const
|
||||
{
|
||||
QList<Element *> list;
|
||||
for (const auto &element : m_elements) {
|
||||
if (element) {
|
||||
list << element.data();
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::apply
|
||||
Push the change of every element, as one undo step.
|
||||
*/
|
||||
void MultiElementInfoWidget::apply()
|
||||
{
|
||||
QUndoCommand *undo = associatedUndo();
|
||||
if (!undo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto list = elements();
|
||||
if (list.isEmpty() || !list.first()->diagram()) {
|
||||
delete undo;
|
||||
return;
|
||||
}
|
||||
|
||||
m_applying = true;
|
||||
list.first()->diagram()->undoStack().push(undo);
|
||||
m_applying = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::associatedUndo
|
||||
@return the command giving each element its new information, or
|
||||
nullptr if no element would change.
|
||||
A field left alone gives each element back the value it had when the
|
||||
editor was filled, so restoring a field in live edit undoes it.
|
||||
*/
|
||||
QUndoCommand *MultiElementInfoWidget::associatedUndo() const
|
||||
{
|
||||
QMap<QPointer<Element>, QPair<DiagramContext, DiagramContext>> changes;
|
||||
|
||||
for (Element *element : elements())
|
||||
{
|
||||
const DiagramContext old_info = element->elementInformations();
|
||||
const DiagramContext snap = m_snapshot.value(element, old_info);
|
||||
DiagramContext new_info = old_info;
|
||||
|
||||
for (const Row *row : m_rows)
|
||||
{
|
||||
QString txt;
|
||||
switch (row->state) {
|
||||
case Keep:
|
||||
if (snap.contains(row->key)) {
|
||||
new_info.addValue(row->key, snap.value(row->key),
|
||||
snap.keyMustShow(row->key));
|
||||
} else {
|
||||
new_info.remove(row->key);
|
||||
}
|
||||
continue;
|
||||
case Clear:
|
||||
new_info.remove(row->key);
|
||||
continue;
|
||||
case Set:
|
||||
if (!row->edit->hasAcceptableInput()) {
|
||||
continue;
|
||||
}
|
||||
txt = row->edit->text();
|
||||
txt.remove(QStringLiteral("\r"));
|
||||
txt.remove(QStringLiteral("\n"));
|
||||
if (txt.isEmpty()) {
|
||||
new_info.remove(row->key);
|
||||
} else {
|
||||
new_info.addValue(row->key, txt,
|
||||
old_info.contains(row->key)
|
||||
? old_info.keyMustShow(row->key)
|
||||
: true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//The element keeps following its numbering scheme as long as
|
||||
//its formula is left as it was; a formula given by hand is its own.
|
||||
if (new_info.value(QETInformation::ELMT_FORMULA)
|
||||
== snap.value(QETInformation::ELMT_FORMULA)
|
||||
&& snap.contains(QETInformation::ELMT_FORMULA_ID)) {
|
||||
new_info.addValue(QETInformation::ELMT_FORMULA_ID,
|
||||
snap.value(QETInformation::ELMT_FORMULA_ID), false);
|
||||
} else {
|
||||
new_info.remove(QETInformation::ELMT_FORMULA_ID);
|
||||
}
|
||||
|
||||
for (const Flag *flag : m_flags)
|
||||
{
|
||||
if (flag->check->isHidden()) {
|
||||
continue;
|
||||
}
|
||||
if (!flag->changed) {
|
||||
if (snap.contains(flag->key)) {
|
||||
new_info.addValue(flag->key, snap.value(flag->key),
|
||||
snap.keyMustShow(flag->key));
|
||||
} else {
|
||||
new_info.remove(flag->key);
|
||||
}
|
||||
} else {
|
||||
new_info.addValue(flag->key, flagText(flag->check->isChecked()));
|
||||
}
|
||||
}
|
||||
|
||||
if (new_info != old_info) {
|
||||
changes.insert(QPointer<Element>(element), qMakePair(old_info, new_info));
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new MultiElementInfoCommand(changes, m_session);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::reset
|
||||
Bring back every field to what the elements had, and in live edit
|
||||
give it back to the elements.
|
||||
*/
|
||||
void MultiElementInfoWidget::reset()
|
||||
{
|
||||
m_updating = true;
|
||||
for (Row *row : std::as_const(m_rows)) {
|
||||
restoreRow(row);
|
||||
}
|
||||
for (Flag *flag : std::as_const(m_flags)) {
|
||||
restoreFlag(flag);
|
||||
}
|
||||
m_updating = false;
|
||||
updateLabelRow();
|
||||
changed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::updateUi
|
||||
Read the elements again, forgetting any pending change.
|
||||
*/
|
||||
void MultiElementInfoWidget::updateUi()
|
||||
{
|
||||
takeSnapshot();
|
||||
fillRows();
|
||||
updateHeader();
|
||||
updateSuggestions();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::fieldState
|
||||
@return what the field @p key does on apply
|
||||
*/
|
||||
MultiElementInfoWidget::FieldState MultiElementInfoWidget::fieldState(const QString &key) const
|
||||
{
|
||||
const Row *row = rowForKey(key);
|
||||
return row ? row->state : Keep;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::setFieldText
|
||||
Type @p text in the field @p key, as the user would.
|
||||
*/
|
||||
void MultiElementInfoWidget::setFieldText(const QString &key, const QString &text)
|
||||
{
|
||||
if (Row *row = rowForKey(key)) {
|
||||
row->edit->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::setFieldCleared
|
||||
Tick or untick the erase box of the field @p key, as the user would.
|
||||
*/
|
||||
void MultiElementInfoWidget::setFieldCleared(const QString &key, bool cleared)
|
||||
{
|
||||
if (Row *row = rowForKey(key)) {
|
||||
row->erase->setChecked(cleared);
|
||||
rowErased(row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::restoreField
|
||||
Click the restore button of the field @p key.
|
||||
*/
|
||||
void MultiElementInfoWidget::restoreField(const QString &key)
|
||||
{
|
||||
if (Row *row = rowForKey(key)) {
|
||||
restoreRow(row);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::fieldIsMixed
|
||||
@return true if the elements had different values for @p key
|
||||
*/
|
||||
bool MultiElementInfoWidget::fieldIsMixed(const QString &key) const
|
||||
{
|
||||
const Row *row = rowForKey(key);
|
||||
return row && row->mixed;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::setLiveEdit
|
||||
@param live_edit : true to apply every change at once
|
||||
@return always true
|
||||
*/
|
||||
bool MultiElementInfoWidget::setLiveEdit(bool live_edit)
|
||||
{
|
||||
m_live_edit = live_edit;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::buildInterface
|
||||
One row per information key: name, text, erase box, restore button.
|
||||
The numbering scheme, label freezing and number picker of the
|
||||
one-element editor are left out: they are about one element.
|
||||
*/
|
||||
void MultiElementInfoWidget::buildInterface()
|
||||
{
|
||||
auto main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_header = new QLabel(this);
|
||||
m_header->setWordWrap(true);
|
||||
m_header->setContentsMargins(5, 5, 5, 0);
|
||||
main_layout->addWidget(m_header);
|
||||
|
||||
auto flags_layout = new QVBoxLayout();
|
||||
main_layout->addLayout(flags_layout);
|
||||
|
||||
const QList<QPair<QString, QString>> flags {
|
||||
{EXCLUDE_FROM_BOM, tr("Exclude from the bill of materials")},
|
||||
{POTENTIAL_ISOLATING, tr("Function separation")},
|
||||
{AUTO_NUM_LOCKED, tr("Exclude from automatic numbering")}
|
||||
};
|
||||
for (const auto &pair : flags)
|
||||
{
|
||||
auto flag = new Flag;
|
||||
flag->key = pair.first;
|
||||
flag->check = new QCheckBox(pair.second, this);
|
||||
flag->restore = new QToolButton(this);
|
||||
flag->restore->setIcon(QET::Icons::EditUndo);
|
||||
flag->restore->setAutoRaise(true);
|
||||
flag->restore->setToolTip(tr("Restore the values the elements had"));
|
||||
auto row_layout = new QHBoxLayout();
|
||||
row_layout->setContentsMargins(5, 0, 5, 0);
|
||||
row_layout->addWidget(flag->check, 1);
|
||||
row_layout->addWidget(flag->restore);
|
||||
flags_layout->addLayout(row_layout);
|
||||
m_flags << flag;
|
||||
|
||||
connect(flag->check, &QCheckBox::clicked, this, [this, flag]() {
|
||||
flagClicked(flag);
|
||||
});
|
||||
connect(flag->restore, &QToolButton::clicked, this, [this, flag]() {
|
||||
restoreFlag(flag);
|
||||
changed();
|
||||
});
|
||||
}
|
||||
|
||||
auto scroll = new QScrollArea(this);
|
||||
scroll->setWidgetResizable(true);
|
||||
scroll->setFrameShape(QFrame::NoFrame);
|
||||
auto content = new QWidget(scroll);
|
||||
auto grid = new QGridLayout(content);
|
||||
grid->setColumnStretch(1, 1);
|
||||
|
||||
QStringList keys = QETInformation::elementInfoKeys();
|
||||
//It has its own check box above, see ElementInfoWidget::buildInterface
|
||||
keys.removeAll(EXCLUDE_FROM_BOM);
|
||||
|
||||
int line = 0;
|
||||
for (const QString &key : std::as_const(keys))
|
||||
{
|
||||
auto row = new Row;
|
||||
row->key = key;
|
||||
row->label = new QLabel(QETInformation::translatedInfoKey(key), content);
|
||||
row->edit = new QLineEdit(content);
|
||||
row->edit->setClearButtonEnabled(true);
|
||||
row->erase = new QCheckBox(content);
|
||||
row->erase->setToolTip(tr("Delete this text"));
|
||||
row->restore = new QToolButton(content);
|
||||
row->restore->setIcon(QET::Icons::EditUndo);
|
||||
row->restore->setAutoRaise(true);
|
||||
row->restore->setToolTip(tr("Restore the values the elements had"));
|
||||
|
||||
if (key == QETInformation::ELMT_WIDTH
|
||||
|| key == QETInformation::ELMT_HEIGHT
|
||||
|| key == QETInformation::ELMT_DEPTH) {
|
||||
row->edit->setValidator(new QETInformation::NumericInfoValidator(row->edit));
|
||||
row->edit->setToolTip(tr("Decimal number with a dot as separator (e.g. 80.5)"));
|
||||
}
|
||||
|
||||
grid->addWidget(row->label, line, 0);
|
||||
grid->addWidget(row->edit, line, 1);
|
||||
grid->addWidget(row->erase, line, 2);
|
||||
grid->addWidget(row->restore, line, 3);
|
||||
++line;
|
||||
m_rows << row;
|
||||
|
||||
connect(row->edit, &QLineEdit::textChanged, this, [this, row]() {
|
||||
if (!m_updating) {
|
||||
rowEdited(row);
|
||||
}
|
||||
});
|
||||
connect(row->erase, &QCheckBox::clicked, this, [this, row]() {
|
||||
rowErased(row);
|
||||
});
|
||||
connect(row->restore, &QToolButton::clicked, this, [this, row]() {
|
||||
restoreRow(row);
|
||||
changed();
|
||||
});
|
||||
}
|
||||
grid->setRowStretch(line, 1);
|
||||
|
||||
scroll->setWidget(content);
|
||||
main_layout->addWidget(scroll, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::takeSnapshot
|
||||
Keep the information every element has now: the values a field left
|
||||
alone gives back.
|
||||
*/
|
||||
void MultiElementInfoWidget::takeSnapshot()
|
||||
{
|
||||
m_session = ++s_last_session;
|
||||
m_snapshot.clear();
|
||||
for (Element *element : elements()) {
|
||||
m_snapshot.insert(element, element->elementInformations());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::fillRows
|
||||
Show in each field the value shared by every element, or that the
|
||||
values differ.
|
||||
*/
|
||||
void MultiElementInfoWidget::fillRows()
|
||||
{
|
||||
const auto list = elements();
|
||||
m_updating = true;
|
||||
|
||||
for (Row *row : std::as_const(m_rows))
|
||||
{
|
||||
row->mixed = false;
|
||||
row->initial.clear();
|
||||
bool first = true;
|
||||
for (Element *element : list) {
|
||||
const QString value = m_snapshot.value(element).value(row->key).toString();
|
||||
if (first) {
|
||||
row->initial = value;
|
||||
first = false;
|
||||
} else if (value != row->initial) {
|
||||
row->mixed = true;
|
||||
row->initial.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
restoreRow(row);
|
||||
}
|
||||
|
||||
bool all_terminals = !list.isEmpty();
|
||||
for (Element *element : list) {
|
||||
if (element->elementData().m_type != ElementData::Terminal) {
|
||||
all_terminals = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Flag *flag : std::as_const(m_flags))
|
||||
{
|
||||
flag->check->setVisible(flag->key == EXCLUDE_FROM_BOM || all_terminals);
|
||||
flag->restore->setVisible(flag->check->isVisible());
|
||||
|
||||
int on = 0;
|
||||
for (Element *element : list) {
|
||||
if (QET::infoFlagIsTrue(m_snapshot.value(element).value(flag->key).toString())) {
|
||||
++on;
|
||||
}
|
||||
}
|
||||
flag->initial = on == 0 ? Qt::Unchecked
|
||||
: on == list.size() ? Qt::Checked
|
||||
: Qt::PartiallyChecked;
|
||||
restoreFlag(flag);
|
||||
}
|
||||
|
||||
m_updating = false;
|
||||
updateLabelRow();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::updateHeader
|
||||
Say how many elements are edited, and that the rest of the selection
|
||||
is not.
|
||||
*/
|
||||
void MultiElementInfoWidget::updateHeader()
|
||||
{
|
||||
QString text = tr("Elements edited: %1").arg(elements().size());
|
||||
if (m_other_items > 0) {
|
||||
text += QStringLiteral("\n")
|
||||
+ tr("Other selected items, left unchanged: %1").arg(m_other_items);
|
||||
}
|
||||
m_header->setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::updateSuggestions
|
||||
Offer, while typing, the values the project already uses,
|
||||
the way ElementInfoWidget::updateSuggestions does.
|
||||
*/
|
||||
void MultiElementInfoWidget::updateSuggestions()
|
||||
{
|
||||
const auto list = elements();
|
||||
Diagram *diagram = list.isEmpty() ? nullptr : list.first()->diagram();
|
||||
QETProject *project = diagram ? diagram->project() : nullptr;
|
||||
if (!project || !project->dataBase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Only a column of element_info can be queried; the key becomes
|
||||
//part of the SQL text, so it is checked against that list.
|
||||
const QStringList columns = QETInformation::elementInfoKeys();
|
||||
for (Row *row : std::as_const(m_rows))
|
||||
{
|
||||
if (row->key == QETInformation::ELMT_LABEL || !columns.contains(row->key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList values;
|
||||
auto query = project->dataBase()->newQuery(QStringLiteral(
|
||||
"SELECT v, MAX(n) FROM ("
|
||||
"SELECT \"%1\" AS v, COUNT(*) AS n FROM element_info "
|
||||
"WHERE \"%1\" IS NOT NULL AND \"%1\" != '' "
|
||||
"GROUP BY \"%1\") "
|
||||
"GROUP BY v COLLATE NOCASE "
|
||||
"ORDER BY v COLLATE NOCASE").arg(row->key));
|
||||
while (query.next()) {
|
||||
values << query.value(0).toString();
|
||||
}
|
||||
|
||||
if (values.isEmpty()) {
|
||||
row->edit->setCompleter(nullptr);
|
||||
continue;
|
||||
}
|
||||
auto completer = new QCompleter(values, row->edit);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setFilterMode(Qt::MatchContains);
|
||||
row->edit->setCompleter(completer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::rowEdited
|
||||
The user typed in a field: its text goes to every element, unless
|
||||
it is back to what they all had.
|
||||
*/
|
||||
void MultiElementInfoWidget::rowEdited(Row *row)
|
||||
{
|
||||
const QString text = row->edit->text();
|
||||
if (row->mixed ? text.isEmpty() : text == row->initial) {
|
||||
row->state = Keep;
|
||||
} else {
|
||||
row->state = Set;
|
||||
}
|
||||
updateRowLook(row);
|
||||
if (row->key == QETInformation::ELMT_FORMULA) {
|
||||
updateLabelRow();
|
||||
}
|
||||
changed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::rowErased
|
||||
The erase box of a field was ticked or unticked.
|
||||
*/
|
||||
void MultiElementInfoWidget::rowErased(Row *row)
|
||||
{
|
||||
m_updating = true;
|
||||
if (row->erase->isChecked()) {
|
||||
row->state = Clear;
|
||||
row->edit->clear();
|
||||
} else {
|
||||
row->state = Keep;
|
||||
row->edit->setText(row->initial);
|
||||
}
|
||||
m_updating = false;
|
||||
updateRowLook(row);
|
||||
if (row->key == QETInformation::ELMT_FORMULA) {
|
||||
updateLabelRow();
|
||||
}
|
||||
changed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::restoreRow
|
||||
Bring a field back to what the elements had. Does not apply.
|
||||
*/
|
||||
void MultiElementInfoWidget::restoreRow(Row *row)
|
||||
{
|
||||
const bool was_updating = m_updating;
|
||||
m_updating = true;
|
||||
row->state = Keep;
|
||||
row->erase->setChecked(false);
|
||||
row->edit->setText(row->initial);
|
||||
m_updating = was_updating;
|
||||
updateRowLook(row);
|
||||
if (!m_updating && row->key == QETInformation::ELMT_FORMULA) {
|
||||
updateLabelRow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::restoreFlag
|
||||
Bring a check box back to what the elements had. Does not apply.
|
||||
*/
|
||||
void MultiElementInfoWidget::restoreFlag(Flag *flag)
|
||||
{
|
||||
flag->changed = false;
|
||||
flag->check->setTristate(flag->initial == Qt::PartiallyChecked);
|
||||
flag->check->setCheckState(flag->initial);
|
||||
flag->restore->setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::flagClicked
|
||||
A click gives the box's state to every element. A box which started
|
||||
"partly checked" goes on as a plain on/off box: cycling back through
|
||||
"partly" is what its restore button is for.
|
||||
*/
|
||||
void MultiElementInfoWidget::flagClicked(Flag *flag)
|
||||
{
|
||||
if (flag->check->isTristate()) {
|
||||
flag->check->setTristate(false);
|
||||
if (flag->check->checkState() == Qt::PartiallyChecked) {
|
||||
flag->check->setCheckState(Qt::Checked);
|
||||
}
|
||||
}
|
||||
flag->changed = flag->check->checkState() != flag->initial;
|
||||
flag->restore->setEnabled(flag->changed);
|
||||
changed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::updateRowLook
|
||||
A field whose values differ says so; a field which will change
|
||||
something has its restore button on and its name in bold.
|
||||
*/
|
||||
void MultiElementInfoWidget::updateRowLook(Row *row)
|
||||
{
|
||||
row->edit->setDisabled(row->state == Clear);
|
||||
if (row->state == Clear) {
|
||||
row->edit->setPlaceholderText(tr("Delete this text"));
|
||||
} else if (row->mixed) {
|
||||
row->edit->setPlaceholderText(tr("Multiple values"));
|
||||
} else {
|
||||
row->edit->setPlaceholderText(QString());
|
||||
}
|
||||
row->restore->setEnabled(row->state != Keep);
|
||||
QFont font = row->label->font();
|
||||
font.setBold(row->state != Keep);
|
||||
row->label->setFont(font);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::updateLabelRow
|
||||
As in the one-element editor, the label cannot be typed while the
|
||||
formula field holds a formula: the formula makes the label.
|
||||
*/
|
||||
void MultiElementInfoWidget::updateLabelRow()
|
||||
{
|
||||
Row *formula = rowForKey(QETInformation::ELMT_FORMULA);
|
||||
Row *label = rowForKey(QETInformation::ELMT_LABEL);
|
||||
if (formula && label) {
|
||||
const bool has_formula = !formula->edit->text().isEmpty();
|
||||
label->edit->setEnabled(!has_formula && label->state != Clear);
|
||||
label->erase->setEnabled(!has_formula);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::changed
|
||||
In live edit, every change is applied at once.
|
||||
*/
|
||||
void MultiElementInfoWidget::changed()
|
||||
{
|
||||
if (m_live_edit && !m_updating) {
|
||||
apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief MultiElementInfoWidget::elementInfoChanged
|
||||
The information of an element was changed by something else than
|
||||
this editor (undo, another editor): read the elements again.
|
||||
*/
|
||||
void MultiElementInfoWidget::elementInfoChanged()
|
||||
{
|
||||
if (m_applying || m_refresh_pending) {
|
||||
return;
|
||||
}
|
||||
//An undo changes every element one after the other:
|
||||
//read them once, when it is done.
|
||||
m_refresh_pending = true;
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
m_refresh_pending = false;
|
||||
updateUi();
|
||||
});
|
||||
}
|
||||
|
||||
MultiElementInfoWidget::Row *MultiElementInfoWidget::rowForKey(const QString &key) const
|
||||
{
|
||||
for (Row *row : m_rows) {
|
||||
if (row->key == key) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef MULTIELEMENTINFOWIDGET_H
|
||||
#define MULTIELEMENTINFOWIDGET_H
|
||||
|
||||
#include "../PropertiesEditor/propertieseditorwidget.h"
|
||||
#include "../diagramcontext.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
|
||||
class Element;
|
||||
class QCheckBox;
|
||||
class QGraphicsItem;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QToolButton;
|
||||
|
||||
/**
|
||||
@brief The MultiElementInfoWidget class
|
||||
Edit the information (manufacturer, supplier, references...) of
|
||||
several elements at once (issue #1439).
|
||||
|
||||
Each field shows the value when every element has the same one, or
|
||||
"Multiple values" when they differ. A field left alone keeps each
|
||||
element's own value, a typed text is given to every element, and the
|
||||
erase box clears the field on every element. The restore button of a
|
||||
field, and reset(), bring back the values the elements had.
|
||||
All the changes of one apply are a single undo step.
|
||||
*/
|
||||
class MultiElementInfoWidget : public PropertiesEditorWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MultiElementInfoWidget(const QList<Element *> &elements,
|
||||
int other_items = 0,
|
||||
QWidget *parent = nullptr);
|
||||
~MultiElementInfoWidget() override;
|
||||
|
||||
void setElements(const QList<Element *> &elements, int other_items = 0);
|
||||
QList<Element *> elements() const;
|
||||
|
||||
void apply() override;
|
||||
void reset() override;
|
||||
QUndoCommand *associatedUndo() const override;
|
||||
QString title() const override {return tr("Information");}
|
||||
void updateUi() override;
|
||||
bool setLiveEdit(bool live_edit) override;
|
||||
|
||||
static bool takesInformations(const Element *element);
|
||||
static QList<Element *> informationElements(const QList<QGraphicsItem *> &items);
|
||||
static QList<QGraphicsItem *> withoutOwnTexts(const QList<QGraphicsItem *> &items);
|
||||
|
||||
//What a field will do to the elements on apply
|
||||
enum FieldState {Keep, Set, Clear};
|
||||
FieldState fieldState(const QString &key) const;
|
||||
void setFieldText(const QString &key, const QString &text);
|
||||
void setFieldCleared(const QString &key, bool cleared);
|
||||
void restoreField(const QString &key);
|
||||
bool fieldIsMixed(const QString &key) const;
|
||||
|
||||
private:
|
||||
struct Row {
|
||||
QString key;
|
||||
QLabel *label = nullptr;
|
||||
QLineEdit *edit = nullptr;
|
||||
QCheckBox *erase = nullptr;
|
||||
QToolButton *restore = nullptr;
|
||||
QString initial;
|
||||
bool mixed = false;
|
||||
FieldState state = Keep;
|
||||
};
|
||||
struct Flag {
|
||||
QString key;
|
||||
QCheckBox *check = nullptr;
|
||||
QToolButton *restore = nullptr;
|
||||
Qt::CheckState initial = Qt::Unchecked;
|
||||
bool changed = false;
|
||||
};
|
||||
|
||||
void buildInterface();
|
||||
void takeSnapshot();
|
||||
void fillRows();
|
||||
void updateHeader();
|
||||
void updateSuggestions();
|
||||
void rowEdited(Row *row);
|
||||
void rowErased(Row *row);
|
||||
void restoreRow(Row *row);
|
||||
void restoreFlag(Flag *flag);
|
||||
void flagClicked(Flag *flag);
|
||||
void updateRowLook(Row *row);
|
||||
void updateLabelRow();
|
||||
void changed();
|
||||
void elementInfoChanged();
|
||||
Row *rowForKey(const QString &key) const;
|
||||
|
||||
QList<QPointer<Element>> m_elements;
|
||||
QHash<Element *, DiagramContext> m_snapshot;
|
||||
int m_other_items = 0;
|
||||
QLabel *m_header = nullptr;
|
||||
QList<Row *> m_rows;
|
||||
QList<Flag *> m_flags;
|
||||
bool m_updating = false;
|
||||
bool m_applying = false;
|
||||
bool m_refresh_pending = false;
|
||||
quint64 m_session = 0;
|
||||
};
|
||||
|
||||
#endif // MULTIELEMENTINFOWIDGET_H
|
||||
Reference in New Issue
Block a user