mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2025-09-13 20:23:04 +02:00
Il est desormais possible de deplacer les champs de texte des conducteurs.
Leur deplacement est limite aux alentours du trajet de leur conducteur parent, Reorganisation du code pour tout ce qui est relatif aux deplacements d'items sur les schemas. git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/branches/0.3@987 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
parent
89cb80855a
commit
c8732714b8
@ -419,6 +419,13 @@ void Conductor::paint(QPainter *qp, const QStyleOptionGraphicsItem *options, QWi
|
||||
qp -> save();
|
||||
qp -> setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
/*
|
||||
qp -> save();
|
||||
qp -> setPen(Qt::blue);
|
||||
qp -> drawPath(variableShape(60.0).simplified());
|
||||
qp -> restore();
|
||||
*/
|
||||
|
||||
// determine la couleur du conducteur
|
||||
QColor final_conductor_color(properties_.color);
|
||||
if (isSelected()) {
|
||||
@ -514,6 +521,13 @@ Diagram *Conductor::diagram() const {
|
||||
return(qobject_cast<Diagram *>(scene()));
|
||||
}
|
||||
|
||||
/**
|
||||
@return le champ de texte associe a ce conducteur
|
||||
*/
|
||||
ConductorTextItem *Conductor::textItem() const {
|
||||
return(text_item);
|
||||
}
|
||||
|
||||
/**
|
||||
Methode de validation d'element XML
|
||||
@param e Un element XML sense represente un Conducteur
|
||||
@ -569,6 +583,10 @@ void Conductor::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
}
|
||||
segment = segment -> nextSegment();
|
||||
}
|
||||
if (moving_segment || moving_point) {
|
||||
// en cas de debut de modification de conducteur, on memorise la position du champ de texte
|
||||
before_mov_text_pos_ = text_item -> pos();
|
||||
}
|
||||
}
|
||||
QGraphicsPathItem::mousePressEvent(e);
|
||||
if (e -> modifiers() & Qt::ControlModifier) {
|
||||
@ -733,9 +751,39 @@ QRectF Conductor::boundingRect() const {
|
||||
}
|
||||
|
||||
/**
|
||||
@return La forme / zone "cliquable" du conducteur
|
||||
@return La forme / zone "cliquable" du conducteur (epaisseur : 5.0px).
|
||||
@see variableShape()
|
||||
*/
|
||||
QPainterPath Conductor::shape() const {
|
||||
return(variableShape(5.0));
|
||||
}
|
||||
|
||||
/**
|
||||
@return la distance en dessous de laquelle on considere qu'un point est a
|
||||
proximite du trajet du conducteur. La valeur est actuellement fixee a
|
||||
60.0px.
|
||||
*/
|
||||
qreal Conductor::nearDistance() const {
|
||||
return(60.0);
|
||||
}
|
||||
|
||||
/**
|
||||
@return la zone dans laquelle dont on considere que tous les points sont a
|
||||
proximite du trajet du conducteur.
|
||||
@see nearDistance()
|
||||
@see variableShape()
|
||||
*/
|
||||
QPainterPath Conductor::nearShape() const {
|
||||
return(variableShape(nearDistance()));
|
||||
}
|
||||
|
||||
/**
|
||||
@return la forme du conducteur
|
||||
@param thickness la moitie de l'epaisseur voulue pour cette forme
|
||||
*/
|
||||
QPainterPath Conductor::variableShape(const qreal &thickness) const {
|
||||
qreal my_thickness = qAbs(thickness);
|
||||
|
||||
QList<QPointF> points = segmentsToPoints();
|
||||
QPainterPath area;
|
||||
QPointF previous_point;
|
||||
@ -764,15 +812,24 @@ QPainterPath Conductor::shape() const {
|
||||
qreal p2_x = point2 -> x();
|
||||
qreal p2_y = point2 -> y();
|
||||
area.setFillRule(Qt::OddEvenFill);
|
||||
area.addRect(p1_x - 5.0, p1_y - 5.0, 10.0 + p2_x - p1_x, 10.0 + p2_y - p1_y);
|
||||
area.addRect(p1_x - my_thickness, p1_y - my_thickness, my_thickness * 2.0 + p2_x - p1_x, my_thickness * 2.0 + p2_y - p1_y);
|
||||
}
|
||||
previous_point = point;
|
||||
area.setFillRule(Qt::WindingFill);
|
||||
area.addRect(point.x() - 5.0, point.y() - 5.0, 10.0, 10.0);
|
||||
area.addRect(point.x() - my_thickness, point.y() - my_thickness, my_thickness * 2.0, my_thickness * 2.0 );
|
||||
}
|
||||
return(area);
|
||||
}
|
||||
|
||||
/**
|
||||
@param point un point, exprime dans les coordonnees du conducteur
|
||||
@return true si le point est a proximite du conducteur, c-a-d a moins de
|
||||
60px du conducteur.
|
||||
*/
|
||||
bool Conductor::isNearConductor(const QPointF &point) {
|
||||
return(variableShape(60.1).contains(point));
|
||||
}
|
||||
|
||||
/**
|
||||
Renvoie une valeur donnee apres l'avoir bornee entre deux autres valeurs,
|
||||
en y ajoutant une marge interne.
|
||||
@ -886,6 +943,14 @@ bool Conductor::fromXml(QDomElement &e) {
|
||||
// recupere la "configuration" du conducteur
|
||||
properties_.fromXml(e);
|
||||
readProperties();
|
||||
qreal user_pos_x, user_pos_y;
|
||||
if (
|
||||
QET::attributeIsAReal(e, "userx", &user_pos_x) &&
|
||||
QET::attributeIsAReal(e, "usery", &user_pos_y)
|
||||
) {
|
||||
text_item -> forceMovedByUser(true);
|
||||
text_item -> setPos(user_pos_x, user_pos_y);
|
||||
}
|
||||
text_item -> setRotationAngle(e.attribute("rotation").toDouble());
|
||||
|
||||
// parcourt les elements XML "segment" et en extrait deux listes de longueurs
|
||||
@ -984,6 +1049,10 @@ QDomElement Conductor::toXml(QDomDocument &d, QHash<Terminal *, int> &table_adr_
|
||||
if (text_item -> rotationAngle()) {
|
||||
e.setAttribute("rotation", QString("%1").arg(text_item -> rotationAngle()));
|
||||
}
|
||||
if (text_item -> wasMovedByUser()) {
|
||||
e.setAttribute("userx", QString("%1").arg(text_item -> pos().x()));
|
||||
e.setAttribute("usery", QString("%1").arg(text_item -> pos().y()));
|
||||
}
|
||||
return(e);
|
||||
}
|
||||
|
||||
@ -1043,10 +1112,21 @@ ConductorSegment *Conductor::middleSegment() {
|
||||
@see middleSegment()
|
||||
*/
|
||||
void Conductor::calculateTextItemPosition() {
|
||||
if (properties_.type != ConductorProperties::Multi) return;
|
||||
if (!text_item) return;
|
||||
|
||||
if (text_item -> wasMovedByUser()) {
|
||||
// le champ de texte a ete deplace par l'utilisateur :
|
||||
// on verifie qu'il est encore a proximite du conducteur
|
||||
QPointF text_item_pos = text_item -> pos();
|
||||
QPainterPath near_shape = nearShape();
|
||||
if (!near_shape.contains(text_item_pos)) {
|
||||
text_item -> setPos(movePointIntoPolygon(text_item_pos, near_shape));
|
||||
}
|
||||
} else {
|
||||
// positionnement automatique basique
|
||||
text_item -> setPos(middleSegment() -> middle());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Sauvegarde le profil courant du conducteur pour l'utiliser ulterieurement
|
||||
@ -1058,7 +1138,14 @@ void Conductor::saveProfile(bool undo) {
|
||||
conductor_profiles[current_path_type].fromConductor(this);
|
||||
Diagram *dia = diagram();
|
||||
if (undo && dia) {
|
||||
dia -> undoStack().push(new ChangeConductorCommand(this, old_profile, conductor_profiles[current_path_type], current_path_type));
|
||||
ChangeConductorCommand *undo_object = new ChangeConductorCommand(
|
||||
this,
|
||||
old_profile,
|
||||
conductor_profiles[current_path_type],
|
||||
current_path_type
|
||||
);
|
||||
undo_object -> setConductorTextItemMove(before_mov_text_pos_, text_item -> pos());
|
||||
dia -> undoStack().push(undo_object);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1138,6 +1225,15 @@ void Conductor::readProperties() {
|
||||
text_item -> setVisible(properties_.type == ConductorProperties::Multi);
|
||||
}
|
||||
|
||||
/**
|
||||
S'assure que le texte du conducteur est a une position raisonnable
|
||||
Cette methode ne fait rien si ce conducteur n'affiche pas son champ de
|
||||
texte.
|
||||
*/
|
||||
void Conductor::adjustTextItemPosition() {
|
||||
calculateTextItemPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
Met a jour les proprietes du conducteur apres modification du champ de texte affiche
|
||||
*/
|
||||
@ -1378,3 +1474,55 @@ void Conductor::deleteSegments() {
|
||||
segments = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@param point Un point situe a l'exterieur du polygone
|
||||
@param polygon Le polygone dans lequel on veut rapatrier le point
|
||||
@return la position du point, une fois ramene dans le polygone, ou plus
|
||||
exactement sur le bord du polygone
|
||||
*/
|
||||
QPointF Conductor::movePointIntoPolygon(const QPointF &point, const QPainterPath &polygon) {
|
||||
// decompose le polygone en lignes et points
|
||||
QList<QPolygonF> polygons = polygon.simplified().toSubpathPolygons();
|
||||
QList<QLineF> lines;
|
||||
QList<QPointF> points;
|
||||
foreach(QPolygonF polygon, polygons) {
|
||||
if (polygon.count() <= 1) continue;
|
||||
|
||||
// on recense les lignes et les points
|
||||
for (int i = 1 ; i < polygon.count() ; ++ i) {
|
||||
lines << QLineF(polygon.at(i - 1), polygon.at(i));
|
||||
points << polygon.at(i -1);
|
||||
}
|
||||
}
|
||||
|
||||
// on fait des projetes orthogonaux du point sur les differents segments du
|
||||
// polygone, en les triant par longueur croissante
|
||||
QMap<qreal, QPointF> intersections;
|
||||
foreach (QLineF line, lines) {
|
||||
QPointF intersection_point;
|
||||
if (QET::orthogonalProjection(point, line, &intersection_point)) {
|
||||
intersections.insert(QLineF(intersection_point, point).length(), intersection_point);
|
||||
}
|
||||
}
|
||||
if (intersections.count()) {
|
||||
// on determine la plus courte longueur pour un projete orthogonal
|
||||
QPointF the_point = intersections[intersections.keys().first()];
|
||||
return(the_point);
|
||||
} else {
|
||||
// determine le coin du polygone le plus proche du point exterieur
|
||||
qreal minimum_length = -1;
|
||||
int point_index = -1;
|
||||
for (int i = 0 ; i < points.count() ; ++ i) {
|
||||
qreal length = qAbs(QLineF(points.at(i), point).length());
|
||||
if (minimum_length < 0 || length < minimum_length) {
|
||||
minimum_length = length;
|
||||
point_index = i;
|
||||
}
|
||||
}
|
||||
// on connait desormais le coin le plus proche du texte
|
||||
|
||||
// aucun projete orthogonal n'a donne quoi que ce soit, on met le texte sur un des coins du polygone
|
||||
return(points.at(point_index));
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "conductorprofile.h"
|
||||
#include "conductorproperties.h"
|
||||
class ConductorSegment;
|
||||
class DiagramTextItem;
|
||||
class ConductorTextItem;
|
||||
class Element;
|
||||
typedef QPair<QPointF, Qt::Corner> ConductorBend;
|
||||
typedef QHash<Qt::Corner, ConductorProfile> ConductorProfilesGroup;
|
||||
@ -61,10 +61,15 @@ class Conductor : public QObject, public QGraphicsPathItem {
|
||||
/// @return true si ce conducteur est detruit
|
||||
bool isDestroyed() const { return(destroyed); }
|
||||
Diagram *diagram() const;
|
||||
ConductorTextItem *textItem() const;
|
||||
void updatePath(const QRectF & = QRectF());
|
||||
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
|
||||
QRectF boundingRect() const;
|
||||
virtual QPainterPath shape() const;
|
||||
virtual qreal nearDistance() const;
|
||||
virtual QPainterPath nearShape() const;
|
||||
virtual QPainterPath variableShape(const qreal &) const;
|
||||
virtual bool isNearConductor(const QPointF &);
|
||||
qreal length();
|
||||
ConductorSegment *middleSegment();
|
||||
bool containsPoint(const QPointF &) const;
|
||||
@ -81,6 +86,7 @@ class Conductor : public QObject, public QGraphicsPathItem {
|
||||
void setProfiles(const ConductorProfilesGroup &);
|
||||
ConductorProfilesGroup profiles() const;
|
||||
void readProperties();
|
||||
void adjustTextItemPosition();
|
||||
|
||||
public slots:
|
||||
void displayedTextChanged();
|
||||
@ -100,7 +106,7 @@ class Conductor : public QObject, public QGraphicsPathItem {
|
||||
/// booleen indiquant si le fil est encore valide
|
||||
bool destroyed;
|
||||
/// champ de texte editable pour les conducteurs non unifilaires
|
||||
DiagramTextItem *text_item;
|
||||
ConductorTextItem *text_item;
|
||||
/// segments composant le conducteur
|
||||
ConductorSegment *segments;
|
||||
/// attributs lies aux manipulations a la souris
|
||||
@ -110,6 +116,7 @@ class Conductor : public QObject, public QGraphicsPathItem {
|
||||
int moved_point;
|
||||
qreal previous_z_value;
|
||||
ConductorSegment *moved_segment;
|
||||
QPointF before_mov_text_pos_;
|
||||
/// booleen indiquant si le conducteur a ete modifie manuellement par l'utilisateur
|
||||
bool modified_path;
|
||||
/// booleen indiquant s'il faut sauver le profil courant au plus tot
|
||||
@ -146,5 +153,6 @@ class Conductor : public QObject, public QGraphicsPathItem {
|
||||
static qreal conductor_bound(qreal, qreal, qreal, qreal = 0.0);
|
||||
static qreal conductor_bound(qreal, qreal, bool);
|
||||
static Qt::Corner movementType(const QPointF &, const QPointF &);
|
||||
static QPointF movePointIntoPolygon(const QPointF &, const QPainterPath &);
|
||||
};
|
||||
#endif
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include "conductortextitem.h"
|
||||
#include "conductor.h"
|
||||
#include "diagramcommands.h"
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@ -25,11 +26,11 @@
|
||||
*/
|
||||
ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *parent_diagram) :
|
||||
DiagramTextItem(parent_conductor, parent_diagram),
|
||||
parent_conductor_(parent_conductor)
|
||||
parent_conductor_(parent_conductor),
|
||||
moved_by_user_(false)
|
||||
{
|
||||
// par defaut, les DiagramTextItem sont Selectable et Movable
|
||||
// on desactive Movable pour les textes des conducteurs
|
||||
setFlag(QGraphicsItem::ItemIsMovable, false);
|
||||
// cela nous convient, on ne touche pas a ces flags
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,11 +41,11 @@ ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *paren
|
||||
*/
|
||||
ConductorTextItem::ConductorTextItem(const QString &text, Conductor *parent_conductor, Diagram *parent_diagram) :
|
||||
DiagramTextItem(text, parent_conductor, parent_diagram),
|
||||
parent_conductor_(parent_conductor)
|
||||
parent_conductor_(parent_conductor),
|
||||
moved_by_user_(false)
|
||||
{
|
||||
// par defaut, les DiagramTextItem sont Selectable et Movable
|
||||
// on desactive Movable pour les textes des conducteurs
|
||||
setFlag(QGraphicsItem::ItemIsMovable, false);
|
||||
// cela nous convient, on ne touche pas a ces flags
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,8 +69,16 @@ Conductor *ConductorTextItem::parentConductor() const {
|
||||
@param e L'element XML representant le champ de texte
|
||||
*/
|
||||
void ConductorTextItem::fromXml(const QDomElement &e) {
|
||||
setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
|
||||
setPlainText(e.attribute("text"));
|
||||
|
||||
qreal user_pos_x, user_pos_y;
|
||||
if (
|
||||
QET::attributeIsAReal(e, "userx", &user_pos_x) &&
|
||||
QET::attributeIsAReal(e, "usery", &user_pos_y)
|
||||
) {
|
||||
setPos(user_pos_x, user_pos_y);
|
||||
}
|
||||
|
||||
setRotationAngle(e.attribute("rotation").toDouble());
|
||||
}
|
||||
|
||||
@ -79,11 +88,95 @@ void ConductorTextItem::fromXml(const QDomElement &e) {
|
||||
*/
|
||||
QDomElement ConductorTextItem::toXml(QDomDocument &document) const {
|
||||
QDomElement result = document.createElement("input");
|
||||
result.setAttribute("x", QString("%1").arg(pos().x()));
|
||||
result.setAttribute("y", QString("%1").arg(pos().y()));
|
||||
result.setAttribute("userx", QString("%1").arg(pos().x()));
|
||||
result.setAttribute("usery", QString("%1").arg(pos().y()));
|
||||
result.setAttribute("text", toPlainText());
|
||||
if (rotationAngle()) {
|
||||
result.setAttribute("rotation", QString("%1").arg(rotationAngle()));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@return true si ce champ de texte a ete explictement deplace par
|
||||
l'utilisateur, false sinon
|
||||
*/
|
||||
bool ConductorTextItem::wasMovedByUser() const {
|
||||
return(moved_by_user_);
|
||||
}
|
||||
|
||||
/**
|
||||
@param moved_by_user true pour que la position du texte soit consideree
|
||||
comme ayant ete definie par l'utilisateur (et donc soit sauvegardee), false
|
||||
pour remettre le texte a sa position originelle
|
||||
*/
|
||||
void ConductorTextItem::forceMovedByUser(bool moved_by_user) {
|
||||
if (moved_by_user == moved_by_user_) return;
|
||||
|
||||
moved_by_user_ = moved_by_user;
|
||||
if (!moved_by_user && parent_conductor_) {
|
||||
parent_conductor_ -> adjustTextItemPosition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Gere les clics de souris lies au champ de texte
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void ConductorTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
|
||||
before_mov_pos_ = pos();
|
||||
}
|
||||
DiagramTextItem::mousePressEvent(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Gere les mouvements de souris lies au champ de texte
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void ConductorTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (textInteractionFlags() & Qt::TextEditable) {
|
||||
QGraphicsTextItem::mouseMoveEvent(e);
|
||||
} else if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
|
||||
QPointF old_pos = pos();
|
||||
QPointF intended_pos = mapToParent(e -> pos()) - matrix().map(e -> buttonDownPos(Qt::LeftButton));
|
||||
|
||||
// si ce texte est attache a un conducteur, alors ses mouvements seront
|
||||
// limites a une certaine distance du trace de ce conducteur
|
||||
if (parent_conductor_) {
|
||||
if (parent_conductor_ -> isNearConductor(intended_pos)) {
|
||||
setPos(intended_pos);
|
||||
}
|
||||
}
|
||||
|
||||
} else e -> ignore();
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le relachement de souris
|
||||
Cette methode cree un objet d'annulation pour le deplacement
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void ConductorTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (flags() & QGraphicsItem::ItemIsMovable) {
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
// on cree un objet d'annulation correspondant au deplacement qui s'acheve
|
||||
QPointF applied_movement = pos() - before_mov_pos_;
|
||||
|
||||
if (!applied_movement.isNull()) {
|
||||
// on cree un objet d'annulation seulement pour ce champ de texte
|
||||
MoveConductorsTextsCommand *undo_object = new MoveConductorsTextsCommand(diagram_ptr);
|
||||
undo_object -> addTextMovement(this, before_mov_pos_, pos(), moved_by_user_);
|
||||
|
||||
// on active le flag indiquant que ce champ de texte a ete explicitement repositionne par l'utilisateur
|
||||
moved_by_user_ = true;
|
||||
|
||||
diagram_ptr -> undoStack().push(undo_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
||||
QGraphicsTextItem::mouseReleaseEvent(e);
|
||||
}
|
||||
}
|
||||
|
@ -47,9 +47,18 @@ class ConductorTextItem : public DiagramTextItem {
|
||||
// methodes
|
||||
public:
|
||||
virtual int type() const { return Type; }
|
||||
virtual bool wasMovedByUser() const;
|
||||
virtual void forceMovedByUser(bool);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
|
||||
|
||||
// attributs
|
||||
private:
|
||||
Conductor *parent_conductor_;
|
||||
bool moved_by_user_;
|
||||
QPointF before_mov_pos_;
|
||||
};
|
||||
#endif
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "diagramcontent.h"
|
||||
#include "diagramposition.h"
|
||||
#include "elementtextitem.h"
|
||||
#include "elementsmover.h"
|
||||
#include "elementtextsmover.h"
|
||||
#include "exportdialog.h"
|
||||
#include "ghostelement.h"
|
||||
#include "independenttextitem.h"
|
||||
@ -41,7 +43,6 @@ Diagram::Diagram(QObject *parent) :
|
||||
QGraphicsScene(parent),
|
||||
draw_grid(true),
|
||||
use_border(true),
|
||||
moved_elements_fetched(false),
|
||||
draw_terminals(true),
|
||||
draw_colored_conductors_(true),
|
||||
project_(0),
|
||||
@ -58,6 +59,10 @@ Diagram::Diagram(QObject *parent) :
|
||||
t.setStyle(Qt::DashLine);
|
||||
conductor_setter -> setPen(t);
|
||||
conductor_setter -> setLine(QLineF(QPointF(0.0, 0.0), QPointF(0.0, 0.0)));
|
||||
|
||||
// initialise les objets gerant les deplacements
|
||||
elements_mover_ = new ElementsMover(); // deplacements d'elements/conducteurs/textes
|
||||
element_texts_mover_ = new ElementTextsMover(); // deplacements d'ElementTextItem
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,6 +74,10 @@ Diagram::~Diagram() {
|
||||
// suppression du QGIManager - tous les elements qu'il connait sont supprimes
|
||||
delete qgi_manager;
|
||||
|
||||
// suppression des objets gerant les deplacements
|
||||
delete elements_mover_;
|
||||
delete element_texts_mover_;
|
||||
|
||||
// recense les items supprimables
|
||||
QList<QGraphicsItem *> deletable_items;
|
||||
foreach(QGraphicsItem *qgi, items()) {
|
||||
@ -140,7 +149,8 @@ void Diagram::keyPressEvent(QKeyEvent *e) {
|
||||
case Qt::Key_Down: movement = QPointF(0.0, +yGrid); break;
|
||||
}
|
||||
if (!movement.isNull() && !focusItem()) {
|
||||
moveElements(movement);
|
||||
beginMoveElements();
|
||||
continueMoveElements(movement);
|
||||
}
|
||||
}
|
||||
QGraphicsScene::keyPressEvent(e);
|
||||
@ -154,14 +164,11 @@ void Diagram::keyReleaseEvent(QKeyEvent *e) {
|
||||
if (!isReadOnly()) {
|
||||
// detecte le relachement d'une touche de direction ( = deplacement d'elements)
|
||||
if (
|
||||
(e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right ||\
|
||||
e -> key() == Qt::Key_Up || e -> key() == Qt::Key_Down) &&\
|
||||
!current_movement.isNull() && !e -> isAutoRepeat()
|
||||
(e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right ||
|
||||
e -> key() == Qt::Key_Up || e -> key() == Qt::Key_Down) &&
|
||||
!e -> isAutoRepeat()
|
||||
) {
|
||||
// cree un objet d'annulation pour le mouvement qui vient de se finir
|
||||
undoStack().push(new MoveElementsCommand(this, selectedContent(), current_movement));
|
||||
invalidateMovedElements();
|
||||
current_movement = QPointF();
|
||||
endMoveElements();
|
||||
}
|
||||
}
|
||||
QGraphicsScene::keyReleaseEvent(e);
|
||||
@ -561,9 +568,9 @@ bool Diagram::fromXml(QDomElement &document, QPointF position, bool consider_inf
|
||||
|
||||
// remplissage des listes facultatives
|
||||
if (content_ptr) {
|
||||
content_ptr -> elements = added_elements;
|
||||
content_ptr -> conductorsToMove = added_conductors;
|
||||
content_ptr -> textFields = added_texts;
|
||||
content_ptr -> elements = added_elements.toSet();
|
||||
content_ptr -> conductorsToMove = added_conductors.toSet();
|
||||
content_ptr -> textFields = added_texts.toSet();
|
||||
}
|
||||
|
||||
return(true);
|
||||
@ -804,110 +811,59 @@ QList<CustomElement *> Diagram::customElements() const {
|
||||
}
|
||||
|
||||
/**
|
||||
Oublie la liste des elements et conducteurs en mouvement
|
||||
Initialise un deplacement d'elements, conducteurs et champs de texte sur le
|
||||
schema.
|
||||
@param driver_item Item deplace par la souris et ne necessitant donc pas
|
||||
d'etre deplace lors des appels a continueMovement.
|
||||
@see ElementsMover
|
||||
*/
|
||||
void Diagram::invalidateMovedElements() {
|
||||
if (!moved_elements_fetched) return;
|
||||
moved_elements_fetched = false;
|
||||
elements_to_move.clear();
|
||||
conductors_to_move.clear();
|
||||
conductors_to_update.clear();
|
||||
texts_to_move.clear();
|
||||
elements_texts_to_move.clear();
|
||||
int Diagram::beginMoveElements(QGraphicsItem *driver_item) {
|
||||
return(elements_mover_ -> beginMovement(this, driver_item));
|
||||
}
|
||||
|
||||
/**
|
||||
Reconstruit la liste des elements et conducteurs en mouvement
|
||||
Prend en compte un mouvement composant un deplacement d'elements,
|
||||
conducteurs et champs de texte
|
||||
@param movement mouvement a ajouter au deplacement en cours
|
||||
@see ElementsMover
|
||||
*/
|
||||
void Diagram::fetchMovedElements() {
|
||||
// recupere les elements deplaces
|
||||
foreach (QGraphicsItem *item, selectedItems()) {
|
||||
if (Element *elmt = qgraphicsitem_cast<Element *>(item)) {
|
||||
elements_to_move << elmt;
|
||||
} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
|
||||
texts_to_move << iti;
|
||||
} else if (ElementTextItem *eti = qgraphicsitem_cast<ElementTextItem *>(item)) {
|
||||
elements_texts_to_move << eti;
|
||||
}
|
||||
}
|
||||
|
||||
// pour chaque element deplace, determine les conducteurs qui seront modifies
|
||||
foreach(Element *elmt, elements_to_move) {
|
||||
foreach(Terminal *terminal, elmt -> terminals()) {
|
||||
foreach(Conductor *conductor, terminal -> conductors()) {
|
||||
Terminal *other_terminal;
|
||||
if (conductor -> terminal1 == terminal) {
|
||||
other_terminal = conductor -> terminal2;
|
||||
} else {
|
||||
other_terminal = conductor -> terminal1;
|
||||
}
|
||||
// si les deux elements du conducteur sont deplaces
|
||||
if (elements_to_move.contains(other_terminal -> parentElement())) {
|
||||
conductors_to_move << conductor;
|
||||
} else {
|
||||
conductors_to_update << conductor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moved_elements_fetched = true;
|
||||
void Diagram::continueMoveElements(const QPointF &movement) {
|
||||
elements_mover_ -> continueMovement(movement);
|
||||
}
|
||||
|
||||
/**
|
||||
Deplace les elements, conducteurs et textes independants selectionnes en
|
||||
gerant au mieux les conducteurs (seuls les conducteurs dont un seul des
|
||||
elements est deplace sont recalcules, les autres sont deplaces).
|
||||
@param diff Translation a effectuer
|
||||
@param dontmove QGraphicsItem (optionnel) a ne pas deplacer ; note : ce
|
||||
parametre ne concerne que les elements et les champs de texte.
|
||||
Finalise un deplacement d'elements, conducteurs et champs de texte
|
||||
@see ElementsMover
|
||||
*/
|
||||
void Diagram::moveElements(const QPointF &diff, QGraphicsItem *dontmove) {
|
||||
// inutile de deplacer les autres elements s'il n'y a pas eu de mouvement concret
|
||||
if (diff.isNull()) return;
|
||||
current_movement += diff;
|
||||
|
||||
// deplace les elements selectionnes
|
||||
foreach(Element *element, elementsToMove()) {
|
||||
if (dontmove && element == dontmove) continue;
|
||||
element -> setPos(element -> pos() + diff);
|
||||
}
|
||||
|
||||
// deplace certains conducteurs
|
||||
foreach(Conductor *conductor, conductorsToMove()) {
|
||||
conductor -> setPos(conductor -> pos() + diff);
|
||||
}
|
||||
|
||||
// recalcule les autres conducteurs
|
||||
foreach(Conductor *conductor, conductorsToUpdate()) {
|
||||
conductor -> updatePath();
|
||||
}
|
||||
|
||||
// deplace les champs de texte
|
||||
foreach(DiagramTextItem *dti, independentTextsToMove()) {
|
||||
if (dontmove && dti == dontmove) continue;
|
||||
dti -> setPos(dti -> pos() + diff);
|
||||
}
|
||||
void Diagram::endMoveElements() {
|
||||
elements_mover_ -> endMovement();
|
||||
}
|
||||
|
||||
/**
|
||||
Deplace les champs de textes selectionnes ET rattaches a un element
|
||||
@param diff Translation a effectuer, exprimee dans les coordonnees de la
|
||||
scene
|
||||
@param dontmove ElementTextItem (optionnel) a ne pas deplacer
|
||||
Initialise un deplacement d'ElementTextItems
|
||||
@param driver_item Item deplace par la souris et ne necessitant donc pas
|
||||
d'etre deplace lors des appels a continueMovement.
|
||||
@see ElementTextsMover
|
||||
*/
|
||||
void Diagram::moveElementsTexts(const QPointF &diff, ElementTextItem *dontmove) {
|
||||
// inutile de deplacer les autres textes s'il n'y a pas eu de mouvement concret
|
||||
if (diff.isNull()) return;
|
||||
current_movement += diff;
|
||||
|
||||
// deplace les champs de texte rattaches a un element
|
||||
foreach(ElementTextItem *eti, elementTextsToMove()) {
|
||||
if (dontmove && eti == dontmove) continue;
|
||||
QPointF applied_movement = eti -> mapMovementToParent(eti-> mapMovementFromScene(diff));
|
||||
eti -> setPos(eti -> pos() + applied_movement);
|
||||
int Diagram::beginMoveElementTexts(QGraphicsItem *driver_item) {
|
||||
return(element_texts_mover_ -> beginMovement(this, driver_item));
|
||||
}
|
||||
|
||||
/**
|
||||
Prend en compte un mouvement composant un deplacement d'ElementTextItems
|
||||
@param movement mouvement a ajouter au deplacement en cours
|
||||
@see ElementTextsMover
|
||||
*/
|
||||
void Diagram::continueMoveElementTexts(const QPointF &movement) {
|
||||
element_texts_mover_ -> continueMovement(movement);
|
||||
}
|
||||
|
||||
/**
|
||||
Finalise un deplacement d'ElementTextItems
|
||||
@see ElementTextsMover
|
||||
*/
|
||||
void Diagram::endMoveElementTexts() {
|
||||
element_texts_mover_ -> endMovement();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1084,18 +1040,17 @@ DiagramContent Diagram::content() const {
|
||||
@return le contenu selectionne du schema.
|
||||
*/
|
||||
DiagramContent Diagram::selectedContent() {
|
||||
invalidateMovedElements();
|
||||
DiagramContent dc;
|
||||
dc.elements = elementsToMove().toList();
|
||||
dc.textFields = independentTextsToMove().toList();
|
||||
dc.conductorsToMove = conductorsToMove().toList();
|
||||
dc.conductorsToUpdate = conductorsToUpdate().toList();
|
||||
|
||||
// recupere les elements deplaces
|
||||
foreach (QGraphicsItem *item, selectedItems()) {
|
||||
if (Element *elmt = qgraphicsitem_cast<Element *>(item)) {
|
||||
dc.elements << elmt;
|
||||
} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
|
||||
dc.textFields << iti;
|
||||
} else if (Conductor *c = qgraphicsitem_cast<Conductor *>(item)) {
|
||||
// recupere les conducteurs selectionnes isoles (= non deplacables mais supprimables)
|
||||
foreach(QGraphicsItem *qgi, items()) {
|
||||
if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
|
||||
if (
|
||||
c -> isSelected() &&\
|
||||
!c -> terminal1 -> parentItem() -> isSelected() &&\
|
||||
!c -> terminal2 -> parentItem() -> isSelected()
|
||||
) {
|
||||
@ -1103,7 +1058,27 @@ DiagramContent Diagram::selectedContent() {
|
||||
}
|
||||
}
|
||||
}
|
||||
invalidateMovedElements();
|
||||
|
||||
// pour chaque element deplace, determine les conducteurs qui seront modifies
|
||||
foreach(Element *elmt, dc.elements) {
|
||||
foreach(Terminal *terminal, elmt -> terminals()) {
|
||||
foreach(Conductor *conductor, terminal -> conductors()) {
|
||||
Terminal *other_terminal;
|
||||
if (conductor -> terminal1 == terminal) {
|
||||
other_terminal = conductor -> terminal2;
|
||||
} else {
|
||||
other_terminal = conductor -> terminal1;
|
||||
}
|
||||
// si les deux elements du conducteur sont deplaces
|
||||
if (dc.elements.contains(other_terminal -> parentElement())) {
|
||||
dc.conductorsToMove << conductor;
|
||||
} else {
|
||||
dc.conductorsToUpdate << conductor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(dc);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,9 @@ class DiagramPosition;
|
||||
class DiagramTextItem;
|
||||
class Element;
|
||||
class ElementsLocation;
|
||||
class ElementsMover;
|
||||
class ElementTextItem;
|
||||
class ElementTextsMover;
|
||||
class IndependentTextItem;
|
||||
class QETProject;
|
||||
class Terminal;
|
||||
@ -63,8 +65,6 @@ class Diagram : public QGraphicsScene {
|
||||
ConductorProperties defaultConductorProperties;
|
||||
/// Dimensions et cartouches du schema
|
||||
BorderInset border_and_inset;
|
||||
/// Mouvement en cours lors d'un deplacement d'elements et conducteurs
|
||||
QPointF current_movement;
|
||||
/// taille de la grille en abscisse
|
||||
static const int xGrid;
|
||||
/// taille de la grille en ordonnee
|
||||
@ -74,14 +74,10 @@ class Diagram : public QGraphicsScene {
|
||||
|
||||
private:
|
||||
QGraphicsLineItem *conductor_setter;
|
||||
ElementsMover *elements_mover_;
|
||||
ElementTextsMover *element_texts_mover_;
|
||||
bool draw_grid;
|
||||
bool use_border;
|
||||
bool moved_elements_fetched;
|
||||
QSet<Element *> elements_to_move;
|
||||
QSet<Conductor *> conductors_to_move;
|
||||
QSet<Conductor *> conductors_to_update;
|
||||
QSet<IndependentTextItem *> texts_to_move;
|
||||
QSet<ElementTextItem *> elements_texts_to_move;
|
||||
QGIManager *qgi_manager;
|
||||
QUndoStack *undo_stack;
|
||||
bool draw_terminals;
|
||||
@ -154,20 +150,17 @@ class Diagram : public QGraphicsScene {
|
||||
bool isEmpty() const;
|
||||
|
||||
QList<CustomElement *> customElements() const;
|
||||
void invalidateMovedElements();
|
||||
void fetchMovedElements();
|
||||
const QSet<Element *> &elementsToMove();
|
||||
const QSet<Conductor *> &conductorsToMove();
|
||||
const QSet<Conductor *> &conductorsToUpdate();
|
||||
const QSet<IndependentTextItem *> &independentTextsToMove();
|
||||
const QSet<ElementTextItem *> &elementTextsToMove();
|
||||
QSet<DiagramTextItem *> selectedTexts() const;
|
||||
QSet<Conductor *> selectedConductors() const;
|
||||
DiagramContent content() const;
|
||||
DiagramContent selectedContent();
|
||||
bool canRotateSelection() const;
|
||||
void moveElements(const QPointF &, QGraphicsItem * = 0);
|
||||
void moveElementsTexts(const QPointF &, ElementTextItem * = 0);
|
||||
int beginMoveElements(QGraphicsItem * = 0);
|
||||
void continueMoveElements(const QPointF &);
|
||||
void endMoveElements();
|
||||
int beginMoveElementTexts(QGraphicsItem * = 0);
|
||||
void continueMoveElementTexts(const QPointF &);
|
||||
void endMoveElementTexts();
|
||||
bool usesElement(const ElementsLocation &);
|
||||
|
||||
QUndoStack &undoStack();
|
||||
@ -272,36 +265,6 @@ inline Diagram::BorderOptions Diagram::borderOptions() {
|
||||
return(retour);
|
||||
}
|
||||
|
||||
/// @return la liste des elements a deplacer
|
||||
inline const QSet<Element *> &Diagram::elementsToMove() {
|
||||
if (!moved_elements_fetched) fetchMovedElements();
|
||||
return(elements_to_move);
|
||||
}
|
||||
|
||||
/// @return la liste des conducteurs a deplacer
|
||||
inline const QSet<Conductor *> &Diagram::conductorsToMove() {
|
||||
if (!moved_elements_fetched) fetchMovedElements();
|
||||
return(conductors_to_move);
|
||||
}
|
||||
|
||||
/// @return la liste des conducteurs a modifier (typiquement les conducteurs dont seul un element est deplace)
|
||||
inline const QSet<Conductor *> &Diagram::conductorsToUpdate() {
|
||||
if (!moved_elements_fetched) fetchMovedElements();
|
||||
return(conductors_to_update);
|
||||
}
|
||||
|
||||
/// @return la liste des textes a deplacer
|
||||
inline const QSet<IndependentTextItem *> &Diagram::independentTextsToMove() {
|
||||
if (!moved_elements_fetched) fetchMovedElements();
|
||||
return(texts_to_move);
|
||||
}
|
||||
|
||||
/// @return la liste des textes rattaches a un element qui sont a deplacer
|
||||
inline const QSet<ElementTextItem *> &Diagram::elementTextsToMove() {
|
||||
if (!moved_elements_fetched) fetchMovedElements();
|
||||
return(elements_texts_to_move);
|
||||
}
|
||||
|
||||
/// @return la pile d'annulations de ce schema
|
||||
inline QUndoStack &Diagram::undoStack() {
|
||||
return(*undo_stack);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "diagramcommands.h"
|
||||
#include "element.h"
|
||||
#include "conductor.h"
|
||||
#include "conductortextitem.h"
|
||||
#include "diagram.h"
|
||||
#include "elementtextitem.h"
|
||||
#include "independenttextitem.h"
|
||||
@ -357,12 +358,37 @@ void MoveElementsCommand::move(const QPointF &actual_movement) {
|
||||
conductor -> updatePath();
|
||||
}
|
||||
|
||||
// repositionne les textes des conducteurs mis a jour
|
||||
foreach(ConductorTextItem *text_item, moved_conductor_texts_.keys()) {
|
||||
// determine s'il s'agit d'un undo ou d'un redo
|
||||
qreal coef = actual_movement.x() / movement.x();
|
||||
// -1 : undo, 1 : redo
|
||||
QPointF desired_pos = coef > 0 ? moved_conductor_texts_[text_item].second : moved_conductor_texts_[text_item].first;
|
||||
text_item -> setPos(desired_pos);
|
||||
}
|
||||
|
||||
// deplace les textes
|
||||
foreach(DiagramTextItem *text, content_to_move.textFields) {
|
||||
text -> setPos(text -> pos() + actual_movement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Ajoute un champ de texte de conducteur a deplacer
|
||||
@param text_item Champ de texte a deplacer
|
||||
@param old_pos Position du champ de texte avant le deplacement
|
||||
@param new_pos Position du champ de texte apres le deplacement
|
||||
*/
|
||||
void MoveElementsCommand::addConductorTextItemMovement(ConductorTextItem *text_item, const QPointF &old_pos, const QPointF &new_pos) {
|
||||
if (moved_conductor_texts_.contains(text_item)) return;
|
||||
if (!text_item -> wasMovedByUser()) return;
|
||||
if (new_pos == old_pos) return;
|
||||
moved_conductor_texts_.insert(
|
||||
text_item,
|
||||
qMakePair(old_pos, new_pos)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@param diagram Schema sur lequel on deplace des champs de texte
|
||||
@ -421,6 +447,89 @@ void MoveElementsTextsCommand::move(const QPointF &actual_movement) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@param diagram Schema sur lequel on deplace des champs de texte
|
||||
@param texts Textes deplaces : chaque ConductorTextItem est associe a un
|
||||
couple de position : avant et apres le deplacement
|
||||
@param m translation subie par les elements
|
||||
@param parent QUndoCommand parent
|
||||
*/
|
||||
MoveConductorsTextsCommand::MoveConductorsTextsCommand(
|
||||
Diagram *diagram,
|
||||
QUndoCommand *parent
|
||||
) :
|
||||
QUndoCommand(parent),
|
||||
diagram(diagram),
|
||||
first_redo(true)
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructeur
|
||||
MoveConductorsTextsCommand::~MoveConductorsTextsCommand() {
|
||||
}
|
||||
|
||||
/// annule le deplacement
|
||||
void MoveConductorsTextsCommand::undo() {
|
||||
foreach(ConductorTextItem *cti, texts_to_move_.keys()) {
|
||||
QPointF movement = texts_to_move_[cti].first;
|
||||
bool was_already_moved = texts_to_move_[cti].second;
|
||||
|
||||
cti -> forceMovedByUser(was_already_moved);
|
||||
if (was_already_moved) {
|
||||
cti -> setPos(cti -> pos() - movement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// refait le deplacement
|
||||
void MoveConductorsTextsCommand::redo() {
|
||||
if (first_redo) {
|
||||
first_redo = false;
|
||||
} else {
|
||||
foreach(ConductorTextItem *cti, texts_to_move_.keys()) {
|
||||
QPointF movement = texts_to_move_[cti].first;
|
||||
|
||||
cti -> forceMovedByUser(true);
|
||||
cti -> setPos(cti -> pos() + movement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Ajout un mouvement de champ de texte a cet objet
|
||||
@param text_item Champ de texte deplace ; si celui-ci est deja connu de l'objet d'annulation, il sera ignore
|
||||
@param old_pos Position du champ de texte avant le mouvement
|
||||
@param new_pos Position du champ de texte apres le mouvement
|
||||
@param alread_moved true si le champ de texte etait deja a une position personnalisee par l'utilisateur, false sinon
|
||||
*/
|
||||
void MoveConductorsTextsCommand::addTextMovement(ConductorTextItem *text_item, const QPointF &old_pos, const QPointF &new_pos, bool already_moved) {
|
||||
// si le champ de texte est deja connu de l'objet d'annulation, il sera ignore
|
||||
if (texts_to_move_.contains(text_item)) return;
|
||||
|
||||
// on memorise le champ de texte, en l'associant au mouvement effectue et a son etat avant le deplacement
|
||||
texts_to_move_.insert(text_item, qMakePair(new_pos - old_pos, already_moved));
|
||||
|
||||
// met a jour la description de l'objet d'annulation
|
||||
regenerateTextLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
Genere la description de l'objet d'annulation
|
||||
*/
|
||||
void MoveConductorsTextsCommand::regenerateTextLabel() {
|
||||
QString moved_content_sentence = QET::ElementsAndConductorsSentence(0, 0, texts_to_move_.count());
|
||||
|
||||
setText(
|
||||
QString(
|
||||
QObject::tr(
|
||||
"d\351placer %1",
|
||||
"undo caption - %1 is a sentence listing the moved content"
|
||||
).arg(moved_content_sentence)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@param dti Champ de texte modifie
|
||||
@ -621,12 +730,28 @@ ChangeConductorCommand::~ChangeConductorCommand() {
|
||||
/// Annule la modification du conducteur
|
||||
void ChangeConductorCommand::undo() {
|
||||
conductor -> setProfile(old_profile, path_type);
|
||||
conductor -> textItem() -> setPos(text_pos_before_mov_);
|
||||
}
|
||||
|
||||
/// Refait la modification du conducteur
|
||||
void ChangeConductorCommand::redo() {
|
||||
if (first_redo) first_redo = false;
|
||||
else conductor -> setProfile(new_profile, path_type);
|
||||
if (first_redo) {
|
||||
first_redo = false;
|
||||
} else {
|
||||
conductor -> setProfile(new_profile, path_type);
|
||||
conductor -> textItem() -> setPos(text_pos_after_mov_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Integre dans cet objet d'annulation le repositionnement du champ de texte
|
||||
du conducteur
|
||||
@param pos_before Position du texte avant la modification du conducteur
|
||||
@param pos_after Position du texte apres la modification du conducteur
|
||||
*/
|
||||
void ChangeConductorCommand::setConductorTextItemMove(const QPointF &pos_before, const QPointF &pos_after) {
|
||||
text_pos_before_mov_ = pos_before;
|
||||
text_pos_after_mov_ = pos_after;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,6 +189,7 @@ class MoveElementsCommand : public QUndoCommand {
|
||||
virtual void undo();
|
||||
virtual void redo();
|
||||
virtual void move(const QPointF &);
|
||||
virtual void addConductorTextItemMovement(ConductorTextItem *, const QPointF &, const QPointF &);
|
||||
|
||||
// attributs
|
||||
private:
|
||||
@ -198,6 +199,16 @@ class MoveElementsCommand : public QUndoCommand {
|
||||
DiagramContent content_to_move;
|
||||
/// mouvement effectue
|
||||
QPointF movement;
|
||||
/**
|
||||
Deplacer des elements ou champs de texte entraine des conducteurs.
|
||||
Soit ces conducteurs sont betement deplaces, soit leur trajet est
|
||||
recalcule.
|
||||
Si leur trajet est recalcule, les champs de texte dont la position a ete
|
||||
personnalisee par l'utilisateur
|
||||
Liste des champs de texte de conducteurs dont la position a ete modifiee
|
||||
par des mises
|
||||
*/
|
||||
QHash<ConductorTextItem *, QPair<QPointF, QPointF> > moved_conductor_texts_;
|
||||
/// booleen pour ne pas executer le premier redo()
|
||||
bool first_redo;
|
||||
};
|
||||
@ -232,6 +243,37 @@ class MoveElementsTextsCommand : public QUndoCommand {
|
||||
bool first_redo;
|
||||
};
|
||||
|
||||
/**
|
||||
Cette classe represente l'action de deplacer des champs de texte rattaches
|
||||
a des conducteurs sur un schema
|
||||
*/
|
||||
class MoveConductorsTextsCommand : public QUndoCommand {
|
||||
// constructeurs, destructeur
|
||||
public:
|
||||
MoveConductorsTextsCommand(Diagram *, QUndoCommand * = 0);
|
||||
virtual ~MoveConductorsTextsCommand();
|
||||
private:
|
||||
MoveConductorsTextsCommand(const MoveConductorsTextsCommand &);
|
||||
|
||||
// methodes
|
||||
public:
|
||||
virtual void undo();
|
||||
virtual void redo();
|
||||
virtual void addTextMovement(ConductorTextItem *, const QPointF &, const QPointF &, bool = false);
|
||||
|
||||
private:
|
||||
void regenerateTextLabel();
|
||||
|
||||
// attributs
|
||||
private:
|
||||
/// schema sur lequel on deplace les elements
|
||||
Diagram *diagram;
|
||||
/// liste des champs de texte a deplacer
|
||||
QHash<ConductorTextItem *, QPair<QPointF, bool> > texts_to_move_;
|
||||
/// booleen pour ne pas executer le premier redo()
|
||||
bool first_redo;
|
||||
};
|
||||
|
||||
/**
|
||||
Cette classe represente la modification d'un champ de texte
|
||||
*/
|
||||
@ -331,6 +373,7 @@ class ChangeConductorCommand : public QUndoCommand {
|
||||
public:
|
||||
virtual void undo();
|
||||
virtual void redo();
|
||||
virtual void setConductorTextItemMove(const QPointF &, const QPointF &);
|
||||
|
||||
// attributs
|
||||
private:
|
||||
@ -342,6 +385,10 @@ class ChangeConductorCommand : public QUndoCommand {
|
||||
ConductorProfile new_profile;
|
||||
/// Type de trajet
|
||||
Qt::Corner path_type;
|
||||
/// Position du champ de texte avant le changement
|
||||
QPointF text_pos_before_mov_;
|
||||
/// Position du champ de texte apres le changement
|
||||
QPointF text_pos_after_mov_;
|
||||
/// booleen pour ne pas executer le premier redo()
|
||||
bool first_redo;
|
||||
};
|
||||
|
@ -50,16 +50,16 @@ DiagramContent::~DiagramContent() {
|
||||
@return tous les conducteurs
|
||||
*/
|
||||
QList<Conductor *> DiagramContent::conductors(int filter) const {
|
||||
QList<Conductor *> result;
|
||||
QSet<Conductor *> result;
|
||||
if (filter & ConductorsToMove) result += conductorsToMove;
|
||||
if (filter & ConductorsToUpdate) result += conductorsToUpdate;
|
||||
if (filter & OtherConductors) result += otherConductors;
|
||||
if (filter & SelectedOnly) {
|
||||
foreach(Conductor *conductor, result) {
|
||||
if (!conductor -> isSelected()) result.removeOne(conductor);
|
||||
if (!conductor -> isSelected()) result.remove(conductor);
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
return(result.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,15 +50,15 @@ class DiagramContent {
|
||||
};
|
||||
|
||||
/// Elements de texte du schema
|
||||
QList<Element *> elements;
|
||||
QSet<Element *> elements;
|
||||
/// Champs de texte independants du schema
|
||||
QList<IndependentTextItem *> textFields;
|
||||
QSet<IndependentTextItem *> textFields;
|
||||
/// Conducteurs a mettre a jour du schema
|
||||
QList<Conductor *> conductorsToUpdate;
|
||||
QSet<Conductor *> conductorsToUpdate;
|
||||
/// Conducteurs a deplacer du schema
|
||||
QList<Conductor *> conductorsToMove;
|
||||
QSet<Conductor *> conductorsToMove;
|
||||
/// Conducteurs isoles (ni a deplacer, ni a mettre a jour)
|
||||
QList<Conductor *> otherConductors;
|
||||
QSet<Conductor *> otherConductors;
|
||||
|
||||
QList<Conductor *> conductors(int = AnyConductor) const;
|
||||
QList<QGraphicsItem *> items(int = All) const;
|
||||
|
@ -198,6 +198,9 @@ void DiagramTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o
|
||||
void DiagramTextItem::focusInEvent(QFocusEvent *e) {
|
||||
QGraphicsTextItem::focusInEvent(e);
|
||||
|
||||
// empeche le deplacement du texte pendant son edition
|
||||
setFlag(QGraphicsItem::ItemIsMovable, false);
|
||||
|
||||
// memorise le texte avant que l'utilisateur n'y touche
|
||||
previous_text_ = toPlainText();
|
||||
// cela permettra de determiner si l'utilisateur a modifie le texte a la fin de l'edition
|
||||
@ -223,6 +226,9 @@ void DiagramTextItem::focusOutEvent(QFocusEvent *e) {
|
||||
|
||||
// hack a la con pour etre re-entrant
|
||||
setTextInteractionFlags(Qt::NoTextInteraction);
|
||||
|
||||
// autorise de nouveau le deplacement du texte
|
||||
setFlag(QGraphicsItem::ItemIsMovable, true);
|
||||
QTimer::singleShot(0, this, SIGNAL(lostFocus()));
|
||||
}
|
||||
|
||||
@ -242,55 +248,6 @@ void DiagramTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le clic sur le champ de texte
|
||||
*/
|
||||
void DiagramTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (e -> modifiers() & Qt::ControlModifier) {
|
||||
setSelected(!isSelected());
|
||||
}
|
||||
QGraphicsTextItem::mousePressEvent(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Gere les mouvements de souris lies au champ de texte
|
||||
*/
|
||||
void DiagramTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (textInteractionFlags() & Qt::TextEditable) {
|
||||
QGraphicsTextItem::mouseMoveEvent(e);
|
||||
} else if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
|
||||
QPointF oldPos = pos();
|
||||
setPos(mapToParent(e -> pos()) - matrix().map(e -> buttonDownPos(Qt::LeftButton)));
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
diagram_ptr -> moveElements(pos() - oldPos, this);
|
||||
}
|
||||
} else e -> ignore();
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le relachement de souris
|
||||
Cette methode a ete reimplementee pour tenir a jour la liste des elements
|
||||
et conducteurs a deplacer au niveau du schema.
|
||||
*/
|
||||
void DiagramTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
if ((flags() & QGraphicsItem::ItemIsMovable) && (!diagram_ptr -> current_movement.isNull())) {
|
||||
diagram_ptr -> undoStack().push(
|
||||
new MoveElementsCommand(
|
||||
diagram_ptr,
|
||||
diagram_ptr -> selectedContent(),
|
||||
diagram_ptr -> current_movement
|
||||
)
|
||||
);
|
||||
diagram_ptr -> current_movement = QPointF();
|
||||
}
|
||||
diagram_ptr -> invalidateMovedElements();
|
||||
}
|
||||
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
||||
QGraphicsTextItem::mouseReleaseEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Effetue la rotation du texte en elle-meme
|
||||
Pour les DiagramTextItem, la rotation s'effectue autour du point (0, 0).
|
||||
|
@ -62,9 +62,6 @@ class DiagramTextItem : public QGraphicsTextItem {
|
||||
virtual void focusInEvent(QFocusEvent *);
|
||||
virtual void focusOutEvent(QFocusEvent *);
|
||||
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void applyRotation(const qreal &);
|
||||
|
||||
// signaux
|
||||
|
@ -686,7 +686,7 @@ void DiagramView::editSelectionProperties() {
|
||||
|
||||
// cas d'un element selectionne
|
||||
if (selection.elements.count() == 1) {
|
||||
editElement(selection.elements.at(0));
|
||||
editElement(selection.elements.toList().at(0));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,8 @@ Element::Element(QGraphicsItem *parent, Diagram *scene) :
|
||||
QObject(),
|
||||
QGraphicsItem(parent, scene),
|
||||
internal_connections(false),
|
||||
must_highlight_(false)
|
||||
must_highlight_(false),
|
||||
first_move_(true)
|
||||
{
|
||||
setZValue(10);
|
||||
}
|
||||
@ -315,9 +316,11 @@ void Element::setPos(qreal x, qreal y) {
|
||||
}
|
||||
|
||||
/**
|
||||
Gere l'enfoncement d'un bouton de la souris
|
||||
Gere le clic sur l'element
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void Element::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
first_move_ = true;
|
||||
if (e -> modifiers() & Qt::ControlModifier) {
|
||||
setSelected(!isSelected());
|
||||
}
|
||||
@ -330,12 +333,33 @@ void Element::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
*/
|
||||
void Element::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (isSelected() && e -> buttons() & Qt::LeftButton) {
|
||||
QPointF oldPos = pos();
|
||||
// l'element est en train d'etre deplace
|
||||
Diagram *diagram_ptr = diagram();
|
||||
if (diagram_ptr) {
|
||||
if (first_move_) {
|
||||
// il s'agit du premier mouvement du deplacement, on le signale
|
||||
// au schema parent
|
||||
diagram_ptr -> beginMoveElements(this);
|
||||
}
|
||||
}
|
||||
|
||||
// on applique le mouvement impose par la souris
|
||||
QPointF old_pos = pos();
|
||||
setPos(mapToParent(e -> pos()) - matrix().map(e -> buttonDownPos(Qt::LeftButton)));
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
diagram_ptr -> moveElements(pos() - oldPos, this);
|
||||
|
||||
// on calcule le mouvement reellement applique par setPos()
|
||||
QPointF effective_movement = pos() - old_pos;
|
||||
|
||||
if (diagram_ptr) {
|
||||
// on signale le mouvement ainsi applique au schema parent, qui
|
||||
// l'appliquera aux autres items selectionnes selon son bon vouloir
|
||||
diagram_ptr -> continueMoveElements(effective_movement);
|
||||
}
|
||||
} else e -> ignore();
|
||||
|
||||
if (first_move_) {
|
||||
first_move_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,20 +368,10 @@ void Element::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
et conducteurs a deplacer au niveau du schema.
|
||||
*/
|
||||
void Element::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
||||
Diagram *diagram_ptr = diagram();
|
||||
if (diagram_ptr) {
|
||||
if (!diagram_ptr -> current_movement.isNull()) {
|
||||
diagram_ptr -> undoStack().push(
|
||||
new MoveElementsCommand(
|
||||
diagram_ptr,
|
||||
diagram_ptr -> selectedContent(),
|
||||
diagram_ptr -> current_movement
|
||||
)
|
||||
);
|
||||
diagram_ptr -> current_movement = QPointF();
|
||||
}
|
||||
diagram_ptr -> invalidateMovedElements();
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
diagram_ptr -> endMoveElements();
|
||||
}
|
||||
|
||||
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
||||
QGraphicsItem::mouseReleaseEvent(e);
|
||||
}
|
||||
|
@ -129,6 +129,7 @@ class Element : public QObject, public QGraphicsItem {
|
||||
private:
|
||||
bool internal_connections;
|
||||
bool must_highlight_;
|
||||
bool first_move_;
|
||||
void drawSelection(QPainter *, const QStyleOptionGraphicsItem *);
|
||||
void drawHighlight(QPainter *, const QStyleOptionGraphicsItem *);
|
||||
void updatePixmap();
|
||||
|
178
sources/elementsmover.cpp
Normal file
178
sources/elementsmover.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2006-2010 Xavier Guerrin
|
||||
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 "elementsmover.h"
|
||||
#include "conductor.h"
|
||||
#include "conductortextitem.h"
|
||||
#include "diagram.h"
|
||||
#include "diagramcommands.h"
|
||||
#include "element.h"
|
||||
#include "independenttextitem.h"
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
*/
|
||||
ElementsMover::ElementsMover() :
|
||||
movement_running_(false),
|
||||
current_movement_(),
|
||||
diagram_(0),
|
||||
movement_driver_(0),
|
||||
moved_content_()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Destructeur
|
||||
*/
|
||||
ElementsMover::~ElementsMover() {
|
||||
}
|
||||
|
||||
/**
|
||||
@return true si ce gestionnaire de deplacement est pret a etre utilise,
|
||||
false sinon. Un gestionnaire de deplacement est pret a etre utilise a partir
|
||||
du moment ou le mouvement precedemment gere n'est plus en cours.
|
||||
*/
|
||||
bool ElementsMover::isReady() const {
|
||||
return(!movement_running_);
|
||||
}
|
||||
|
||||
/**
|
||||
Demarre un nouveau mouvement d'element
|
||||
@param diagram Schema sur lequel se deroule le deplacement
|
||||
@param driver_item Item deplace par la souris et ne necessitant donc pas
|
||||
d'etre deplace lors des appels a continueMovement.
|
||||
@return le nombre d'items concernes par le deplacement, ou -1 si le
|
||||
mouvement n'a pas ete initie
|
||||
*/
|
||||
int ElementsMover::beginMovement(Diagram *diagram, QGraphicsItem *driver_item) {
|
||||
// il ne doit pas y avoir de mouvement en cours
|
||||
if (movement_running_) return(-1);
|
||||
|
||||
// on s'assure que l'on dispose d'un schema pour travailler
|
||||
if (!diagram) return(-1);
|
||||
diagram_ = diagram;
|
||||
|
||||
// on prend en compte le driver_item
|
||||
movement_driver_ = driver_item;
|
||||
|
||||
// au debut du mouvement, le deplacement est nul
|
||||
current_movement_ = QPointF(0.0, 0.0);
|
||||
|
||||
// on stocke dans cet objet les items concernes par le deplacement
|
||||
moved_content_ = diagram -> selectedContent();
|
||||
|
||||
// on a egalement besoin de retenir la position des champs de textes
|
||||
// rattaches a un conducteur (ConductorTextItem) si cette position a ete
|
||||
// personnalisee.
|
||||
// ceci n'est necessaire que pour les conducteurs dont le trajet sera
|
||||
// recalcule a cause du mouvement
|
||||
foreach(Conductor *conductor, moved_content_.conductorsToUpdate) {
|
||||
if (ConductorTextItem *text_item = conductor -> textItem()) {
|
||||
if (text_item -> wasMovedByUser()) {
|
||||
updated_conductors_text_pos_.insert(
|
||||
text_item,
|
||||
text_item -> pos()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// on s'assure qu'il y a quelque chose a deplacer
|
||||
if (!moved_content_.count()) return(-1);
|
||||
|
||||
// a ce stade, on dispose de toutes les informations necessaires pour
|
||||
// prendre en compte les mouvements
|
||||
|
||||
// il y a desormais un mouvement en cours
|
||||
movement_running_ = true;
|
||||
|
||||
return(moved_content_.count());
|
||||
}
|
||||
|
||||
/**
|
||||
Ajoute un mouvement au deplacement en cours. Cette methode
|
||||
@param movement mouvement a ajouter au deplacement en cours
|
||||
*/
|
||||
void ElementsMover::continueMovement(const QPointF &movement) {
|
||||
// un mouvement doit avoir ete initie
|
||||
if (!movement_running_) return;
|
||||
|
||||
// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
|
||||
if (movement.isNull()) return;
|
||||
|
||||
// prise en compte du mouvement
|
||||
current_movement_ += movement;
|
||||
|
||||
// deplace les elements selectionnes
|
||||
foreach(Element *element, moved_content_.elements) {
|
||||
if (movement_driver_ && element == movement_driver_) continue;
|
||||
element -> setPos(element -> pos() + movement);
|
||||
}
|
||||
|
||||
// deplace certains conducteurs
|
||||
foreach(Conductor *conductor, moved_content_.conductorsToMove) {
|
||||
conductor -> setPos(conductor -> pos() + movement);
|
||||
}
|
||||
|
||||
// recalcule les autres conducteurs
|
||||
foreach(Conductor *conductor, moved_content_.conductorsToUpdate) {
|
||||
conductor -> updatePath();
|
||||
}
|
||||
|
||||
// deplace les champs de texte
|
||||
foreach(IndependentTextItem *text_field, moved_content_.textFields) {
|
||||
if (movement_driver_ && text_field == movement_driver_) continue;
|
||||
text_field -> setPos(text_field -> pos() + movement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Termine le deplacement en creant un objet d'annulation et en l'ajoutant a
|
||||
la QUndoStack du schema concerne.
|
||||
@see Diagram::undoStack()
|
||||
*/
|
||||
void ElementsMover::endMovement() {
|
||||
// un mouvement doit avoir ete initie
|
||||
if (!movement_running_) return;
|
||||
|
||||
// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
|
||||
if (!current_movement_.isNull()) {
|
||||
// cree un objet d'annulation pour le mouvement ainsi realise
|
||||
MoveElementsCommand *undo_object = new MoveElementsCommand(
|
||||
diagram_,
|
||||
moved_content_,
|
||||
current_movement_
|
||||
);
|
||||
|
||||
// ajoute les informations necessaires au repositionnement des champs
|
||||
// de textes des conducteurs
|
||||
foreach(ConductorTextItem *text_item, updated_conductors_text_pos_.keys()) {
|
||||
if (text_item -> pos() != updated_conductors_text_pos_[text_item]) {
|
||||
undo_object -> addConductorTextItemMovement(
|
||||
text_item,
|
||||
updated_conductors_text_pos_[text_item],
|
||||
text_item -> pos()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
diagram_ -> undoStack().push(undo_object);
|
||||
}
|
||||
|
||||
// il n'y a plus de mouvement en cours
|
||||
movement_running_ = false;
|
||||
}
|
52
sources/elementsmover.h
Normal file
52
sources/elementsmover.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2006-2010 Xavier Guerrin
|
||||
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 ELEMENTS_MOVER_H
|
||||
#define ELEMENTS_MOVER_H
|
||||
#include <QtGui>
|
||||
#include "diagramcontent.h"
|
||||
class ConductorTextItem;
|
||||
class Diagram;
|
||||
/**
|
||||
Cette classe permet de gerer le deplacement des differents items composant
|
||||
un schema electrique sur ce schema.
|
||||
*/
|
||||
class ElementsMover {
|
||||
// constructeurs, destructeur
|
||||
public:
|
||||
ElementsMover();
|
||||
virtual ~ElementsMover();
|
||||
private:
|
||||
ElementsMover(const ElementsMover &);
|
||||
|
||||
// methodes
|
||||
public:
|
||||
bool isReady() const;
|
||||
int beginMovement(Diagram *, QGraphicsItem * = 0);
|
||||
void continueMovement(const QPointF &);
|
||||
void endMovement();
|
||||
|
||||
// attributs
|
||||
private:
|
||||
bool movement_running_;
|
||||
QPointF current_movement_;
|
||||
Diagram *diagram_;
|
||||
QGraphicsItem *movement_driver_;
|
||||
DiagramContent moved_content_;
|
||||
QHash<ConductorTextItem *, QPointF> updated_conductors_text_pos_;
|
||||
};
|
||||
#endif
|
@ -218,6 +218,18 @@ void ElementTextItem::applyRotation(const qreal &angle) {
|
||||
QGraphicsTextItem::setTransform(rotation, true);
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le clic sur le champ de texte
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void ElementTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
first_move_ = true;
|
||||
if (e -> modifiers() & Qt::ControlModifier) {
|
||||
setSelected(!isSelected());
|
||||
}
|
||||
DiagramTextItem::mousePressEvent(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Gere les mouvements de souris lies au champ de texte
|
||||
@param e Objet decrivant l'evenement souris
|
||||
@ -246,13 +258,17 @@ void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
QPointF parent_movement = mapMovementToParent(movement);
|
||||
setPos(pos() + parent_movement);
|
||||
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
int moved_texts_count = diagram_ptr -> elementTextsToMove().count();
|
||||
Diagram *diagram_ptr = diagram();
|
||||
if (diagram_ptr) {
|
||||
if (first_move_) {
|
||||
// on signale le debut d'un deplacement d'ElementTextItems au schema parent
|
||||
int moved_texts_count = diagram_ptr -> beginMoveElementTexts(this);
|
||||
|
||||
// s'il n'y a qu'un seul texte deplace, on met en valeur l'element parent
|
||||
if (moved_texts_count == 1 && parent_element_ && first_move_) {
|
||||
if (moved_texts_count == 1 && parent_element_) {
|
||||
parent_element_ -> setHighlighted(true);
|
||||
parent_element_ -> update();
|
||||
first_move_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -264,9 +280,13 @@ void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
QPointF scene_effective_movement = mapMovementToScene(mapMovementFromParent(effective_movement));
|
||||
|
||||
// on applique le mouvement subi aux autres textes a deplacer
|
||||
diagram_ptr -> moveElementsTexts(scene_effective_movement, this);
|
||||
diagram_ptr -> continueMoveElementTexts(scene_effective_movement);
|
||||
}
|
||||
} else e -> ignore();
|
||||
|
||||
if (first_move_) {
|
||||
first_move_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,28 +296,14 @@ void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
*/
|
||||
void ElementTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
int moved_texts_count = diagram_ptr -> elementTextsToMove().count();
|
||||
|
||||
// s'il n'y a qu'un seul texte deplace, on arrete de mettre en valeur l'element parent
|
||||
if (moved_texts_count == 1) {
|
||||
first_move_ = true;
|
||||
// on arrete de mettre en valeur l'element parent
|
||||
if (parent_element_) {
|
||||
if (parent_element_ -> isHighlighted()) {
|
||||
parent_element_ -> setHighlighted(false);
|
||||
}
|
||||
}
|
||||
|
||||
// on cree un objet d'annulation correspondant au deplacement qui s'acheve
|
||||
if ((flags() & QGraphicsItem::ItemIsMovable) && (!diagram_ptr -> current_movement.isNull())) {
|
||||
diagram_ptr -> undoStack().push(
|
||||
new MoveElementsTextsCommand(
|
||||
diagram_ptr,
|
||||
diagram_ptr -> elementTextsToMove(),
|
||||
diagram_ptr -> current_movement
|
||||
)
|
||||
);
|
||||
diagram_ptr -> current_movement = QPointF();
|
||||
}
|
||||
diagram_ptr -> invalidateMovedElements();
|
||||
diagram_ptr -> endMoveElementTexts();
|
||||
}
|
||||
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
||||
QGraphicsTextItem::mouseReleaseEvent(e);
|
||||
|
@ -72,6 +72,7 @@ class ElementTextItem : public DiagramTextItem {
|
||||
|
||||
protected:
|
||||
virtual void applyRotation(const qreal &);
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual QVariant itemChange(GraphicsItemChange, const QVariant &);
|
||||
|
141
sources/elementtextsmover.cpp
Normal file
141
sources/elementtextsmover.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2006-2010 Xavier Guerrin
|
||||
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 "elementtextsmover.h"
|
||||
#include "conductor.h"
|
||||
#include "elementtextitem.h"
|
||||
#include "diagram.h"
|
||||
#include "diagramcommands.h"
|
||||
#include "element.h"
|
||||
#include "independenttextitem.h"
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
*/
|
||||
ElementTextsMover::ElementTextsMover() :
|
||||
movement_running_(false),
|
||||
current_movement_(),
|
||||
diagram_(0),
|
||||
movement_driver_(0),
|
||||
moved_texts_()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Destructeur
|
||||
*/
|
||||
ElementTextsMover::~ElementTextsMover() {
|
||||
}
|
||||
|
||||
/**
|
||||
@return true si ce gestionnaire de deplacement est pret a etre utilise,
|
||||
false sinon. Un gestionnaire de deplacement est pret a etre utilise a partir
|
||||
du moment ou le mouvement precedemment gere n'est plus en cours.
|
||||
*/
|
||||
bool ElementTextsMover::isReady() const {
|
||||
return(!movement_running_);
|
||||
}
|
||||
|
||||
/**
|
||||
Demarre un nouveau mouvement d'ElementTextItems
|
||||
@param diagram Schema sur lequel se deroule le deplacement
|
||||
@param driver_item Item deplace par la souris et ne necessitant donc pas
|
||||
d'etre deplace lors des appels a continueMovement.
|
||||
@return le nombre d'items concernes par le deplacement, ou -1 si le
|
||||
mouvement n'a pas ete initie
|
||||
*/
|
||||
int ElementTextsMover::beginMovement(Diagram *diagram, QGraphicsItem *driver_item) {
|
||||
// il ne doit pas y avoir de mouvement en cours
|
||||
if (movement_running_) return(-1);
|
||||
|
||||
// on s'assure que l'on dispose d'un schema pour travailler
|
||||
if (!diagram) return(-1);
|
||||
diagram_ = diagram;
|
||||
|
||||
// on prend en compte le driver_item
|
||||
movement_driver_ = driver_item;
|
||||
|
||||
// au debut du mouvement, le deplacement est nul
|
||||
current_movement_ = QPointF(0.0, 0.0);
|
||||
|
||||
// on stocke dans cet objet les items concernes par le deplacement
|
||||
moved_texts_.clear();
|
||||
foreach(QGraphicsItem *item, diagram -> selectedItems()) {
|
||||
if (ElementTextItem *text_item = qgraphicsitem_cast<ElementTextItem *>(item)) {
|
||||
moved_texts_ << text_item;
|
||||
}
|
||||
}
|
||||
|
||||
// on s'assure qu'il y a quelque chose a deplacer
|
||||
if (!moved_texts_.count()) return(-1);
|
||||
|
||||
// a ce stade, on dispose de toutes les informations necessaires pour
|
||||
// prendre en compte les mouvements
|
||||
|
||||
// il y a desormais un mouvement en cours
|
||||
movement_running_ = true;
|
||||
|
||||
return(moved_texts_.count());
|
||||
}
|
||||
|
||||
/**
|
||||
Ajoute un mouvement au deplacement en cours. Cette methode
|
||||
@param movement mouvement a ajouter au deplacement en cours
|
||||
*/
|
||||
void ElementTextsMover::continueMovement(const QPointF &movement) {
|
||||
// un mouvement doit avoir ete initie
|
||||
if (!movement_running_) return;
|
||||
|
||||
// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
|
||||
if (movement.isNull()) return;
|
||||
|
||||
// prise en compte du mouvement
|
||||
current_movement_ += movement;
|
||||
|
||||
// deplace les elements selectionnes
|
||||
foreach(ElementTextItem *text_item, moved_texts_) {
|
||||
if (movement_driver_ && text_item == movement_driver_) continue;
|
||||
QPointF applied_movement = text_item -> mapMovementToParent(text_item-> mapMovementFromScene(movement));
|
||||
text_item -> setPos(text_item -> pos() + applied_movement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Termine le deplacement en creant un objet d'annulation et en l'ajoutant a
|
||||
la QUndoStack du schema concerne.
|
||||
@see Diagram::undoStack()
|
||||
*/
|
||||
void ElementTextsMover::endMovement() {
|
||||
// un mouvement doit avoir ete initie
|
||||
if (!movement_running_) return;
|
||||
|
||||
// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
|
||||
if (!current_movement_.isNull()) {
|
||||
// cree un objet d'annulation pour le mouvement ainsi realise
|
||||
MoveElementsTextsCommand*undo_object = new MoveElementsTextsCommand(
|
||||
diagram_,
|
||||
moved_texts_,
|
||||
current_movement_
|
||||
);
|
||||
|
||||
diagram_ -> undoStack().push(undo_object);
|
||||
}
|
||||
|
||||
// il n'y a plus de mouvement en cours
|
||||
movement_running_ = false;
|
||||
}
|
51
sources/elementtextsmover.h
Normal file
51
sources/elementtextsmover.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2006-2010 Xavier Guerrin
|
||||
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 ELEMENT_TEXTS_MOVER_H
|
||||
#define ELEMENT_TEXTS_MOVER_H
|
||||
#include <QtGui>
|
||||
#include "diagramcontent.h"
|
||||
class ElementTextItem;
|
||||
class Diagram;
|
||||
/**
|
||||
Cette classe permet de gerer le deplacement des ElementTextItems d'un schema
|
||||
electrique.
|
||||
*/
|
||||
class ElementTextsMover {
|
||||
// constructeurs, destructeur
|
||||
public:
|
||||
ElementTextsMover();
|
||||
virtual ~ElementTextsMover();
|
||||
private:
|
||||
ElementTextsMover(const ElementTextsMover &);
|
||||
|
||||
// methodes
|
||||
public:
|
||||
bool isReady() const;
|
||||
int beginMovement(Diagram *, QGraphicsItem * = 0);
|
||||
void continueMovement(const QPointF &);
|
||||
void endMovement();
|
||||
|
||||
// attributs
|
||||
private:
|
||||
bool movement_running_;
|
||||
QPointF current_movement_;
|
||||
Diagram *diagram_;
|
||||
QGraphicsItem *movement_driver_;
|
||||
QSet<ElementTextItem *> moved_texts_;
|
||||
};
|
||||
#endif
|
@ -23,7 +23,8 @@
|
||||
@param parent_diagram Le schema auquel est rattache le champ de texte
|
||||
*/
|
||||
IndependentTextItem::IndependentTextItem(Diagram *parent_diagram) :
|
||||
DiagramTextItem(0, parent_diagram)
|
||||
DiagramTextItem(0, parent_diagram),
|
||||
first_move_(true)
|
||||
{
|
||||
}
|
||||
|
||||
@ -33,7 +34,8 @@ IndependentTextItem::IndependentTextItem(Diagram *parent_diagram) :
|
||||
@param parent_diagram Le schema auquel est rattache le champ de texte
|
||||
*/
|
||||
IndependentTextItem::IndependentTextItem(const QString &text, Diagram *parent_diagram) :
|
||||
DiagramTextItem(text, 0, parent_diagram)
|
||||
DiagramTextItem(text, 0, parent_diagram),
|
||||
first_move_(true)
|
||||
{
|
||||
}
|
||||
|
||||
@ -68,3 +70,68 @@ QDomElement IndependentTextItem::toXml(QDomDocument &document) const {
|
||||
return(result);
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le clic sur le champ de texte
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void IndependentTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
||||
first_move_ = true;
|
||||
if (e -> modifiers() & Qt::ControlModifier) {
|
||||
setSelected(!isSelected());
|
||||
}
|
||||
DiagramTextItem::mousePressEvent(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Gere les mouvements de souris lies au champ de texte
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void IndependentTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (textInteractionFlags() & Qt::TextEditable) {
|
||||
DiagramTextItem::mouseMoveEvent(e);
|
||||
} else if ((flags() & QGraphicsItem::ItemIsMovable) && isSelected() && (e -> buttons() & Qt::LeftButton)) {
|
||||
// le champ de texte est en train d'etre deplace
|
||||
Diagram *diagram_ptr = diagram();
|
||||
if (diagram_ptr) {
|
||||
if (first_move_) {
|
||||
// il s'agit du premier mouvement du deplacement, on le signale
|
||||
// au schema parent
|
||||
diagram_ptr -> beginMoveElements(this);
|
||||
}
|
||||
}
|
||||
|
||||
// on applique le mouvement impose par la souris
|
||||
QPointF old_pos = pos();
|
||||
setPos(mapToParent(e -> pos()) - matrix().map(e -> buttonDownPos(Qt::LeftButton)));
|
||||
|
||||
// on calcule le mouvement reellement applique par setPos()
|
||||
QPointF effective_movement = pos() - old_pos;
|
||||
|
||||
if (diagram_ptr) {
|
||||
// on signale le mouvement ainsi applique au schema parent, qui
|
||||
// l'appliquera aux autres items selectionnes selon son bon vouloir
|
||||
diagram_ptr -> continueMoveElements(effective_movement);
|
||||
}
|
||||
} else e -> ignore();
|
||||
|
||||
if (first_move_) {
|
||||
first_move_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Gere le relachement de souris
|
||||
Cette methode a ete reimplementee pour tenir a jour la liste des elements
|
||||
et conducteurs a deplacer au niveau du schema.
|
||||
@param e Objet decrivant l'evenement souris
|
||||
*/
|
||||
void IndependentTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
||||
if (flags() & QGraphicsItem::ItemIsMovable) {
|
||||
if (Diagram *diagram_ptr = diagram()) {
|
||||
diagram_ptr -> endMoveElements();
|
||||
}
|
||||
}
|
||||
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
||||
DiagramTextItem::mouseReleaseEvent(e);
|
||||
}
|
||||
}
|
||||
|
@ -44,5 +44,13 @@ class IndependentTextItem : public DiagramTextItem {
|
||||
virtual int type() const { return Type; }
|
||||
virtual void fromXml(const QDomElement &);
|
||||
virtual QDomElement toXml(QDomDocument &) const;
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
|
||||
|
||||
private:
|
||||
bool first_move_;
|
||||
};
|
||||
#endif
|
||||
|
@ -104,6 +104,53 @@ QET::Orientation QET::previousOrientation(QET::Orientation o) {
|
||||
return((QET::Orientation)(o - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@param line Un segment de droite
|
||||
@param point Un point
|
||||
@return true si le point appartient au segment de droite, false sinon
|
||||
*/
|
||||
bool QET::lineContainsPoint(const QLineF &line, const QPointF &point) {
|
||||
QLineF point_line(line.p1(), point);
|
||||
if (point_line.unitVector() != line.unitVector()) return(false);
|
||||
return(point_line.length() <= line.length());
|
||||
}
|
||||
|
||||
/**
|
||||
@param point Un point donne
|
||||
@param line Un segment de droite donnee
|
||||
@param intersection si ce pointeur est different de 0, le QPointF ainsi
|
||||
designe contiendra les coordonnees du projete orthogonal, meme si celui-ci
|
||||
n'appartient pas au segment de droite
|
||||
@return true si le projete orthogonal du point sur la droite appartient au
|
||||
segment de droite.
|
||||
*/
|
||||
bool QET::orthogonalProjection(const QPointF &point, const QLineF &line, QPointF *intersection) {
|
||||
// recupere le vecteur normal de `line'
|
||||
QLineF line_normal_vector(line.normalVector());
|
||||
QPointF normal_vector(line_normal_vector.dx(), line_normal_vector.dy());
|
||||
|
||||
// cree une droite perpendiculaire a `line' passant par `point'
|
||||
QLineF perpendicular_line(point, point + normal_vector);
|
||||
|
||||
// determine le point d'intersection des deux droites = le projete orthogonal
|
||||
QPointF intersection_point;
|
||||
QLineF::IntersectType it = line.intersect(perpendicular_line, &intersection_point);
|
||||
|
||||
// ne devrait pas arriver (mais bon...)
|
||||
if (it == QLineF::NoIntersection) return(false);
|
||||
|
||||
// fournit le point d'intersection a l'appelant si necessaire
|
||||
if (intersection) {
|
||||
*intersection = intersection_point;
|
||||
}
|
||||
|
||||
// determine si le point d'intersection appartient au segment de droite
|
||||
if (QET::lineContainsPoint(line, intersection_point)) {
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
/**
|
||||
Permet de savoir si l'attribut nom_attribut d'un element XML e est bien un
|
||||
entier. Si oui, sa valeur est copiee dans entier.
|
||||
|
@ -104,6 +104,8 @@ namespace QET {
|
||||
bool surLeMemeAxe(QET::Orientation, QET::Orientation);
|
||||
bool estHorizontale(QET::Orientation);
|
||||
bool estVerticale(QET::Orientation);
|
||||
bool lineContainsPoint(const QLineF &, const QPointF &);
|
||||
bool orthogonalProjection(const QPointF &, const QLineF &, QPointF * = 0);
|
||||
bool attributeIsAnInteger(const QDomElement &, QString , int * = NULL);
|
||||
bool attributeIsAReal(const QDomElement &, QString , qreal * = NULL);
|
||||
QString ElementsAndConductorsSentence(int, int, int = 0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user