mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-28 04:54:13 +02:00
Merge pull request #1074 from ispyisail/feat/1070-item-groups
Add Group and Ungroup for items on a folio
This commit is contained in:
@@ -234,6 +234,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/exportpropertieswidget.h
|
||||
${QET_DIR}/sources/genericpanel.cpp
|
||||
${QET_DIR}/sources/genericpanel.h
|
||||
${QET_DIR}/sources/itemgroups.cpp
|
||||
${QET_DIR}/sources/itemgroups.h
|
||||
${QET_DIR}/sources/lastusedstyle.cpp
|
||||
${QET_DIR}/sources/lastusedstyle.h
|
||||
${QET_DIR}/sources/machine_info.cpp
|
||||
@@ -839,6 +841,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/undocommand/removediagramcommand.h
|
||||
${QET_DIR}/sources/undocommand/setautonumcontextcommand.cpp
|
||||
${QET_DIR}/sources/undocommand/setautonumcontextcommand.h
|
||||
${QET_DIR}/sources/undocommand/groupitemscommand.cpp
|
||||
${QET_DIR}/sources/undocommand/groupitemscommand.h
|
||||
${QET_DIR}/sources/undocommand/alignselectioncommand.cpp
|
||||
${QET_DIR}/sources/undocommand/alignselectioncommand.h
|
||||
${QET_DIR}/sources/undocommand/rotateselectioncommand.cpp
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "../diagram.h"
|
||||
#include "../diagramposition.h"
|
||||
#include "../elementprovider.h"
|
||||
#include "../itemgroups.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/diagramimageitem.h"
|
||||
@@ -669,6 +670,13 @@ QUuid drawingItemUuid(QObject *object)
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
/// The group_uuid column of @p item's row: its group, or NULL.
|
||||
QVariant groupValue(const QGraphicsItem *item)
|
||||
{
|
||||
const QUuid group = ItemGroups::groupOf(item);
|
||||
return group.isNull() ? QVariant() : QVariant(group.toString());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@@ -751,6 +759,33 @@ void projectDataBase::removeDrawingItem(QGraphicsItem *item)
|
||||
forgetDrawingItem(object);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::itemGroupChanged
|
||||
@a item joined or left a group (discussion #1070). An element's row is
|
||||
updated at once; a drawing item's is queued like any other change to it.
|
||||
@param item
|
||||
*/
|
||||
void projectDataBase::itemGroupChanged(QGraphicsItem *item)
|
||||
{
|
||||
if (auto element = qgraphicsitem_cast<Element *>(item))
|
||||
{
|
||||
QSqlQuery update(m_data_base);
|
||||
update.prepare(QStringLiteral("UPDATE element SET group_uuid = :group_uuid WHERE uuid = :uuid"));
|
||||
update.bindValue(QStringLiteral(":group_uuid"), groupValue(element));
|
||||
update.bindValue(QStringLiteral(":uuid"), element->uuid().toString());
|
||||
if (!update.exec()) {
|
||||
qDebug() << "projectDataBase::itemGroupChanged update error : " << update.lastError();
|
||||
}
|
||||
m_content_changed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
QGraphicsObject *object = item ? item->toGraphicsObject() : nullptr;
|
||||
if (object && !drawingItemTable(object).isEmpty()) {
|
||||
m_dirty_drawing_items.insert(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief projectDataBase::drawingItemChanged
|
||||
Queue the sender's row to be rewritten.
|
||||
@@ -884,6 +919,7 @@ bool projectDataBase::writeDrawingItem(QObject *object)
|
||||
query->bindValue(QStringLiteral(":y"), rect.y());
|
||||
query->bindValue(QStringLiteral(":width"), rect.width());
|
||||
query->bindValue(QStringLiteral(":height"), rect.height());
|
||||
query->bindValue(QStringLiteral(":group_uuid"), groupValue(item));
|
||||
if (!query->exec()) {
|
||||
qDebug() << "projectDataBase::writeDrawingItem error : " << query->lastError();
|
||||
return true;
|
||||
@@ -985,6 +1021,7 @@ bool projectDataBase::createDataBase()
|
||||
"pos VARCHAR(6) NOT NULL,"
|
||||
"type VARCHAR(50),"
|
||||
"sub_type VARCHAR(50),"
|
||||
"group_uuid VARCHAR(50),"
|
||||
"FOREIGN KEY (diagram_uuid) REFERENCES diagram (uuid)"
|
||||
")");
|
||||
if (!query_.exec(element_table)) {
|
||||
@@ -1085,7 +1122,8 @@ bool projectDataBase::createDataBase()
|
||||
"uuid VARCHAR(50) PRIMARY KEY NOT NULL, "
|
||||
"diagram_uuid VARCHAR(50) NOT NULL, "
|
||||
"pos VARCHAR(6), "
|
||||
"x REAL, y REAL, width REAL, height REAL, ");
|
||||
"x REAL, y REAL, width REAL, height REAL, "
|
||||
"group_uuid VARCHAR(50), ");
|
||||
for (const QString &table : {
|
||||
QStringLiteral("CREATE TABLE shape (") + drawing_columns +
|
||||
"type VARCHAR(20), color VARCHAR(20), fill VARCHAR(20), "
|
||||
@@ -1516,8 +1554,8 @@ void projectDataBase::prepareQuery()
|
||||
|
||||
//DRAWING ITEMS. OR REPLACE: a row is rewritten in place on every
|
||||
//change, see writeDrawingItem().
|
||||
const QString drawing_columns("uuid, diagram_uuid, pos, x, y, width, height");
|
||||
const QString drawing_values(":uuid, :diagram_uuid, :pos, :x, :y, :width, :height");
|
||||
const QString drawing_columns("uuid, diagram_uuid, pos, x, y, width, height, group_uuid");
|
||||
const QString drawing_values(":uuid, :diagram_uuid, :pos, :x, :y, :width, :height, :group_uuid");
|
||||
m_insert_shape_query = QSqlQuery(m_data_base);
|
||||
m_insert_shape_query.prepare("INSERT OR REPLACE INTO shape (" + drawing_columns +
|
||||
", type, color, fill) VALUES (" + drawing_values +
|
||||
@@ -1561,7 +1599,7 @@ void projectDataBase::prepareQuery()
|
||||
m_diagram_info_order_changed.prepare("UPDATE diagram_info SET folio = :folio WHERE diagram_uuid = :uuid");
|
||||
|
||||
//INSERT ELEMENT
|
||||
QString insert_element_query("INSERT INTO element (uuid, diagram_uuid, pos, type, sub_type) VALUES (:uuid, :diagram_uuid, :pos, :type, :sub_type)");
|
||||
QString insert_element_query("INSERT INTO element (uuid, diagram_uuid, pos, type, sub_type, group_uuid) VALUES (:uuid, :diagram_uuid, :pos, :type, :sub_type, :group_uuid)");
|
||||
m_insert_elements_query = QSqlQuery(m_data_base);
|
||||
m_insert_elements_query.prepare(insert_element_query);
|
||||
|
||||
@@ -1675,6 +1713,7 @@ void projectDataBase::bindElementValues(QSqlQuery &query, Element *element, Diag
|
||||
query.bindValue(QStringLiteral(":pos"), diagram->convertPosition(element->scenePos()).toString());
|
||||
query.bindValue(QStringLiteral(":type"), element_data.typeToString());
|
||||
query.bindValue(QStringLiteral(":sub_type"), element_data.masterTypeToString());
|
||||
query.bindValue(QStringLiteral(":group_uuid"), groupValue(element));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,6 +105,7 @@ class projectDataBase : public QObject
|
||||
//furniture. Anything else passed here is ignored.
|
||||
void addDrawingItem (QGraphicsItem *item);
|
||||
void removeDrawingItem (QGraphicsItem *item);
|
||||
void itemGroupChanged (QGraphicsItem *item);
|
||||
|
||||
private slots:
|
||||
//Refresh the sender()'s row after Conductor::setProperties().
|
||||
|
||||
+85
-2
@@ -42,7 +42,9 @@
|
||||
#include "qetinformation.h"
|
||||
#include "qetproject.h"
|
||||
#include "diagramsortkeys.h"
|
||||
#include "itemgroups.h"
|
||||
#include "textgrid.h"
|
||||
#include <QGraphicsView>
|
||||
#include <QTextStream>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
@@ -121,6 +123,7 @@ namespace {
|
||||
for (T *item : items) {
|
||||
Entry entry{stack_rank.value(item, INT_MAX), QString(),
|
||||
item->toXml(document)};
|
||||
ItemGroups::write(entry.xml, item);
|
||||
// Only an item the stacking query missed needs a tiebreak.
|
||||
if (entry.rank == INT_MAX) {
|
||||
QTextStream stream(&entry.xml_text);
|
||||
@@ -210,6 +213,7 @@ Diagram::Diagram(QETProject *project) :
|
||||
connect(&border_and_titleblock,
|
||||
&BorderTitleBlock::needTitleBlockTemplate,
|
||||
this, &Diagram::setTitleBlockTemplate);
|
||||
|
||||
connect(&border_and_titleblock,
|
||||
&BorderTitleBlock::informationChanged,
|
||||
this, &Diagram::titleChanged);
|
||||
@@ -432,7 +436,9 @@ void Diagram::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
rememberSelection();
|
||||
QGraphicsScene::mousePressEvent(event);
|
||||
completeGroupSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,6 +477,9 @@ void Diagram::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
|
||||
QGraphicsScene::mouseReleaseEvent(event);
|
||||
//A click on an already selected item changes the selection on
|
||||
//release, not on press (Ctrl toggles it, a plain click keeps only it).
|
||||
completeGroupSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1245,8 +1254,9 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
if (!list_elements.isEmpty()) {
|
||||
auto dom_elements = document.createElement(QStringLiteral("elements"));
|
||||
for (auto elmt : list_elements) {
|
||||
dom_elements.appendChild(elmt->toXml(document,
|
||||
table_adr_id));
|
||||
QDomElement dom_element = elmt->toXml(document, table_adr_id);
|
||||
ItemGroups::write(dom_element, elmt);
|
||||
dom_elements.appendChild(dom_element);
|
||||
// If copy is active we have to undo the changes we have made during creating(filling) 'list_elements'
|
||||
if(is_copy_command && (elmt->linkType() == Element::Slave || elmt->linkType()&Element::AllReport))
|
||||
restoreText(elmt);
|
||||
@@ -1666,6 +1676,7 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
delete nvel_elmt;
|
||||
qDebug() << QStringLiteral("Diagram::fromXml() : Le chargement des parametres d'un element a echoue");
|
||||
} else {
|
||||
ItemGroups::setGroup(nvel_elmt, ItemGroups::read(element_xml));
|
||||
added_elements << nvel_elmt;
|
||||
}
|
||||
}
|
||||
@@ -1701,6 +1712,7 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
IndependentTextItem *iti = new IndependentTextItem();
|
||||
iti -> fromXml(text_xml);
|
||||
settle_uuid(iti, text_xml, QStringLiteral("input"), added_texts.size());
|
||||
ItemGroups::setGroup(iti, ItemGroups::read(text_xml));
|
||||
addItem(iti);
|
||||
added_texts << iti;
|
||||
}
|
||||
@@ -1713,6 +1725,7 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
DiagramImageItem *dii = new DiagramImageItem ();
|
||||
dii -> fromXml(image_xml);
|
||||
settle_uuid(dii, image_xml, QStringLiteral("image"), added_images.size());
|
||||
ItemGroups::setGroup(dii, ItemGroups::read(image_xml));
|
||||
addItem(dii);
|
||||
added_images << dii;
|
||||
}
|
||||
@@ -1725,6 +1738,7 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
QetShapeItem *dii = new QetShapeItem (QPointF(0,0));
|
||||
dii -> fromXml(shape_xml);
|
||||
settle_uuid(dii, shape_xml, QStringLiteral("shape"), added_shapes.size());
|
||||
ItemGroups::setGroup(dii, ItemGroups::read(shape_xml));
|
||||
addItem(dii);
|
||||
added_shapes << dii;
|
||||
}
|
||||
@@ -2127,6 +2141,75 @@ void Diagram::selectAllTextFields()
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::setItemGroup
|
||||
Put @a item in @a group, or take it out of its group if @a group is
|
||||
null, and keep its row in the project database in step.
|
||||
@param item
|
||||
@param group
|
||||
*/
|
||||
void Diagram::setItemGroup(QGraphicsItem *item, const QUuid &group)
|
||||
{
|
||||
ItemGroups::setGroup(item, group);
|
||||
if (m_project) {
|
||||
m_project->dataBase()->itemGroupChanged(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::completeGroupSelection
|
||||
Make the selection whole groups again after the user changed it with a
|
||||
click, see ItemGroups::completeSelection(). Called from the mouse
|
||||
handlers and, when a rubber band is released, from DiagramView -- not
|
||||
on every selectionChanged(): code that selects items itself (export,
|
||||
search, Tab cycling) deselects and reselects one item at a time and
|
||||
must not have groups pulled back in behind it.
|
||||
Left alone while a rubber band is being dragged, which reselects exactly
|
||||
what it covers on every mouse step.
|
||||
*/
|
||||
void Diagram::completeGroupSelection()
|
||||
{
|
||||
for (QGraphicsView *view : views()) {
|
||||
if (!view->rubberBandRect().isNull()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QGraphicsItem *> previous;
|
||||
for (const QPointer<QGraphicsObject> &item : std::as_const(m_previous_selection)) {
|
||||
if (item) {
|
||||
previous << item.data();
|
||||
}
|
||||
}
|
||||
|
||||
//One selectionChanged() for the whole group, not one per member:
|
||||
//the properties dock rebuilds on each.
|
||||
blockSignals(true);
|
||||
const bool changed = ItemGroups::completeSelection(
|
||||
this, previous,
|
||||
QApplication::keyboardModifiers().testFlag(Qt::ControlModifier));
|
||||
blockSignals(false);
|
||||
if (changed) {
|
||||
emit selectionChanged();
|
||||
}
|
||||
rememberSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::rememberSelection
|
||||
Keep the current selection, the "before" completeGroupSelection() needs
|
||||
to tell a member being Ctrl+clicked off.
|
||||
*/
|
||||
void Diagram::rememberSelection()
|
||||
{
|
||||
m_previous_selection.clear();
|
||||
for (QGraphicsItem *item : selectedItems()) {
|
||||
if (QGraphicsObject *object = item->toGraphicsObject()) {
|
||||
m_previous_selection << object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::selectNextItem
|
||||
Select the next (or, if @a forward is false, the previous) selectable
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "qgimanager.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QUuid>
|
||||
#include <QtWidgets>
|
||||
#include <QtXml>
|
||||
@@ -138,6 +139,10 @@ class Diagram : public QGraphicsScene
|
||||
bool m_freeze_new_conductors_;
|
||||
QUuid m_uuid = QUuid::createUuid();
|
||||
|
||||
//Selection before the current click, see completeGroupSelection()
|
||||
QList<QPointer<QGraphicsObject>> m_previous_selection;
|
||||
void rememberSelection();
|
||||
|
||||
bool uuidUsedByOtherDiagram(const QUuid &uuid) const;
|
||||
QUuid derivedUuid(const QDomElement &root, const QString &reason) const;
|
||||
|
||||
@@ -282,6 +287,7 @@ class Diagram : public QGraphicsScene
|
||||
const QString& title, const QString& seq,
|
||||
NumerotationContext *nc);
|
||||
void changeZValue(QET::DepthOption option);
|
||||
void setItemGroup(QGraphicsItem *item, const QUuid &group);
|
||||
|
||||
public slots:
|
||||
void adjustSceneRect ();
|
||||
@@ -299,6 +305,7 @@ class Diagram : public QGraphicsScene
|
||||
void invertSelection();
|
||||
void selectAllConductors();
|
||||
void selectAllTextFields();
|
||||
void completeGroupSelection();
|
||||
|
||||
signals:
|
||||
void showDiagram (Diagram *);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "diagramcommands.h"
|
||||
|
||||
#include "diagram.h"
|
||||
#include "itemgroups.h"
|
||||
#include "qetgraphicsitem/conductortextitem.h"
|
||||
#include "qetgraphicsitem/diagramimageitem.h"
|
||||
#include "qetgraphicsitem/dynamicelementtextitem.h"
|
||||
@@ -216,6 +217,27 @@ void PasteDiagramCommand::redo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Pasted groups become new groups: the members of one source
|
||||
//group all get the same new uuid, never the source's, or the
|
||||
//copy would join the original's group. After the elements got
|
||||
//their own uuids: the database row is found by uuid, and before
|
||||
//that it would have been the source element's row.
|
||||
QHash<QUuid, QUuid> renewed_groups;
|
||||
for (QGraphicsItem *item : content.items(DiagramContent::Elements
|
||||
| DiagramContent::TextFields
|
||||
| DiagramContent::Images
|
||||
| DiagramContent::Shapes))
|
||||
{
|
||||
const QUuid source_group = ItemGroups::groupOf(item);
|
||||
if (source_group.isNull()) {
|
||||
continue;
|
||||
}
|
||||
if (!renewed_groups.contains(source_group)) {
|
||||
renewed_groups.insert(source_group, QUuid::createUuid());
|
||||
}
|
||||
diagram -> setItemGroup(item, renewed_groups.value(source_group));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -122,6 +122,13 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
|
||||
});
|
||||
|
||||
connect(m_diagram, &Diagram::showDiagram, this, &DiagramView::showDiagram);
|
||||
//The diagram leaves group completion alone while a rubber band is
|
||||
//dragged; finish it when the band is released (null rect).
|
||||
connect(this, &QGraphicsView::rubberBandChanged, this, [this](QRect rect) {
|
||||
if (rect.isNull()) {
|
||||
m_diagram->completeGroupSelection();
|
||||
}
|
||||
});
|
||||
connect(m_diagram, &QGraphicsScene::sceneRectChanged, this, &DiagramView::adjustSceneRect);
|
||||
connect(&(m_diagram -> border_and_titleblock), &BorderTitleBlock::informationChanged, this, &DiagramView::updateWindowTitle);
|
||||
connect(diagram, &Diagram::findElementRequired, this, &DiagramView::findElementRequired);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "qetgraphicsitem/diagramimageitem.h"
|
||||
#include "qetgraphicsitem/independenttextitem.h"
|
||||
#include "qetgraphicsitem/qetshapeitem.h"
|
||||
#include "itemgroups.h"
|
||||
#include "qetinformation.h"
|
||||
|
||||
/*
|
||||
@@ -833,6 +834,20 @@ void ElementsPanelWidget::duplicateDiagram()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Groups too: a group of the copy is its own, so selecting it
|
||||
// is the same on both folios but the database tells them apart.
|
||||
QHash<QUuid, QUuid> renewed_groups;
|
||||
for (QGraphicsItem *item : new_diagram->items()) {
|
||||
const QUuid source_group = ItemGroups::groupOf(item);
|
||||
if (source_group.isNull()) {
|
||||
continue;
|
||||
}
|
||||
if (!renewed_groups.contains(source_group)) {
|
||||
renewed_groups.insert(source_group, QUuid::createUuid());
|
||||
}
|
||||
new_diagram->setItemGroup(item, renewed_groups.value(source_group));
|
||||
}
|
||||
}
|
||||
|
||||
elements_panel->reload();
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
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 "itemgroups.h"
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QGraphicsItem>
|
||||
#include <QGraphicsScene>
|
||||
#include <QSet>
|
||||
|
||||
/**
|
||||
@return the uuid of the group @a item belongs to, or a null uuid.
|
||||
*/
|
||||
QUuid ItemGroups::groupOf(const QGraphicsItem *item)
|
||||
{
|
||||
return item ? item->data(data_key).toUuid() : QUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
Put @a item in @a group, or take it out of any group if @a group is null.
|
||||
*/
|
||||
void ItemGroups::setGroup(QGraphicsItem *item, const QUuid &group)
|
||||
{
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
item->setData(data_key, group.isNull() ? QVariant() : QVariant(group));
|
||||
}
|
||||
|
||||
/**
|
||||
Write @a item's group, if it has one, on its XML element @a xml.
|
||||
Nothing is written otherwise, so a project without groups saves exactly
|
||||
as before.
|
||||
*/
|
||||
void ItemGroups::write(QDomElement &xml, const QGraphicsItem *item)
|
||||
{
|
||||
const QUuid group = groupOf(item);
|
||||
if (!group.isNull()) {
|
||||
xml.setAttribute(QString::fromLatin1(xml_attribute), group.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return the group written on @a xml, or a null uuid.
|
||||
*/
|
||||
QUuid ItemGroups::read(const QDomElement &xml)
|
||||
{
|
||||
return QUuid(xml.attribute(QString::fromLatin1(xml_attribute)));
|
||||
}
|
||||
|
||||
/**
|
||||
Make the selection of @a scene whole groups again after it changed.
|
||||
A group with a selected member is selected entirely, except when the
|
||||
change took members of an entirely selected group out of the selection
|
||||
while @a toggling (Ctrl+click on a member): then the whole group leaves
|
||||
the selection, as the user meant.
|
||||
@param previous : the selection before the change
|
||||
@param toggling : true when Ctrl is held
|
||||
@return true if the selection was changed
|
||||
*/
|
||||
bool ItemGroups::completeSelection(QGraphicsScene *scene,
|
||||
const QList<QGraphicsItem *> &previous,
|
||||
bool toggling)
|
||||
{
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSet<QUuid> touched;
|
||||
const QList<QGraphicsItem *> selected = scene->selectedItems();
|
||||
for (QGraphicsItem *item : selected) {
|
||||
const QUuid group = groupOf(item);
|
||||
if (!group.isNull()) {
|
||||
touched << group;
|
||||
}
|
||||
}
|
||||
|
||||
//Groups that just lost a member from the selection
|
||||
QSet<QUuid> shrunk;
|
||||
for (QGraphicsItem *item : previous) {
|
||||
if (item->scene() == scene && !item->isSelected()) {
|
||||
const QUuid group = groupOf(item);
|
||||
if (!group.isNull()) {
|
||||
shrunk << group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (touched.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//One pass over the folio for every touched group, not one per group:
|
||||
//select all on a folio of 2000 items in 200 groups took 44 ms the
|
||||
//other way.
|
||||
bool changed = false;
|
||||
for (QGraphicsItem *item : scene->items())
|
||||
{
|
||||
const QUuid group = groupOf(item);
|
||||
if (group.isNull() || !touched.contains(group)) {
|
||||
continue;
|
||||
}
|
||||
const bool select = !(toggling && shrunk.contains(group));
|
||||
if (item->isSelected() != select) {
|
||||
item->setSelected(select);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 ITEMGROUPS_H
|
||||
#define ITEMGROUPS_H
|
||||
|
||||
#include <QList>
|
||||
#include <QUuid>
|
||||
|
||||
class QDomElement;
|
||||
class QGraphicsItem;
|
||||
class QGraphicsScene;
|
||||
|
||||
/**
|
||||
Groups of items on a folio (discussion #1070): symbols, free texts,
|
||||
shapes and pictures that select, move, copy and delete together.
|
||||
|
||||
A group is not an object in the scene. Each member keeps its place and
|
||||
carries the group's uuid, and selecting one member selects them all;
|
||||
moving, copying and deleting then need nothing new, because they act on
|
||||
the selection. Re-parenting the members under a QGraphicsItemGroup would
|
||||
make every position group-relative, which every piece of code reading a
|
||||
symbol's position would have to learn about.
|
||||
|
||||
This part knows nothing of Diagram, so it can be tested on a plain scene.
|
||||
*/
|
||||
namespace ItemGroups
|
||||
{
|
||||
/// QGraphicsItem::data() key holding an item's group uuid.
|
||||
inline constexpr int data_key = 0x475250; // "GRP"
|
||||
|
||||
/// XML attribute holding it, on <element>, <input>, <shape> and <image>.
|
||||
inline constexpr char xml_attribute[] = "group";
|
||||
|
||||
QUuid groupOf(const QGraphicsItem *item);
|
||||
void setGroup(QGraphicsItem *item, const QUuid &group);
|
||||
|
||||
void write(QDomElement &xml, const QGraphicsItem *item);
|
||||
QUuid read(const QDomElement &xml);
|
||||
|
||||
bool completeSelection(QGraphicsScene *scene,
|
||||
const QList<QGraphicsItem *> &previous,
|
||||
bool toggling);
|
||||
}
|
||||
|
||||
#endif // ITEMGROUPS_H
|
||||
@@ -66,6 +66,7 @@
|
||||
#include "undocommand/addelementtextcommand.h"
|
||||
#include "utils/qetsettings.h"
|
||||
#include "utils/qetutils.h"
|
||||
#include "undocommand/groupitemscommand.h"
|
||||
#include "undocommand/alignselectioncommand.h"
|
||||
#include "undocommand/rotateselectioncommand.h"
|
||||
#include "undocommand/rotatetextscommand.h"
|
||||
@@ -815,6 +816,8 @@ void QETDiagramEditor::setUpActions()
|
||||
m_find_element = m_selection_actions_group.addAction( QET::Icons::ZoomDraw, tr("Retrouver dans le panel") );
|
||||
m_edit_selection = m_selection_actions_group.addAction( QET::Icons::ElementEdit, tr("Éditer l'item sélectionné") );
|
||||
m_group_selected_texts = m_selection_actions_group.addAction( QET::Icons::textGroup, tr("Grouper les textes sélectionnés"));
|
||||
m_group_selection = m_selection_actions_group.addAction( tr("Grouper") );
|
||||
m_ungroup_selection = m_selection_actions_group.addAction( tr("Dégrouper") );
|
||||
|
||||
ShortcutManager::instance().registerAction(m_delete_selection, "diagrameditor.delete_selection", tr("Éditeur de schémas"), Qt::Key_Delete);
|
||||
ShortcutManager::instance().registerAction(m_rotate_selection, "diagrameditor.rotate_selection", tr("Éditeur de schémas"), Qt::Key_Space);
|
||||
@@ -920,6 +923,14 @@ void QETDiagramEditor::setUpActions()
|
||||
m_find_element ->setData("find_selected_element");
|
||||
m_edit_selection ->setData("edit_selected_element");
|
||||
m_group_selected_texts->setData("group_selected_texts");
|
||||
m_group_selection ->setData("group_selection");
|
||||
m_ungroup_selection ->setData("ungroup_selection");
|
||||
|
||||
//No default key: Ctrl+G is "jump to element". A user can bind one.
|
||||
ShortcutManager::instance().registerAction(m_group_selection, "diagrameditor.group_selection", tr("Éditeur de schémas"), QKeySequence());
|
||||
ShortcutManager::instance().registerAction(m_ungroup_selection, "diagrameditor.ungroup_selection", tr("Éditeur de schémas"), QKeySequence());
|
||||
m_group_selection ->setStatusTip(tr("Groupe les éléments, textes, formes et images sélectionnés : ils se sélectionnent, se déplacent et se copient ensemble", "status bar tip"));
|
||||
m_ungroup_selection->setStatusTip(tr("Défait les groupes sélectionnés", "status bar tip"));
|
||||
|
||||
connect(&m_selection_actions_group, &QActionGroup::triggered, this, &QETDiagramEditor::selectionGroupTriggered);
|
||||
|
||||
@@ -2068,6 +2079,15 @@ void QETDiagramEditor::selectionGroupTriggered(QAction *action)
|
||||
findElementInPanel(currentElement()->location());
|
||||
else if (value == "edit_selected_element")
|
||||
dv->editSelection();
|
||||
else if (value == "group_selection" || value == "ungroup_selection")
|
||||
{
|
||||
GroupItemsCommand *command = value == "group_selection"
|
||||
? GroupItemsCommand::group(diagram)
|
||||
: GroupItemsCommand::ungroup(diagram);
|
||||
if (command) {
|
||||
diagram->undoStack().push(command);
|
||||
}
|
||||
}
|
||||
else if (value == "group_selected_texts")
|
||||
{
|
||||
QList<DynamicElementTextItem *> deti_list = dc.m_element_texts.values();
|
||||
@@ -2250,7 +2270,9 @@ void QETDiagramEditor::slot_updateComplexActions()
|
||||
<< m_rotate_selection
|
||||
<< m_rotate_group_selection
|
||||
<< m_edit_selection
|
||||
<< m_group_selected_texts;
|
||||
<< m_group_selected_texts
|
||||
<< m_group_selection
|
||||
<< m_ungroup_selection;
|
||||
for(QAction *action : action_list)
|
||||
action->setEnabled(false);
|
||||
m_align_actions_group.setEnabled(false);
|
||||
@@ -2315,6 +2337,9 @@ void QETDiagramEditor::slot_updateComplexActions()
|
||||
else
|
||||
m_group_selected_texts->setDisabled(true);
|
||||
|
||||
m_group_selection ->setEnabled(GroupItemsCommand::canGroup(diagram_));
|
||||
m_ungroup_selection->setEnabled(GroupItemsCommand::canUngroup(diagram_));
|
||||
|
||||
// actions need only one editable item
|
||||
int selected_image = dc.count(DiagramContent::Images);
|
||||
|
||||
|
||||
@@ -256,6 +256,8 @@ class QETDiagramEditor : public QETMainWindow
|
||||
*m_rotate_texts, ///< Direct selected text items to a specific angle
|
||||
*m_find_element, ///< Find the selected element in the panel
|
||||
*m_group_selected_texts = nullptr,
|
||||
*m_group_selection = nullptr, ///< Group the selected items (#1070)
|
||||
*m_ungroup_selection = nullptr, ///< Ungroup the selected groups
|
||||
*m_close_file, ///< Close current project file
|
||||
*m_save_file, ///< Save current project
|
||||
*m_save_file_as, ///< Save current project as a specific file
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
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 "groupitemscommand.h"
|
||||
|
||||
#include "../diagram.h"
|
||||
#include "../itemgroups.h"
|
||||
#include "../qetgraphicsitem/diagramimageitem.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/independenttextitem.h"
|
||||
#include "../qetgraphicsitem/qetshapeitem.h"
|
||||
|
||||
#include <QSet>
|
||||
|
||||
namespace {
|
||||
QList<QGraphicsObject *> selectedGroupable(Diagram *diagram)
|
||||
{
|
||||
QList<QGraphicsObject *> items;
|
||||
for (QGraphicsItem *item : diagram->selectedItems()) {
|
||||
if (GroupItemsCommand::isGroupable(item)) {
|
||||
items << item->toGraphicsObject();
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return a command grouping the selected symbols, free texts, shapes and
|
||||
pictures of @a diagram into one new group, or nullptr if fewer than two
|
||||
are selected. The selection already holds whole groups (see
|
||||
ItemGroups::completeSelection()), so grouping items that were in groups
|
||||
merges those groups into the new one.
|
||||
*/
|
||||
GroupItemsCommand *GroupItemsCommand::group(Diagram *diagram)
|
||||
{
|
||||
if (!canGroup(diagram)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto command = new GroupItemsCommand(diagram, selectedGroupable(diagram), QUuid::createUuid());
|
||||
command->setText(QObject::tr("Grouper %n objet(s)", "", command->m_changes.size()));
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
@return a command taking every member of the selected groups of
|
||||
@a diagram out of its group, or nullptr if no group is selected.
|
||||
*/
|
||||
GroupItemsCommand *GroupItemsCommand::ungroup(Diagram *diagram)
|
||||
{
|
||||
if (!canUngroup(diagram)) {
|
||||
return nullptr;
|
||||
}
|
||||
QList<QGraphicsObject *> items;
|
||||
for (QGraphicsObject *item : selectedGroupable(diagram)) {
|
||||
if (!ItemGroups::groupOf(item).isNull()) {
|
||||
items << item;
|
||||
}
|
||||
}
|
||||
auto command = new GroupItemsCommand(diagram, items, QUuid());
|
||||
command->setText(QObject::tr("Dégrouper %n objet(s)", "", command->m_changes.size()));
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
@return true for the kinds of item a group can hold: symbols, free texts,
|
||||
shapes and pictures. Never conductors, which follow their symbols, and
|
||||
never a symbol's own texts, which belong to it.
|
||||
*/
|
||||
bool GroupItemsCommand::isGroupable(const QGraphicsItem *item)
|
||||
{
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
switch (item->type())
|
||||
{
|
||||
case Element::Type:
|
||||
case IndependentTextItem::Type:
|
||||
case QetShapeItem::Type:
|
||||
case DiagramImageItem::Type:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if the selection of @a diagram holds at least two items
|
||||
that could be grouped, and is not already exactly one group.
|
||||
*/
|
||||
bool GroupItemsCommand::canGroup(Diagram *diagram)
|
||||
{
|
||||
if (!diagram || diagram->isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
const QList<QGraphicsObject *> items = selectedGroupable(diagram);
|
||||
if (items.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
QSet<QUuid> groups;
|
||||
for (QGraphicsObject *item : items) {
|
||||
groups << ItemGroups::groupOf(item);
|
||||
}
|
||||
return groups.size() > 1 || groups.contains(QUuid());
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if the selection of @a diagram holds a grouped item.
|
||||
*/
|
||||
bool GroupItemsCommand::canUngroup(Diagram *diagram)
|
||||
{
|
||||
if (!diagram || diagram->isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
for (QGraphicsObject *item : selectedGroupable(diagram)) {
|
||||
if (!ItemGroups::groupOf(item).isNull()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief GroupItemsCommand::GroupItemsCommand
|
||||
@param diagram : diagram holding @a items
|
||||
@param items : items to put in @a group
|
||||
@param group : the new group, or a null uuid to ungroup
|
||||
*/
|
||||
GroupItemsCommand::GroupItemsCommand(Diagram *diagram,
|
||||
const QList<QGraphicsObject *> &items,
|
||||
const QUuid &group) :
|
||||
m_diagram(diagram),
|
||||
m_group(group)
|
||||
{
|
||||
for (QGraphicsObject *item : items) {
|
||||
m_changes.append({item, ItemGroups::groupOf(item)});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief GroupItemsCommand::undo
|
||||
*/
|
||||
void GroupItemsCommand::undo()
|
||||
{
|
||||
apply(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief GroupItemsCommand::redo
|
||||
*/
|
||||
void GroupItemsCommand::redo()
|
||||
{
|
||||
apply(true);
|
||||
}
|
||||
|
||||
void GroupItemsCommand::apply(bool redo)
|
||||
{
|
||||
if (!m_diagram) {
|
||||
return;
|
||||
}
|
||||
m_diagram->showMe();
|
||||
for (const Change &change : std::as_const(m_changes)) {
|
||||
if (change.item) {
|
||||
m_diagram->setItemGroup(change.item, redo ? m_group : change.before);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 GROUPITEMSCOMMAND_H
|
||||
#define GROUPITEMSCOMMAND_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QUndoCommand>
|
||||
#include <QUuid>
|
||||
|
||||
class Diagram;
|
||||
class QGraphicsItem;
|
||||
class QGraphicsObject;
|
||||
|
||||
/**
|
||||
@brief The GroupItemsCommand class
|
||||
Groups or ungroups the selected items of a diagram (discussion #1070).
|
||||
Undo gives every item back the exact group it had, so a redo after it
|
||||
still refers to the same groups.
|
||||
*/
|
||||
class GroupItemsCommand : public QUndoCommand
|
||||
{
|
||||
public:
|
||||
static GroupItemsCommand *group(Diagram *diagram);
|
||||
static GroupItemsCommand *ungroup(Diagram *diagram);
|
||||
static bool isGroupable(const QGraphicsItem *item);
|
||||
static bool canGroup(Diagram *diagram);
|
||||
static bool canUngroup(Diagram *diagram);
|
||||
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
GroupItemsCommand(Diagram *diagram, const QList<QGraphicsObject *> &items, const QUuid &group);
|
||||
void apply(bool redo);
|
||||
|
||||
struct Change {
|
||||
QPointer<QGraphicsObject> item;
|
||||
QUuid before;
|
||||
};
|
||||
|
||||
QPointer<Diagram> m_diagram;
|
||||
QList<Change> m_changes;
|
||||
QUuid m_group;
|
||||
};
|
||||
|
||||
#endif // GROUPITEMSCOMMAND_H
|
||||
@@ -111,6 +111,12 @@ add_test(NAME tst_alignment COMMAND tst_alignment)
|
||||
target_include_directories(tst_alignment PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_alignment PRIVATE Qt::Test)
|
||||
|
||||
# itemgroups.cpp keeps group membership and the selection rule apart from
|
||||
# Diagram, so it is tested here on a plain scene of rectangles.
|
||||
add_executable(tst_itemgroups tst_itemgroups.cpp ${QET_DIR}/sources/itemgroups.cpp)
|
||||
add_test(NAME tst_itemgroups COMMAND tst_itemgroups)
|
||||
target_include_directories(tst_itemgroups PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_itemgroups PRIVATE Qt::Test Qt::Widgets Qt::Xml)
|
||||
# legacyelementtypes.h is header-only: the rewrite of element type names in
|
||||
# nomenclature queries saved before June 2022.
|
||||
add_executable(tst_legacyelementtypes tst_legacyelementtypes.cpp)
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
#include <QtTest>
|
||||
#include <QDomDocument>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QGraphicsScene>
|
||||
|
||||
#include "itemgroups.h"
|
||||
|
||||
class tst_itemgroups : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QGraphicsScene *scene = nullptr;
|
||||
QGraphicsRectItem *a = nullptr, *b = nullptr, *c = nullptr, *d = nullptr, *e = nullptr;
|
||||
QUuid g1, g2;
|
||||
|
||||
QGraphicsRectItem *add()
|
||||
{
|
||||
auto item = scene->addRect(0, 0, 10, 10);
|
||||
item->setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
return item;
|
||||
}
|
||||
|
||||
QList<QGraphicsItem *> selection() const { return scene->selectedItems(); }
|
||||
|
||||
// Select exactly @a items, as Qt leaves the scene after a click.
|
||||
void select(const QList<QGraphicsItem *> &items)
|
||||
{
|
||||
scene->clearSelection();
|
||||
for (auto item : items) {
|
||||
item->setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool selected(std::initializer_list<QGraphicsItem *> expected) const
|
||||
{
|
||||
QList<QGraphicsItem *> got = selection();
|
||||
if (got.size() != int(expected.size())) {
|
||||
return false;
|
||||
}
|
||||
for (auto item : expected) {
|
||||
if (!got.contains(item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private slots:
|
||||
// a, b in g1; c alone; d, e in g2
|
||||
void init()
|
||||
{
|
||||
scene = new QGraphicsScene;
|
||||
a = add(); b = add(); c = add(); d = add(); e = add();
|
||||
g1 = QUuid::createUuid();
|
||||
g2 = QUuid::createUuid();
|
||||
ItemGroups::setGroup(a, g1);
|
||||
ItemGroups::setGroup(b, g1);
|
||||
ItemGroups::setGroup(d, g2);
|
||||
ItemGroups::setGroup(e, g2);
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
delete scene;
|
||||
scene = nullptr;
|
||||
}
|
||||
|
||||
void clickingAMemberSelectsTheGroup()
|
||||
{
|
||||
select({a});
|
||||
QVERIFY(ItemGroups::completeSelection(scene, {}, false));
|
||||
QVERIFY(selected({a, b}));
|
||||
}
|
||||
|
||||
// Releasing a click on one member of the selected group leaves only it
|
||||
// selected in Qt; the group must stay whole.
|
||||
void reclickingAMemberKeepsTheGroup()
|
||||
{
|
||||
select({b});
|
||||
ItemGroups::completeSelection(scene, {a, b}, false);
|
||||
QVERIFY(selected({a, b}));
|
||||
}
|
||||
|
||||
void ctrlClickingAMemberOffDeselectsTheGroup()
|
||||
{
|
||||
select({b});
|
||||
ItemGroups::completeSelection(scene, {a, b}, true);
|
||||
QVERIFY(selected({}));
|
||||
}
|
||||
|
||||
void ctrlClickingAMemberOnAddsTheGroup()
|
||||
{
|
||||
select({c, a});
|
||||
ItemGroups::completeSelection(scene, {c}, true);
|
||||
QVERIFY(selected({a, b, c}));
|
||||
}
|
||||
|
||||
void otherGroupsAreUntouched()
|
||||
{
|
||||
select({b, d, e});
|
||||
ItemGroups::completeSelection(scene, {a, b, d, e}, true);
|
||||
QVERIFY(selected({d, e}));
|
||||
}
|
||||
|
||||
void ungroupedItemsAreUntouched()
|
||||
{
|
||||
select({c});
|
||||
QVERIFY(!ItemGroups::completeSelection(scene, {}, false));
|
||||
QVERIFY(selected({c}));
|
||||
}
|
||||
|
||||
// A member taken off the scene (deleted, kept by the undo stack) must
|
||||
// not count as "deselected".
|
||||
void itemsLeavingTheSceneAreIgnored()
|
||||
{
|
||||
select({a, b});
|
||||
scene->removeItem(b);
|
||||
ItemGroups::completeSelection(scene, {a, b}, true);
|
||||
QVERIFY(selected({a}));
|
||||
delete b;
|
||||
b = nullptr;
|
||||
}
|
||||
|
||||
void xmlRoundTrip()
|
||||
{
|
||||
QDomDocument doc;
|
||||
QDomElement grouped = doc.createElement(QStringLiteral("element"));
|
||||
ItemGroups::write(grouped, a);
|
||||
QCOMPARE(ItemGroups::read(grouped), g1);
|
||||
|
||||
QDomElement alone = doc.createElement(QStringLiteral("element"));
|
||||
ItemGroups::write(alone, c);
|
||||
QVERIFY(!alone.hasAttribute(QStringLiteral("group")));
|
||||
QVERIFY(ItemGroups::read(alone).isNull());
|
||||
|
||||
ItemGroups::setGroup(a, QUuid());
|
||||
QVERIFY(ItemGroups::groupOf(a).isNull());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_itemgroups)
|
||||
#include "tst_itemgroups.moc"
|
||||
Reference in New Issue
Block a user