Merge sources dir branch devel to trunk

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@2613 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
scorpio810
2013-11-14 10:11:22 +00:00
parent 434849c3eb
commit 971ce1facb
54 changed files with 594 additions and 1152 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONDUCTOR_H
#define CONDUCTOR_H
#include <QtGui>
#include "terminal.h"
#include "conductorprofile.h"
#include "conductorproperties.h"
#include "qetdiagrameditor.h"
class ConductorSegment;
class ConductorTextItem;
class Element;
typedef QPair<QPointF, Qt::Corner> ConductorBend;
typedef QHash<Qt::Corner, ConductorProfile> ConductorProfilesGroup;
/**
This class represents a conductor, i.e. a wire between two element
terminals.
*/
class Conductor : public QObject, public QGraphicsPathItem {
Q_OBJECT
// constructors, destructor
public:
Conductor(Terminal *, Terminal *, Diagram * = 0);
virtual ~Conductor();
private:
Conductor(const Conductor &);
// attributes
public:
enum { Type = UserType + 1001 };
enum Highlight { None, Normal, Alert };
/// First terminal the wire is attached to
Terminal *terminal1;
/// Second terminal the wire is attached to
Terminal *terminal2;
// methods
public:
/**
Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
Conductor.
@return the QGraphicsItem type
*/
virtual int type() const { return Type; }
void destroy();
/// @return true if this conductor is destroyed
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;
QString text() const;
void setText(const QString &);
static bool valideXml(QDomElement &);
bool fromXml(QDomElement &);
QDomElement toXml(QDomDocument &, QHash<Terminal *, int> &) const;
const QList<ConductorSegment *> segmentsList() const;
void setProperties(const ConductorProperties &);
ConductorProperties properties() const;
void setProfile(const ConductorProfile &, Qt::Corner);
ConductorProfile profile(Qt::Corner) const;
void setProfiles(const ConductorProfilesGroup &);
ConductorProfilesGroup profiles() const;
void readProperties();
void adjustTextItemPosition();
virtual Highlight highlight() const;
virtual void setHighlighted(Highlight);
void autoText();
QSet<Conductor *> relatedPotentialConductors(QList <Terminal *> *t_list=0);
QETDiagramEditor* diagramEditor() const;
public slots:
void displayedTextChanged();
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *);
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *);
virtual QVariant itemChange(GraphicsItemChange, const QVariant &);
private:
/// Functional properties
ConductorProperties properties_;
/// Whether this conductor is still valid
bool destroyed_;
/// Text input for non simple, non-singleline conductors
ConductorTextItem *text_item;
/// Segments composing the conductor
ConductorSegment *segments;
/// Attributs related to mouse interaction
QPointF press_point;
bool moving_point;
bool moving_segment;
int moved_point;
qreal previous_z_value;
ConductorSegment *moved_segment;
QPointF before_mov_text_pos_;
/// Whether the conductor was manually modified by users
bool modified_path;
/// Whether the current profile should be saved as soon as possible
bool has_to_save_profile;
/// conductor profile: "photography" of what the conductor is supposed to look
/// like - there is one profile per kind of traject
ConductorProfilesGroup conductor_profiles;
/// QPen et QBrush objects used to draw conductors
static QPen conductor_pen;
static QBrush conductor_brush;
static QBrush square_brush;
static bool pen_and_brush_initialized;
/// Scale factor to render square used to move segments
qreal segments_squares_scale_;
/// Define whether and how the conductor should be highlighted
Highlight must_highlight_;
private:
void segmentsToPath();
void saveProfile(bool = true);
void generateConductorPath(const QPointF &, QET::Orientation, const QPointF &, QET::Orientation);
void updateConductorPath(const QPointF &, QET::Orientation, const QPointF &, QET::Orientation);
uint segmentsCount(QET::ConductorSegmentType = QET::Both) const;
QList<QPointF> segmentsToPoints() const;
QSet<Conductor *> relatedConductors() const;
QList<ConductorBend> bends() const;
QList<QPointF> junctions() const;
void pointsToSegments(QList<QPointF>);
bool hasClickedOn(QPointF, QPointF) const;
void calculateTextItemPosition();
Qt::Corner currentPathType() const;
void deleteSegments();
static int getCoeff(const qreal &, const qreal &);
static int getSign(const qreal &);
QHash<ConductorSegmentProfile *, qreal> shareOffsetBetweenSegments(const qreal &offset, const QList<ConductorSegmentProfile *> &, const qreal & = 0.01) const;
static QPointF extendTerminal(const QPointF &, QET::Orientation, qreal = 9.0);
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

View File

@@ -0,0 +1,223 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "conductortextitem.h"
#include "conductor.h"
#include "diagramcommands.h"
/**
Constructeur
@param parent_conductor Conducteur auquel ce texte est rattache
@param parent_diagram Schema auquel ce texte et son conducteur parent sont rattaches
*/
ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *parent_diagram) :
DiagramTextItem(parent_conductor, parent_diagram),
parent_conductor_(parent_conductor),
moved_by_user_(false),
rotate_by_user_(false),
first_move_(true)
{
// par defaut, les DiagramTextItem sont Selectable et Movable
// cela nous convient, on ne touche pas a ces flags
}
/**
Constructeur
@param text Le texte affiche par le champ de texte
@param parent_conductor Conducteur auquel ce texte est rattache
@param parent_diagram Schema auquel ce texte et son conducteur parent sont rattaches
*/
ConductorTextItem::ConductorTextItem(const QString &text, Conductor *parent_conductor, Diagram *parent_diagram) :
DiagramTextItem(text, parent_conductor, parent_diagram),
parent_conductor_(parent_conductor),
moved_by_user_(false),
rotate_by_user_(false),
first_move_(true)
{
// par defaut, les DiagramTextItem sont Selectable et Movable
// cela nous convient, on ne touche pas a ces flags
}
/**
Destructeur
*/
ConductorTextItem::~ConductorTextItem() {
}
/**
@return le conducteur parent de ce champ de texte, ou 0 si celui-ci n'en a
pas
*/
Conductor *ConductorTextItem::parentConductor() const {
return(parent_conductor_);
}
/**
Permet de lire le texte a mettre dans le champ a partir d'un element XML.
Cette methode se base sur la position du champ pour assigner ou non la
valeur a ce champ.
@param e L'element XML representant le champ de texte
*/
void ConductorTextItem::fromXml(const QDomElement &e) {
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());
}
/**
@param document Le document XML a utiliser
@return L'element XML representant ce champ de texte
*/
QDomElement ConductorTextItem::toXml(QDomDocument &document) const {
QDomElement result = document.createElement("input");
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_);
}
/**
* @brief ConductorTextItem::wasRotateByUser
* @return true if text was explicit moved by user else false
*/
bool ConductorTextItem::wasRotateByUser() const {
return(rotate_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();
}
}
/**
* @brief ConductorTextItem::forceRotateByUser
* @param rotate_by_user true pour que la rotation du texte soit consideree
comme ayant ete definie par l'utilisateur (et donc soit sauvegardee), false
pour remettre le texte a sont angle originelle
*/
void ConductorTextItem::forceRotateByUser(bool rotate_by_user) {
if (rotate_by_user == rotate_by_user_) return;
rotate_by_user_ = rotate_by_user;
if (!rotate_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();
}
first_move_ = true;
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)) {
if (first_move_) {
mouse_to_origin_movement_ = before_mov_pos_ - mapToParent(e -> buttonDownPos(Qt::LeftButton));
}
QPointF intended_pos = mapToParent(e -> pos()) + mouse_to_origin_movement_;
// 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);
parent_conductor_ -> setHighlighted(Conductor::Normal);
} else {
parent_conductor_ -> setHighlighted(Conductor::Alert);
}
}
} else e -> ignore();
if (first_move_) {
first_move_ = false;
}
}
/**
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 (parent_conductor_) {
parent_conductor_ -> setHighlighted(Conductor::None);
}
}
}
if (!(e -> modifiers() & Qt::ControlModifier)) {
QGraphicsTextItem::mouseReleaseEvent(e);
}
}

View File

@@ -0,0 +1,69 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONDUCTOR_TEXT_ITEM_H
#define CONDUCTOR_TEXT_ITEM_H
#include "diagramtextitem.h"
class Conductor;
/**
This class represents a text item attached to a parent conductor.
It may be moved and edited by users.
It may also be rotated to any angle.
Its movements are however limited to a particular distance around its
parent conductor.
*/
class ConductorTextItem : public DiagramTextItem {
Q_OBJECT
// constructors, destructor
public:
ConductorTextItem(Conductor * = 0, Diagram * = 0);
ConductorTextItem(const QString &, Conductor * = 0, Diagram * = 0);
virtual ~ConductorTextItem();
private:
ConductorTextItem(const ConductorTextItem &);
// attributes
public:
enum { Type = UserType + 1006 };
Conductor *parentConductor() const;
virtual void fromXml(const QDomElement &);
virtual QDomElement toXml(QDomDocument &) const;
// methods
public:
virtual int type() const { return Type; }
virtual bool wasMovedByUser() const;
virtual bool wasRotateByUser() const;
virtual void forceMovedByUser(bool);
virtual void forceRotateByUser(bool);
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
// attributes
private:
Conductor *parent_conductor_;
bool moved_by_user_;
bool rotate_by_user_;
QPointF before_mov_pos_;
bool first_move_;
QPointF mouse_to_origin_movement_;
};
#endif

View File

@@ -0,0 +1,808 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "customelement.h"
#include "elementtextitem.h"
#include "diagram.h"
#include "qetapp.h"
#include "partline.h"
#include "elementdefinition.h"
#include <iostream>
/**
Constructeur de la classe CustomElement. Permet d'instancier un element
utilisable comme un element fixe a la difference que l'element perso est
construit a partir d'une description au format XML. Celle-ci est recuperee
a l'emplacement indique.
@param location Emplacement de la definition d'element a utiliser
@param qgi Le QGraphicsItem parent de cet element
@param s Le Schema affichant cet element
@param state Un pointeur facultatif vers un entier. La valeur de cet entier
sera changee de maniere a refleter le deroulement de l'instanciation :
- 0 : L'instanciation a reussi
- 1 : l'emplacement n'a pas permis d'acceder a une definition d'element
- 2 : la definition n'etait pas lisible
- 3 : la definition n'etait pas valide / exploitable / utilisable
- 4 : Le document XML n'est pas un element "definition"
- 5 : Les attributs de la definition ne sont pas presents et / ou valides
- 6 : La definition est vide
- 7 : L'analyse d'un element XML decrivant une partie du dessin de l'element a echoue
- 8 : Aucune partie du dessin n'a pu etre chargee
*/
CustomElement::CustomElement(const ElementsLocation &location, QGraphicsItem *qgi, Diagram *s, int *state) :
FixedElement(qgi, s),
elmt_state(-1),
location_(location),
forbid_antialiasing(false)
{
// recupere la definition de l'element
ElementsCollectionItem *element_item = QETApp::collectionItem(location);
ElementDefinition *element_definition;
if (
!element_item ||\
!element_item -> isElement() ||\
!(element_definition = qobject_cast<ElementDefinition *>(element_item))
) {
if (state) *state = 1;
elmt_state = 1;
return;
}
if (!element_definition -> isReadable()) {
if (state) *state = 2;
elmt_state = 2;
return;
}
if (element_definition -> isNull()) {
if (state) *state = 3;
elmt_state = 3;
return;
}
buildFromXml(element_definition -> xml(), &elmt_state);
if (state) *state = elmt_state;
if (elmt_state) return;
if (state) *state = 0;
elmt_state = 0;
}
CustomElement::CustomElement(const QDomElement &xml_def_elmt, QGraphicsItem *qgi, Diagram *s, int *state) : FixedElement(qgi, s) {
int elmt_state = -1;
buildFromXml(xml_def_elmt, &elmt_state);
if (state) *state = elmt_state;
}
/**
Construit l'element personnalise a partir d'un element XML representant sa
definition.
@param xml_def_elmt
@param state Un pointeur facultatif vers un entier. La valeur de cet entier
sera changee de maniere a refleter le deroulement de l'instanciation :
- 0 : La construction s'est bien passee
- 4 : Le document XML n'est pas un element "definition"
- 5 : Les attributs de la definition ne sont pas presents et / ou valides
- 6 : La definition est vide
- 7 : L'analyse d'un element XML decrivant une partie du dessin de l'element a echoue
- 8 : Aucune partie du dessin n'a pu etre chargee
@return true si le chargement a reussi, false sinon
*/
bool CustomElement::buildFromXml(const QDomElement &xml_def_elmt, int *state) {
if (xml_def_elmt.tagName() != "definition" || xml_def_elmt.attribute("type") != "element") {
if (state) *state = 4;
return(false);
}
// verifie basiquement que la version actuelle est capable de lire ce fichier
if (xml_def_elmt.hasAttribute("version")) {
bool conv_ok;
qreal element_version = xml_def_elmt.attribute("version").toDouble(&conv_ok);
if (conv_ok && QET::version.toDouble() < element_version) {
std::cerr << qPrintable(
QObject::tr("Avertissement : l'\351l\351ment "
" a \351t\351 enregistr\351 avec une version"
" ult\351rieure de QElectroTech.")
) << std::endl;
}
}
// ces attributs doivent etre presents et valides
int w, h, hot_x, hot_y;
if (
!QET::attributeIsAnInteger(xml_def_elmt, QString("width"), &w) ||\
!QET::attributeIsAnInteger(xml_def_elmt, QString("height"), &h) ||\
!QET::attributeIsAnInteger(xml_def_elmt, QString("hotspot_x"), &hot_x) ||\
!QET::attributeIsAnInteger(xml_def_elmt, QString("hotspot_y"), &hot_y) ||\
!validOrientationAttribute(xml_def_elmt)
) {
if (state) *state = 5;
return(false);
}
// on peut d'ores et deja specifier la taille et le hotspot
setSize(w, h);
setHotspot(QPoint(hot_x, hot_y));
setInternalConnections(xml_def_elmt.attribute("ic") == "true");
// la definition est supposee avoir des enfants
if (xml_def_elmt.firstChild().isNull()) {
if (state) *state = 6;
return(false);
}
// initialisation du QPainter (pour dessiner l'element)
QPainter qp;
qp.begin(&drawing);
QPainter low_zoom_qp;
low_zoom_qp.begin(&low_zoom_drawing);
QPen tmp;
tmp.setWidthF(1.0); // ligne vaudou pour prise en compte du setCosmetic - ne pas enlever
tmp.setCosmetic(true);
low_zoom_qp.setPen(tmp);
// extrait les noms de la definition XML
names.fromXml(xml_def_elmt);
setToolTip(name());
// parcours des enfants de la definition : parties du dessin
int parsed_elements_count = 0;
for (QDomNode node = xml_def_elmt.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
QDomElement elmts = node.toElement();
if (elmts.isNull()) continue;
if (elmts.tagName() == "description") {
// gestion de la description graphique de l'element
// = parcours des differentes parties du dessin
for (QDomNode n = node.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
QDomElement qde = n.toElement();
if (qde.isNull()) continue;
if (parseElement(qde, qp)) {
++ parsed_elements_count;
QString current_tag = qde.tagName();
if (current_tag != "terminal" && current_tag != "input") {
forbid_antialiasing = true;
parseElement(qde, low_zoom_qp);
forbid_antialiasing = false;
}
} else {
if (state) *state = 7;
return(false);
}
}
}
}
// fin du dessin
qp.end();
low_zoom_qp.end();
// il doit y avoir au moins un element charge
if (!parsed_elements_count) {
if (state) *state = 8;
return(false);
} else {
if (state) *state = 0;
return(true);
}
}
/**
Destructeur
*/
CustomElement::~CustomElement() {
}
/// @return la liste des bornes de cet element
QList<Terminal *> CustomElement::terminals() const {
return(list_terminals);
}
/// @return la liste des conducteurs rattaches a cet element
QList<Conductor *> CustomElement::conductors() const {
QList<Conductor *> conductors;
foreach(Terminal *t, list_terminals) conductors << t -> conductors();
return(conductors);
}
/// @return la liste des textes de cet element
QList<ElementTextItem *> CustomElement::texts() const {
return(list_texts_);
}
/**
@return Le nombre de bornes que l'element possede
*/
int CustomElement::terminalsCount() const {
return(list_terminals.size());
}
/**
Dessine le composant sur le Diagram
@param qp Le QPainter a utiliser pour dessiner l'element
@param options Les options graphiques
*/
void CustomElement::paint(QPainter *qp, const QStyleOptionGraphicsItem *options) {
if (options && options -> levelOfDetail < 1.0) {
low_zoom_drawing.play(qp);
} else {
drawing.play(qp);
}
}
/**
Analyse et prend en compte un element XML decrivant une partie du dessin
de l'element perso. Si l'analyse reussit, la partie est ajoutee au dessin.
Cette partie peut etre une borne, une ligne, une ellipse, un cercle, un arc
de cercle ou un polygone. Cette methode renvoie false si l'analyse
d'une de ces formes echoue. Si l'analyse reussit ou dans le cas d'une forme
inconnue, cette methode renvoie true. A l'exception des bornes, toutes les
formes peuvent avoir un attribut style. @see setPainterStyle
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseElement(QDomElement &e, QPainter &qp) {
if (e.tagName() == "terminal") return(parseTerminal(e));
else if (e.tagName() == "line") return(parseLine(e, qp));
else if (e.tagName() == "rect") return(parseRect(e, qp));
else if (e.tagName() == "ellipse") return(parseEllipse(e, qp));
else if (e.tagName() == "circle") return(parseCircle(e, qp));
else if (e.tagName() == "arc") return(parseArc(e, qp));
else if (e.tagName() == "polygon") return(parsePolygon(e, qp));
else if (e.tagName() == "text") return(parseText(e, qp));
else if (e.tagName() == "input") return(parseInput(e));
else return(true); // on n'est pas chiant, on ignore l'element inconnu
}
/**
Analyse un element XML suppose representer une ligne. Si l'analyse
reussit, la ligne est ajoutee au dessin.
La ligne est definie par les attributs suivants :
- x1, y1 : reels, coordonnees d'une extremite de la ligne
- x2, y2 : reels, coordonnees de l'autre extremite de la ligne
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseLine(QDomElement &e, QPainter &qp) {
// verifie la presence et la validite des attributs obligatoires
qreal x1, y1, x2, y2;
if (!QET::attributeIsAReal(e, QString("x1"), &x1)) return(false);
if (!QET::attributeIsAReal(e, QString("y1"), &y1)) return(false);
if (!QET::attributeIsAReal(e, QString("x2"), &x2)) return(false);
if (!QET::attributeIsAReal(e, QString("y2"), &y2)) return(false);
QET::EndType first_end = QET::endTypeFromString(e.attribute("end1"));
QET::EndType second_end = QET::endTypeFromString(e.attribute("end2"));
qreal length1, length2;
if (!QET::attributeIsAReal(e, QString("length1"), &length1)) length1 = 1.5;
if (!QET::attributeIsAReal(e, QString("length2"), &length2)) length2 = 1.5;
qp.save();
setPainterStyle(e, qp);
QPen t = qp.pen();
t.setJoinStyle(Qt::MiterJoin);
qp.setPen(t);
QLineF line(x1, y1, x2, y2);
QPointF point1(line.p1());
QPointF point2(line.p2());
qreal line_length(line.length());
qreal pen_width = qp.pen().widthF();
// determine s'il faut dessiner les extremites
bool draw_1st_end, draw_2nd_end;
qreal reduced_line_length = line_length - (length1 * PartLine::requiredLengthForEndType(first_end));
draw_1st_end = first_end && reduced_line_length >= 0;
if (draw_1st_end) {
reduced_line_length -= (length2 * PartLine::requiredLengthForEndType(second_end));
} else {
reduced_line_length = line_length - (length2 * PartLine::requiredLengthForEndType(second_end));
}
draw_2nd_end = second_end && reduced_line_length >= 0;
// dessine la premiere extremite
QPointF start_point, stop_point;
if (draw_1st_end) {
QList<QPointF> four_points1(PartLine::fourEndPoints(point1, point2, length1));
if (first_end == QET::Circle) {
qp.drawEllipse(QRectF(four_points1[0] - QPointF(length1, length1), QSizeF(length1 * 2.0, length1 * 2.0)));
start_point = four_points1[1];
} else if (first_end == QET::Diamond) {
qp.drawPolygon(QPolygonF() << four_points1[1] << four_points1[2] << point1 << four_points1[3]);
start_point = four_points1[1];
} else if (first_end == QET::Simple) {
qp.drawPolyline(QPolygonF() << four_points1[3] << point1 << four_points1[2]);
start_point = point1;
} else if (first_end == QET::Triangle) {
qp.drawPolygon(QPolygonF() << four_points1[0] << four_points1[2] << point1 << four_points1[3]);
start_point = four_points1[0];
}
// ajuste le depart selon l'epaisseur du trait
if (pen_width && (first_end == QET::Simple || first_end == QET::Circle)) {
start_point = QLineF(start_point, point2).pointAt(pen_width / 2.0 / line_length);
}
} else {
start_point = point1;
}
// dessine la seconde extremite
if (draw_2nd_end) {
QList<QPointF> four_points2(PartLine::fourEndPoints(point2, point1, length2));
if (second_end == QET::Circle) {
qp.drawEllipse(QRectF(four_points2[0] - QPointF(length2, length2), QSizeF(length2 * 2.0, length2 * 2.0)));
stop_point = four_points2[1];
} else if (second_end == QET::Diamond) {
qp.drawPolygon(QPolygonF() << four_points2[2] << point2 << four_points2[3] << four_points2[1]);
stop_point = four_points2[1];
} else if (second_end == QET::Simple) {
qp.drawPolyline(QPolygonF() << four_points2[3] << point2 << four_points2[2]);
stop_point = point2;
} else if (second_end == QET::Triangle) {
qp.drawPolygon(QPolygonF() << four_points2[0] << four_points2[2] << point2 << four_points2[3] << four_points2[0]);
stop_point = four_points2[0];
}
// ajuste l'arrivee selon l'epaisseur du trait
if (pen_width && (second_end == QET::Simple || second_end == QET::Circle)) {
stop_point = QLineF(point1, stop_point).pointAt((line_length - (pen_width / 2.0)) / line_length);
}
} else {
stop_point = point2;
}
qp.drawLine(start_point, stop_point);
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer un rectangle. Si l'analyse
reussit, le rectangle est ajoute au dessin.
Le rectangle est defini par les attributs suivants :
- x : abscisse du coin superieur gauche du rectangle
- y : ordonnee du coin superieur gauche du rectangle
- width : largeur du rectangle
- height : hauteur du rectangle
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseRect(QDomElement &e, QPainter &qp) {
// verifie la presence des attributs obligatoires
qreal rect_x, rect_y, rect_w, rect_h;
if (!QET::attributeIsAReal(e, QString("x"), &rect_x)) return(false);
if (!QET::attributeIsAReal(e, QString("y"), &rect_y)) return(false);
if (!QET::attributeIsAReal(e, QString("width"), &rect_w)) return(false);
if (!QET::attributeIsAReal(e, QString("height"), &rect_h)) return(false);
qp.save();
setPainterStyle(e, qp);
// force le type de jointures pour les rectangles
QPen p = qp.pen();
p.setJoinStyle(Qt::MiterJoin);
qp.setPen(p);
qp.drawRect(QRectF(rect_x, rect_y, rect_w, rect_h));
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer un cercle. Si l'analyse
reussit, le cercle est ajoute au dessin.
Le cercle est defini par les attributs suivants :
- x : abscisse du coin superieur gauche de la quadrature du cercle
- y : ordonnee du coin superieur gauche de la quadrature du cercle
- diameter : diametre du cercle
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseCircle(QDomElement &e, QPainter &qp) {
// verifie la presence des attributs obligatoires
qreal cercle_x, cercle_y, cercle_r;
if (!QET::attributeIsAReal(e, QString("x"), &cercle_x)) return(false);
if (!QET::attributeIsAReal(e, QString("y"), &cercle_y)) return(false);
if (!QET::attributeIsAReal(e, QString("diameter"), &cercle_r)) return(false);
qp.save();
setPainterStyle(e, qp);
qp.drawEllipse(QRectF(cercle_x, cercle_y, cercle_r, cercle_r));
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer une ellipse. Si l'analyse
reussit, l'ellipse est ajoutee au dessin.
L'ellipse est definie par les attributs suivants :
- x : abscisse du coin superieur gauche du rectangle dans lequel s'inscrit l'ellipse
- y : ordonnee du coin superieur gauche du rectangle dans lequel s'inscrit l'ellipse
- width : dimension de la diagonale horizontale de l'ellipse
- height : dimension de la diagonale verticale de l'ellipse
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseEllipse(QDomElement &e, QPainter &qp) {
// verifie la presence des attributs obligatoires
qreal ellipse_x, ellipse_y, ellipse_l, ellipse_h;
if (!QET::attributeIsAReal(e, QString("x"), &ellipse_x)) return(false);
if (!QET::attributeIsAReal(e, QString("y"), &ellipse_y)) return(false);
if (!QET::attributeIsAReal(e, QString("width"), &ellipse_l)) return(false);
if (!QET::attributeIsAReal(e, QString("height"), &ellipse_h)) return(false);
qp.save();
setPainterStyle(e, qp);
qp.drawEllipse(QRectF(ellipse_x, ellipse_y, ellipse_l, ellipse_h));
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer un arc de cercle. Si l'analyse
reussit, l'arc de cercle est ajoute au dessin.
L'arc de cercle est defini par les quatres parametres d'une ellipse (en fait
l'ellipse dans laquelle s'inscrit l'arc de cercle) auxquels s'ajoutent les
attributs suivants :
- start : angle de depart : l'angle "0 degre" est a trois heures
- angle : etendue (en degres) de l'arc de cercle ; une valeur positive
va dans le sens contraire des aiguilles d'une montre
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseArc(QDomElement &e, QPainter &qp) {
// verifie la presence des attributs obligatoires
qreal arc_x, arc_y, arc_l, arc_h, arc_s, arc_a;
if (!QET::attributeIsAReal(e, QString("x"), &arc_x)) return(false);
if (!QET::attributeIsAReal(e, QString("y"), &arc_y)) return(false);
if (!QET::attributeIsAReal(e, QString("width"), &arc_l)) return(false);
if (!QET::attributeIsAReal(e, QString("height"), &arc_h)) return(false);
if (!QET::attributeIsAReal(e, QString("start"), &arc_s)) return(false);
if (!QET::attributeIsAReal(e, QString("angle"), &arc_a)) return(false);
qp.save();
setPainterStyle(e, qp);
qp.drawArc(QRectF(arc_x, arc_y, arc_l, arc_h), (int)(arc_s * 16), (int)(arc_a * 16));
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer un polygone. Si l'analyse
reussit, le polygone est ajoute au dessin.
Le polygone est defini par une serie d'attributs x1, x2, ..., xn et autant
d'attributs y1, y2, ..., yn representant les coordonnees des differents
points du polygone.
Il est possible d'obtenir un polygone non ferme en utilisant closed="false"
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parsePolygon(QDomElement &e, QPainter &qp) {
int i = 1;
while(true) {
if (QET::attributeIsAReal(e, QString("x%1").arg(i)) && QET::attributeIsAReal(e, QString("y%1").arg(i))) ++ i;
else break;
}
if (i < 3) return(false);
QVector<QPointF> points(i-1);
for (int j = 1 ; j < i ; ++ j) {
points.insert(
j - 1,
QPointF(
e.attribute(QString("x%1").arg(j)).toDouble(),
e.attribute(QString("y%1").arg(j)).toDouble()
)
);
}
qp.save();
setPainterStyle(e, qp);
if (e.attribute("closed") == "false") qp.drawPolyline(points.data(), i-1);
else qp.drawPolygon(points.data(), i-1);
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer un texte. Si l'analyse
reussit, le texte est ajoute au dessin.
Le texte est defini par une position, une chaine de caracteres et une
taille.
@param e L'element XML a analyser
@param qp Le QPainter a utiliser pour dessiner l'element perso
@return true si l'analyse reussit, false sinon
*/
bool CustomElement::parseText(QDomElement &e, QPainter &qp) {
qreal pos_x, pos_y;
int size;
if (
!QET::attributeIsAReal(e, "x", &pos_x) ||\
!QET::attributeIsAReal(e, "y", &pos_y) ||\
!QET::attributeIsAnInteger(e, "size", &size) ||\
!e.hasAttribute("text")
) return(false);
qp.save();
setPainterStyle(e, qp);
// determine la police a utiliser et en recupere les metriques associees
QFont used_font = QETApp::diagramTextsFont(size);
QFontMetrics qfm(used_font);
QColor text_color = (e.attribute("color") != "white"? Qt::black : Qt::white);
// instancie un QTextDocument (comme la classe QGraphicsTextItem) pour
// generer le rendu graphique du texte
QTextDocument text_document;
text_document.setDefaultFont(used_font);
text_document.setPlainText(e.attribute("text"));
// Se positionne aux coordonnees indiquees dans la description du texte
qp.setTransform(QTransform(), false);
qp.translate(pos_x, pos_y);
// Pivote le systeme de coordonnees du QPainter pour effectuer le rendu
// dans le bon sens
qreal default_rotation_angle = 0.0;
if (QET::attributeIsAReal(e, "rotation", &default_rotation_angle)) {
qp.rotate(default_rotation_angle);
}
/*
Deplace le systeme de coordonnees du QPainter pour effectuer le rendu au
bon endroit ; note : on soustrait l'ascent() de la police pour
determiner le coin superieur gauche du texte alors que la position
indiquee correspond a la baseline.
*/
QPointF qpainter_offset(0.0, -qfm.ascent());
// ajuste le decalage selon la marge du document texte
#if QT_VERSION >= 0x040500
text_document.setDocumentMargin(0.0);
#else
// il semblerait qu'avant Qt 4.5, le documentMargin vaille 2.0 (et pas 4.0)
qpainter_offset.rx() -= 2.0;
qpainter_offset.ry() -= 2.0;
#endif
qp.translate(qpainter_offset);
// force the palette used to render the QTextDocument
QAbstractTextDocumentLayout::PaintContext ctx;
ctx.palette.setColor(QPalette::Text, text_color);
text_document.documentLayout() -> draw(&qp, ctx);
qp.restore();
return(true);
}
/**
Analyse un element XML suppose representer un champ de texte editable par
l'utilisateur. Si l'analyse reussit, le champ est ajoute au dessin.
Le texte est defini par :
- une position
- une chaine de caracteres facultative utilisee comme valeur par defaut
- une taille
- le fait de subir les rotations de l'element ou non
@param e L'element XML a analyser
@return Un pointeur vers l'objet ElementTextItem ainsi cree si l'analyse reussit, 0 sinon
*/
ElementTextItem *CustomElement::parseInput(QDomElement &e) {
qreal pos_x, pos_y;
int size;
if (
!QET::attributeIsAReal(e, "x", &pos_x) ||\
!QET::attributeIsAReal(e, "y", &pos_y) ||\
!QET::attributeIsAnInteger(e, "size", &size)
) return(0);
ElementTextItem *eti = new ElementTextItem(e.attribute("text"), this);
eti -> setFont(QETApp::diagramTextsFont(size));
// position du champ de texte
eti -> setOriginalPos(QPointF(pos_x, pos_y));
eti -> setPos(pos_x, pos_y);
// rotation du champ de texte
qreal original_rotation_angle = 0.0;
QET::attributeIsAReal(e, "rotation", &original_rotation_angle);
eti -> setOriginalRotationAngle(original_rotation_angle);
eti -> setRotationAngle(original_rotation_angle);
// comportement du champ lorsque son element parent est pivote
eti -> setFollowParentRotations(e.attribute("rotate") == "true");
list_texts_ << eti;
return(eti);
}
/**
Analyse un element XML suppose representer une borne. Si l'analyse
reussit, la borne est ajoutee a l'element.
Une borne est definie par les attributs suivants :
- x, y : coordonnees de la borne
- orientation : orientation de la borne = Nord (n), Sud (s), Est (e) ou Ouest (w)
@param e L'element XML a analyser
@return Un pointeur vers l'objet Terminal ainsi cree, 0 sinon
*/
Terminal *CustomElement::parseTerminal(QDomElement &e) {
// verifie la presence et la validite des attributs obligatoires
qreal terminalx, terminaly;
QET::Orientation terminalo;
if (!QET::attributeIsAReal(e, QString("x"), &terminalx)) return(0);
if (!QET::attributeIsAReal(e, QString("y"), &terminaly)) return(0);
if (!e.hasAttribute("orientation")) return(0);
if (e.attribute("orientation") == "n") terminalo = QET::North;
else if (e.attribute("orientation") == "s") terminalo = QET::South;
else if (e.attribute("orientation") == "e") terminalo = QET::East;
else if (e.attribute("orientation") == "w") terminalo = QET::West;
else return(0);
Terminal *new_terminal = new Terminal(terminalx, terminaly, terminalo, this, qobject_cast<Diagram *>(scene()));
new_terminal -> setZValue(420); // valeur arbitraire pour maintenir les bornes au-dessus des champs de texte
list_terminals << new_terminal;
return(new_terminal);
}
/**
Active / desactive l'antialiasing sur un QPainter
@param qp Le QPainter a modifier
@param aa Booleen a true pour activer l'antialiasing, a false pour le desactiver
*/
void CustomElement::setQPainterAntiAliasing(QPainter &qp, bool aa) {
if (forbid_antialiasing) aa = false;
qp.setRenderHint(QPainter::Antialiasing, aa);
qp.setRenderHint(QPainter::TextAntialiasing, aa);
qp.setRenderHint(QPainter::SmoothPixmapTransform, aa);
}
/**
Verifie si l'attribut "orientation" de l'element XML e correspond bien a la
syntaxe decrivant les orientations possibles pour un element.
Cette syntaxe comprend exactement 4 lettres :
- une pour le Nord
- une pour l'Est
- une pour le Sud
- une pour l'Ouest
Pour chaque orientation, on indique si elle est :
- l'orientation par defaut : d
- une orientation autorisee : y
- une orientation interdire : n
Exemple : "dnny" represente un element par defaut oriente vers le nord et qui
peut etre oriente vers l'ouest mais pas vers le sud ou vers l'est.
@param e Element XML
@return true si l'attribut "orientation" est valide, false sinon
*/
bool CustomElement::validOrientationAttribute(const QDomElement &e) {
int ori = e.attribute("orientation").toInt();
if(ori >= 0 && ori <=3) return true;
return false;
}
/**
Applique les parametres de style definis dans l'attribut "style" de
l'element XML e au QPainter qp
Les styles possibles sont :
- line-style : style du trait
- dashed : trait en pointilles (tirets)
- dashdotted : Traits et points
- dotted : trait en pointilles (points)
- normal : trait plein [par defaut]
- line-weight : epaiseur du trait
- thin : trait fin
- normal : trait d'epaisseur 1 [par defaut]
- filling : remplissage de la forme
- white : remplissage blanc
- black : remplissage noir
- red : remplissage rouge
- blue : remplissage bleu
- green : remplissage vert
- none : pas de remplissage [par defaut]
- color : couleur du trait et du texte
- white : trait noir [par defaut]
- black : trait blanc
- red : trait rouge
- blue : trait bleu
- green : trait vert
Les autres valeurs ne sont pas prises en compte.
@param e L'element XML a parser
@param qp Le QPainter a modifier en fonction des styles
*/
void CustomElement::setPainterStyle(QDomElement &e, QPainter &qp) {
// recupere le QPen et la QBrush du QPainter
QPen pen = qp.pen();
QBrush brush = qp.brush();
// attributs par defaut
pen.setJoinStyle(Qt::BevelJoin);
pen.setCapStyle(Qt::SquareCap);
// recupere la liste des couples style / valeur
QStringList styles = e.attribute("style").split(";", QString::SkipEmptyParts);
// agit sur le QPen et la QBrush en fonction des valeurs rencontrees
QRegExp rx("^\\s*([a-z-]+)\\s*:\\s*([a-z-]+)\\s*$");
foreach (QString style, styles) {
if (rx.exactMatch(style)) {
QString style_name = rx.cap(1);
QString style_value = rx.cap(2);
if (style_name == "line-style") {
if (style_value == "dashed") pen.setStyle(Qt::DashLine);
else if (style_value == "dotted") pen.setStyle(Qt::DotLine);
else if (style_value == "dashdotted") pen.setStyle(Qt::DashDotLine);
else if (style_value == "normal") pen.setStyle(Qt::SolidLine);
} else if (style_name == "line-weight") {
if (style_value == "thin") pen.setWidth(0);
else if (style_value == "normal") pen.setWidthF(1.0);
else if (style_value == "none") pen.setColor(QColor(0, 0, 0, 0));
} else if (style_name == "filling") {
if (style_value == "white") {
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::white);
} else if (style_value == "black") {
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::black);
} else if (style_value == "blue") {
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::blue);
} else if (style_value == "red") {
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::red);
} else if (style_value == "green") {
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::green);
} else if (style_value == "none") {
brush.setStyle(Qt::NoBrush);
}
} else if (style_name == "color") {
if (style_value == "black") {
pen.setColor(QColor(0, 0, 0, pen.color().alpha()));
} else if (style_value == "white") {
pen.setColor(QColor(255, 255, 255, pen.color().alpha()));
} else if (style_value == "red") {
pen.setColor(Qt::red);
}else if (style_value == "blue") {
pen.setColor(Qt::blue);
}else if (style_value == "green") {
pen.setColor(Qt::green);
}
}
}
}
// affectation du QPen et de la QBrush modifies au QPainter
qp.setPen(pen);
qp.setBrush(brush);
// mise en place (ou non) de l'antialiasing
setQPainterAntiAliasing(qp, e.attribute("antialias") == "true");
}

View File

@@ -0,0 +1,132 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CUSTOM_ELEMENT_H
#define CUSTOM_ELEMENT_H
#include "fixedelement.h"
#include <QtGui>
#include "nameslist.h"
#include "elementslocation.h"
class ElementTextItem;
class Terminal;
/**
This class represents an electrical element; it may be used like a fixed
element, the difference being that the CustomElement reads its description
(names, drawing, behavior) from an XML document.
*/
class CustomElement : public FixedElement {
Q_OBJECT
// constructors, destructor
public:
CustomElement(const ElementsLocation &, QGraphicsItem * = 0, Diagram * = 0, int * = 0);
CustomElement(const QDomElement &, QGraphicsItem * = 0, Diagram * = 0, int * = 0);
virtual ~CustomElement();
private:
CustomElement(const CustomElement &);
// attributes
protected:
int elmt_state; // hold the error code in case the instanciation fails, or 0 if everything went well
NamesList names;
ElementsLocation location_;
QPicture drawing;
QPicture low_zoom_drawing;
QList<Terminal *> list_terminals;
QList<ElementTextItem *> list_texts_;
bool forbid_antialiasing;
// methods
public:
virtual QList<Terminal *> terminals() const;
virtual QList<Conductor *> conductors() const;
virtual QList<ElementTextItem *> texts() const;
virtual int terminalsCount() const;
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *);
QString typeId() const;
ElementsLocation location() const;
bool isNull() const;
int state() const;
QString name() const;
protected:
virtual bool buildFromXml(const QDomElement &, int * = 0);
virtual bool parseElement(QDomElement &, QPainter &);
virtual bool parseLine(QDomElement &, QPainter &);
virtual bool parseRect(QDomElement &, QPainter &);
virtual bool parseEllipse(QDomElement &, QPainter &);
virtual bool parseCircle(QDomElement &, QPainter &);
virtual bool parseArc(QDomElement &, QPainter &);
virtual bool parsePolygon(QDomElement &, QPainter &);
virtual bool parseText(QDomElement &, QPainter &);
virtual ElementTextItem *parseInput(QDomElement &);
virtual Terminal *parseTerminal(QDomElement &);
virtual void setQPainterAntiAliasing(QPainter &, bool);
virtual bool validOrientationAttribute(const QDomElement &);
virtual void setPainterStyle(QDomElement &, QPainter &);
};
/**
@return The element type ID; considering a CustomElement, this means the
@location of its XML description.
@see location()
*/
inline QString CustomElement::typeId() const {
return(location_.path());
}
/**
@return the location of the XML document describing this element.
*/
inline ElementsLocation CustomElement::location() const {
return(location_);
}
/**
@return true if this element is null, i.e. if its XML description could not
be loaded.
*/
inline bool CustomElement::isNull() const {
return(elmt_state);
}
/**
@return An integer representing the state of this element:
- 0: instantiation succeeded
- 1: the file does not exist
- 2: the file could not be opened
- 3: The file is not a valid XML document
- 4: The XML document does not have a "definition" root element.
- 5: The definition attributes are missing or invalid
- 6: The definition is empty
- 7: The parsing of an XML element describing an element drawing primitive failed
- 8: No primitive could be loadedAucune partie du dessin n'a pu etre chargee
*/
inline int CustomElement::state() const {
return(elmt_state);
}
/**
@return The name of this element.
*/
inline QString CustomElement::name() const {
return(names.name(location_.baseName()));
}
#endif

View File

@@ -0,0 +1,222 @@
/*
Copyright 2006-2013 QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "diagramimageitem.h"
#include "diagramcommands.h"
/**
* @brief DiagramImageItem::DiagramImageItem
* Constructor without pixmap
* @param parent_item the parent graphics item
*/
DiagramImageItem::DiagramImageItem(QetGraphicsItem *parent_item):
QetGraphicsItem(parent_item)
{
setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
}
/**
* @brief DiagramImageItem::DiagramImageItem
* Constructor with pixmap
* @param pixmap the pixmap to be draw
* @param parent_item the parent graphic item
*/
DiagramImageItem::DiagramImageItem(const QPixmap &pixmap, QetGraphicsItem *parent_item):
QetGraphicsItem(parent_item),
pixmap_(pixmap)
{
setTransformOriginPoint(boundingRect().center());
setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
}
/**
* @brief DiagramImageItem::~DiagramImageItem
* Destructor
*/
DiagramImageItem::~DiagramImageItem() {
}
/**
* @brief DiagramImageItem::paint
* Draw the pixmap.
* @param painter the Qpainter to use for draw the pixmap
* @param option the style option
* @param widget the QWidget where we draw the pixmap
*/
void DiagramImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
painter -> drawPixmap(pixmap_.rect(),pixmap_);
if (isSelected()) {
painter -> save();
// Annulation des renderhints
painter -> setRenderHint(QPainter::Antialiasing, false);
painter -> setRenderHint(QPainter::TextAntialiasing, false);
painter -> setRenderHint(QPainter::SmoothPixmapTransform, false);
// Dessin du cadre de selection en noir a partir du boundingrect
QPen t(Qt::black);
t.setStyle(Qt::DashLine);
painter -> setPen(t);
painter -> drawRect(boundingRect());
painter -> restore();
}
}
/**
* @brief DiagramImageItem::setScale
* @param scale the value of @scale must be betwen 1 and 200
*/
void DiagramImageItem::PreviewScale(int scale) {
if (scale >= 1 && scale <= 200) {
qreal new_scale = scale;
new_scale /= 100;
setScale(new_scale);
}
}
/**
* @brief Edit the image with ....
*/
void DiagramImageItem::editProperty() {
if (diagram() -> isReadOnly()) return;
//the range for scale image and divisor factor
int min_range = 1;
int max_range = 200;
int factor_range = 100;
//the dialog
QDialog property_dialog;
property_dialog.setWindowTitle(tr("\311diter les propri\351t\351s d'une image", "window title"));
//the main layout
QVBoxLayout *dialog_layout = new QVBoxLayout(&property_dialog);
//GroupBox for resizer image
QGroupBox *resize_groupe = new QGroupBox(tr("Dimension de l'image", "image size"));
dialog_layout -> addWidget(resize_groupe);
QHBoxLayout *resize_layout = new QHBoxLayout(resize_groupe);
//slider
QSlider *slider = new QSlider(Qt::Horizontal, &property_dialog);
slider->setRange(min_range, max_range);
qreal scale_= scale();
slider -> setValue(scale_*factor_range);
//spinbox
QSpinBox *spin_box = new QSpinBox(&property_dialog);
spin_box -> setRange(min_range, max_range);
spin_box -> setValue(scale_*factor_range);
spin_box -> setSuffix(" %");
//synchro slider with spinbox
connect(slider, SIGNAL(valueChanged(int)), spin_box, SLOT(setValue(int)));
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(PreviewScale(int)));
connect(spin_box, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
//add slider and spinbox to layout
resize_layout -> addWidget(slider);
resize_layout -> addWidget(spin_box);
//dialog button, box
QDialogButtonBox *dbb = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog_layout -> addWidget(dbb);
connect(dbb, SIGNAL(accepted()), &property_dialog, SLOT(accept()));
connect(dbb, SIGNAL(rejected()), &property_dialog, SLOT(reject()));
//dialog is accepted...
if (property_dialog.exec() == QDialog::Accepted) {
qreal new_scale = slider -> value();
new_scale /= factor_range;
if (scale_ != new_scale) diagram()->undoStack().push(new ImageResizerCommand(this, scale_, new_scale));
}
//...or not
else setScale(scale_);
return;
}
/**
* @brief DiagramImageItem::setPixmap
* Set the new pixmap to be draw
* @param pixmap the new pixmap
*/
void DiagramImageItem::setPixmap(const QPixmap &pixmap) {
pixmap_ = pixmap;
setTransformOriginPoint(boundingRect().center());
}
/**
* @brief DiagramImageItem::boundingRect
* the outer bounds of the item as a rectangle,
* if no pixmap are set, return a default QRectF
* @return a QRectF represent the bounding rectangle
*/
QRectF DiagramImageItem::boundingRect() const {
if (!pixmap_.isNull()) {
return (QRectF(pixmap_.rect()));
} else {
QRectF bound;
return (bound);
}
}
/**
Load the image from this xml element
@param e xml element that define an image
*/
bool DiagramImageItem::fromXml(const QDomElement &e) {
if (e.tagName() != "image") return (false);
QDomNode image_node = e.firstChild();
if (!image_node.isText()) return (false);
//load xml image to QByteArray
QByteArray array;
array = QByteArray::fromBase64(e.text().toAscii());
//Set QPixmap from the @array
QPixmap pixmap;
pixmap.loadFromData(array);
setPixmap(pixmap);
setScale(e.attribute("size").toDouble());
applyRotation(e.attribute("rotation").toDouble());
setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
return (true);
}
/**
@param document Le document XML a utiliser
@return L'element XML representant l'image
*/
QDomElement DiagramImageItem::toXml(QDomDocument &document) const {
QDomElement result = document.createElement("image");
//write some attribute
result.setAttribute("x", QString("%1").arg(pos().x()));
result.setAttribute("y", QString("%1").arg(pos().y()));
result.setAttribute("rotation", QString("%1").arg(rotation()));
result.setAttribute("size", QString("%1").arg(scale()));
//write the pixmap in the xml element after he was been transformed to base64
QByteArray array;
QBuffer buffer(&array);
buffer.open(QIODevice::ReadWrite);
pixmap_.save(&buffer, "PNG");
QDomText base64 = document.createTextNode(array.toBase64());
result.appendChild(base64);
return(result);
}

View File

@@ -0,0 +1,67 @@
/*
Copyright 2006-2013 QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DIAGRAM_IMAGE_ITEM_H
#define DIAGRAM_IMAGE_ITEM_H
#include <QtGui>
#include "qetgraphicsitem.h"
/**
This class represents a selectable, movable and editable image on a
diagram.
@see QGraphicsItem::GraphicsItemFlags
*/
class DiagramImageItem : public QetGraphicsItem {
Q_OBJECT
// constructors, destructor
public:
DiagramImageItem(QetGraphicsItem * = 0);
DiagramImageItem(const QPixmap &pixmap, QetGraphicsItem * = 0);
virtual ~DiagramImageItem();
// attributes
public:
enum { Type = UserType + 1007 };
// methods
public:
/**
Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
DiagramImageItem
@return the QGraphicsItem type
*/
virtual int type() const { return Type; }
virtual bool fromXml(const QDomElement &);
virtual QDomElement toXml(QDomDocument &) const;
virtual void editProperty();
void setPixmap(const QPixmap &pixmap);
virtual QRectF boundingRect() const;
protected:
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
signals:
private slots:
void PreviewScale(int);
protected:
QPixmap pixmap_;
};
#endif

View File

@@ -0,0 +1,328 @@
/*
Copyright 2006-2013 QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "diagramtextitem.h"
#include "diagramcommands.h"
#include "qet.h"
#include "qetapp.h"
#include "richtext/richtexteditor_p.h"
/**
Constructeur
@param parent Le QGraphicsItem parent du champ de texte
@param parent_diagram Le schema auquel appartient le champ de texte
*/
DiagramTextItem::DiagramTextItem(QGraphicsItem *parent, Diagram *parent_diagram) :
QGraphicsTextItem(parent, parent_diagram),
previous_text_(),
rotation_angle_(0.0)
{
//set Zvalue at 10 to be upper than the DiagramImageItem
setZValue(10);
setDefaultTextColor(Qt::black);
setFont(QETApp::diagramTextsFont());
setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
connect(this, SIGNAL(lostFocus()), this, SLOT(setNonFocusable()));
}
/**
Constructeur
@param text Le texte affiche par le champ de texte
@param parent Le QGraphicsItem parent du champ de texte
@param parent_diagram Le schema auquel appartient le champ de texte
*/
DiagramTextItem::DiagramTextItem(const QString &text, QGraphicsItem *parent, Diagram *parent_diagram) :
QGraphicsTextItem(text, parent, parent_diagram),
previous_text_(text),
rotation_angle_(0.0)
{
//set Zvalue at 10 to be upper than the DiagramImageItem
setZValue(10);
setDefaultTextColor(Qt::black);
setFont(QETApp::diagramTextsFont());
setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
connect(this, SIGNAL(lostFocus()), this, SLOT(setNonFocusable()));
}
/// Destructeur
DiagramTextItem::~DiagramTextItem() {
}
/**
@return le Diagram auquel ce texte appartient, ou 0 si ce texte n'est
rattache a aucun schema
*/
Diagram *DiagramTextItem::diagram() const {
return(qobject_cast<Diagram *>(scene()));
}
/**
@return l'angle de rotation actuel de ce texte
*/
qreal DiagramTextItem::rotationAngle() const {
return(rotation_angle_);
}
/**
Permet de tourner le texte a un angle donne de maniere absolue.
Un angle de 0 degres correspond a un texte horizontal non retourne.
@param rotation Nouvel angle de rotation de ce texte
@see applyRotation
*/
void DiagramTextItem::setRotationAngle(const qreal &rotation) {
qreal applied_rotation = QET::correctAngle(rotation);
applyRotation(applied_rotation - rotation_angle_);
rotation_angle_ = applied_rotation;
}
/**
Permet de tourner le texte de maniere relative.
L'angle added_rotation est ajoute a l'orientation actuelle du texte.
@param added_rotation Angle a ajouter a la rotation actuelle
@see applyRotation
*/
void DiagramTextItem::rotateBy(const qreal &added_rotation) {
qreal applied_added_rotation = QET::correctAngle(added_rotation);
rotation_angle_ = QET::correctAngle(rotation_angle_ + applied_added_rotation);
applyRotation(applied_added_rotation);
}
/**
Traduit en coordonnees de la scene un mouvement / vecteur initialement
exprime en coordonnees locales.
@param movement Vecteur exprime en coordonnees locales
@return le meme vecteur, exprime en coordonnees de la scene
*/
QPointF DiagramTextItem::mapMovementToScene(const QPointF &movement) const {
// on definit deux points en coordonnees locales
QPointF local_origin(0.0, 0.0);
QPointF local_movement_point(movement);
// on les mappe sur la scene
QPointF scene_origin(mapToScene(local_origin));
QPointF scene_movement_point(mapToScene(local_movement_point));
// on calcule le vecteur represente par ces deux points
return(scene_movement_point - scene_origin);
}
/**
Traduit en coordonnees locales un mouvement / vecteur initialement
exprime en coordonnees de la scene.
@param movement Vecteur exprime en coordonnees de la scene
@return le meme vecteur, exprime en coordonnees locales
*/
QPointF DiagramTextItem::mapMovementFromScene(const QPointF &movement) const {
// on definit deux points sur la scene
QPointF scene_origin(0.0, 0.0);
QPointF scene_movement_point(movement);
// on les mappe sur ce QGraphicsItem
QPointF local_origin(mapFromScene(scene_origin));
QPointF local_movement_point(mapFromScene(scene_movement_point));
// on calcule le vecteur represente par ces deux points
return(local_movement_point - local_origin);
}
/**
Traduit en coordonnees de l'item parent un mouvement / vecteur initialement
exprime en coordonnees locales.
@param movement Vecteur exprime en coordonnees locales
@return le meme vecteur, exprime en coordonnees du parent
*/
QPointF DiagramTextItem::mapMovementToParent(const QPointF &movement) const {
// on definit deux points en coordonnees locales
QPointF local_origin(0.0, 0.0);
QPointF local_movement_point(movement);
// on les mappe sur la scene
QPointF parent_origin(mapToParent(local_origin));
QPointF parent_movement_point(mapToParent(local_movement_point));
// on calcule le vecteur represente par ces deux points
return(parent_movement_point - parent_origin);
}
/**
Traduit en coordonnees locales un mouvement / vecteur initialement
exprime en coordonnees du parent.
@param movement Vecteur exprime en coordonnees du parent
@return le meme vecteur, exprime en coordonnees locales
*/
QPointF DiagramTextItem::mapMovementFromParent(const QPointF &movement) const {
// on definit deux points sur le parent
QPointF parent_origin(0.0, 0.0);
QPointF parent_movement_point(movement);
// on les mappe sur ce QGraphicsItem
QPointF local_origin(mapFromParent(parent_origin));
QPointF local_movement_point(mapFromParent(parent_movement_point));
// on calcule le vecteur represente par ces deux points
return(local_movement_point - local_origin);
}
/**
Dessine le champ de texte.
Cette methode delegue simplement le travail a QGraphicsTextItem::paint apres
avoir desactive l'antialiasing.
@param painter Le QPainter a utiliser pour dessiner le champ de texte
@param option Les options de style pour le champ de texte
@param widget Le QWidget sur lequel on dessine
*/
void DiagramTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
painter -> setRenderHint(QPainter::Antialiasing, false);
QGraphicsTextItem::paint(painter, option, widget);
}
/**
Gere la prise de focus du champ de texte
@param e Objet decrivant la prise de focus
*/
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_ = toHtml();
// cela permettra de determiner si l'utilisateur a modifie le texte a la fin de l'edition
}
/**
Gere la perte de focus du champ de texte
@param e Objet decrivant la perte de focus
*/
void DiagramTextItem::focusOutEvent(QFocusEvent *e) {
QGraphicsTextItem::focusOutEvent(e);
// signale la modification du texte si besoin
if (toPlainText() != previous_text_) {
emit(diagramTextChanged(this, previous_text_, toHtml()));
previous_text_ = toHtml();
}
// deselectionne le texte
QTextCursor cursor = textCursor();
cursor.clearSelection();
setTextCursor(cursor);
// 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()));
}
/**
Gere les double-clics sur ce champ de texte.
@param event un QGraphicsSceneMouseEvent decrivant le double-clic
*/
void DiagramTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
if (!(textInteractionFlags() & Qt::TextEditable)) {
// rend le champ de texte editable
setTextInteractionFlags(Qt::TextEditorInteraction);
// edite le champ de texte
setFocus(Qt::MouseFocusReason);
} else {
QGraphicsTextItem::mouseDoubleClickEvent(event);
}
}
/**
Effectue 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 filles
@param angle Angle de la rotation a effectuer
*/
void DiagramTextItem::applyRotation(const qreal &angle) {
setRotation(QET::correctAngle(rotation()+angle));
}
/**
Change la position du champ de texte en veillant a ce qu'il
reste sur la grille du schema auquel il appartient.
@param p Nouvelles coordonnees de l'element
*/
void DiagramTextItem::setPos(const QPointF &p) {
if (p == pos()) return;
// pas la peine de positionner sur la grille si l'element n'est pas sur un Diagram
if (scene()) {
// arrondit l'abscisse a 10 px pres
int p_x = qRound(p.x() / (Diagram::xGrid * 1.0)) * Diagram::xGrid;
// arrondit l'ordonnee a 10 px pres
int p_y = qRound(p.y() / (Diagram::yGrid * 1.0)) * Diagram::yGrid;
QGraphicsTextItem::setPos(p_x, p_y);
} else QGraphicsTextItem::setPos(p);
}
/**
Change la position du champ de texte en veillant a ce que l'il
reste sur la grille du schema auquel il appartient.
@param x Nouvelle abscisse de l'element
@param y Nouvelle ordonnee de l'element
*/
void DiagramTextItem::setPos(qreal x, qreal y) {
setPos(QPointF(x, y));
}
/**
@return la position du champ de texte
*/
QPointF DiagramTextItem::pos() const {
return(QGraphicsTextItem::pos());
}
/// Rend le champ de texte non focusable
void DiagramTextItem::setNonFocusable() {
setFlag(QGraphicsTextItem::ItemIsFocusable, false);
}
/**
* @brief DiagramTextItem::setHtmlText
* @param txt
*/
void DiagramTextItem::setHtmlText(const QString &txt) {
setHtml( txt );
}
/**
* @brief Edit the text with HtmlEditor
*/
void DiagramTextItem::edit() {
//Open the HtmlEditor
qdesigner_internal::RichTextEditorDialog *editor = new qdesigner_internal::RichTextEditorDialog();
// connect the in/out
connect(editor, SIGNAL(applyEditText(const QString &)), this, SLOT(setHtmlText(const QString &)));
// load the Html txt
editor->setText( toHtml() );
// show
editor->show();
}

View File

@@ -0,0 +1,85 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DIAGRAM_TEXT_ITEM_H
#define DIAGRAM_TEXT_ITEM_H
#include <QtGui>
#include "diagram.h"
/**
This class represents a selectable, movable and editable text field on a
diagram.
@see QGraphicsItem::GraphicsItemFlags
*/
class DiagramTextItem : public QGraphicsTextItem {
Q_OBJECT
// constructors, destructor
public:
DiagramTextItem(QGraphicsItem * = 0, Diagram * = 0);
DiagramTextItem(const QString &, QGraphicsItem * = 0, Diagram * = 0);
virtual ~DiagramTextItem();
// attributes
public:
enum { Type = UserType + 1004 };
// methods
public:
/**
Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
DiagramTextItem
@return the QGraphicsItem type
*/
virtual int type() const { return Type; }
Diagram *diagram() const;
virtual void fromXml(const QDomElement &) = 0;
virtual QDomElement toXml(QDomDocument &) const = 0;
virtual void setPos(const QPointF &);
virtual void setPos(qreal, qreal);
virtual QPointF pos() const;
qreal rotationAngle() const;
void setRotationAngle(const qreal &);
void rotateBy(const qreal &);
void edit();
QPointF mapMovementToScene(const QPointF &) const;
QPointF mapMovementFromScene(const QPointF &) const;
QPointF mapMovementToParent(const QPointF &) const;
QPointF mapMovementFromParent(const QPointF &) const;
protected:
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
virtual void focusInEvent(QFocusEvent *);
virtual void focusOutEvent(QFocusEvent *);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
virtual void applyRotation(const qreal &);
signals:
/// signal emitted when the text field loses focus
void lostFocus();
/// signal emitted after text was changed
void diagramTextChanged(DiagramTextItem *, const QString &, const QString &);
public slots:
void setNonFocusable();
void setHtmlText(const QString &);
private:
/// Previous text value
QString previous_text_;
/// angle of rotation of the text field
qreal rotation_angle_;
};
#endif

View File

@@ -0,0 +1,444 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "element.h"
#include "qetapp.h"
#include "diagram.h"
#include "conductor.h"
#include "elementtextitem.h"
#include "diagramcommands.h"
#include <QtDebug>
/**
Constructeur pour un element sans scene ni parent
*/
Element::Element(QGraphicsItem *parent, Diagram *scene) :
QetGraphicsItem(parent),
internal_connections_(false),
must_highlight_(false)
{
setZValue(10);
}
/**
Destructeur
*/
Element::~Element() {
}
/**
@return true si l'element est mis en evidence
*/
bool Element::isHighlighted() const {
return(must_highlight_);
}
/**
@param hl true pour mettre l'element en evidence, false sinon
*/
void Element::setHighlighted(bool hl) {
must_highlight_ = hl;
update();
}
/**
Methode principale de dessin de l'element
@param painter Le QPainter utilise pour dessiner l'elment
@param options Les options de style a prendre en compte
@param widget Le widget sur lequel on dessine
*/
void Element::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) {
#ifndef Q_WS_WIN
// corrige un bug de rendu ne se produisant que lors du rendu sur QGraphicsScene sous X11 au zoom par defaut
static bool must_correct_rendering_bug = QETApp::settings().value("correct-rendering", false).toBool();
if (must_correct_rendering_bug) {
Diagram *dia = diagram();
if (dia && options -> levelOfDetail == 1.0 && widget) {
// calcule la rotation qu'a subi l'element
qreal applied_rotation = 90.0 * orientation();
if (applied_rotation == 90.0) painter -> translate(1.0, -1.0);
else if (applied_rotation == 180.0) painter -> translate(-1.0, -1.0);
else if (applied_rotation == 270.0) painter -> translate(-1.0, 1.0);
}
}
#endif
if (must_highlight_) drawHighlight(painter, options);
// Dessin de l'element lui-meme
paint(painter, options);
// Dessin du cadre de selection si necessaire
if (isSelected()) drawSelection(painter, options);
}
/**
@return Le rectangle delimitant le contour de l'element
*/
QRectF Element::boundingRect() const {
return(QRectF(QPointF(-hotspot_coord.x(), -hotspot_coord.y()), dimensions));
}
/**
Definit la taille de l'element sur le schema. Les tailles doivent etre
des multiples de 10 ; si ce n'est pas le cas, les dimensions indiquees
seront arrrondies aux dizaines superieures.
@param wid Largeur de l'element
@param hei Hauteur de l'element
@return La taille finale de l'element
*/
QSize Element::setSize(int wid, int hei) {
prepareGeometryChange();
// chaque dimension indiquee est arrondie a la dizaine superieure
while (wid % 10) ++ wid;
while (hei % 10) ++ hei;
// les dimensions finales sont conservees et retournees
return(dimensions = QSize(wid, hei));
}
/**
@return la taille de l'element sur le schema
*/
QSize Element::size() const {
return(dimensions);
}
/**
Definit le hotspot de l'element par rapport au coin superieur gauche de son rectangle delimitant.
Necessite que la taille ait deja ete definie
@param hs Coordonnees du hotspot
*/
QPoint Element::setHotspot(QPoint hs) {
// la taille doit avoir ete definie
prepareGeometryChange();
if (dimensions.isNull()) hotspot_coord = QPoint(0, 0);
else {
// les coordonnees indiquees ne doivent pas depasser les dimensions de l'element
int hsx = qMin(hs.x(), dimensions.width());
int hsy = qMin(hs.y(), dimensions.height());
hotspot_coord = QPoint(hsx, hsy);
}
return(hotspot_coord);
}
/**
@return Le hotspot courant de l'element
*/
QPoint Element::hotspot() const {
return(hotspot_coord);
}
/**
Selectionne l'element
*/
void Element::select() {
setSelected(true);
}
/**
Deselectionne l'element
*/
void Element::deselect() {
setSelected(false);
}
/**
@return La pixmap de l'element
*/
QPixmap Element::pixmap() {
if (preview.isNull()) updatePixmap(); // on genere la pixmap si ce n'est deja fait
return(preview);
}
/**
* @brief Element::rotateBy
* this methode is redefined for handle child item
* @param angle
*/
void Element::rotateBy(const qreal &angle) {
qreal applied_angle = QET::correctAngle(angle);
applyRotation(applied_angle + rotation());
//update the path of conductor
foreach(QGraphicsItem *qgi, childItems()) {
if (Terminal *p = qgraphicsitem_cast<Terminal *>(qgi)) {
p -> updateConductor();
}
}
// repositionne les textes de l'element qui ne comportent pas l'option "FollowParentRotations"
foreach(ElementTextItem *eti, texts()) {
if (!eti -> followParentRotations()) {
// on souhaite pivoter le champ de texte par rapport a son centre
QPointF eti_center = eti -> boundingRect().center();
// pour ce faire, on repere la position de son centre par rapport a son parent
QPointF parent_eti_center_before = eti -> mapToParent(eti_center);
// on applique ensuite une simple rotation contraire, qui sera donc appliquee sur le milieu du cote gauche du champ de texte
eti -> rotateBy(-applied_angle);
// on regarde ensuite la nouvelle position du centre du champ de texte par rapport a son parent
QPointF parent_eti_center_after = eti -> mapToParent(eti_center);
// on determine la translation a appliquer
QPointF eti_translation = parent_eti_center_before - parent_eti_center_after;
// on applique cette translation
eti -> setPos(eti -> pos() + eti_translation);
}
}
}
/*** Methodes protegees ***/
/**
Dessine un petit repere (axes x et y) relatif a l'element
@param painter Le QPainter a utiliser pour dessiner les axes
@param options Les options de style a prendre en compte
*/
void Element::drawAxes(QPainter *painter, const QStyleOptionGraphicsItem *options) {
Q_UNUSED(options);
painter -> setPen(Qt::blue);
painter -> drawLine(0, 0, 10, 0);
painter -> drawLine(7,-3, 10, 0);
painter -> drawLine(7, 3, 10, 0);
painter -> setPen(Qt::red);
painter -> drawLine(0, 0, 0, 10);
painter -> drawLine(0, 10,-3, 7);
painter -> drawLine(0, 10, 3, 7);
}
/*** Methodes privees ***/
/**
Dessine le cadre de selection de l'element de maniere systematiquement non antialiasee.
@param painter Le QPainter a utiliser pour dessiner les bornes.
@param options Les options de style a prendre en compte
*/
void Element::drawSelection(QPainter *painter, const QStyleOptionGraphicsItem *options) {
Q_UNUSED(options);
painter -> save();
// Annulation des renderhints
painter -> setRenderHint(QPainter::Antialiasing, false);
painter -> setRenderHint(QPainter::TextAntialiasing, false);
painter -> setRenderHint(QPainter::SmoothPixmapTransform, false);
// Dessin du cadre de selection en gris
QPen t;
t.setColor(Qt::gray);
t.setStyle(Qt::DashDotLine);
painter -> setPen(t);
// Le dessin se fait a partir du rectangle delimitant
painter -> drawRoundRect(boundingRect().adjusted(1, 1, -1, -1), 10, 10);
painter -> restore();
}
/**
Dessine le cadre de selection de l'element de maniere systematiquement non antialiasee.
@param painter Le QPainter a utiliser pour dessiner les bornes.
@param options Les options de style a prendre en compte
*/
void Element::drawHighlight(QPainter *painter, const QStyleOptionGraphicsItem *options) {
Q_UNUSED(options);
painter -> save();
qreal gradient_radius = qMin(boundingRect().width(), boundingRect().height()) / 2.0;
QRadialGradient gradient(
boundingRect().center(),
gradient_radius,
boundingRect().center()
);
gradient.setColorAt(0.0, QColor::fromRgb(69, 137, 255, 255));
gradient.setColorAt(1.0, QColor::fromRgb(69, 137, 255, 0));
QBrush brush(gradient);
painter -> setPen(Qt::NoPen);
painter -> setBrush(brush);
// Le dessin se fait a partir du rectangle delimitant
painter -> drawRoundRect(boundingRect().adjusted(1, 1, -1, -1), 10, 10);
painter -> restore();
}
/**
Fonction initialisant et dessinant la pixmap de l'element.
*/
void Element::updatePixmap() {
// Pixmap transparente faisant la taille de base de l'element
preview = QPixmap(dimensions);
preview.fill(QColor(255, 255, 255, 0));
// QPainter sur la pixmap, avec antialiasing
QPainter p(&preview);
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
// Translation de l'origine du repere de la pixmap
p.translate(hotspot_coord);
// L'element se dessine sur la pixmap
paint(&p, 0);
}
/**
Permet de savoir si un element XML (QDomElement) represente bien un element
@param e Le QDomElement a valide
@return true si l'element XML est un Element, false sinon
*/
bool Element::valideXml(QDomElement &e) {
// verifie le nom du tag
if (e.tagName() != "element") return(false);
// verifie la presence des attributs minimaux
if (!e.hasAttribute("type")) return(false);
if (!e.hasAttribute("x")) return(false);
if (!e.hasAttribute("y")) return(false);
bool conv_ok;
// parse l'abscisse
e.attribute("x").toDouble(&conv_ok);
if (!conv_ok) return(false);
// parse l'ordonnee
e.attribute("y").toDouble(&conv_ok);
if (!conv_ok) return(false);
return(true);
}
/**
Methode d'import XML. Cette methode est appelee lors de l'import de contenu
XML (coller, import, ouverture de fichier...) afin que l'element puisse
gerer lui-meme l'importation de ses bornes. Ici, comme cette classe est
caracterisee par un nombre fixe de bornes, l'implementation exige de
retrouver exactement ses bornes dans le fichier XML.
@param e L'element XML a analyser.
@param table_id_adr Reference vers la table de correspondance entre les IDs
du fichier XML et les adresses en memoire. Si l'import reussit, il faut y
ajouter les bons couples (id, adresse).
@return true si l'import a reussi, false sinon
*/
bool Element::fromXml(QDomElement &e, QHash<int, Terminal *> &table_id_adr, bool handle_inputs_rotation) {
/*
les bornes vont maintenant etre recensees pour associer leurs id a leur adresse reelle
ce recensement servira lors de la mise en place des fils
*/
QList<QDomElement> liste_terminals;
foreach(QDomElement qde, QET::findInDomElement(e, "terminals", "terminal")) {
if (Terminal::valideXml(qde)) liste_terminals << qde;
}
QHash<int, Terminal *> priv_id_adr;
int terminals_non_trouvees = 0;
foreach(QGraphicsItem *qgi, childItems()) {
if (Terminal *p = qgraphicsitem_cast<Terminal *>(qgi)) {
bool terminal_trouvee = false;
foreach(QDomElement qde, liste_terminals) {
if (p -> fromXml(qde)) {
priv_id_adr.insert(qde.attribute("id").toInt(), p);
terminal_trouvee = true;
// We used to break here, because we did not expect
// several terminals to share the same position.
// Of course, it finally happened.
}
}
if (!terminal_trouvee) ++ terminals_non_trouvees;
}
}
if (terminals_non_trouvees > 0) {
return(false);
} else {
// verifie que les associations id / adr n'entrent pas en conflit avec table_id_adr
foreach(int id_trouve, priv_id_adr.keys()) {
if (table_id_adr.contains(id_trouve)) {
// cet element possede un id qui est deja reference (= conflit)
return(false);
}
}
// copie des associations id / adr
foreach(int id_trouve, priv_id_adr.keys()) {
table_id_adr.insert(id_trouve, priv_id_adr.value(id_trouve));
}
}
// importe les valeurs des champs de texte
QList<QDomElement> inputs = QET::findInDomElement(e, "inputs", "input");
foreach(QGraphicsItem *qgi, childItems()) {
if (ElementTextItem *eti = qgraphicsitem_cast<ElementTextItem *>(qgi)) {
foreach(QDomElement input, inputs) eti -> fromXml(input);
}
}
// position, selection
setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
// orientation
bool conv_ok;
int read_ori = e.attribute("orientation").toInt(&conv_ok);
if (!conv_ok || read_ori < 0 || read_ori > 3) read_ori = 0;
if (handle_inputs_rotation) {
rotateBy(90*read_ori);
} else {
applyRotation(90*read_ori);
}
return(true);
}
/**
Permet d'exporter l'element en XML
@param document Document XML a utiliser
@param table_adr_id Table de correspondance entre les adresses des bornes
et leur id dans la representation XML ; cette table completee par cette
methode
@return L'element XML representant cet element electrique
*/
QDomElement Element::toXml(QDomDocument &document, QHash<Terminal *, int> &table_adr_id) const {
QDomElement element = document.createElement("element");
// type
element.setAttribute("type", typeId());
// position, selection et orientation
element.setAttribute("x", QString("%1").arg(pos().x()));
element.setAttribute("y", QString("%1").arg(pos().y()));
element.setAttribute("orientation", QString("%1").arg(orientation()));
/* recupere le premier id a utiliser pour les bornes de cet element */
int id_terminal = 0;
if (!table_adr_id.isEmpty()) {
// trouve le plus grand id
int max_id_t = -1;
foreach (int id_t, table_adr_id.values()) {
if (id_t > max_id_t) max_id_t = id_t;
}
id_terminal = max_id_t + 1;
}
// enregistrement des bornes de l'appareil
QDomElement xml_terminals = document.createElement("terminals");
// pour chaque enfant de l'element
foreach(Terminal *t, terminals()) {
// alors on enregistre la borne
QDomElement terminal = t -> toXml(document);
terminal.setAttribute("id", id_terminal);
table_adr_id.insert(t, id_terminal ++);
xml_terminals.appendChild(terminal);
}
element.appendChild(xml_terminals);
// enregistrement des champ de texte de l'appareil
QDomElement inputs = document.createElement("inputs");
foreach(ElementTextItem *eti, texts()) {
inputs.appendChild(eti -> toXml(document));
}
element.appendChild(inputs);
return(element);
}

View File

@@ -0,0 +1,152 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ELEMENT_H
#define ELEMENT_H
#include <QtGui>
#include "terminal.h"
#include "qetgraphicsitem.h"
class Diagram;
class ElementTextItem;
/**
This is the base class for electrical elements.
*/
class Element : public QetGraphicsItem {
Q_OBJECT
// constructors, destructor
public:
Element(QGraphicsItem * = 0, Diagram * = 0);
virtual ~Element();
private:
Element(const Element &);
// attributes
public:
enum { Type = UserType + 1000 };
protected:
private:
QSize dimensions;
QPoint hotspot_coord;
QPixmap preview;
// methods
public:
/**
Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into an
Element.
@return the QGraphicsItem type
*/
virtual int type() const { return Type; }
// pure virtual methods to be defined in derived classes
/// @return the list of terminals for this element
virtual QList<Terminal *> terminals() const = 0;
/// @return the list of conductors attached to this element
virtual QList<Conductor *> conductors() const = 0;
/// @return the list of text items attached to this element
virtual QList<ElementTextItem *> texts() const = 0;
/// @return the current number of terminals of this element
virtual int terminalsCount() const = 0;
/// @return the minimum number of terminals for this element
virtual int minTerminalsCount() const = 0;
/// @return the maximum number of terminals for this element
virtual int maxTerminalsCount() const = 0;
/**
Draw this element
*/
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *) = 0;
/// @return This element type ID
virtual QString typeId() const = 0;
/// @return the human name for this element
virtual QString name() const = 0;
virtual bool isHighlighted() const;
virtual void setHighlighted(bool);
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
QRectF boundingRect() const;
QSize setSize(int, int);
QSize size() const;
QPixmap pixmap();
// methods related to the hotspot
QPoint setHotspot(QPoint);
QPoint hotspot() const;
// selection-related methods
void select();
void deselect();
// methods related to internal connections
bool internalConnections();
void setInternalConnections(bool);
virtual void rotateBy(const qreal &);
// methods related to XML import/export
static bool valideXml(QDomElement &);
virtual bool fromXml(QDomElement &, QHash<int, Terminal *> &, bool = false);
virtual QDomElement toXml(QDomDocument &, QHash<Terminal *, int> &) const;
// orientation-related methods
int orientation() const;
protected:
void drawAxes(QPainter *, const QStyleOptionGraphicsItem *);
private:
bool internal_connections_;
bool must_highlight_;
void drawSelection(QPainter *, const QStyleOptionGraphicsItem *);
void drawHighlight(QPainter *, const QStyleOptionGraphicsItem *);
void updatePixmap();
};
/**
Indicate whether this element allows internal connections, i.e. whether its
terminals can be linked together using a conductor.
@return true if internal connections are accepted, false otherwise
*/
inline bool Element::internalConnections() {
return(internal_connections_);
}
/**
Specify whether this element allows internal connections, i.e. whether its
terminals can be linked together using a conductor.
@return true for internal connections to be accepted, false otherwise
*/
inline void Element::setInternalConnections(bool ic) {
internal_connections_ = ic;
}
/**
Indicate the current orientation of this element
O = 0°
1 = 90°
2 = 180°
3 = 270°
@return the current orientation of this element
*/
inline int Element::orientation() const {
return(QET::correctAngle(rotation())/90);
}
#endif

View File

@@ -0,0 +1,310 @@
/*
Copyright 2006-2012 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 "elementtextitem.h"
#include "diagram.h"
#include "diagramcommands.h"
#include "element.h"
/**
Constructeur
@param parent_element Le QGraphicsItem parent du champ de texte
@param parent_diagram Le schema auquel appartient le champ de texte
*/
ElementTextItem::ElementTextItem(Element *parent_element, Diagram *parent_diagram) :
DiagramTextItem(parent_element, parent_diagram),
parent_element_(parent_element),
follow_parent_rotations(false),
original_rotation_angle_(0.0),
first_move_(true)
{
// par defaut, les DiagramTextItem sont Selectable et Movable
// cela nous convient, on ne touche pas a ces flags
adjustItemPosition(1);
// ajuste la position du QGraphicsItem lorsque le QTextDocument change
connect(document(), SIGNAL(blockCountChanged(int)), this, SLOT(adjustItemPosition(int)));
connect(document(), SIGNAL(contentsChanged()), this, SLOT(adjustItemPosition()));
}
/**
Constructeur
@param parent_element L'element parent du champ de texte
@param parent_diagram Le schema auquel appartient le champ de texte
@param text Le texte affiche par le champ de texte
*/
ElementTextItem::ElementTextItem(const QString &text, Element *parent_element, Diagram *parent_diagram) :
DiagramTextItem(text, parent_element, parent_diagram),
parent_element_(parent_element),
follow_parent_rotations(false),
original_rotation_angle_(0.0),
first_move_(true)
{
// par defaut, les DiagramTextItem sont Selectable et Movable
// cela nous convient, on ne touche pas a ces flags
adjustItemPosition(1);
// ajuste la position du QGraphicsItem lorsque le QTextDocument change
connect(document(), SIGNAL(blockCountChanged(int)), this, SLOT(adjustItemPosition(int)));
connect(document(), SIGNAL(contentsChanged()), this, SLOT(adjustItemPosition()));
}
/// Destructeur
ElementTextItem::~ElementTextItem() {
}
/**
@return L'element parent de ce champ de texte, ou 0 si celui-ci n'en a pas.
*/
Element *ElementTextItem::parentElement() const {
return(parent_element_);
}
/**
Modifie la position du champ de texte
@param pos La nouvelle position du champ de texte
*/
void ElementTextItem::setPos(const QPointF &pos) {
QGraphicsTextItem::setPos(pos);
}
/**
Modifie la position du champ de texte
@param x La nouvelle abscisse du champ de texte
@param y La nouvelle ordonnee du champ de texte
*/
void ElementTextItem::setPos(qreal x, qreal y) {
setPos(QPointF(x, y));
}
/**
@return La position (bidouillee) du champ de texte
*/
QPointF ElementTextItem::pos() const {
return(QGraphicsTextItem::pos());
}
/**
Permet de lire le texte a mettre dans le champ a partir d'un element XML.
Cette methode se base sur la position du champ pour assigner ou non la
valeur a ce champ.
@param e L'element XML representant le champ de texte
*/
void ElementTextItem::fromXml(const QDomElement &e) {
QPointF _pos = pos();
if (
qFuzzyCompare(qreal(e.attribute("x").toDouble()), _pos.x()) &&
qFuzzyCompare(qreal(e.attribute("y").toDouble()), _pos.y())
) {
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);
}
qreal xml_rotation_angle;
if (QET::attributeIsAReal(e, "userrotation", &xml_rotation_angle)) {
setRotationAngle(xml_rotation_angle);
}
}
}
/**
@param document Le document XML a utiliser
@return L'element XML representant ce champ de texte
*/
QDomElement ElementTextItem::toXml(QDomDocument &document) const {
QDomElement result = document.createElement("input");
result.setAttribute("x", QString("%1").arg(originalPos().x()));
result.setAttribute("y", QString("%1").arg(originalPos().y()));
if (pos() != originalPos()) {
result.setAttribute("userx", QString("%1").arg(pos().x()));
result.setAttribute("usery", QString("%1").arg(pos().y()));
}
result.setAttribute("text", toPlainText());
if (rotationAngle() != originalRotationAngle()) {
result.setAttribute("userrotation", QString("%1").arg(rotationAngle()));
}
return(result);
}
/**
@param p Position originale / de reference pour ce champ
Cette position est utilisee lors de l'export en XML
*/
void ElementTextItem::setOriginalPos(const QPointF &p) {
original_position = p;
}
/**
@return la position originale / de reference pour ce champ
*/
QPointF ElementTextItem::originalPos() const {
return(original_position);
}
/**
Definit l'angle de rotation original de ce champ de texte
@param rotation_angle un angle de rotation
*/
void ElementTextItem::setOriginalRotationAngle(const qreal &rotation_angle) {
original_rotation_angle_ = QET::correctAngle(rotation_angle);
}
/**
@return l'angle de rotation original de ce champ de texte
*/
qreal ElementTextItem::originalRotationAngle() const {
return(original_rotation_angle_);
}
/**
Set the font used to render the text item to \a font.
*/
void ElementTextItem::setFont(const QFont &font) {
DiagramTextItem::setFont(font);
adjustItemPosition(1);
}
/**
Cette methode s'assure que la position de l'ElementTextItem est coherente
en ajustant :
* la transformation de base qui permet de considerer que l'origine
correspond au milieu du bord gauche du champ de texte
* l'origine utilisee lors des appels a setRotation et setScale
@param new_block_count Nombre de blocs dans l'ElementTextItem
*/
void ElementTextItem::adjustItemPosition(int new_block_count) {
Q_UNUSED(new_block_count);
qreal origin_offset = boundingRect().bottom() / 2.0;
QTransform base_translation;
base_translation.translate(0.0, -origin_offset);
setTransform(base_translation, false);
setTransformOriginPoint(0.0, origin_offset);
}
/**
Effetue la rotation du texte en elle-meme
Pour les ElementTextItem, la rotation s'effectue autour du milieu du bord
gauche du champ de texte.
@param angle Angle de la rotation a effectuer
*/
void ElementTextItem::applyRotation(const qreal &angle) {
QGraphicsTextItem::setRotation(QGraphicsTextItem::rotation() + angle);
}
/**
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
*/
void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
if (textInteractionFlags() & Qt::TextEditable) {
DiagramTextItem::mouseMoveEvent(e);
} else if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
QPointF old_pos = pos();
/*
Utiliser e -> pos() directement aurait pour effet de positionner
l'origine du champ de texte a la position indiquee par le curseur,
ce qui n'est pas l'effet recherche
Au lieu de cela, on applique a la position actuelle le vecteur
definissant le mouvement effectue depuis la derniere position
cliquee avec le bouton gauche
*/
QPointF movement = e -> pos() - e -> buttonDownPos(Qt::LeftButton);
/*
Les methodes pos() et setPos() travaillent toujours avec les
coordonnees de l'item parent (ou de la scene s'il n'y a pas d'item
parent). On n'oublie donc pas de mapper le mouvement fraichement
calcule sur l'item parent avant de l'appliquer.
*/
QPointF parent_movement = mapMovementToParent(movement);
setPos(pos() + parent_movement);
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();
}
}
/*
Comme setPos() n'est pas oblige d'appliquer exactement la
valeur qu'on lui fournit, on calcule le mouvement reellement
applique.
*/
QPointF effective_movement = pos() - old_pos;
QPointF scene_effective_movement = mapMovementToScene(mapMovementFromParent(effective_movement));
// on applique le mouvement subi aux autres textes a deplacer
diagram_ptr -> continueMoveElementTexts(scene_effective_movement);
}
} else e -> ignore();
if (first_move_) {
first_move_ = false;
}
}
/**
Gere le relachement de souris
Cette methode cree un objet d'annulation pour le deplacement
@param e Objet decrivant l'evenement souris
*/
void ElementTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
if (Diagram *diagram_ptr = diagram()) {
// on arrete de mettre en valeur l'element parent
if (parent_element_) {
if (parent_element_ -> isHighlighted()) {
parent_element_ -> setHighlighted(false);
}
}
diagram_ptr -> endMoveElementTexts();
}
if (!(e -> modifiers() & Qt::ControlModifier)) {
QGraphicsTextItem::mouseReleaseEvent(e);
}
}

View File

@@ -0,0 +1,96 @@
/*
Copyright 2006-2012 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_TEXT_ITEM_H
#define ELEMENT_TEXT_ITEM_H
#include "diagramtextitem.h"
#include <QtXml>
class Diagram;
class Element;
/**
This class represents a text item attached to an element. Users can change its
value, adjust its position (defined relatively to its parent element), and
direct it to any angle.
*/
class ElementTextItem : public DiagramTextItem {
Q_OBJECT
// constructors, destructor
public:
ElementTextItem(Element * = 0, Diagram * = 0);
ElementTextItem(const QString &, Element * = 0, Diagram * = 0);
virtual ~ElementTextItem();
// attributes
public:
enum { Type = UserType + 1003 };
private:
Element *parent_element_;
bool follow_parent_rotations;
QPointF original_position;
qreal original_rotation_angle_;
bool first_move_;
// methods
public:
virtual int type() const { return Type; }
Element *parentElement() const;
/// @return the rectangle defining the bounds of this text item
virtual QRectF boundingRect() const { return(QGraphicsTextItem::boundingRect().adjusted(0.0, -1.1, 0.0, 0.0)); }
bool followParentRotations() const;
void setFollowParentRotations(bool);
void fromXml(const QDomElement &);
QDomElement toXml(QDomDocument &) const;
void setPos(const QPointF &);
void setPos(qreal, qreal);
virtual QPointF pos() const;
void setOriginalPos(const QPointF &);
QPointF originalPos() const;
void setOriginalRotationAngle(const qreal &);
qreal originalRotationAngle() const;
virtual void setFont(const QFont &);
public slots:
void adjustItemPosition(int = 0);
protected:
virtual void applyRotation(const qreal &);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
};
/**
Element text items can optionally be applied a counter-rotation when their
parent element is rotated, thus preserving their readability.
@return whether this text item follows the rotations of its parent element.
*/
inline bool ElementTextItem::followParentRotations() const {
return(follow_parent_rotations);
}
/**
Element text items can optionally be applied a counter-rotation when their
parent element is rotated, thus preserving their readability.
@param frp whether this text item should follow the rotations of its parent
element.
*/
inline void ElementTextItem::setFollowParentRotations(bool frp) {
follow_parent_rotations = frp;
}
#endif

View File

@@ -0,0 +1,43 @@
/*
Copyright 2006-2012 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 "fixedelement.h"
/**
Constructeur
*/
FixedElement::FixedElement(QGraphicsItem *parent, Diagram *scene) : Element(parent, scene) {
}
/**
Destructeur
*/
FixedElement::~FixedElement() {
}
/**
@return Le nombre minimal de bornes que l'element peut avoir
*/
int FixedElement::minTerminalsCount() const {
return(terminalsCount());
}
/**
@return Le nombre maximal de bornes que l'element peut avoir
*/
int FixedElement::maxTerminalsCount() const {
return(terminalsCount());
}

View File

@@ -0,0 +1,42 @@
/*
Copyright 2006-2012 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 ELEMENTFIXE_H
#define ELEMENTFIXE_H
#include "element.h"
/**
This class represents an element having a fixed number of terminals.
*/
class FixedElement : public Element {
Q_OBJECT
// constructors, destructor
public:
FixedElement(QGraphicsItem * = 0, Diagram * = 0);
virtual ~FixedElement();
// methods
public:
int minTerminalsCount() const;
int maxTerminalsCount() const;
virtual int terminalsCount() const = 0;
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *) = 0;
virtual QString typeId() const = 0;
virtual QString name() const = 0;
};
#endif

View File

@@ -0,0 +1,191 @@
/*
Copyright 2006-2012 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 "ghostelement.h"
#include "qet.h"
#include "terminal.h"
#include "elementtextitem.h"
#include "diagramcommands.h"
/**
Constructeur
@param location Emplacement de la definition d'element a utiliser
@param qgi Le QGraphicsItem parent de cet element
@param d Le schema affichant cet element
*/
GhostElement::GhostElement(
const ElementsLocation &location,
QGraphicsItem *qgi,
Diagram *d
) :
CustomElement(location, qgi, d)
{
QString tooltip_string = QString(
tr("<u>\311l\351ment manquant\240:</u> %1")
).arg(location_.toString());
setToolTip(tooltip_string);
}
/**
Destructeur
*/
GhostElement::~GhostElement() {
}
/**
@param e L'element XML a analyser.
@param table_id_adr Reference vers la table de correspondance entre les IDs
du fichier XML et les adresses en memoire. Si l'import reussit, il faut y
ajouter les bons couples (id, adresse).
@return true si l'import a reussi, false sinon
*/
bool GhostElement::fromXml(QDomElement &e, QHash<int, Terminal *> &table_id_adr, bool handle_inputs_rotation) {
// instancie les bornes decrites dans l'element XML
terminalsFromXml(e, table_id_adr);
// instancie les champs de texte decrits dans l'element XML
foreach(QDomElement qde, QET::findInDomElement(e, "inputs", "input")) {
qde.setAttribute("size", 9); // arbitraire
if (ElementTextItem *new_input = CustomElement::parseInput(qde)) {
new_input -> fromXml(qde);
}
qde.removeAttribute("size");
}
/*
maintenant que l'element fantome connait toutes les bornes et tous les
champs de texte, on peut determiner une taille appropriee
*/
QRect final_bounding_rect = minimalBoundingRect().united(childrenBoundingRect()).toAlignedRect();
setSize(final_bounding_rect.width(), final_bounding_rect.height());
setHotspot(QPoint() - final_bounding_rect.topLeft());
setInternalConnections(true);
// on peut desormais confectionner le rendu de l'element
generateDrawings();
// position, selection
setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
// orientation
bool conv_ok;
int read_ori = e.attribute("orientation").toInt(&conv_ok);
if (!conv_ok || read_ori < 0 || read_ori > 3) read_ori = 0;
if (handle_inputs_rotation) {
rotateBy(90*read_ori);
} else {
applyRotation(90*read_ori);
}
return(true);
}
/**
@return le bounding rect minimum, utilise si l'element fantome n'a ni champ
de texte ni borne.
*/
QRectF GhostElement::minimalBoundingRect() const {
return(
QRectF(
QPointF(-10.0, -10.0),
QSizeF(20.0, 20.0)
)
);
}
/**
Gere l'import des bornes
@param e L'element XML a analyser.
@param table_id_adr Reference vers la table de correspondance entre les IDs
du fichier XML et les adresses en memoire. Si l'import reussit, il faut y
ajouter les bons couples (id, adresse).
@return true si l'import a reussi, false sinon
*/
bool GhostElement::terminalsFromXml(QDomElement &e, QHash<int, Terminal *> &table_id_adr) {
// instancie les bornes decrites dans l'element XML
foreach(QDomElement qde, QET::findInDomElement(e, "terminals", "terminal")) {
if (!Terminal::valideXml(qde)) continue;
// modifie certains attributs pour que l'analyse par la classe CustomElement reussisse
int previous_x_value = qde.attribute("x").toInt();
int previous_y_value = qde.attribute("y").toInt();
int previous_ori_value = qde.attribute("orientation").toInt();
qreal x_add = 0.0, y_add = 0.0;
if (previous_ori_value == QET::North) y_add = -Terminal::terminalSize;
else if (previous_ori_value == QET::East) x_add = Terminal::terminalSize;
else if (previous_ori_value == QET::South) y_add = Terminal::terminalSize;
else if (previous_ori_value == QET::West) x_add = -Terminal::terminalSize;
qde.setAttribute("x", previous_x_value + x_add);
qde.setAttribute("y", previous_y_value + y_add);
qde.setAttribute("orientation", QET::orientationToString(static_cast<QET::Orientation>(previous_ori_value)));
if (Terminal *new_terminal = CustomElement::parseTerminal(qde)) {
table_id_adr.insert(qde.attribute("id").toInt(), new_terminal);
}
// restaure les attributs modifies
qde.setAttribute("x", previous_x_value);
qde.setAttribute("y", previous_y_value);
qde.setAttribute("orientation", previous_ori_value);
}
return(true);
}
/**
Genere les rendus de l'element fantome : il s'agit d'un rectangle
representant grosso modo l'espace que devait prendre l'element initial.
En son centre est dessine un point d'interrogation. Une petite croix indique
le point de saisie de l'element.
*/
void GhostElement::generateDrawings() {
// style de dessin
QPen t(QBrush(Qt::black), 1.0);
// rendu normal
QPainter qp;
qp.begin(&drawing);
qp.setPen(t);
qp.setRenderHint(QPainter::Antialiasing, false);
generateDrawing(&qp);
qp.end();
// rendu low_zoom
QPainter low_zoom_qp;
low_zoom_qp.begin(&low_zoom_drawing);
t.setCosmetic(true);
low_zoom_qp.setRenderHint(QPainter::Antialiasing, false);
low_zoom_qp.setPen(t);
generateDrawing(&low_zoom_qp);
low_zoom_qp.end();
}
/**
Genere un rendu de l'element fantome
@see generateDrawings
*/
void GhostElement::generateDrawing(QPainter *painter) {
// une petite croix indique le point de saisie de l'element
painter -> drawLine(QLineF(-1.0, 0.0, 1.0, 0.0));
painter -> drawLine(QLineF(0.0, -1.0, 0.0, 1.0));
// rectangle avec un point d'interrogation au centre
QRectF drawn_rect = boundingRect().adjusted(4.0, 4.0, -4.0, -4.0);
painter -> drawRect(drawn_rect);
painter -> drawText(drawn_rect, Qt::AlignHCenter | Qt::AlignVCenter, "?");
}

View File

@@ -0,0 +1,53 @@
/*
Copyright 2006-2012 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 GHOST_ELEMENT_H
#define GHOST_ELEMENT_H
#include "customelement.h"
class Diagram;
class QGraphicsItem;
class ElementsLocation;
class Terminal;
/**
The GhostElement class inherits CustomElement. A GhostElement aims at
visually replacing a CustomElement whose definition could not be loaded.
This way, instead of not loading an element, thus potentially losing its
position, its orientation, its child text items and conductors, one can
substitute a GhostElement. The GhostElement will extrapolate the position
of terminals and text items from the rest of the diagram. It is visually
rendered using a simple rectangle.
*/
class GhostElement : public CustomElement {
Q_OBJECT
// constructor, destructor
public:
GhostElement(const ElementsLocation &, QGraphicsItem * = 0, Diagram * = 0);
virtual ~GhostElement();
// methods
public:
virtual bool fromXml(QDomElement &, QHash<int, Terminal *> &, bool = false);
protected:
QRectF minimalBoundingRect() const;
bool terminalsFromXml(QDomElement &, QHash<int, Terminal *> &);
void generateDrawings();
void generateDrawing(QPainter *);
};
#endif

View File

@@ -0,0 +1,140 @@
/*
Copyright 2006-2012 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 "independenttextitem.h"
#include "diagram.h"
/**
Constructeur
@param parent_diagram Le schema auquel est rattache le champ de texte
*/
IndependentTextItem::IndependentTextItem(Diagram *parent_diagram) :
DiagramTextItem(0, parent_diagram),
first_move_(true)
{
}
/**
Constructeur
@param text Le texte affiche par le champ de texte
@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),
first_move_(true)
{
}
/// Destructeur
IndependentTextItem::~IndependentTextItem() {
}
/**
Permet de lire le texte a mettre dans le champ a partir d'un element XML.
Cette methode se base sur la position du champ pour assigner ou non la
valeur a ce champ.
@param e L'element XML representant le champ de texte
*/
void IndependentTextItem::fromXml(const QDomElement &e) {
setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
setHtml(e.attribute("text"));
setRotationAngle(e.attribute("rotation").toDouble());
}
/**
@param document Le document XML a utiliser
@return L'element XML representant ce champ de texte
*/
QDomElement IndependentTextItem::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("text", toHtml());
if (rotationAngle()) {
result.setAttribute("rotation", QString("%1").arg(rotationAngle()));
}
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();
if (first_move_) {
mouse_to_origin_movement_ = old_pos - e -> buttonDownScenePos(Qt::LeftButton);
}
QPointF expected_pos = e-> scenePos() + mouse_to_origin_movement_;
setPos(expected_pos); // setPos() will snap the expected position to the grid
// 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

@@ -0,0 +1,58 @@
/*
Copyright 2006-2012 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 INDEPENDENT_TEXT_ITEM_H
#define INDEPENDENT_TEXT_ITEM_H
#include <QtGui>
#include "diagramtextitem.h"
/**
This class represents an independent text field on a particular diagram.
It may be moved, edited, and rotated.
*/
class IndependentTextItem : public DiagramTextItem {
Q_OBJECT
// constructors, destructor
public:
IndependentTextItem(Diagram * = 0);
IndependentTextItem(const QString &, Diagram* = 0);
virtual ~IndependentTextItem();
// attributes
public:
enum { Type = UserType + 1005 };
// methods
public:
/**
Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into an
IndependentTextItem.
@return le type de QGraphicsItem
*/
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_;
QPointF mouse_to_origin_movement_;
};
#endif

View File

@@ -0,0 +1,155 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qetgraphicsitem.h"
/**
* @brief QetGraphicsItem::QetGraphicsItem Default constructor
* @param uuid, uuid of the item
* @param diagram, diagram aka QGraphicsScene of the item
* @param parent, Parent Item
*/
QetGraphicsItem::QetGraphicsItem(QGraphicsItem *parent):
QGraphicsObject(parent),
first_move_(true)
{
}
QetGraphicsItem::~QetGraphicsItem()
{}
void QetGraphicsItem::editProperty(){}
/**
* @brief QetGraphicsItem::diagram
*return the diagram of this item
*/
Diagram* QetGraphicsItem::diagram() const{
return(qobject_cast<Diagram *>(scene()));
}
/**
* @brief QetGraphicsItem::setPos
*set the position of the item to p
* @param p the new position of item
*/
void QetGraphicsItem::setPos(const QPointF &p) {
if (p == pos()) return;
if (scene()) {
// arrondit l'abscisse a 10 px pres
int p_x = qRound(p.x() / (Diagram::xGrid * 1.0)) * Diagram::xGrid;
// arrondit l'ordonnee a 10 px pres
int p_y = qRound(p.y() / (Diagram::yGrid * 1.0)) * Diagram::yGrid;
QGraphicsItem::setPos(p_x, p_y);
} else QGraphicsItem::setPos(p);
}
/**
* @brief QetGraphicsItem::setPos
*set the position of the item
* @param x new abscisse of item
* @param y new ordonne of item
*/
void QetGraphicsItem::setPos(qreal x, qreal y) {
setPos(QPointF(x, y));
}
/**
Permet de tourner l'item de maniere relative.
L'angle added_rotation est ajoute a l'orientation actuelle du image.
@param added_rotation Angle a ajouter a la rotation actuelle
@see applyRotation
*/
void QetGraphicsItem::rotateBy(const qreal &added_rotation) {
qreal applied_added_rotation = QET::correctAngle(added_rotation + rotation());
applyRotation(applied_added_rotation);
}
/**
Effectue la rotation de l'item en lui même
Cette methode peut toutefois etre redefinie dans des classes filles
@param angle Angle de la rotation a effectuer
*/
void QetGraphicsItem::applyRotation(const qreal &angle) {
setRotation(QET::correctAngle(angle));
}
/**
* @brief QetGraphicsItem::mousePressEvent
*handle the mouse click
* @param e
*/
void QetGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
first_move_ = true;
if (e -> modifiers() & Qt::ControlModifier) {
setSelected(!isSelected());
}
QGraphicsItem::mousePressEvent(e);
}
/**
* @brief QetGraphicsItem::mouseDoubleClickEvent
*handle the mouse double click
* @param e
*/
void QetGraphicsItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e) {
Q_UNUSED (e);
editProperty();
}
/**
* @brief QetGraphicsItem::mouseMoveEvent
*handle mouse movement
* @param e
*/
void QetGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
if (isSelected() && e -> buttons() & Qt::LeftButton) {
//Item is moving
if(diagram() && first_move_) {
//It's the first movement, we signal it to parent diagram
diagram() -> beginMoveElements(this);
}
//we apply the mouse movement
QPointF old_pos = pos();
if (first_move_) {
mouse_to_origin_movement_ = old_pos - e -> buttonDownScenePos(Qt::LeftButton);
}
QPointF expected_pos = e -> scenePos() + mouse_to_origin_movement_;
setPos(expected_pos); // setPos() will snap the expected position to the grid
//we calcul the real movement apply by setPos()
QPointF effective_movement = pos() - old_pos;
if (diagram()) {
//we signal the real movement apply to diagram,
//who he apply to other selected item
diagram() -> continueMoveElements(effective_movement);
}
} else e -> ignore();
if (first_move_) first_move_ = false;
}
/**
* @brief QetGraphicsItem::mouseReleaseEvent
*handle mouse release click
* @param e
*/
void QetGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
if (diagram()) diagram() -> endMoveElements();
if (!(e -> modifiers() & Qt::ControlModifier)) QGraphicsItem::mouseReleaseEvent(e);
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2006-2013 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QETGRAPHICSITEM_H
#define QETGRAPHICSITEM_H
#include "diagram.h"
class QetGraphicsItem : public QGraphicsObject {
Q_OBJECT
public:
//constructor destructor
QetGraphicsItem(QGraphicsItem *parent = 0);
virtual ~QetGraphicsItem() = 0;
//abstarct methode
virtual void editProperty ();
//public methode
Diagram* diagram() const;
virtual void setPos(const QPointF &p);
virtual void setPos(qreal x, qreal y);
virtual void rotateBy(const qreal &);
virtual void applyRotation(const qreal &);
signals:
public slots:
//protected method
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *e);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *e);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *e);
protected:
bool first_move_;
QPointF mouse_to_origin_movement_;
};
#endif // QETGRAPHICSITEM_H