mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-05 18:54:14 +02:00
649f06b3b8
A submenu under View (Affichage > Afficher) with one tick per kind: symbol texts, wire texts, free texts, shapes, pictures, tables and cross-references. Unticking one hides it on every folio of every open project; the status bar says how many kinds are hidden. Session only, not saved in the project. Each item is tagged with its kind by its constructor (ShownKinds::tag), so an item created while its kind is hidden starts hidden whatever created it. A tag rather than type(): the cross-reference under a contact's label is a plain QGraphicsTextItem. Items that already hide themselves (wire text switched off, one text per potential, a cross-reference snapped elsewhere) now go through ShownKinds::setVisible(): the kind state can only veto a show, and apply() re-shows only what it hid itself (hidden_key), never what an item hid. Conductor::updateTextVisibility() replaces the same visibility rule pasted in diagrampropertiesdialog.cpp and qetscriptapi.cpp. Hidden items are not selectable (Qt), so Select All, copy and delete skip them. Print, PDF and image export render the scene and leave them out; the DXF export walks items itself and skips them through ShownKinds::isHidden(). CrossRefItem::linkedChanged() keeps following its slaves while hidden only by kind, so its label is right when shown. Known limitation: QGraphicsScene::itemsBoundingRect() still counts hidden items, so Zoom to fit and borderless image export keep their space. Verified: tst_shownkinds (12 cases, two mutation checks) and ctest 55/55; GUI hide/show of symbol texts, wire texts (incl. one text per potential), free texts, shapes and cross-references returns pixel-identical folios; with nothing hidden, --export-dxf of the 24 examples is byte-identical to master (133 files) and --export-pdf renders identically (133 pages). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
1003 lines
29 KiB
C++
1003 lines
29 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 "elementtextitemgroup.h"
|
|
#include "../shownkinds.h"
|
|
#include "../qetproject.h"
|
|
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
|
#include "../diagram.h"
|
|
#include "../qetapp.h"
|
|
#include "../undocommand/addelementtextcommand.h"
|
|
#include "crossrefitem.h"
|
|
#include "dynamicelementtextitem.h"
|
|
#include "element.h"
|
|
#include "masterelement.h"
|
|
#include "qgraphicsitemutility.h"
|
|
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QPainter>
|
|
#include <utility>
|
|
|
|
/**
|
|
@brief sorting
|
|
@param qgia
|
|
@param qgib
|
|
@return
|
|
*/
|
|
bool sorting(QGraphicsItem *qgia, QGraphicsItem *qgib)
|
|
{
|
|
return qgia->pos().y() < qgib->pos().y();
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::ElementTextItemGroup
|
|
@param name :
|
|
@param parent :
|
|
*/
|
|
ElementTextItemGroup::ElementTextItemGroup(const QString &name,
|
|
Element *parent) :
|
|
QGraphicsItemGroup(parent),
|
|
m_name(name),
|
|
m_parent_element(parent)
|
|
{
|
|
ShownKinds::tag(this, ShownKinds::SymbolTexts);
|
|
setFlags(QGraphicsItem::ItemIsSelectable
|
|
| QGraphicsItem::ItemIsMovable);
|
|
connect(parent,
|
|
&Element::linkedElementChanged,
|
|
this,
|
|
&ElementTextItemGroup::updateXref);
|
|
if(parent->diagram())
|
|
m_project_xref_connection = connect(
|
|
parent->diagram()->project(),
|
|
&QETProject::XRefPropertiesChanged,
|
|
this,
|
|
&ElementTextItemGroup::updateXref);
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::itemChange
|
|
The group is very often built while its element is not on a scene yet
|
|
(project or element loading): the connection to the project was then
|
|
impossible and the first updateXref() ran without a diagram, so the
|
|
cross ref of a master waiting for its slaves stayed invisible until an
|
|
unrelated settings change happened to refresh it. Do both here, the
|
|
moment the group really reaches the scene.
|
|
@param change
|
|
@param value
|
|
@return
|
|
*/
|
|
QVariant ElementTextItemGroup::itemChange(
|
|
QGraphicsItem::GraphicsItemChange change,
|
|
const QVariant &value)
|
|
{
|
|
if (change == QGraphicsItem::ItemSceneHasChanged)
|
|
{
|
|
if (m_parent_element
|
|
&& m_parent_element->diagram()
|
|
&& m_parent_element->diagram()->project())
|
|
{
|
|
QETProject *project = m_parent_element->diagram()->project();
|
|
if (!m_project_xref_connection)
|
|
m_project_xref_connection = connect(
|
|
project,
|
|
&QETProject::XRefPropertiesChanged,
|
|
this,
|
|
&ElementTextItemGroup::updateXref);
|
|
updateXref();
|
|
}
|
|
}
|
|
return QGraphicsItemGroup::itemChange(change, value);
|
|
}
|
|
|
|
ElementTextItemGroup::~ElementTextItemGroup()
|
|
{}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::addToGroup
|
|
@param item
|
|
*/
|
|
void ElementTextItemGroup::addToGroup(QGraphicsItem *item)
|
|
{
|
|
if(item->type() == DynamicElementTextItem::Type)
|
|
{
|
|
//Before adding text to this group we must set the text at the same rotation of this group
|
|
if((item->rotation() != rotation()) && !m_block_alignment_update)
|
|
item->setRotation(rotation());
|
|
|
|
QGraphicsItemGroup::addToGroup(item);
|
|
updateAlignment();
|
|
|
|
DynamicElementTextItem *deti = qgraphicsitem_cast<DynamicElementTextItem *>(item);
|
|
connect(deti, &DynamicElementTextItem::fontChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
connect(deti, &DynamicElementTextItem::textChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
connect(deti, &DynamicElementTextItem::textFromChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
connect(deti, &DynamicElementTextItem::infoNameChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
connect(deti, &DynamicElementTextItem::compositeTextChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
connect(deti, &DynamicElementTextItem::plainTextChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
connect(deti, &DynamicElementTextItem::textWidthChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
|
|
connect(deti, &DynamicElementTextItem::textFromChanged,
|
|
this, &ElementTextItemGroup::updateXref);
|
|
connect(deti, &DynamicElementTextItem::infoNameChanged,
|
|
this, &ElementTextItemGroup::updateXref);
|
|
|
|
updateXref();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::removeFromGroup
|
|
@param item
|
|
*/
|
|
void ElementTextItemGroup::removeFromGroup(QGraphicsItem *item)
|
|
{
|
|
QGraphicsItemGroup::removeFromGroup(item);
|
|
//the item transformation is not reset, we must do it ourselves,
|
|
// because for example if the group rotation is 45°
|
|
//When item is removed from group,
|
|
// visually the item is unchanged (so 45°)
|
|
// but if we call item->rotation() the returned value is 0.
|
|
item->resetTransform();
|
|
item->setRotation(this->rotation());
|
|
item->setFlag(QGraphicsItem::ItemIsSelectable, true);
|
|
updateAlignment();
|
|
|
|
if(DynamicElementTextItem *deti = qgraphicsitem_cast<DynamicElementTextItem *>(item))
|
|
{
|
|
disconnect(deti, &DynamicElementTextItem::fontChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
disconnect(deti, &DynamicElementTextItem::textChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
disconnect(deti, &DynamicElementTextItem::textFromChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
disconnect(deti, &DynamicElementTextItem::infoNameChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
disconnect(deti, &DynamicElementTextItem::compositeTextChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
disconnect(deti, &DynamicElementTextItem::plainTextChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
disconnect(deti, &DynamicElementTextItem::textWidthChanged,
|
|
this, &ElementTextItemGroup::updateAlignment);
|
|
|
|
disconnect(deti, &DynamicElementTextItem::textFromChanged,
|
|
this, &ElementTextItemGroup::updateXref);
|
|
disconnect(deti, &DynamicElementTextItem::infoNameChanged,
|
|
this, &ElementTextItemGroup::updateXref);
|
|
|
|
updateXref();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::blockAlignmentUpdate
|
|
If true, the texts in this group are never aligned, moved, rotated etc...
|
|
the texts stay as it was, until blockAlignmentUpdate is set to false.
|
|
@param block
|
|
*/
|
|
void ElementTextItemGroup::blockAlignmentUpdate(bool block)
|
|
{
|
|
m_block_alignment_update = block;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::setAlignement
|
|
Set the alignement of this group
|
|
@param alignement
|
|
*/
|
|
void ElementTextItemGroup::setAlignment(Qt::Alignment alignement)
|
|
{
|
|
m_alignment = alignement;
|
|
updateAlignment();
|
|
emit alignmentChanged(alignement);
|
|
}
|
|
|
|
Qt::Alignment ElementTextItemGroup::alignment() const
|
|
{
|
|
return m_alignment;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::setAlignment
|
|
Update the alignement of the items in this group, according
|
|
to the current alignement.
|
|
@param alignement
|
|
*/
|
|
void ElementTextItemGroup::updateAlignment()
|
|
{
|
|
if(m_block_alignment_update)
|
|
return;
|
|
|
|
prepareGeometryChange();
|
|
|
|
QList <DynamicElementTextItem *> texts = this->texts();
|
|
|
|
qreal rotation_ = rotation();
|
|
|
|
//Set the rotation of this group to 0° relative to the scene
|
|
qreal rot = rotation();
|
|
QGraphicsItem *parent = parentItem();
|
|
while (parent) {
|
|
rot += parent->rotation();
|
|
parent = parent->parentItem();
|
|
}
|
|
if(rot != 0)
|
|
setRotation(rotation() - rot);
|
|
|
|
|
|
if(texts.size() == 1)
|
|
{
|
|
prepareGeometryChange();
|
|
|
|
QGraphicsItem *first = texts.first();
|
|
setPos(mapFromScene(first->mapToScene(pos())));
|
|
first->setPos(0,0);
|
|
}
|
|
else if (texts.size() > 1)
|
|
{
|
|
qreal width = 0;
|
|
for(QGraphicsItem *item : texts)
|
|
if(item->boundingRect().width() > width)
|
|
width = item->boundingRect().width();
|
|
|
|
prepareGeometryChange();
|
|
std::sort(texts.begin(), texts.end(), sorting);
|
|
|
|
qreal y_offset = 0;
|
|
|
|
if(m_alignment == Qt::AlignLeft)
|
|
{
|
|
QPointF ref = texts.first()->pos();
|
|
|
|
for(QGraphicsItem *item : texts)
|
|
{
|
|
item->setPos(0, ref.y()+y_offset);
|
|
y_offset+=item->boundingRect().height() + m_vertical_adjustment;
|
|
}
|
|
}
|
|
else if(m_alignment == Qt::AlignVCenter)
|
|
{
|
|
QPointF ref(width/2,0);
|
|
|
|
for(QGraphicsItem *item : texts)
|
|
{
|
|
item->setPos(ref.x() - item->boundingRect().width()/2,
|
|
ref.y() + y_offset);
|
|
y_offset+=item->boundingRect().height() + m_vertical_adjustment;
|
|
}
|
|
}
|
|
else if (m_alignment == Qt::AlignRight)
|
|
{
|
|
QPointF ref(width,0);
|
|
|
|
for(QGraphicsItem *item : texts)
|
|
{
|
|
item->setPos(ref.x() - item->boundingRect().width(),
|
|
ref.y() + y_offset);
|
|
y_offset+=item->boundingRect().height() + m_vertical_adjustment;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Restore the rotation
|
|
setRotation(rotation_);
|
|
|
|
if(m_Xref_item)
|
|
m_Xref_item->autoPos();
|
|
if(m_slave_Xref_item)
|
|
{
|
|
int slave_offset = 0;
|
|
Element *master_elmt = m_parent_element->linkedElements().isEmpty()
|
|
? nullptr : m_parent_element->linkedElements().first();
|
|
if (master_elmt && m_parent_element->diagram()) {
|
|
XRefProperties xrp = m_parent_element->diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
|
slave_offset = xrp.slaveOffset();
|
|
}
|
|
adjustSlaveXrefPos(slave_offset);
|
|
}
|
|
if(m_hold_to_bottom_of_page)
|
|
autoPos();
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::setVerticalAdjustment
|
|
Set the value of the vertical adjustment to v.
|
|
The vertical adjutment is use to adjust
|
|
the space between the texts of this group.
|
|
@param v
|
|
*/
|
|
void ElementTextItemGroup::setVerticalAdjustment(int v)
|
|
{
|
|
prepareGeometryChange();
|
|
m_vertical_adjustment = v;
|
|
updateAlignment();
|
|
emit verticalAdjustmentChanged(v);
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::setName
|
|
@param name Set the name of this group
|
|
*/
|
|
void ElementTextItemGroup::setName(QString name)
|
|
{
|
|
m_name = std::move(name);
|
|
emit nameChanged(m_name);
|
|
}
|
|
|
|
void ElementTextItemGroup::setHoldToBottomPage(bool hold)
|
|
{
|
|
if(m_hold_to_bottom_of_page == hold)
|
|
return;
|
|
|
|
m_hold_to_bottom_of_page = hold;
|
|
if(m_hold_to_bottom_of_page)
|
|
{
|
|
setFlag(QGraphicsItem::ItemIsSelectable, false);
|
|
setFlag(QGraphicsItem::ItemIsMovable, false);
|
|
connect(m_parent_element, &Element::yChanged, this, &ElementTextItemGroup::autoPos);
|
|
connect(m_parent_element, &Element::rotationChanged, this, &ElementTextItemGroup::autoPos);
|
|
if(m_parent_element->linkType() == Element::Master)
|
|
{
|
|
//We use timer to let the time of the parent element
|
|
// xref to be updated,
|
|
// before updating the position of this group
|
|
//because the position of this group is related
|
|
// to the size of the parent element Xref
|
|
m_linked_changed_timer = connect(
|
|
m_parent_element,
|
|
&Element::linkedElementChanged,
|
|
[this]()
|
|
{QTimer::singleShot(200,
|
|
this,
|
|
&ElementTextItemGroup::autoPos);}
|
|
);
|
|
if(m_parent_element->diagram())
|
|
m_XrefChanged_timer = connect(
|
|
m_parent_element->diagram()->project(),
|
|
&QETProject::XRefPropertiesChanged,
|
|
[this]()
|
|
{QTimer::singleShot(200,
|
|
this,
|
|
&ElementTextItemGroup::autoPos);}
|
|
);
|
|
}
|
|
autoPos();
|
|
}
|
|
else
|
|
{
|
|
setFlag(QGraphicsItem::ItemIsSelectable, true);
|
|
setFlag(QGraphicsItem::ItemIsMovable, true);
|
|
disconnect(m_parent_element, &Element::yChanged,
|
|
this, &ElementTextItemGroup::autoPos);
|
|
disconnect(m_parent_element, &Element::rotationChanged,
|
|
this, &ElementTextItemGroup::autoPos);
|
|
if(m_parent_element->linkType() == Element::Master)
|
|
{
|
|
disconnect(m_linked_changed_timer);
|
|
if(m_XrefChanged_timer)
|
|
disconnect(m_XrefChanged_timer);
|
|
}
|
|
}
|
|
|
|
emit holdToBottomPageChanged(hold);
|
|
}
|
|
|
|
void ElementTextItemGroup::setFrame(const bool frame)
|
|
{
|
|
m_frame = frame;
|
|
update();
|
|
emit frameChanged(m_frame);
|
|
}
|
|
|
|
bool ElementTextItemGroup::frame() const
|
|
{
|
|
return m_frame;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::texts
|
|
@return Every texts in this group
|
|
*/
|
|
QList<DynamicElementTextItem *> ElementTextItemGroup::texts() const
|
|
{
|
|
QList<DynamicElementTextItem *> list;
|
|
for(QGraphicsItem *qgi : childItems())
|
|
{
|
|
if(qgi->type() == DynamicElementTextItem::Type)
|
|
list << static_cast<DynamicElementTextItem *>(qgi);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::diagram
|
|
@return The diagram of this group, or nullptr if this group is not in a diagram
|
|
*/
|
|
Diagram *ElementTextItemGroup::diagram() const
|
|
{
|
|
if(scene())
|
|
return static_cast<Diagram *>(scene());
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::parentElement
|
|
@return The parent element of this group or nullptr
|
|
*/
|
|
Element *ElementTextItemGroup::parentElement() const
|
|
{
|
|
if(parentItem() && parentItem()->type() == Element::Type)
|
|
return static_cast<Element *>(parentItem());
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::toXml
|
|
Export data of this group to xml
|
|
@param dom_document
|
|
@return
|
|
*/
|
|
QDomElement ElementTextItemGroup::toXml(QDomDocument &dom_document) const
|
|
{
|
|
QDomElement dom_element = dom_document.createElement(this->xmlTaggName());
|
|
dom_element.setAttribute("name", m_name);
|
|
|
|
dom_element.setAttribute("x", QString::number(pos().x()));
|
|
dom_element.setAttribute("y", QString::number(pos().y()));
|
|
|
|
QMetaEnum me = QMetaEnum::fromType<Qt::Alignment>();
|
|
dom_element.setAttribute("alignment", me.valueToKey(m_alignment));
|
|
|
|
dom_element.setAttribute("rotation", this->rotation());
|
|
dom_element.setAttribute("vertical_adjustment", m_vertical_adjustment);
|
|
dom_element.setAttribute("frame", m_frame? "true" : "false");
|
|
|
|
dom_element.setAttribute("hold_to_bottom_page",
|
|
m_hold_to_bottom_of_page == true ? "true" : "false");
|
|
|
|
QDomElement dom_texts = dom_document.createElement("texts");
|
|
for(DynamicElementTextItem *deti : texts())
|
|
{
|
|
QDomElement text = dom_document.createElement("text");
|
|
text.setAttribute("uuid", deti->uuid().toString());
|
|
dom_texts.appendChild(text);
|
|
}
|
|
|
|
dom_element.appendChild(dom_texts);
|
|
return dom_element;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::fromXml
|
|
Import data of this group from xml
|
|
@param dom_element
|
|
*/
|
|
void ElementTextItemGroup::fromXml(QDomElement &dom_element)
|
|
{
|
|
if (dom_element.tagName() != xmlTaggName()) {
|
|
qDebug() << "ElementTextItemGroup::fromXml : Wrong tagg name";
|
|
return;
|
|
}
|
|
|
|
setName(dom_element.attribute("name", "no name"));
|
|
QMetaEnum me = QMetaEnum::fromType<Qt::Alignment>();
|
|
setAlignment(
|
|
Qt::Alignment(
|
|
me.keyToValue(
|
|
dom_element.attribute(
|
|
"alignment")
|
|
.toStdString()
|
|
.data()
|
|
)
|
|
)
|
|
);
|
|
|
|
setPos(dom_element.attribute("x", QString::number(0)).toDouble(),
|
|
dom_element.attribute("y", QString::number(0)).toDouble());
|
|
|
|
setRotation(dom_element.attribute("rotation", QString::number(0)).toDouble());
|
|
setVerticalAdjustment(dom_element.attribute("vertical_adjustment").toInt());
|
|
setFrame(dom_element.attribute("frame", "false") == "true"? true : false);
|
|
|
|
QString hold = dom_element.attribute("hold_to_bottom_page", "false");
|
|
setHoldToBottomPage(hold == "true" ? true : false);
|
|
|
|
if(parentElement())
|
|
{
|
|
m_block_alignment_update = true;
|
|
for(const QDomElement& text : QET::findInDomElement(dom_element, "texts", "text"))
|
|
{
|
|
DynamicElementTextItem *deti = nullptr;
|
|
QUuid uuid(text.attribute("uuid"));
|
|
|
|
for(DynamicElementTextItem *txt : parentElement()->dynamicTextItems())
|
|
if(txt->uuid() == uuid)
|
|
deti = txt;
|
|
|
|
if (deti)
|
|
parentElement()->addTextToGroup(deti, this);
|
|
}
|
|
m_block_alignment_update = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::paint
|
|
@param painter
|
|
@param option
|
|
@param widget
|
|
*/
|
|
void ElementTextItemGroup::paint(
|
|
QPainter *painter,
|
|
const QStyleOptionGraphicsItem *option,
|
|
QWidget *widget)
|
|
{
|
|
Q_UNUSED(option);
|
|
Q_UNUSED(widget);
|
|
if(isSelected())
|
|
{
|
|
painter->save();
|
|
QPen t;
|
|
t.setColor(Qt::gray);
|
|
t.setStyle(Qt::DashDotLine);
|
|
t.setCosmetic(true);
|
|
painter->setPen(t);
|
|
painter->drawRoundedRect(boundingRect().adjusted(1, 1, -1, -1),
|
|
10, 10);
|
|
|
|
painter->restore();
|
|
}
|
|
if(m_frame)
|
|
{
|
|
qreal font_size = 1;
|
|
QRectF rect;
|
|
for(DynamicElementTextItem *deti : this->texts())
|
|
{
|
|
font_size = std::max(font_size, deti->font().pointSizeF());
|
|
rect = rect.united(mapFromItem(deti, deti->frameRect()).boundingRect());
|
|
}
|
|
|
|
//Adjust the thickness according to the font size
|
|
qreal w=0.3;
|
|
if (font_size >= 5)
|
|
{
|
|
w = font_size*0.1;
|
|
if(w > 2.5)
|
|
w = 2.5;
|
|
}
|
|
|
|
painter->save();
|
|
QPen pen;
|
|
pen.setWidthF(w);
|
|
painter->setPen(pen);
|
|
painter->setRenderHint(QPainter::Antialiasing);
|
|
|
|
//Adjust the rounding of the rectangle according to the size of the font
|
|
qreal ro = font_size/3;
|
|
painter->drawRoundedRect(rect, ro, ro);
|
|
painter->restore();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::boundingRect
|
|
@return
|
|
*/
|
|
QRectF ElementTextItemGroup::boundingRect() const
|
|
{
|
|
//If we refer to the Qt doc, the bounding rect of a QGraphicsItemGroup,
|
|
//is the bounding of all childrens in the group
|
|
//When add an item in the group, the bounding rect is good, but
|
|
//if we move an item already in the group, the bounding rect of the group stay unchanged.
|
|
//We reimplement this function to avoid this behavior.
|
|
QRectF rect;
|
|
for(QGraphicsItem *qgi : texts())
|
|
{
|
|
QRectF r(qgi->pos(), QSize(qgi->boundingRect().width(),
|
|
qgi->boundingRect().height()));
|
|
rect = rect.united(r);
|
|
}
|
|
return rect;
|
|
}
|
|
|
|
void ElementTextItemGroup::setRotation(qreal angle)
|
|
{
|
|
QGraphicsItemGroup::setRotation(angle);
|
|
emit rotationChanged(angle);
|
|
}
|
|
|
|
void ElementTextItemGroup::setPos(const QPointF &pos)
|
|
{
|
|
QPointF old_pos = this->pos();
|
|
QGraphicsItemGroup::setPos(pos);
|
|
if (old_pos.x() != this->pos().x())
|
|
emit xChanged();
|
|
if (old_pos.y() != this->pos().y())
|
|
emit yChanged();
|
|
}
|
|
|
|
void ElementTextItemGroup::setPos(qreal x, qreal y)
|
|
{
|
|
QPointF old_pos = this->pos();
|
|
QGraphicsItemGroup::setPos(x,y);
|
|
if (old_pos.x() != this->pos().x())
|
|
emit xChanged();
|
|
if (old_pos.y() != this->pos().y())
|
|
emit yChanged();
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::mousePressEvent
|
|
@param event
|
|
*/
|
|
void ElementTextItemGroup::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if(event->button() == Qt::LeftButton)
|
|
{
|
|
m_first_move = true;
|
|
if(event->modifiers() & Qt::ControlModifier)
|
|
setSelected(!isSelected());
|
|
}
|
|
|
|
QGraphicsItemGroup::mousePressEvent(event);
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::mouseMoveEvent
|
|
@param event
|
|
*/
|
|
void ElementTextItemGroup::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if((event->buttons() & Qt::LeftButton) && (flags() & ItemIsMovable))
|
|
{
|
|
if(diagram() && m_first_move)
|
|
diagram()->elementTextsMover().beginMovement(diagram(),
|
|
this);
|
|
|
|
if(m_first_move)
|
|
{
|
|
m_initial_position = pos();
|
|
if(parentElement())
|
|
parentElement()->setHighlighted(true);
|
|
}
|
|
|
|
QPointF current_parent_pos;
|
|
QPointF button_down_parent_pos;
|
|
current_parent_pos = mapToParent(mapFromScene(event->scenePos()));
|
|
button_down_parent_pos = mapToParent(mapFromScene(event->buttonDownScenePos(Qt::LeftButton)));
|
|
|
|
QPointF new_pos = m_initial_position + current_parent_pos - button_down_parent_pos;
|
|
event->modifiers() == Qt::ControlModifier ? setPos(new_pos) : setPos(Diagram::snapToTextGrid(new_pos));
|
|
|
|
if(diagram())
|
|
diagram()->elementTextsMover().continueMovement(event);
|
|
} else {
|
|
event->ignore();
|
|
}
|
|
|
|
if(m_first_move)
|
|
m_first_move = false;
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::mouseReleaseEvent
|
|
@param event
|
|
*/
|
|
void ElementTextItemGroup::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if(diagram())
|
|
{
|
|
diagram()->elementTextsMover().endMovement();
|
|
if(parentElement())
|
|
parentElement()->setHighlighted(false);
|
|
}
|
|
|
|
if(!(event->modifiers() & Qt::ControlModifier))
|
|
QGraphicsItemGroup::mouseReleaseEvent(event);
|
|
}
|
|
|
|
void ElementTextItemGroup::mouseDoubleClickEvent(
|
|
QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if(m_slave_Xref_item)
|
|
{
|
|
if(m_slave_Xref_item->boundingRect().contains(mapToItem(m_slave_Xref_item, event->pos())))
|
|
{
|
|
if(parentElement()->linkType() == Element::Slave && !parentElement()->linkedElements().isEmpty())
|
|
{
|
|
m_slave_Xref_item->setDefaultTextColor(Qt::black);
|
|
Element *elmt = parentElement()->linkedElements().first();
|
|
|
|
//Unselect and ungrab mouse to prevent unwanted
|
|
//move when linked element is in the same scene of this.
|
|
setSelected(false);
|
|
ungrabMouse();
|
|
|
|
if(scene() != elmt->scene())
|
|
elmt->diagram()->showMe();
|
|
elmt->setSelected(true);
|
|
|
|
//Zoom to the element
|
|
for(QGraphicsView *view : elmt->scene()->views())
|
|
{
|
|
QRectF fit = elmt->sceneBoundingRect();
|
|
fit.adjust(-200, -200, 200, 200);
|
|
view->fitInView(fit, Qt::KeepAspectRatioByExpanding);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ElementTextItemGroup::keyPressEvent
|
|
@param event
|
|
*/
|
|
void ElementTextItemGroup::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
if(event->modifiers() == Qt::ControlModifier)
|
|
{
|
|
if(event->key() == Qt::Key_Left && m_alignment != Qt::AlignLeft)
|
|
{
|
|
if(diagram())
|
|
diagram()->undoStack().push(new AlignmentTextsGroupCommand(this, Qt::AlignLeft));
|
|
else
|
|
setAlignment(Qt::AlignLeft);
|
|
}
|
|
else if (event->key() == Qt::Key_Up && m_alignment != Qt::AlignVCenter)
|
|
{
|
|
if(diagram())
|
|
diagram()->undoStack().push(new AlignmentTextsGroupCommand(this, Qt::AlignVCenter));
|
|
else
|
|
setAlignment(Qt::AlignVCenter);
|
|
}
|
|
else if (event->key() == Qt::Key_Right && m_alignment != Qt::AlignRight)
|
|
{
|
|
if(diagram())
|
|
diagram()->undoStack().push(new AlignmentTextsGroupCommand(this, Qt::AlignRight));
|
|
else
|
|
setAlignment(Qt::AlignRight);
|
|
}
|
|
}
|
|
event->ignore();
|
|
}
|
|
|
|
void ElementTextItemGroup::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
|
|
{
|
|
//The pos of the event is not in this item coordinate,
|
|
//but in child item hovered by the mouse, so we use the scene pos.
|
|
if(m_slave_Xref_item &&
|
|
m_slave_Xref_item->boundingRect().contains(m_slave_Xref_item->mapFromScene(event->scenePos())))
|
|
m_slave_Xref_item->setDefaultTextColor(Qt::blue);
|
|
|
|
QGraphicsItemGroup::hoverEnterEvent(event);
|
|
}
|
|
|
|
void ElementTextItemGroup::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
|
|
{
|
|
if(m_slave_Xref_item)
|
|
m_slave_Xref_item->setDefaultTextColor(Qt::black);
|
|
|
|
QGraphicsItemGroup::hoverLeaveEvent(event);
|
|
}
|
|
|
|
void ElementTextItemGroup::updateXref()
|
|
{
|
|
if(m_parent_element->diagram())
|
|
{
|
|
QETProject *project = m_parent_element->diagram()->project();
|
|
|
|
if(m_parent_element->linkType() == Element::Master)
|
|
{
|
|
XRefProperties xrp = project->defaultXRefProperties(m_parent_element->kindInformations()["type"].toString());
|
|
|
|
if(xrp.snapTo() == XRefProperties::Label &&
|
|
(!m_parent_element->linkedElements().isEmpty()
|
|
|| CrossRefItem::showAllConfiguredSlaves(m_parent_element, xrp)))
|
|
{
|
|
//At least one text owned by this group must be set with
|
|
//textFrom -> element info and element info name -> label
|
|
//for display a xref
|
|
for(DynamicElementTextItem *deti : texts())
|
|
{
|
|
if(deti->textFrom() == DynamicElementTextItem::ElementInfo &&
|
|
deti->infoName() == "label")
|
|
{
|
|
if(!m_Xref_item)
|
|
m_Xref_item = new CrossRefItem(m_parent_element, this);
|
|
m_Xref_item->autoPos();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(m_parent_element->linkType() == Element::Slave &&
|
|
!m_parent_element->linkedElements().isEmpty())
|
|
{
|
|
Element *master_elmt = m_parent_element->linkedElements().first();
|
|
XRefProperties xrp = project->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
|
|
|
//Champ de texte: store xref in element informations
|
|
if(xrp.getXrefPos() == Qt::AlignHCenter)
|
|
{
|
|
for(DynamicElementTextItem *deti : texts())
|
|
{
|
|
if(deti->textFrom() == DynamicElementTextItem::ElementInfo && deti->infoName() == "xref")
|
|
{
|
|
QString xref_label = xrp.slaveLabel();
|
|
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
|
|
|
DiagramContext dc = m_parent_element->elementInformations();
|
|
if(dc.value("xref").toString() != xref_label)
|
|
{
|
|
dc.addValue("xref", xref_label);
|
|
m_parent_element->setElementInformations(dc);
|
|
}
|
|
|
|
//Set up connections for future updates
|
|
if(m_update_slave_Xref_connection.isEmpty())
|
|
{
|
|
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(project, &QETProject::projectDiagramsOrderChanged, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(project, &QETProject::diagramRemoved, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(project, &QETProject::XRefPropertiesChanged, this, &ElementTextItemGroup::updateXref);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
//No "xref" text found: fall through to cleanup
|
|
}
|
|
|
|
for(DynamicElementTextItem *deti : texts())
|
|
{
|
|
if((deti->textFrom() == DynamicElementTextItem::ElementInfo && deti->infoName() == "label") ||
|
|
(deti->textFrom() == DynamicElementTextItem::CompositeText && deti->compositeText().contains("%{label")))
|
|
{
|
|
QString xref_label = xrp.slaveLabel();
|
|
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
|
|
|
if(!m_slave_Xref_item)
|
|
{
|
|
m_slave_Xref_item = new QGraphicsTextItem(xref_label, this);
|
|
ShownKinds::tag(m_slave_Xref_item, ShownKinds::CrossReferences);
|
|
m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5));
|
|
|
|
m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(master_elmt, &Element::yChanged, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(master_elmt, &Element::elementInfoChange, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(project, &QETProject::projectDiagramsOrderChanged, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(project, &QETProject::diagramRemoved, this, &ElementTextItemGroup::updateXref);
|
|
m_update_slave_Xref_connection << connect(project, &QETProject::XRefPropertiesChanged, this, &ElementTextItemGroup::updateXref);
|
|
}
|
|
else
|
|
m_slave_Xref_item->setPlainText(xref_label);
|
|
|
|
adjustSlaveXrefPos(xrp.slaveOffset());
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//There is no reason to have a xref, we delete it if exist
|
|
if(m_Xref_item)
|
|
{
|
|
delete m_Xref_item;
|
|
m_Xref_item = nullptr;
|
|
}
|
|
m_update_slave_Xref_connection.clear();
|
|
|
|
if(m_slave_Xref_item)
|
|
{
|
|
delete m_slave_Xref_item;
|
|
m_slave_Xref_item = nullptr;
|
|
|
|
//If position changed to Champ de texte, store xref in element info
|
|
if(m_parent_element->linkType() == Element::Slave &&
|
|
!m_parent_element->linkedElements().isEmpty() &&
|
|
m_parent_element->diagram())
|
|
{
|
|
Element *master_elmt = m_parent_element->linkedElements().first();
|
|
if(master_elmt)
|
|
{
|
|
XRefProperties xrp = m_parent_element->diagram()->project()->defaultXRefProperties(master_elmt->kindInformations()["type"].toString());
|
|
if(xrp.getXrefPos() == Qt::AlignHCenter)
|
|
{
|
|
QString xref_label = xrp.slaveLabel();
|
|
xref_label = autonum::AssignVariables::formulaToLabel(xref_label, master_elmt->rSequenceStruct(), master_elmt->diagram(), master_elmt);
|
|
|
|
DiagramContext dc = m_parent_element->elementInformations();
|
|
if(dc.value("xref").toString() != xref_label)
|
|
{
|
|
dc.addValue("xref", xref_label);
|
|
m_parent_element->setElementInformations(dc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Remove stale "xref" from elementInformations when no longer needed
|
|
if(m_parent_element->linkType() == Element::Slave &&
|
|
m_parent_element->diagram())
|
|
{
|
|
for(DynamicElementTextItem *deti : texts())
|
|
{
|
|
if(deti->textFrom() == DynamicElementTextItem::ElementInfo && deti->infoName() == "xref")
|
|
{
|
|
DiagramContext dc = m_parent_element->elementInformations();
|
|
if(!dc.value("xref").toString().isEmpty())
|
|
{
|
|
dc.remove("xref");
|
|
m_parent_element->setElementInformations(dc);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ElementTextItemGroup::adjustSlaveXrefPos(int slave_offset)
|
|
{
|
|
QRectF r = boundingRect();
|
|
QPointF pos(r.center().x() - m_slave_Xref_item->boundingRect().width()/2,
|
|
r.bottom() + slave_offset);
|
|
m_slave_Xref_item->setPos(pos);
|
|
}
|
|
|
|
void ElementTextItemGroup::autoPos()
|
|
{
|
|
int offset = 5;
|
|
|
|
if(m_parent_element->linkType() == Element::Master)
|
|
{
|
|
if(!diagram())
|
|
return;
|
|
|
|
MasterElement *master = static_cast<MasterElement *>(m_parent_element);
|
|
XRefProperties xrp = diagram()->project()->defaultXRefProperties(master->kindInformations()["type"].toString());
|
|
if(xrp.snapTo() == XRefProperties::Bottom)
|
|
{
|
|
QRectF rectXref = master->XrefBoundingRect();
|
|
offset = xrp.offset() <= 40 ? 5 : xrp.offset();
|
|
|
|
offset += (int)rectXref.height();
|
|
}
|
|
}
|
|
qreal r = rotation();
|
|
QGIUtility::centerToBottomDiagram(this, m_parent_element, offset);
|
|
//centerToBottomDiagram change the rotation of this group if needed,
|
|
//but setRotation is not a virtual function of QGraphicsItem, and the function centerToBottomDiagram
|
|
//work with a QGraphicsItem. So we emit the signal if rotation changed
|
|
if(rotation() != r)
|
|
emit rotationChanged(rotation());
|
|
}
|
|
|