mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-29 05:44:14 +02:00
a8f940505d
Discussion #1070 proposed that rotate, like move, copy and delete, works on the whole group once one of its items is clicked. Rotate (Space) turned each member on its own spot instead, so rotating a group pulled it apart: two grouped texts side by side ended up each turned in place, no longer side by side. When the selection is exactly one whole group -- wires aside, which follow their symbols -- Rotate now turns it as one piece around its centre, as "Pivoter le groupe" (Shift+Space) already does (ItemGroups::soleWholeGroup()). Any other selection, including a single member picked out of its group, rotates as before. In the GUI, on two grouped texts selected by one click: Space on master leaves both where they were, turned; here it gives exactly what Shift+Space gives on both (both texts swung around the group's centre). tst_itemgroups: 4 new checks; without the whole-group condition, a picked member counts as a group and fails. ctest 24/24. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
156 lines
4.3 KiB
C++
156 lines
4.3 KiB
C++
/*
|
|
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;
|
|
}
|
|
|
|
/**
|
|
@return the group @a selected is exactly, whole -- every item in it
|
|
belongs to that group and every member of the group is in it -- or a null
|
|
uuid. Rotating such a selection turns the group as one piece rather than
|
|
each member in place (discussion #1070). A member picked out on its own
|
|
is not a whole group, and turns in place.
|
|
@param selected : the selected items that can be members (the caller
|
|
leaves out wires, which follow their symbols)
|
|
*/
|
|
QUuid ItemGroups::soleWholeGroup(const QList<QGraphicsItem *> &selected)
|
|
{
|
|
if (selected.isEmpty() || !selected.first()->scene()) {
|
|
return QUuid();
|
|
}
|
|
const QUuid group = groupOf(selected.first());
|
|
if (group.isNull()) {
|
|
return QUuid();
|
|
}
|
|
for (QGraphicsItem *item : selected) {
|
|
if (groupOf(item) != group) {
|
|
return QUuid();
|
|
}
|
|
}
|
|
for (QGraphicsItem *item : selected.first()->scene()->items()) {
|
|
if (groupOf(item) == group && !item->isSelected()) {
|
|
return QUuid();
|
|
}
|
|
}
|
|
return group;
|
|
}
|