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:
xavier 2010-05-08 21:24:43 +00:00
parent 89cb80855a
commit c8732714b8
25 changed files with 1177 additions and 287 deletions

View File

@ -419,6 +419,13 @@ void Conductor::paint(QPainter *qp, const QStyleOptionGraphicsItem *options, QWi
qp -> save(); qp -> save();
qp -> setRenderHint(QPainter::Antialiasing, false); qp -> setRenderHint(QPainter::Antialiasing, false);
/*
qp -> save();
qp -> setPen(Qt::blue);
qp -> drawPath(variableShape(60.0).simplified());
qp -> restore();
*/
// determine la couleur du conducteur // determine la couleur du conducteur
QColor final_conductor_color(properties_.color); QColor final_conductor_color(properties_.color);
if (isSelected()) { if (isSelected()) {
@ -514,6 +521,13 @@ Diagram *Conductor::diagram() const {
return(qobject_cast<Diagram *>(scene())); 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 Methode de validation d'element XML
@param e Un element XML sense represente un Conducteur @param e Un element XML sense represente un Conducteur
@ -569,6 +583,10 @@ void Conductor::mousePressEvent(QGraphicsSceneMouseEvent *e) {
} }
segment = segment -> nextSegment(); 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); QGraphicsPathItem::mousePressEvent(e);
if (e -> modifiers() & Qt::ControlModifier) { 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 { 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(); QList<QPointF> points = segmentsToPoints();
QPainterPath area; QPainterPath area;
QPointF previous_point; QPointF previous_point;
@ -764,15 +812,24 @@ QPainterPath Conductor::shape() const {
qreal p2_x = point2 -> x(); qreal p2_x = point2 -> x();
qreal p2_y = point2 -> y(); qreal p2_y = point2 -> y();
area.setFillRule(Qt::OddEvenFill); 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; previous_point = point;
area.setFillRule(Qt::WindingFill); 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); 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, Renvoie une valeur donnee apres l'avoir bornee entre deux autres valeurs,
en y ajoutant une marge interne. en y ajoutant une marge interne.
@ -886,6 +943,14 @@ bool Conductor::fromXml(QDomElement &e) {
// recupere la "configuration" du conducteur // recupere la "configuration" du conducteur
properties_.fromXml(e); properties_.fromXml(e);
readProperties(); 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()); text_item -> setRotationAngle(e.attribute("rotation").toDouble());
// parcourt les elements XML "segment" et en extrait deux listes de longueurs // 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()) { if (text_item -> rotationAngle()) {
e.setAttribute("rotation", QString("%1").arg(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); return(e);
} }
@ -1043,9 +1112,20 @@ ConductorSegment *Conductor::middleSegment() {
@see middleSegment() @see middleSegment()
*/ */
void Conductor::calculateTextItemPosition() { void Conductor::calculateTextItemPosition() {
if (properties_.type != ConductorProperties::Multi) return;
if (!text_item) return; if (!text_item) return;
text_item -> setPos(middleSegment() -> middle());
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());
}
} }
/** /**
@ -1058,7 +1138,14 @@ void Conductor::saveProfile(bool undo) {
conductor_profiles[current_path_type].fromConductor(this); conductor_profiles[current_path_type].fromConductor(this);
Diagram *dia = diagram(); Diagram *dia = diagram();
if (undo && dia) { 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); 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 Met a jour les proprietes du conducteur apres modification du champ de texte affiche
*/ */
@ -1378,3 +1474,55 @@ void Conductor::deleteSegments() {
segments = NULL; 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));
}
}

View File

@ -22,7 +22,7 @@
#include "conductorprofile.h" #include "conductorprofile.h"
#include "conductorproperties.h" #include "conductorproperties.h"
class ConductorSegment; class ConductorSegment;
class DiagramTextItem; class ConductorTextItem;
class Element; class Element;
typedef QPair<QPointF, Qt::Corner> ConductorBend; typedef QPair<QPointF, Qt::Corner> ConductorBend;
typedef QHash<Qt::Corner, ConductorProfile> ConductorProfilesGroup; typedef QHash<Qt::Corner, ConductorProfile> ConductorProfilesGroup;
@ -61,10 +61,15 @@ class Conductor : public QObject, public QGraphicsPathItem {
/// @return true si ce conducteur est detruit /// @return true si ce conducteur est detruit
bool isDestroyed() const { return(destroyed); } bool isDestroyed() const { return(destroyed); }
Diagram *diagram() const; Diagram *diagram() const;
ConductorTextItem *textItem() const;
void updatePath(const QRectF & = QRectF()); void updatePath(const QRectF & = QRectF());
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
QRectF boundingRect() const; QRectF boundingRect() const;
virtual QPainterPath shape() 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(); qreal length();
ConductorSegment *middleSegment(); ConductorSegment *middleSegment();
bool containsPoint(const QPointF &) const; bool containsPoint(const QPointF &) const;
@ -81,6 +86,7 @@ class Conductor : public QObject, public QGraphicsPathItem {
void setProfiles(const ConductorProfilesGroup &); void setProfiles(const ConductorProfilesGroup &);
ConductorProfilesGroup profiles() const; ConductorProfilesGroup profiles() const;
void readProperties(); void readProperties();
void adjustTextItemPosition();
public slots: public slots:
void displayedTextChanged(); void displayedTextChanged();
@ -100,7 +106,7 @@ class Conductor : public QObject, public QGraphicsPathItem {
/// booleen indiquant si le fil est encore valide /// booleen indiquant si le fil est encore valide
bool destroyed; bool destroyed;
/// champ de texte editable pour les conducteurs non unifilaires /// champ de texte editable pour les conducteurs non unifilaires
DiagramTextItem *text_item; ConductorTextItem *text_item;
/// segments composant le conducteur /// segments composant le conducteur
ConductorSegment *segments; ConductorSegment *segments;
/// attributs lies aux manipulations a la souris /// attributs lies aux manipulations a la souris
@ -110,6 +116,7 @@ class Conductor : public QObject, public QGraphicsPathItem {
int moved_point; int moved_point;
qreal previous_z_value; qreal previous_z_value;
ConductorSegment *moved_segment; ConductorSegment *moved_segment;
QPointF before_mov_text_pos_;
/// booleen indiquant si le conducteur a ete modifie manuellement par l'utilisateur /// booleen indiquant si le conducteur a ete modifie manuellement par l'utilisateur
bool modified_path; bool modified_path;
/// booleen indiquant s'il faut sauver le profil courant au plus tot /// 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, qreal, qreal = 0.0);
static qreal conductor_bound(qreal, qreal, bool); static qreal conductor_bound(qreal, qreal, bool);
static Qt::Corner movementType(const QPointF &, const QPointF &); static Qt::Corner movementType(const QPointF &, const QPointF &);
static QPointF movePointIntoPolygon(const QPointF &, const QPainterPath &);
}; };
#endif #endif

View File

@ -17,6 +17,7 @@
*/ */
#include "conductortextitem.h" #include "conductortextitem.h"
#include "conductor.h" #include "conductor.h"
#include "diagramcommands.h"
/** /**
Constructeur Constructeur
@ -25,11 +26,11 @@
*/ */
ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *parent_diagram) : ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *parent_diagram) :
DiagramTextItem(parent_conductor, 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 // par defaut, les DiagramTextItem sont Selectable et Movable
// on desactive Movable pour les textes des conducteurs // cela nous convient, on ne touche pas a ces flags
setFlag(QGraphicsItem::ItemIsMovable, false);
} }
/** /**
@ -40,11 +41,11 @@ ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *paren
*/ */
ConductorTextItem::ConductorTextItem(const QString &text, Conductor *parent_conductor, Diagram *parent_diagram) : ConductorTextItem::ConductorTextItem(const QString &text, Conductor *parent_conductor, Diagram *parent_diagram) :
DiagramTextItem(text, parent_conductor, 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 // par defaut, les DiagramTextItem sont Selectable et Movable
// on desactive Movable pour les textes des conducteurs // cela nous convient, on ne touche pas a ces flags
setFlag(QGraphicsItem::ItemIsMovable, false);
} }
/** /**
@ -68,8 +69,16 @@ Conductor *ConductorTextItem::parentConductor() const {
@param e L'element XML representant le champ de texte @param e L'element XML representant le champ de texte
*/ */
void ConductorTextItem::fromXml(const QDomElement &e) { void ConductorTextItem::fromXml(const QDomElement &e) {
setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
setPlainText(e.attribute("text")); 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()); setRotationAngle(e.attribute("rotation").toDouble());
} }
@ -79,11 +88,95 @@ void ConductorTextItem::fromXml(const QDomElement &e) {
*/ */
QDomElement ConductorTextItem::toXml(QDomDocument &document) const { QDomElement ConductorTextItem::toXml(QDomDocument &document) const {
QDomElement result = document.createElement("input"); QDomElement result = document.createElement("input");
result.setAttribute("x", QString("%1").arg(pos().x())); result.setAttribute("userx", QString("%1").arg(pos().x()));
result.setAttribute("y", QString("%1").arg(pos().y())); result.setAttribute("usery", QString("%1").arg(pos().y()));
result.setAttribute("text", toPlainText()); result.setAttribute("text", toPlainText());
if (rotationAngle()) { if (rotationAngle()) {
result.setAttribute("rotation", QString("%1").arg(rotationAngle())); result.setAttribute("rotation", QString("%1").arg(rotationAngle()));
} }
return(result); 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);
}
}

View File

@ -47,9 +47,18 @@ class ConductorTextItem : public DiagramTextItem {
// methodes // methodes
public: public:
virtual int type() const { return Type; } 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 // attributs
private: private:
Conductor *parent_conductor_; Conductor *parent_conductor_;
bool moved_by_user_;
QPointF before_mov_pos_;
}; };
#endif #endif

View File

@ -24,6 +24,8 @@
#include "diagramcontent.h" #include "diagramcontent.h"
#include "diagramposition.h" #include "diagramposition.h"
#include "elementtextitem.h" #include "elementtextitem.h"
#include "elementsmover.h"
#include "elementtextsmover.h"
#include "exportdialog.h" #include "exportdialog.h"
#include "ghostelement.h" #include "ghostelement.h"
#include "independenttextitem.h" #include "independenttextitem.h"
@ -41,7 +43,6 @@ Diagram::Diagram(QObject *parent) :
QGraphicsScene(parent), QGraphicsScene(parent),
draw_grid(true), draw_grid(true),
use_border(true), use_border(true),
moved_elements_fetched(false),
draw_terminals(true), draw_terminals(true),
draw_colored_conductors_(true), draw_colored_conductors_(true),
project_(0), project_(0),
@ -58,6 +59,10 @@ Diagram::Diagram(QObject *parent) :
t.setStyle(Qt::DashLine); t.setStyle(Qt::DashLine);
conductor_setter -> setPen(t); conductor_setter -> setPen(t);
conductor_setter -> setLine(QLineF(QPointF(0.0, 0.0), QPointF(0.0, 0.0))); 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 // suppression du QGIManager - tous les elements qu'il connait sont supprimes
delete qgi_manager; delete qgi_manager;
// suppression des objets gerant les deplacements
delete elements_mover_;
delete element_texts_mover_;
// recense les items supprimables // recense les items supprimables
QList<QGraphicsItem *> deletable_items; QList<QGraphicsItem *> deletable_items;
foreach(QGraphicsItem *qgi, items()) { foreach(QGraphicsItem *qgi, items()) {
@ -140,7 +149,8 @@ void Diagram::keyPressEvent(QKeyEvent *e) {
case Qt::Key_Down: movement = QPointF(0.0, +yGrid); break; case Qt::Key_Down: movement = QPointF(0.0, +yGrid); break;
} }
if (!movement.isNull() && !focusItem()) { if (!movement.isNull() && !focusItem()) {
moveElements(movement); beginMoveElements();
continueMoveElements(movement);
} }
} }
QGraphicsScene::keyPressEvent(e); QGraphicsScene::keyPressEvent(e);
@ -154,14 +164,11 @@ void Diagram::keyReleaseEvent(QKeyEvent *e) {
if (!isReadOnly()) { if (!isReadOnly()) {
// detecte le relachement d'une touche de direction ( = deplacement d'elements) // detecte le relachement d'une touche de direction ( = deplacement d'elements)
if ( if (
(e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right ||\ (e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right ||
e -> key() == Qt::Key_Up || e -> key() == Qt::Key_Down) &&\ e -> key() == Qt::Key_Up || e -> key() == Qt::Key_Down) &&
!current_movement.isNull() && !e -> isAutoRepeat() !e -> isAutoRepeat()
) { ) {
// cree un objet d'annulation pour le mouvement qui vient de se finir endMoveElements();
undoStack().push(new MoveElementsCommand(this, selectedContent(), current_movement));
invalidateMovedElements();
current_movement = QPointF();
} }
} }
QGraphicsScene::keyReleaseEvent(e); QGraphicsScene::keyReleaseEvent(e);
@ -561,9 +568,9 @@ bool Diagram::fromXml(QDomElement &document, QPointF position, bool consider_inf
// remplissage des listes facultatives // remplissage des listes facultatives
if (content_ptr) { if (content_ptr) {
content_ptr -> elements = added_elements; content_ptr -> elements = added_elements.toSet();
content_ptr -> conductorsToMove = added_conductors; content_ptr -> conductorsToMove = added_conductors.toSet();
content_ptr -> textFields = added_texts; content_ptr -> textFields = added_texts.toSet();
} }
return(true); 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() { int Diagram::beginMoveElements(QGraphicsItem *driver_item) {
if (!moved_elements_fetched) return; return(elements_mover_ -> beginMovement(this, driver_item));
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();
} }
/** /**
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() { void Diagram::continueMoveElements(const QPointF &movement) {
// recupere les elements deplaces elements_mover_ -> continueMovement(movement);
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;
} }
/** /**
Deplace les elements, conducteurs et textes independants selectionnes en Finalise un deplacement d'elements, conducteurs et champs de texte
gerant au mieux les conducteurs (seuls les conducteurs dont un seul des @see ElementsMover
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.
*/ */
void Diagram::moveElements(const QPointF &diff, QGraphicsItem *dontmove) { void Diagram::endMoveElements() {
// inutile de deplacer les autres elements s'il n'y a pas eu de mouvement concret elements_mover_ -> endMovement();
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);
}
} }
/** /**
Deplace les champs de textes selectionnes ET rattaches a un element Initialise un deplacement d'ElementTextItems
@param diff Translation a effectuer, exprimee dans les coordonnees de la @param driver_item Item deplace par la souris et ne necessitant donc pas
scene d'etre deplace lors des appels a continueMovement.
@param dontmove ElementTextItem (optionnel) a ne pas deplacer @see ElementTextsMover
*/ */
void Diagram::moveElementsTexts(const QPointF &diff, ElementTextItem *dontmove) { int Diagram::beginMoveElementTexts(QGraphicsItem *driver_item) {
// inutile de deplacer les autres textes s'il n'y a pas eu de mouvement concret return(element_texts_mover_ -> beginMovement(this, driver_item));
if (diff.isNull()) return; }
current_movement += diff;
// deplace les champs de texte rattaches a un element /**
foreach(ElementTextItem *eti, elementTextsToMove()) { Prend en compte un mouvement composant un deplacement d'ElementTextItems
if (dontmove && eti == dontmove) continue; @param movement mouvement a ajouter au deplacement en cours
QPointF applied_movement = eti -> mapMovementToParent(eti-> mapMovementFromScene(diff)); @see ElementTextsMover
eti -> setPos(eti -> pos() + applied_movement); */
} 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. @return le contenu selectionne du schema.
*/ */
DiagramContent Diagram::selectedContent() { DiagramContent Diagram::selectedContent() {
invalidateMovedElements();
DiagramContent dc; DiagramContent dc;
dc.elements = elementsToMove().toList();
dc.textFields = independentTextsToMove().toList();
dc.conductorsToMove = conductorsToMove().toList();
dc.conductorsToUpdate = conductorsToUpdate().toList();
// recupere les conducteurs selectionnes isoles (= non deplacables mais supprimables) // recupere les elements deplaces
foreach(QGraphicsItem *qgi, items()) { foreach (QGraphicsItem *item, selectedItems()) {
if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) { 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)
if ( if (
c -> isSelected() &&\
!c -> terminal1 -> parentItem() -> isSelected() &&\ !c -> terminal1 -> parentItem() -> isSelected() &&\
!c -> terminal2 -> 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); return(dc);
} }

View File

@ -30,7 +30,9 @@ class DiagramPosition;
class DiagramTextItem; class DiagramTextItem;
class Element; class Element;
class ElementsLocation; class ElementsLocation;
class ElementsMover;
class ElementTextItem; class ElementTextItem;
class ElementTextsMover;
class IndependentTextItem; class IndependentTextItem;
class QETProject; class QETProject;
class Terminal; class Terminal;
@ -63,8 +65,6 @@ class Diagram : public QGraphicsScene {
ConductorProperties defaultConductorProperties; ConductorProperties defaultConductorProperties;
/// Dimensions et cartouches du schema /// Dimensions et cartouches du schema
BorderInset border_and_inset; BorderInset border_and_inset;
/// Mouvement en cours lors d'un deplacement d'elements et conducteurs
QPointF current_movement;
/// taille de la grille en abscisse /// taille de la grille en abscisse
static const int xGrid; static const int xGrid;
/// taille de la grille en ordonnee /// taille de la grille en ordonnee
@ -74,14 +74,10 @@ class Diagram : public QGraphicsScene {
private: private:
QGraphicsLineItem *conductor_setter; QGraphicsLineItem *conductor_setter;
ElementsMover *elements_mover_;
ElementTextsMover *element_texts_mover_;
bool draw_grid; bool draw_grid;
bool use_border; 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; QGIManager *qgi_manager;
QUndoStack *undo_stack; QUndoStack *undo_stack;
bool draw_terminals; bool draw_terminals;
@ -154,20 +150,17 @@ class Diagram : public QGraphicsScene {
bool isEmpty() const; bool isEmpty() const;
QList<CustomElement *> customElements() 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<DiagramTextItem *> selectedTexts() const;
QSet<Conductor *> selectedConductors() const; QSet<Conductor *> selectedConductors() const;
DiagramContent content() const; DiagramContent content() const;
DiagramContent selectedContent(); DiagramContent selectedContent();
bool canRotateSelection() const; bool canRotateSelection() const;
void moveElements(const QPointF &, QGraphicsItem * = 0); int beginMoveElements(QGraphicsItem * = 0);
void moveElementsTexts(const QPointF &, ElementTextItem * = 0); void continueMoveElements(const QPointF &);
void endMoveElements();
int beginMoveElementTexts(QGraphicsItem * = 0);
void continueMoveElementTexts(const QPointF &);
void endMoveElementTexts();
bool usesElement(const ElementsLocation &); bool usesElement(const ElementsLocation &);
QUndoStack &undoStack(); QUndoStack &undoStack();
@ -272,36 +265,6 @@ inline Diagram::BorderOptions Diagram::borderOptions() {
return(retour); 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 /// @return la pile d'annulations de ce schema
inline QUndoStack &Diagram::undoStack() { inline QUndoStack &Diagram::undoStack() {
return(*undo_stack); return(*undo_stack);

View File

@ -18,6 +18,7 @@
#include "diagramcommands.h" #include "diagramcommands.h"
#include "element.h" #include "element.h"
#include "conductor.h" #include "conductor.h"
#include "conductortextitem.h"
#include "diagram.h" #include "diagram.h"
#include "elementtextitem.h" #include "elementtextitem.h"
#include "independenttextitem.h" #include "independenttextitem.h"
@ -357,12 +358,37 @@ void MoveElementsCommand::move(const QPointF &actual_movement) {
conductor -> updatePath(); 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 // deplace les textes
foreach(DiagramTextItem *text, content_to_move.textFields) { foreach(DiagramTextItem *text, content_to_move.textFields) {
text -> setPos(text -> pos() + actual_movement); 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 Constructeur
@param diagram Schema sur lequel on deplace des champs de texte @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 Constructeur
@param dti Champ de texte modifie @param dti Champ de texte modifie
@ -621,12 +730,28 @@ ChangeConductorCommand::~ChangeConductorCommand() {
/// Annule la modification du conducteur /// Annule la modification du conducteur
void ChangeConductorCommand::undo() { void ChangeConductorCommand::undo() {
conductor -> setProfile(old_profile, path_type); conductor -> setProfile(old_profile, path_type);
conductor -> textItem() -> setPos(text_pos_before_mov_);
} }
/// Refait la modification du conducteur /// Refait la modification du conducteur
void ChangeConductorCommand::redo() { void ChangeConductorCommand::redo() {
if (first_redo) first_redo = false; if (first_redo) {
else conductor -> setProfile(new_profile, path_type); 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;
} }
/** /**

View File

@ -189,6 +189,7 @@ class MoveElementsCommand : public QUndoCommand {
virtual void undo(); virtual void undo();
virtual void redo(); virtual void redo();
virtual void move(const QPointF &); virtual void move(const QPointF &);
virtual void addConductorTextItemMovement(ConductorTextItem *, const QPointF &, const QPointF &);
// attributs // attributs
private: private:
@ -198,6 +199,16 @@ class MoveElementsCommand : public QUndoCommand {
DiagramContent content_to_move; DiagramContent content_to_move;
/// mouvement effectue /// mouvement effectue
QPointF movement; 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() /// booleen pour ne pas executer le premier redo()
bool first_redo; bool first_redo;
}; };
@ -232,6 +243,37 @@ class MoveElementsTextsCommand : public QUndoCommand {
bool first_redo; 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 Cette classe represente la modification d'un champ de texte
*/ */
@ -331,6 +373,7 @@ class ChangeConductorCommand : public QUndoCommand {
public: public:
virtual void undo(); virtual void undo();
virtual void redo(); virtual void redo();
virtual void setConductorTextItemMove(const QPointF &, const QPointF &);
// attributs // attributs
private: private:
@ -342,6 +385,10 @@ class ChangeConductorCommand : public QUndoCommand {
ConductorProfile new_profile; ConductorProfile new_profile;
/// Type de trajet /// Type de trajet
Qt::Corner path_type; 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() /// booleen pour ne pas executer le premier redo()
bool first_redo; bool first_redo;
}; };

View File

@ -50,16 +50,16 @@ DiagramContent::~DiagramContent() {
@return tous les conducteurs @return tous les conducteurs
*/ */
QList<Conductor *> DiagramContent::conductors(int filter) const { QList<Conductor *> DiagramContent::conductors(int filter) const {
QList<Conductor *> result; QSet<Conductor *> result;
if (filter & ConductorsToMove) result += conductorsToMove; if (filter & ConductorsToMove) result += conductorsToMove;
if (filter & ConductorsToUpdate) result += conductorsToUpdate; if (filter & ConductorsToUpdate) result += conductorsToUpdate;
if (filter & OtherConductors) result += otherConductors; if (filter & OtherConductors) result += otherConductors;
if (filter & SelectedOnly) { if (filter & SelectedOnly) {
foreach(Conductor *conductor, result) { foreach(Conductor *conductor, result) {
if (!conductor -> isSelected()) result.removeOne(conductor); if (!conductor -> isSelected()) result.remove(conductor);
} }
} }
return(result); return(result.toList());
} }
/** /**

View File

@ -50,15 +50,15 @@ class DiagramContent {
}; };
/// Elements de texte du schema /// Elements de texte du schema
QList<Element *> elements; QSet<Element *> elements;
/// Champs de texte independants du schema /// Champs de texte independants du schema
QList<IndependentTextItem *> textFields; QSet<IndependentTextItem *> textFields;
/// Conducteurs a mettre a jour du schema /// Conducteurs a mettre a jour du schema
QList<Conductor *> conductorsToUpdate; QSet<Conductor *> conductorsToUpdate;
/// Conducteurs a deplacer du schema /// Conducteurs a deplacer du schema
QList<Conductor *> conductorsToMove; QSet<Conductor *> conductorsToMove;
/// Conducteurs isoles (ni a deplacer, ni a mettre a jour) /// Conducteurs isoles (ni a deplacer, ni a mettre a jour)
QList<Conductor *> otherConductors; QSet<Conductor *> otherConductors;
QList<Conductor *> conductors(int = AnyConductor) const; QList<Conductor *> conductors(int = AnyConductor) const;
QList<QGraphicsItem *> items(int = All) const; QList<QGraphicsItem *> items(int = All) const;

View File

@ -198,6 +198,9 @@ void DiagramTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o
void DiagramTextItem::focusInEvent(QFocusEvent *e) { void DiagramTextItem::focusInEvent(QFocusEvent *e) {
QGraphicsTextItem::focusInEvent(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 // memorise le texte avant que l'utilisateur n'y touche
previous_text_ = toPlainText(); previous_text_ = toPlainText();
// cela permettra de determiner si l'utilisateur a modifie le texte a la fin de l'edition // 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 // hack a la con pour etre re-entrant
setTextInteractionFlags(Qt::NoTextInteraction); setTextInteractionFlags(Qt::NoTextInteraction);
// autorise de nouveau le deplacement du texte
setFlag(QGraphicsItem::ItemIsMovable, true);
QTimer::singleShot(0, this, SIGNAL(lostFocus())); 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 Effetue la rotation du texte en elle-meme
Pour les DiagramTextItem, la rotation s'effectue autour du point (0, 0). Pour les DiagramTextItem, la rotation s'effectue autour du point (0, 0).

View File

@ -62,9 +62,6 @@ class DiagramTextItem : public QGraphicsTextItem {
virtual void focusInEvent(QFocusEvent *); virtual void focusInEvent(QFocusEvent *);
virtual void focusOutEvent(QFocusEvent *); virtual void focusOutEvent(QFocusEvent *);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *); virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
virtual void applyRotation(const qreal &); virtual void applyRotation(const qreal &);
// signaux // signaux

View File

@ -686,7 +686,7 @@ void DiagramView::editSelectionProperties() {
// cas d'un element selectionne // cas d'un element selectionne
if (selection.elements.count() == 1) { if (selection.elements.count() == 1) {
editElement(selection.elements.at(0)); editElement(selection.elements.toList().at(0));
return; return;
} }

View File

@ -30,7 +30,8 @@ Element::Element(QGraphicsItem *parent, Diagram *scene) :
QObject(), QObject(),
QGraphicsItem(parent, scene), QGraphicsItem(parent, scene),
internal_connections(false), internal_connections(false),
must_highlight_(false) must_highlight_(false),
first_move_(true)
{ {
setZValue(10); 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) { void Element::mousePressEvent(QGraphicsSceneMouseEvent *e) {
first_move_ = true;
if (e -> modifiers() & Qt::ControlModifier) { if (e -> modifiers() & Qt::ControlModifier) {
setSelected(!isSelected()); setSelected(!isSelected());
} }
@ -330,12 +333,33 @@ void Element::mousePressEvent(QGraphicsSceneMouseEvent *e) {
*/ */
void Element::mouseMoveEvent(QGraphicsSceneMouseEvent *e) { void Element::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
if (isSelected() && e -> buttons() & Qt::LeftButton) { 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))); 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(); } 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. et conducteurs a deplacer au niveau du schema.
*/ */
void Element::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) { void Element::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
Diagram *diagram_ptr = diagram(); if (Diagram *diagram_ptr = diagram()) {
if (diagram_ptr) { diagram_ptr -> endMoveElements();
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 (!(e -> modifiers() & Qt::ControlModifier)) { if (!(e -> modifiers() & Qt::ControlModifier)) {
QGraphicsItem::mouseReleaseEvent(e); QGraphicsItem::mouseReleaseEvent(e);
} }

View File

@ -129,6 +129,7 @@ class Element : public QObject, public QGraphicsItem {
private: private:
bool internal_connections; bool internal_connections;
bool must_highlight_; bool must_highlight_;
bool first_move_;
void drawSelection(QPainter *, const QStyleOptionGraphicsItem *); void drawSelection(QPainter *, const QStyleOptionGraphicsItem *);
void drawHighlight(QPainter *, const QStyleOptionGraphicsItem *); void drawHighlight(QPainter *, const QStyleOptionGraphicsItem *);
void updatePixmap(); void updatePixmap();

178
sources/elementsmover.cpp Normal file
View 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
View 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

View File

@ -218,6 +218,18 @@ void ElementTextItem::applyRotation(const qreal &angle) {
QGraphicsTextItem::setTransform(rotation, true); 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 Gere les mouvements de souris lies au champ de texte
@param e Objet decrivant l'evenement souris @param e Objet decrivant l'evenement souris
@ -246,13 +258,17 @@ void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
QPointF parent_movement = mapMovementToParent(movement); QPointF parent_movement = mapMovementToParent(movement);
setPos(pos() + parent_movement); setPos(pos() + parent_movement);
if (Diagram *diagram_ptr = diagram()) { Diagram *diagram_ptr = diagram();
int moved_texts_count = diagram_ptr -> elementTextsToMove().count(); if (diagram_ptr) {
// s'il n'y a qu'un seul texte deplace, on met en valeur l'element parent if (first_move_) {
if (moved_texts_count == 1 && parent_element_ && first_move_) { // on signale le debut d'un deplacement d'ElementTextItems au schema parent
parent_element_ -> setHighlighted(true); int moved_texts_count = diagram_ptr -> beginMoveElementTexts(this);
parent_element_ -> update();
first_move_ = false; // s'il n'y a qu'un seul texte deplace, on met en valeur l'element parent
if (moved_texts_count == 1 && parent_element_) {
parent_element_ -> setHighlighted(true);
parent_element_ -> update();
}
} }
/* /*
@ -264,9 +280,13 @@ void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
QPointF scene_effective_movement = mapMovementToScene(mapMovementFromParent(effective_movement)); QPointF scene_effective_movement = mapMovementToScene(mapMovementFromParent(effective_movement));
// on applique le mouvement subi aux autres textes a deplacer // 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(); } else e -> ignore();
if (first_move_) {
first_move_ = false;
}
} }
/** /**
@ -276,28 +296,14 @@ void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
*/ */
void ElementTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) { void ElementTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
if (Diagram *diagram_ptr = diagram()) { if (Diagram *diagram_ptr = diagram()) {
int moved_texts_count = diagram_ptr -> elementTextsToMove().count(); // on arrete de mettre en valeur l'element parent
if (parent_element_) {
// s'il n'y a qu'un seul texte deplace, on arrete de mettre en valeur l'element parent if (parent_element_ -> isHighlighted()) {
if (moved_texts_count == 1) {
first_move_ = true;
if (parent_element_) {
parent_element_ -> setHighlighted(false); parent_element_ -> setHighlighted(false);
} }
} }
// on cree un objet d'annulation correspondant au deplacement qui s'acheve diagram_ptr -> endMoveElementTexts();
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();
} }
if (!(e -> modifiers() & Qt::ControlModifier)) { if (!(e -> modifiers() & Qt::ControlModifier)) {
QGraphicsTextItem::mouseReleaseEvent(e); QGraphicsTextItem::mouseReleaseEvent(e);

View File

@ -72,6 +72,7 @@ class ElementTextItem : public DiagramTextItem {
protected: protected:
virtual void applyRotation(const qreal &); virtual void applyRotation(const qreal &);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
virtual QVariant itemChange(GraphicsItemChange, const QVariant &); virtual QVariant itemChange(GraphicsItemChange, const QVariant &);

View 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;
}

View 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

View File

@ -23,7 +23,8 @@
@param parent_diagram Le schema auquel est rattache le champ de texte @param parent_diagram Le schema auquel est rattache le champ de texte
*/ */
IndependentTextItem::IndependentTextItem(Diagram *parent_diagram) : 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 @param parent_diagram Le schema auquel est rattache le champ de texte
*/ */
IndependentTextItem::IndependentTextItem(const QString &text, Diagram *parent_diagram) : 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); 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);
}
}

View File

@ -44,5 +44,13 @@ class IndependentTextItem : public DiagramTextItem {
virtual int type() const { return Type; } virtual int type() const { return Type; }
virtual void fromXml(const QDomElement &); virtual void fromXml(const QDomElement &);
virtual QDomElement toXml(QDomDocument &) const; virtual QDomElement toXml(QDomDocument &) const;
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
private:
bool first_move_;
}; };
#endif #endif

View File

@ -104,6 +104,53 @@ QET::Orientation QET::previousOrientation(QET::Orientation o) {
return((QET::Orientation)(o - 1)); 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 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. entier. Si oui, sa valeur est copiee dans entier.

View File

@ -104,6 +104,8 @@ namespace QET {
bool surLeMemeAxe(QET::Orientation, QET::Orientation); bool surLeMemeAxe(QET::Orientation, QET::Orientation);
bool estHorizontale(QET::Orientation); bool estHorizontale(QET::Orientation);
bool estVerticale(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 attributeIsAnInteger(const QDomElement &, QString , int * = NULL);
bool attributeIsAReal(const QDomElement &, QString , qreal * = NULL); bool attributeIsAReal(const QDomElement &, QString , qreal * = NULL);
QString ElementsAndConductorsSentence(int, int, int = 0); QString ElementsAndConductorsSentence(int, int, int = 0);