[qet] [987] Il est desormais possible de deplacer les champs de texte des conducteurs.

[ Thread Index | Date Index | More lists.tuxfamily.org/qet Archives ]


Revision: 987
Author:   xavier
Date:     2010-05-08 23:24:43 +0200 (Sat, 08 May 2010)
Log Message:
-----------
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.

Modified Paths:
--------------
    branches/0.3/sources/conductor.cpp
    branches/0.3/sources/conductor.h
    branches/0.3/sources/conductortextitem.cpp
    branches/0.3/sources/conductortextitem.h
    branches/0.3/sources/diagram.cpp
    branches/0.3/sources/diagram.h
    branches/0.3/sources/diagramcommands.cpp
    branches/0.3/sources/diagramcommands.h
    branches/0.3/sources/diagramcontent.cpp
    branches/0.3/sources/diagramcontent.h
    branches/0.3/sources/diagramtextitem.cpp
    branches/0.3/sources/diagramtextitem.h
    branches/0.3/sources/diagramview.cpp
    branches/0.3/sources/element.cpp
    branches/0.3/sources/element.h
    branches/0.3/sources/elementtextitem.cpp
    branches/0.3/sources/elementtextitem.h
    branches/0.3/sources/independenttextitem.cpp
    branches/0.3/sources/independenttextitem.h
    branches/0.3/sources/qet.cpp
    branches/0.3/sources/qet.h

Added Paths:
-----------
    branches/0.3/sources/elementsmover.cpp
    branches/0.3/sources/elementsmover.h
    branches/0.3/sources/elementtextsmover.cpp
    branches/0.3/sources/elementtextsmover.h

Modified: branches/0.3/sources/conductor.cpp
===================================================================
--- branches/0.3/sources/conductor.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/conductor.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -419,6 +419,13 @@
 	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()) {
@@ -515,6 +522,13 @@
 }
 
 /**
+	@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
 	@return true si l'element XML represente bien un Conducteur ; false sinon
@@ -569,6 +583,10 @@
 			}
 			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 @@
 }
 
 /**
-	@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,16 +812,25 @@
 			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.
 	@param tobound valeur a borner
@@ -886,6 +943,14 @@
 	// 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 @@
 	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,9 +1112,20 @@
 	@see middleSegment()
 */
 void Conductor::calculateTextItemPosition() {
-	if (properties_.type != ConductorProperties::Multi) 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 @@
 	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);
 	}
 }
 
@@ -1139,6 +1226,15 @@
 }
 
 /**
+	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
 */
 void Conductor::displayedTextChanged() {
@@ -1378,3 +1474,55 @@
 		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));
+	}
+}

Modified: branches/0.3/sources/conductor.h
===================================================================
--- branches/0.3/sources/conductor.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/conductor.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -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 @@
 	/// @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,10 +86,11 @@
 	void setProfiles(const ConductorProfilesGroup &);
 	ConductorProfilesGroup profiles() const;
 	void readProperties();
+	void adjustTextItemPosition();
 	
 	public slots:
 	void displayedTextChanged();
-
+	
 	protected:
 	virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
 	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
@@ -100,7 +106,7 @@
 	/// 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 @@
 	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 @@
 	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

Modified: branches/0.3/sources/conductortextitem.cpp
===================================================================
--- branches/0.3/sources/conductortextitem.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/conductortextitem.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -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(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 @@
 	@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 @@
 */
 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);
+	}
+}

Modified: branches/0.3/sources/conductortextitem.h
===================================================================
--- branches/0.3/sources/conductortextitem.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/conductortextitem.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -47,9 +47,18 @@
 	// 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

Modified: branches/0.3/sources/diagram.cpp
===================================================================
--- branches/0.3/sources/diagram.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagram.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -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 @@
 	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 @@
 	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 @@
 	// 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 @@
 			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 @@
 	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 @@
 	
 	// 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,113 +811,62 @@
 }
 
 /**
-	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();
+}
+
+/**
 	Permet de savoir si un element est utilise sur un schema
 	@param location Emplacement d'un element
 	@return true si l'element location est utilise sur ce schema, false sinon
@@ -1084,18 +1040,17 @@
 	@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 conducteurs selectionnes isoles (= non deplacables mais supprimables)
-	foreach(QGraphicsItem *qgi, items()) {
-		if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
+	// 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)
 			if (
-				c -> isSelected() &&\
 				!c -> terminal1 -> parentItem() -> isSelected() &&\
 				!c -> terminal2 -> parentItem() -> isSelected()
 			) {
@@ -1103,7 +1058,27 @@
 			}
 		}
 	}
-	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);
 }
 

Modified: branches/0.3/sources/diagram.h
===================================================================
--- branches/0.3/sources/diagram.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagram.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -30,7 +30,9 @@
 class DiagramTextItem;
 class Element;
 class ElementsLocation;
+class ElementsMover;
 class ElementTextItem;
+class ElementTextsMover;
 class IndependentTextItem;
 class QETProject;
 class Terminal;
@@ -63,8 +65,6 @@
 	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 @@
 	
 	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 @@
 	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 @@
 	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);

Modified: branches/0.3/sources/diagramcommands.cpp
===================================================================
--- branches/0.3/sources/diagramcommands.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramcommands.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -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,6 +358,15 @@
 		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);
@@ -364,6 +374,22 @@
 }
 
 /**
+	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
 	@param texts Liste des textes deplaces
@@ -423,6 +449,89 @@
 
 /**
 	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
 	@param before texte avant
 	@param after texte apres
@@ -621,15 +730,31 @@
 /// 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;
+}
+
+/**
 	Constructeur
 	@param cp Conducteurs reinitialises, associes a leur ancien profil
 	@param parent QUndoCommand parent

Modified: branches/0.3/sources/diagramcommands.h
===================================================================
--- branches/0.3/sources/diagramcommands.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramcommands.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -189,6 +189,7 @@
 	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 @@
 	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;
 };
@@ -233,6 +244,37 @@
 };
 
 /**
+	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
 */
 class ChangeDiagramTextCommand : public QUndoCommand {
@@ -331,6 +373,7 @@
 	public:
 	virtual void undo();
 	virtual void redo();
+	virtual void setConductorTextItemMove(const QPointF &, const QPointF &);
 	
 	// attributs
 	private:
@@ -342,6 +385,10 @@
 	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;
 };

Modified: branches/0.3/sources/diagramcontent.cpp
===================================================================
--- branches/0.3/sources/diagramcontent.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramcontent.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -50,16 +50,16 @@
 	@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());
 }
 
 /**

Modified: branches/0.3/sources/diagramcontent.h
===================================================================
--- branches/0.3/sources/diagramcontent.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramcontent.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -50,15 +50,15 @@
 	};
 	
 	/// 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;

Modified: branches/0.3/sources/diagramtextitem.cpp
===================================================================
--- branches/0.3/sources/diagramtextitem.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramtextitem.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -198,6 +198,9 @@
 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 @@
 	
 	// 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()));
 }
 
@@ -243,55 +249,6 @@
 }
 
 /**
-	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).
 	Cette methode peut toutefois etre redefinie dans des classes 

Modified: branches/0.3/sources/diagramtextitem.h
===================================================================
--- branches/0.3/sources/diagramtextitem.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramtextitem.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -62,9 +62,6 @@
 	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

Modified: branches/0.3/sources/diagramview.cpp
===================================================================
--- branches/0.3/sources/diagramview.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/diagramview.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -686,7 +686,7 @@
 		
 		// cas d'un element selectionne
 		if (selection.elements.count() == 1) {
-			editElement(selection.elements.at(0));
+			editElement(selection.elements.toList().at(0));
 			return;
 		}
 		

Modified: branches/0.3/sources/element.cpp
===================================================================
--- branches/0.3/sources/element.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/element.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -30,7 +30,8 @@
 	QObject(),
 	QGraphicsItem(parent, scene),
 	internal_connections(false),
-	must_highlight_(false)
+	must_highlight_(false),
+	first_move_(true)
 {
 	setZValue(10);
 }
@@ -315,9 +316,11 @@
 }
 
 /**
-	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::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 @@
 	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);
 	}

Modified: branches/0.3/sources/element.h
===================================================================
--- branches/0.3/sources/element.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/element.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -129,6 +129,7 @@
 	private:
 	bool internal_connections;
 	bool must_highlight_;
+	bool first_move_;
 	void drawSelection(QPainter *, const QStyleOptionGraphicsItem *);
 	void drawHighlight(QPainter *, const QStyleOptionGraphicsItem *);
 	void updatePixmap();

Added: branches/0.3/sources/elementsmover.cpp
===================================================================
--- branches/0.3/sources/elementsmover.cpp	                        (rev 0)
+++ branches/0.3/sources/elementsmover.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -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;
+}

Added: branches/0.3/sources/elementsmover.h
===================================================================
--- branches/0.3/sources/elementsmover.h	                        (rev 0)
+++ branches/0.3/sources/elementsmover.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -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

Modified: branches/0.3/sources/elementtextitem.cpp
===================================================================
--- branches/0.3/sources/elementtextitem.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/elementtextitem.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -219,6 +219,18 @@
 }
 
 /**
+	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 @@
 		QPointF parent_movement = mapMovementToParent(movement);
 		setPos(pos() + parent_movement);
 		
-		if (Diagram *diagram_ptr = diagram()) {
-			int moved_texts_count = diagram_ptr -> elementTextsToMove().count();
-			// 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_) {
-				parent_element_ -> setHighlighted(true);
-				parent_element_ -> update();
-				first_move_ = false;
+		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_) {
+					parent_element_ -> setHighlighted(true);
+					parent_element_ -> update();
+				}
 			}
 			
 			/*
@@ -264,9 +280,13 @@
 			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::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;
-			if (parent_element_) {
+		// 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);

Modified: branches/0.3/sources/elementtextitem.h
===================================================================
--- branches/0.3/sources/elementtextitem.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/elementtextitem.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -72,6 +72,7 @@
 	
 	protected:
 	virtual void applyRotation(const qreal &);
+	virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
 	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
 	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
 	virtual QVariant itemChange(GraphicsItemChange, const QVariant &);

Added: branches/0.3/sources/elementtextsmover.cpp
===================================================================
--- branches/0.3/sources/elementtextsmover.cpp	                        (rev 0)
+++ branches/0.3/sources/elementtextsmover.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -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;
+}

Added: branches/0.3/sources/elementtextsmover.h
===================================================================
--- branches/0.3/sources/elementtextsmover.h	                        (rev 0)
+++ branches/0.3/sources/elementtextsmover.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -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

Modified: branches/0.3/sources/independenttextitem.cpp
===================================================================
--- branches/0.3/sources/independenttextitem.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/independenttextitem.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -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 @@
 	@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 @@
 	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);
+	}
+}

Modified: branches/0.3/sources/independenttextitem.h
===================================================================
--- branches/0.3/sources/independenttextitem.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/independenttextitem.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -44,5 +44,13 @@
 	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

Modified: branches/0.3/sources/qet.cpp
===================================================================
--- branches/0.3/sources/qet.cpp	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/qet.cpp	2010-05-08 21:24:43 UTC (rev 987)
@@ -105,6 +105,53 @@
 }
 
 /**
+	@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.
 	@param e Element XML

Modified: branches/0.3/sources/qet.h
===================================================================
--- branches/0.3/sources/qet.h	2010-05-08 17:03:05 UTC (rev 986)
+++ branches/0.3/sources/qet.h	2010-05-08 21:24:43 UTC (rev 987)
@@ -104,6 +104,8 @@
 	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);


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/