element-editor: fix rotation, add mirror, add flip for graphical primitives

Now that the problem with the translations of keyboard shortcuts has been resolved and rotation using the space bar works reliably in principle, I took a closer look at the rotation function itself in the element editor.

I noticed, for example, that arcs can be rotated at an angle of 15°. This doesn't really make sense, as the “arc” part doesn't have the “rotation” property. There is only width and height.
And somehow rotating arcs didn't work well: start- and span-angles weren't adjusted.

Lines and polygons can be rotated in 15° increments, which doesn't make much sense, if other parts that can only be rotated in 90° increments are selected at the same time.

To make a long story short:
I reworked the rotation functions of the graphical parts so that now all parts are rotated in 90° steps around the origin! This means that it is now possible to mark several parts and rotate them around the same point at the same time!

In addition, the functions for mirroring graphic parts at y-axis (shortcut "M") and flipping at x-axis (shortcut "F") have been implemented.

I have saved the text elements for later!
(or someone else)
This commit is contained in:
plc-user
2025-02-14 20:31:03 +01:00
parent fc286cca22
commit bd3b39cea3
74 changed files with 4932 additions and 4558 deletions

View File

@@ -531,19 +531,28 @@ void RotateElementsCommand::undo()
PartRectangle* rect = qgraphicsitem_cast<PartRectangle*>(item);
rect->setRotation(rect->rotation()-90);
}
else if (item->type() == PartArc::Type) {
PartArc* arc = qgraphicsitem_cast<PartArc*>(item);
arc->setRotation(arc->rotation()-90);
}
else if (item->type() == PartEllipse::Type) {
PartEllipse* ellipse = qgraphicsitem_cast<PartEllipse*>(item);
ellipse->setRotation(ellipse->rotation()-90);
}
else if (item->type() == PartLine::Type) {
PartLine* line = qgraphicsitem_cast<PartLine*>(item);
line->setRotation(line->rotation()-15);
line->setRotation(line->rotation()-90);
}
else if (item->type() == PartPolygon::Type) {
PartPolygon* poly = qgraphicsitem_cast<PartPolygon*>(item);
poly->setRotation(poly->rotation()-15);
poly->setRotation(poly->rotation()-90);
}
else {
item->setRotation(item->rotation()-15);
item->setRotation(item->rotation()-90);
}
}
}
/**
@brief RotateElementsCommand::redo
*/
@@ -559,17 +568,115 @@ void RotateElementsCommand::redo()
PartRectangle* rect = qgraphicsitem_cast<PartRectangle*>(item);
rect->setRotation(rect->rotation()+90);
}
else if (item->type() == PartArc::Type) {
PartArc* arc = qgraphicsitem_cast<PartArc*>(item);
arc->setRotation(arc->rotation()+90);
}
else if (item->type() == PartEllipse::Type) {
PartEllipse* ellipse = qgraphicsitem_cast<PartEllipse*>(item);
ellipse->setRotation(ellipse->rotation()+90);
}
else if (item->type() == PartLine::Type) {
PartLine* line = qgraphicsitem_cast<PartLine*>(item);
line->setRotation(line->rotation()+15);
line->setRotation(line->rotation()+90);
}
else if (item->type() == PartPolygon::Type) {
PartPolygon* poly = qgraphicsitem_cast<PartPolygon*>(item);
poly->setRotation(poly->rotation()+15);
poly->setRotation(poly->rotation()+90);
}
else {
item->setRotation(item->rotation()+15);
item->setRotation(item->rotation()+90);
}
}
}
MirrorElementsCommand::MirrorElementsCommand(ElementScene *scene, QUndoCommand *parent) :
ElementEditionCommand(QObject::tr("Miroir de sélection", "undo caption"), scene, nullptr, parent)
{
m_items = scene->selectedItems();
}
/**
@brief MirrorElementsCommand::redo
*/
void MirrorElementsCommand::redo()
{
foreach (auto *item, m_items) {
if ((item->type() == PartText::Type) ||
(item->type() == PartDynamicTextField::Type)) {
continue;
} else if (item->type() == PartArc::Type) {
PartArc* arc = qgraphicsitem_cast<PartArc*>(item);
arc->mirror();
} else if (item->type() == PartEllipse::Type) {
PartEllipse* ellipse = qgraphicsitem_cast<PartEllipse*>(item);
ellipse->mirror();
} else if (item->type() == PartLine::Type) {
PartLine* line = qgraphicsitem_cast<PartLine*>(item);
line->mirror();
} else if (item->type() == PartPolygon::Type) {
PartPolygon* poly = qgraphicsitem_cast<PartPolygon*>(item);
poly->mirror();
} else if (item->type() == PartRectangle::Type) {
PartRectangle* rect = qgraphicsitem_cast<PartRectangle*>(item);
rect->mirror();
} else if (item->type() == PartTerminal::Type) {
PartTerminal* term = qgraphicsitem_cast<PartTerminal*>(item);
term->mirror();
}
}
}
/**
@brief MirrorElementsCommand::undo
*/
void MirrorElementsCommand::undo()
{
redo();
}
FlipElementsCommand::FlipElementsCommand(ElementScene *scene, QUndoCommand *parent) :
ElementEditionCommand(QObject::tr("Retourner la sélection", "undo caption"), scene, nullptr, parent)
{
m_items = scene->selectedItems();
}
/**
@brief FlipElementsCommand::redo
*/
void FlipElementsCommand::redo()
{
foreach (auto *item, m_items) {
if ((item->type() == PartText::Type) ||
(item->type() == PartDynamicTextField::Type)) {
continue;
} else if (item->type() == PartArc::Type) {
PartArc* arc = qgraphicsitem_cast<PartArc*>(item);
arc->flip();
} else if (item->type() == PartEllipse::Type) {
PartEllipse* ellipse = qgraphicsitem_cast<PartEllipse*>(item);
ellipse->flip();
} else if (item->type() == PartLine::Type) {
PartLine* line = qgraphicsitem_cast<PartLine*>(item);
line->flip();
} else if (item->type() == PartPolygon::Type) {
PartPolygon* poly = qgraphicsitem_cast<PartPolygon*>(item);
poly->flip();
} else if (item->type() == PartRectangle::Type) {
PartRectangle* rect = qgraphicsitem_cast<PartRectangle*>(item);
rect->flip();
} else if (item->type() == PartTerminal::Type) {
PartTerminal* term = qgraphicsitem_cast<PartTerminal*>(item);
term->flip();
}
}
}
/**
@brief FlipElementsCommand::undo
*/
void FlipElementsCommand::undo()
{
redo();
}

View File

@@ -265,4 +265,26 @@ private:
};
class MirrorElementsCommand : public ElementEditionCommand
{
public:
MirrorElementsCommand(ElementScene *scene, QUndoCommand *parent=nullptr);
void undo() override;
void redo() override;
private:
ElementScene *m_scene =nullptr;
QList<QGraphicsItem*> m_items;
};
class FlipElementsCommand : public ElementEditionCommand
{
public:
FlipElementsCommand(ElementScene *scene, QUndoCommand *parent=nullptr);
void undo() override;
void redo() override;
private:
ElementScene *m_scene =nullptr;
QList<QGraphicsItem*> m_items;
};
#endif

View File

@@ -170,6 +170,62 @@ QPainterPath PartArc::shadowShape() const
return (pps.createStroke(shape));
}
void PartArc::setRotation(qreal angle) {
// idea taken from QET_ElementScaler:
if (angle > 0) {
m_start_angle += 270.0 * 16;
while (m_start_angle < 0) { m_start_angle += (360*16); }
while (m_start_angle >= (360*16)) { m_start_angle -= (360*16); }
qreal width = m_rect.height();
qreal height = m_rect.width();
qreal x = (m_rect.y() + m_rect.height()) * (-1);
qreal y = m_rect.x();
m_rect = QRectF(x, y, width, height);
} else {
m_start_angle -= 270.0 * 16;
while (m_start_angle < 0) { m_start_angle += (360*16); }
while (m_start_angle >= (360*16)) { m_start_angle -= (360*16); }
qreal width = m_rect.height();
qreal height = m_rect.width();
qreal x = m_rect.y();
qreal y = (m_rect.x() + m_rect.width()) * (-1);
m_rect = QRectF(x, y, width, height);
}
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
qreal PartArc::rotation() const {
return qRound(m_rot * 100.0) / 100.0;
}
void PartArc::flip() {
m_start_angle = (-1) * m_start_angle;
m_span_angle = (-1) * m_span_angle;
while (m_start_angle < 0) { m_start_angle += (360*16); }
while (m_start_angle >= (360*16)) { m_start_angle -= (360*16); }
qreal y = ((-1.0) * m_rect.y()) - m_rect.height();
m_rect = QRectF(m_rect.x(), y, m_rect.width(), m_rect.height());
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
void PartArc::mirror() {
m_start_angle = (180.0 * 16) - m_start_angle;
m_span_angle = (-1) * m_span_angle;
while (m_start_angle < 0) { m_start_angle += (360*16); }
while (m_start_angle >= (360*16)) { m_start_angle -= (360*16); }
qreal x = ((-1.0) * m_rect.x()) - m_rect.width();
m_rect = QRectF(x, m_rect.y(), m_rect.width(), m_rect.height());
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
/**
* @brief PartArc::sceneGeometricRect
* @return the minimum,

View File

@@ -60,6 +60,10 @@ class PartArc : public AbstractPartEllipse
void setStartAngle(const int &start_angle) override {AbstractPartEllipse::setStartAngle(start_angle); adjustHandlerPos();}
void setSpanAngle(const int &span_angle) override {AbstractPartEllipse::setSpanAngle(span_angle); adjustHandlerPos();}
QRectF sceneGeometricRect() const override;
void setRotation(qreal angle);
qreal rotation() const;
void flip();
void mirror();
void addHandler() override;
void removeHandler() override;
@@ -83,5 +87,6 @@ class PartArc : public AbstractPartEllipse
m_vector_index = -1;
QPointF m_span_point;
QVector<QetGraphicsHandlerItem *> m_handler_vector;
qreal m_rot = 0;
};
#endif

View File

@@ -236,6 +236,50 @@ bool PartEllipse::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
return false;
}
void PartEllipse::setRotation(qreal angle) {
// idea taken from QET_ElementScaler:
if (angle > 0) {
qreal width = m_rect.height();
qreal height = m_rect.width();
qreal x = (m_rect.y() + m_rect.height()) * (-1);
qreal y = m_rect.x();
m_rect = QRectF(x, y, width, height);
} else {
qreal width = m_rect.height();
qreal height = m_rect.width();
qreal x = m_rect.y();
qreal y = (m_rect.x() + m_rect.width()) * (-1);
m_rect = QRectF(x, y, width, height);
}
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
qreal PartEllipse::rotation() const {
return qRound(m_rot * 100.0) / 100.0;
}
void PartEllipse::flip() {
qreal y = ((-1.0) * m_rect.y()) - m_rect.height();
m_rect = QRectF(m_rect.x(), y, m_rect.width(), m_rect.height());
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
void PartEllipse::mirror() {
qreal x = ((-1.0) * m_rect.x()) - m_rect.width();
m_rect = QRectF(x, m_rect.y(), m_rect.width(), m_rect.height());
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
void PartEllipse::switchResizeMode()
{
if (m_resize_mode == 1)

View File

@@ -27,7 +27,7 @@ class QPropertyUndoCommand;
This class represents an ellipse primitive which may be used to compose the
drawing of an electrical element within the element editor.
*/
class PartEllipse : public AbstractPartEllipse
class PartEllipse : public AbstractPartEllipse
{
Q_OBJECT
@@ -39,6 +39,9 @@ class PartEllipse : public AbstractPartEllipse
private:
PartEllipse(const PartEllipse &);
signals:
void rotationChanged();
// methods
public:
enum { Type = UserType + 1103 };
@@ -57,6 +60,10 @@ class PartEllipse : public AbstractPartEllipse
QPainterPath shape() const override;
QPainterPath shadowShape() const override;
void setRect(const QRectF &rect) override {AbstractPartEllipse::setRect(rect); adjustHandlerPos();}
void setRotation(qreal angle);
qreal rotation() const;
void flip();
void mirror();
void addHandler() override;
void removeHandler() override;
@@ -77,5 +84,6 @@ class PartEllipse : public AbstractPartEllipse
QPropertyUndoCommand *m_undo_command;
int m_resize_mode = 1,
m_vector_index = -1;
qreal m_rot = 0;
};
#endif

View File

@@ -452,7 +452,7 @@ QRectF PartLine::boundingRect() const
/**
@brief PartLine::isUseless
@return true if this part is irrelevant and does not deserve to be Retained / registered.
A line is relevant when is two point is different
A line is relevant when start-point and end-point are different
*/
bool PartLine::isUseless() const
{
@@ -579,15 +579,52 @@ void PartLine::setSecondEndLength(const qreal &l)
}
void PartLine::setRotation(qreal angle) {
QTransform rotation = QTransform().translate(m_line.p1().x(),m_line.p1().y()).rotate(angle-m_rot).translate(-m_line.p1().x(),-m_line.p1().y());
m_rot=angle;
setLine(rotation.map(m_line));
double tmp, x, y;
if (angle > 0) {
tmp = m_line.p1().y();
y = m_line.p1().x();
x = (-1) * tmp;
m_line.setP1(QPointF(x, y));
tmp = m_line.p2().y();
y = m_line.p2().x();
x = (-1) * tmp;
m_line.setP2(QPointF(x, y));
} else {
tmp = m_line.p1().x();
x = m_line.p1().y();
y = (-1) * tmp;
m_line.setP1(QPointF(x, y));
tmp = m_line.p2().x();
x = m_line.p2().y();
y = (-1) * tmp;
m_line.setP2(QPointF(x, y));
}
prepareGeometryChange();
setLine(m_line);
adjustHandlerPos();
emit lineChanged();
}
qreal PartLine::rotation() const {
return m_rot;
return qRound(m_rot * 100.0) / 100.0;
}
void PartLine::flip() {
m_line.setP1(QPointF(m_line.p1().x(), (-1) * m_line.p1().y()));
m_line.setP2(QPointF(m_line.p2().x(), (-1) * m_line.p2().y()));
setLine(m_line);
prepareGeometryChange();
adjustHandlerPos();
emit lineChanged();
}
void PartLine::mirror() {
m_line.setP1(QPointF((-1) * m_line.p1().x(), m_line.p1().y()));
m_line.setP2(QPointF((-1) * m_line.p2().x(), m_line.p2().y()));
setLine(m_line);
prepareGeometryChange();
adjustHandlerPos();
emit lineChanged();
}

View File

@@ -85,17 +85,19 @@ class PartLine : public CustomElementGraphicPart
static QList<QPointF> fourEndPoints(const QPointF &, const QPointF &, const qreal &);
QLineF line() const;
void setLine(const QLineF &line);
void setLine(const QLineF &line);
Qet::EndType firstEndType() const {return first_end;}
void setFirstEndType(const Qet::EndType &et);
void setFirstEndType(const Qet::EndType &et);
Qet::EndType secondEndType() const {return second_end;}
void setSecondEndType(const Qet::EndType &et);
void setSecondEndType(const Qet::EndType &et);
qreal firstEndLength() const {return first_length;}
void setFirstEndLength(const qreal &l);
void setFirstEndLength(const qreal &l);
qreal secondEndLength() const {return second_length;}
void setSecondEndLength(const qreal &l);
void setRotation(qreal angle);
void setSecondEndLength(const qreal &l);
void setRotation(qreal angle);
qreal rotation() const;
void flip();
void mirror();
void addHandler() override;
void removeHandler() override;

View File

@@ -297,17 +297,49 @@ void PartPolygon::resetAllHandlerColor()
void PartPolygon::setRotation(qreal angle) {
for (auto &punkt : m_polygon) {
double tmp, x, y;
if (angle > 0) {
tmp = punkt.y();
y = punkt.x();
x = (-1) * tmp;
} else {
tmp = punkt.x();
x = punkt.y();
y = (-1) * tmp;
}
punkt = QPointF(x, y);
}
QTransform rotation = QTransform().translate(m_polygon.first().x(),m_polygon.first().y()).rotate(angle-m_rot).translate(-m_polygon.first().x(),-m_polygon.first().y());
m_rot=angle;
setPolygon(rotation.map(m_polygon));
setPolygon(m_polygon);
prepareGeometryChange();
adjustHandlerPos();
emit polygonChanged();
}
qreal PartPolygon::rotation() const {
return m_rot;
return qRound(m_rot * 100.0) / 100.0;
}
void PartPolygon::flip() {
for (auto &pt : m_polygon) {
pt = QPointF(pt.x(), (-1) * pt.y());
}
setPolygon(m_polygon);
prepareGeometryChange();
adjustHandlerPos();
emit polygonChanged();
}
void PartPolygon::mirror() {
for (auto &pt : m_polygon) {
pt = QPointF((-1) * pt.x(), pt.y());
}
setPolygon(m_polygon);
prepareGeometryChange();
adjustHandlerPos();
emit polygonChanged();
}
/**

View File

@@ -87,8 +87,10 @@ class PartPolygon : public CustomElementGraphicPart
void setHandlerColor(QPointF pos, const QColor &color) final;
void resetAllHandlerColor() final;
void setRotation (qreal angle);
void setRotation (qreal angle);
qreal rotation () const;
void flip();
void mirror();
void addHandler() override;
void removeHandler() override;

View File

@@ -168,17 +168,62 @@ void PartRectangle::setYRadius(qreal Y)
}
void PartRectangle::setRotation(qreal angle) {
// for whatever reason: with "rect" we need to use scene-positions...
auto pos = mapToScene(m_rect.x(),m_rect.y());
qreal width = m_rect.height();
qreal height = m_rect.width();
qreal x; qreal y;
if (angle > 0) {
x = (pos.y() + m_rect.height()) * (-1);
y = pos.x();
} else {
x = pos.y();
y = (pos.x() + m_rect.width()) * (-1);
}
QTransform rotation = QTransform().rotate(angle-m_rot);
m_rot=angle;
pos = mapFromScene(x, y);
m_rect.setX(pos.x()); m_rect.setY(pos.y());
m_rect.setWidth(width); m_rect.setHeight(height);
std::swap (m_xRadius, m_yRadius);
setRect(rotation.mapRect(m_rect));
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
qreal PartRectangle::rotation() const {
return m_rot;
return qRound(m_rot * 100.0) / 100.0;
}
void PartRectangle::flip() {
// for whatever reason: with "rect" we need to use scene-positions...
qreal height = m_rect.height();
auto pos = mapToScene(m_rect.x(),m_rect.y());
qreal x = pos.x();
qreal y = ((-1.0) * pos.y()) - height;
pos = mapFromScene(x, y);
m_rect.setX(pos.x()); m_rect.setY(pos.y());
m_rect.setHeight(height);
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
void PartRectangle::mirror() {
// for whatever reason: with "rect" we need to use scene-positions...
qreal width = m_rect.width();
auto pos = mapToScene(m_rect.x(),m_rect.y());
qreal x = ((-1.0) * pos.x()) - width;
qreal y = pos.y();
pos = mapFromScene(x, y);
m_rect.setX(pos.x()); m_rect.setY(pos.y());
m_rect.setWidth(width);
prepareGeometryChange();
adjustHandlerPos();
emit rectChanged();
}
/**
@brief PartRectangle::sceneGeometricRect
@return the minimum, margin-less rectangle this part can fit into, in scene

View File

@@ -27,7 +27,7 @@ class QetGraphicsHandlerItem;
drawing of an electrical element within the element editor.
All coordinates is in item coordinate, except pos()
*/
class PartRectangle : public CustomElementGraphicPart
class PartRectangle : public CustomElementGraphicPart
{
Q_OBJECT
@@ -73,6 +73,8 @@ class PartRectangle : public CustomElementGraphicPart
void setYRadius(qreal Y);
void setRotation(qreal angle);
qreal rotation() const;
void flip();
void mirror();
QRectF sceneGeometricRect() const override;
virtual QPointF sceneTopLeft() const;

View File

@@ -166,7 +166,23 @@ void PartTerminal::setRotation(qreal angle) {
else if (180 <= angle_mod && angle_mod < 270) new_ori = Qet::South;
else new_ori = Qet::West;
double tmp, y, x;
if (angle > 0) {
tmp = d->m_pos.y();
y = d->m_pos.x();
x = (-1) * tmp;
} else {
tmp = d->m_pos.x();
x = d->m_pos.y();
y = (-1) * tmp;
}
d->m_pos.setX(x); d->m_pos.setY(y);
setPos(d->m_pos);
setOrientation(new_ori);
emit xChanged();
emit yChanged();
emit orientationChanged();
}
qreal PartTerminal::rotation() const {
@@ -179,6 +195,43 @@ qreal PartTerminal::rotation() const {
return 0;
}
void PartTerminal::flip() {
d->m_pos.setY((-1.0) * d->m_pos.y());
switch (d->m_orientation) {
case Qet::North : setOrientation(Qet::South);
break;
case Qet::East : return;
case Qet::South : setOrientation(Qet::North);
break;
case Qet::West : return;
}
setPos(d->m_pos);
updateSecondPoint();
prepareGeometryChange();
emit yChanged();
emit orientationChanged();
}
void PartTerminal::mirror() {
d->m_pos.setX((-1.0) * d->m_pos.x());
switch (d->m_orientation) {
case Qet::North : return;
case Qet::East : setOrientation(Qet::West);
break;
case Qet::South : return;
case Qet::West : setOrientation(Qet::East);
break;
}
setPos(d->m_pos);
updateSecondPoint();
prepareGeometryChange();
emit xChanged();
emit orientationChanged();
}
/**
@brief PartTerminal::setTerminalName
@param name

View File

@@ -74,8 +74,10 @@ class PartTerminal : public CustomElementGraphicPart
Qet::Orientation orientation() const {return d -> m_orientation;}
void setOrientation(Qet::Orientation ori);
void setRotation(qreal angle);
qreal rotation() const;
void setRotation(qreal angle);
void flip();
void mirror();
QString terminalName() const { return d -> m_name; }

View File

@@ -107,6 +107,9 @@ void QETElementEditor::contextMenu(QPoint p, QList<QAction *> actions)
menu.addAction(ui->m_delete_action);
menu.addAction(ui->m_cut_action);
menu.addAction(ui->m_copy_action);
menu.addSeparator();
menu.addAction((ui->m_mirror_action));
menu.addAction((ui->m_flip_action));
menu.addAction((ui->m_rotate_action));
menu.addSeparator();
menu.addAction(ui->m_paste_action);
@@ -999,6 +1002,15 @@ void QETElementEditor::setupActions()
ui->m_rotate_action -> setShortcut(Qt::Key_Space);
connect(ui->m_rotate_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new RotateElementsCommand(this->elementScene()));});
//Flip action
ui->m_flip_action -> setShortcut(Qt::Key_F);
connect(ui->m_flip_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new FlipElementsCommand(this->elementScene()));});
//Mirror action
ui->m_mirror_action -> setShortcut(Qt::Key_M);
connect(ui->m_mirror_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new MirrorElementsCommand(this->elementScene()));});
//Zoom action
ui->m_zoom_in_action -> setShortcut(QKeySequence::ZoomIn);
ui->m_zoom_out_action -> setShortcut(QKeySequence::ZoomOut);
@@ -1063,7 +1075,9 @@ void QETElementEditor::updateAction()
<< ui->m_cut_action
<< ui->m_copy_action
<< ui->m_delete_action
<< ui->m_rotate_action;
<< ui->m_rotate_action
<< ui->m_flip_action
<< ui->m_mirror_action;
auto items_selected = !m_read_only && m_elmt_scene->selectedItems().count();
for (auto action : qAsConst(select_list)) {
action->setEnabled(items_selected);

View File

@@ -77,6 +77,8 @@
<addaction name="m_edit_author_action"/>
<addaction name="m_edit_element_properties_action"/>
<addaction name="m_rotate_action"/>
<addaction name="m_mirror_action"/>
<addaction name="m_flip_action"/>
</widget>
<widget class="QMenu" name="m_display_menu">
<property name="title">
@@ -493,6 +495,24 @@
<string>Rotation</string>
</property>
</action>
<action name="m_mirror_action">
<property name="icon">
<iconset resource="../../../qelectrotech.qrc">
<normaloff>:/ico/16x16/mirror.png</normaloff>:/ico/16x16/mirror.png</iconset>
</property>
<property name="text">
<string>Mirror</string>
</property>
</action>
<action name="m_flip_action">
<property name="icon">
<iconset resource="../../../qelectrotech.qrc">
<normaloff>:/ico/16x16/flip.png</normaloff>:/ico/16x16/flip.png</iconset>
</property>
<property name="text">
<string>Flip</string>
</property>
</action>
<action name="m_import_dxf">
<property name="icon">
<iconset resource="../../../qelectrotech.qrc">