Import initial

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@1 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
xavierqet
2006-10-27 15:47:22 +00:00
commit 5cadf173c7
71 changed files with 4586 additions and 0 deletions

122
aboutqet.cpp Normal file
View File

@@ -0,0 +1,122 @@
#include "aboutqet.h"
/**
Constructeur
@param parent Le QWidget parent de la boite de dialogue
*/
AboutQET::AboutQET(QWidget *parent) : QDialog(parent) {
// Titre, taille, comportement...
setWindowTitle(tr("\300 propos de QElectrotech"));
setMinimumWidth(680);
setMinimumHeight(350);
setModal(true);
// Trois onglets
QTabWidget *onglets = new QTabWidget(this);
onglets -> addTab(ongletAPropos(), tr("\300 &propos"));
onglets -> addTab(ongletAuteurs(), tr("A&uteurs"));
onglets -> addTab(ongletLicence(), tr("&Accord de licence"));
// Un bouton pour fermer la boite de dialogue
QDialogButtonBox *boutons = new QDialogButtonBox(QDialogButtonBox::Close);
connect(boutons, SIGNAL(accepted()), this, SLOT(accept()));
connect(boutons, SIGNAL(rejected()), this, SLOT(accept()));
// Le tout dans une disposition verticale
QVBoxLayout *disposition = new QVBoxLayout();
disposition -> addWidget(titre());
disposition -> addWidget(onglets);
disposition -> addWidget(boutons);
setLayout(disposition);
}
/**
@return Le titre QElectroTech avec son icone
*/
QWidget *AboutQET::titre() {
QWidget *icone_et_titre = new QWidget();
// icone
QLabel *icone = new QLabel();
icone -> setPixmap(QIcon(":/ico/qelectrotech.png").pixmap(48, 48));
// label "QElectroTech"
QLabel *titre = new QLabel("<span style=\"font-weight:0;font-size:16pt;\">QElectroTech</span>");
titre -> setTextFormat(Qt::RichText);
// le tout dans une grille
QGridLayout *dispo_horiz = new QGridLayout();
dispo_horiz -> addWidget(icone, 0, 0);
dispo_horiz -> addWidget(titre, 0, 1);
dispo_horiz -> setColumnStretch(0, 1);
dispo_horiz -> setColumnStretch(1, 100);
icone_et_titre -> setLayout(dispo_horiz);
return(icone_et_titre);
}
/**
@return Le widget contenu par l'onglet « A propos »
*/
QWidget *AboutQET::ongletAPropos() {
QLabel *apropos = new QLabel(
tr(
"QElectroTech, une application de "
"r\351alisation de sch\351mas \351lectriques.\n\n\251 2006 Les "
"d\351veloppeurs de QElectroTech\n\nMerde on n'a pas de site web"
)
);
apropos -> setAlignment(Qt::AlignCenter);
return(apropos);
}
/**
@return Le widget contenu par l'onglet « Auteurs »
*/
QWidget *AboutQET::ongletAuteurs() {
QLabel *auteurs = new QLabel(
tr(
"Id\351e originale : Beno\356t Ansieau <benoit.ansieau@gmail.com>\n\n"
"Programmation : Xavier Guerrin <xavier.guerrin@gmail.com>"
)
);
auteurs -> setAlignment(Qt::AlignCenter);
return(auteurs);
}
/**
@return Le widget contenu par l'onglet « Accord de Licence »
*/
QWidget *AboutQET::ongletLicence() {
QWidget *licence = new QWidget();
// label
QLabel *titre_licence = new QLabel(tr("Ce programme est sous licence GNU/GPL."));
// Recuperation du texte de la GNU/GPL dans un fichier externe
QFile *fichier_gpl = new QFile("./gnugpl.txt");
QString txt_gpl;
// verifie que le fichier existe
if (!fichier_gpl -> exists()) {
txt_gpl = QString(tr("Le fichier texte contenant la licence GNU/GPL est introuvable - bon bah de toute fa\347on, vous la connaissez par coeur non ?"));
} else {
// ouvre le fichier en mode texte et en lecture seule
if (!fichier_gpl -> open(QIODevice::ReadOnly | QIODevice::Text)) {
txt_gpl = QString(tr("Le fichier texte contenant la licence GNU/GPL existe mais n'a pas pu \352tre ouvert - bon bah de toute fa\347on, vous la connaissez par coeur non ?"));
} else {
// charge le contenu du fichier dans une QString
QTextStream in(fichier_gpl);
txt_gpl = QString("");
while (!in.atEnd()) txt_gpl += in.readLine()+"\n";
// ferme le fichier
fichier_gpl -> close();
}
}
// texte de la GNU/GPL dans une zone de texte scrollable non editable
QTextEdit *texte_licence = new QTextEdit();
texte_licence -> setPlainText(txt_gpl);
texte_licence -> setReadOnly(true);
// le tout dans une disposition verticale
QVBoxLayout *dispo_licence = new QVBoxLayout();
dispo_licence -> addWidget(titre_licence);
dispo_licence -> addWidget(texte_licence);
licence -> setLayout(dispo_licence);
return(licence);
}

18
aboutqet.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef ABOUTQET_H
#define ABOUTQET_H
#include <QtGui>
/**
Cette classe represente la boite de dialogue
« A propos de QElectroTech »
*/
class AboutQET : public QDialog {
Q_OBJECT
public:
AboutQET(QWidget * = 0);
private:
QWidget *titre();
QWidget *ongletAPropos();
QWidget *ongletAuteurs();
QWidget *ongletLicence();
};
#endif

412
borne.cpp Normal file
View File

@@ -0,0 +1,412 @@
#include "borne.h"
#include "schema.h"
#include "element.h"
#include "conducteur.h"
/**
Fonction privee pour initialiser la borne.
@param pf position du point d'amarrage pour un conducteur
@param o orientation de la borne : Qt::Horizontal ou Qt::Vertical
*/
void Borne::initialise(QPointF pf, Borne::Orientation o) {
// definition du pount d'amarrage pour un conducteur
amarrage_conducteur = pf;
// definition de l'orientation de la borne (par defaut : sud)
if (o < Borne::Nord || o > Borne::Ouest) sens = Borne::Sud;
else sens = o;
// calcul de la position du point d'amarrage a l'element
amarrage_elmt = amarrage_conducteur;
switch(sens) {
case Borne::Nord : amarrage_elmt += QPointF(0, TAILLE_BORNE); break;
case Borne::Est : amarrage_elmt += QPointF(-TAILLE_BORNE, 0); break;
case Borne::Ouest : amarrage_elmt += QPointF(TAILLE_BORNE, 0); break;
case Borne::Sud :
default : amarrage_elmt += QPointF(0, -TAILLE_BORNE);
}
// par defaut : pas de conducteur
// QRectF null
br = new QRectF();
borne_precedente = NULL;
// divers
setAcceptsHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
hovered = false;
setToolTip("Borne");
couleur_neutre = QColor(Qt::blue);
couleur_autorise = QColor(Qt::darkGreen);
couleur_prudence = QColor("#ff8000");
couleur_interdit = QColor(Qt::red);
couleur_hovered = couleur_neutre;
}
/**
Constructeur par defaut
*/
Borne::Borne() : QGraphicsItem(0, 0) {
initialise(QPointF(0.0, 0.0), Borne::Sud);
schema_scene = 0;
}
/**
initialise une borne
@param pf position du point d'amarrage pour un conducteur
@param o orientation de la borne : Qt::Horizontal ou Qt::Vertical
@param e Element auquel cette borne appartient
@param s Scene sur laquelle figure cette borne
*/
Borne::Borne(QPointF pf, Borne::Orientation o, Element *e, Schema *s) : QGraphicsItem(e, s) {
initialise(pf, o);
schema_scene = s;
}
/**
initialise une borne
@param pf_x Abscisse du point d'amarrage pour un conducteur
@param pf_y Ordonnee du point d'amarrage pour un conducteur
@param o orientation de la borne : Qt::Horizontal ou Qt::Vertical
@param e Element auquel cette borne appartient
@param s Scene sur laquelle figure cette borne
*/
Borne::Borne(qreal pf_x, qreal pf_y, Borne::Orientation o, Element *e, Schema *s) : QGraphicsItem(e, s) {
initialise(QPointF(pf_x, pf_y), o);
}
/**
Destructeur
*/
Borne::~Borne() {
delete br;
}
/**
Permet de connaitre l'orientation de la borne. Si le parent de la borne
est bien un Element, cette fonction renvoie l'orientation par rapport a
la scene de la borne, en tenant compte du fait que l'element ait pu etre
pivote. Sinon elle renvoie son sens normal.
@return L'orientation actuelle de la Borne.
*/
Borne::Orientation Borne::orientation() const {
//true pour une orientation verticale, false pour une orientation horizontale
if (Element *elt = qgraphicsitem_cast<Element *>(parentItem())) {
if (elt -> orientation()) return(sens);
else {
Borne::Orientation retour;
switch(sens) {
case Borne::Nord : retour = Borne::Ouest; break;
case Borne::Est : retour = Borne::Nord; break;
case Borne::Ouest : retour = Borne::Sud; break;
case Borne::Sud :
default : retour = Borne::Est;
}
return(retour);
}
} else return(sens);
}
/**
Attribue un conducteur a la borne
@param f Le conducteur a rattacher a cette borne
*/
bool Borne::addConducteur(Conducteur *f) {
// pointeur 0 refuse
if (!f) return(false);
// une seule des deux bornes du conducteur doit etre this
Q_ASSERT_X((f -> borne1 == this ^ f -> borne2 == this), "Borne::addConducteur", "Le conducteur devrait etre relie exactement une fois a la borne en cours");
// determine l'autre borne a laquelle cette borne va etre relie grace au conducteur
Borne *autre_borne = (f -> borne1 == this) ? f -> borne2 : f -> borne1;
// verifie que la borne n'est pas deja reliee avec l'autre borne
bool deja_liees = false;
foreach (Conducteur* conducteur, liste_conducteurs) {
if (conducteur -> borne1 == autre_borne || conducteur -> borne2 == autre_borne) deja_liees = true;
}
// si les deux bornes sont deja reliees, on refuse d'ajouter le conducteur
if (deja_liees) return(false);
// sinon on ajoute le conducteur
liste_conducteurs.append(f);
return(true);
}
void Borne::removeConducteur(Conducteur *f) {
int index = liste_conducteurs.indexOf(f);
if (index == -1) return;
liste_conducteurs.removeAt(index);
}
/**
Fonction de dessin des bornes
@param p Le QPainter a utiliser
@param options Les options de dessin
@param widget Le widget sur lequel on dessine
*/
void Borne::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) {
p -> save();
//annulation des renderhints
p -> setRenderHint(QPainter::Antialiasing, false);
p -> setRenderHint(QPainter::TextAntialiasing, false);
p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
// on travaille avec les coordonnees de l'element parent
QPointF f = mapFromParent(amarrage_conducteur);
QPointF e = mapFromParent(amarrage_elmt);
QPen t;
t.setWidthF(1.0);
// dessin de la borne en rouge
t.setColor(Qt::red);
p -> setPen(t);
p -> drawLine(f, e);
// dessin du point d'amarrage au conducteur en bleu
t.setColor(couleur_hovered);
p -> setPen(t);
p -> setBrush(couleur_hovered);
if (hovered) p -> drawEllipse(((int)f.x())-2, ((int)f.y())-2, 5, 5);
else p -> drawPoint(f);
p -> restore();
}
/**
@return Le rectangle (en precision flottante) delimitant la borne et ses alentours.
*/
QRectF Borne::boundingRect() const {
if (br -> isNull()) {
qreal afx = amarrage_conducteur.x();
qreal afy = amarrage_conducteur.y();
qreal aex = amarrage_elmt.x();
qreal aey = amarrage_elmt.y();
QPointF origine;
origine = (afx <= aex && afy <= aey ? amarrage_conducteur : amarrage_elmt);
origine += QPointF(-3.0, -3.0);
qreal w = qAbs((int)(afx - aex)) + 7;
qreal h = qAbs((int)(afy - aey)) + 7;
*br = QRectF(origine, QSizeF(w, h));
}
return(*br);
}
/**
Gere l'entree de la souris sur la zone de la Borne.
*/
void Borne::hoverEnterEvent(QGraphicsSceneHoverEvent *) {
hovered = true;
update();
}
/**
Gere les mouvements de la souris sur la zone de la Borne.
*/
void Borne::hoverMoveEvent(QGraphicsSceneHoverEvent *) {
}
/**
Gere le fait que la souris sorte de la zone de la Borne.
*/
void Borne::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
hovered = false;
update();
}
/**
Gere le fait qu'on enfonce un bouton de la souris sur la Borne.
@param e L'evenement souris correspondant
*/
void Borne::mousePressEvent(QGraphicsSceneMouseEvent *e) {
if (Schema *s = qobject_cast<Schema *>(scene())) {
s -> setDepart(mapToScene(QPointF(amarrage_conducteur)));
s -> setArrivee(e -> scenePos());
s -> poseConducteur(true);
setCursor(Qt::CrossCursor);
}
//QGraphicsItem::mouseReleaseEvent(e);
}
/**
Gere le fait qu'on bouge la souris sur la Borne.
@param e L'evenement souris correspondant
*/
void Borne::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
// pendant la pose d'un conducteur, on adopte un autre curseur
setCursor(Qt::CrossCursor);
// d'un mouvement a l'autre, il faut retirer l'effet hover de la borne precedente
if (borne_precedente != NULL) {
if (borne_precedente == this) hovered = true;
else borne_precedente -> hovered = false;
borne_precedente -> couleur_hovered = borne_precedente -> couleur_neutre;
borne_precedente -> update();
}
// si la scene est un Schema, on actualise le poseur de conducteur
if (Schema *s = qobject_cast<Schema *>(scene())) s -> setArrivee(e -> scenePos());
// on recupere la liste des qgi sous le pointeur
QList<QGraphicsItem *> qgis = scene() -> items(e -> scenePos());
/* le qgi le plus haut
= le poseur de conducteur
= le premier element de la liste
= la liste ne peut etre vide
= on prend le deuxieme element de la liste
*/
Q_ASSERT_X(!(qgis.isEmpty()), "Borne::mouseMoveEvent", "La liste d'items ne devrait pas etre vide");
// s'il y a autre chose que le poseur de conducteur dans la liste
if (qgis.size() > 1) {
// on prend le deuxieme element de la liste
QGraphicsItem *qgi = qgis.at(1);
// si le qgi est une borne...
if (Borne *p = qgraphicsitem_cast<Borne *>(qgi)) {
// ...on lui applique l'effet hover approprie
if (p == this) {
// effet si l'on hover sur la borne de depart
couleur_hovered = couleur_interdit;
} else if (p -> parentItem() == parentItem()) {
// effet si l'on hover sur une borne du meme appareil
if (((Element *)parentItem()) -> connexionsInternesAcceptees())
p -> couleur_hovered = p -> couleur_autorise;
else p -> couleur_hovered = p -> couleur_interdit;
} else if (p -> nbConducteurs()) {
// si la borne a deja un conducteur
// verifie que cette borne n'est pas deja reliee a l'autre borne
bool deja_reliee = false;
foreach (Conducteur *f, liste_conducteurs) {
if (f -> borne1 == p || f -> borne2 == p) {
deja_reliee = true;
break;
}
}
// interdit si les bornes sont deja reliees, prudence sinon
p -> couleur_hovered = deja_reliee ? p -> couleur_interdit : p -> couleur_prudence;
} else {
// effet si on peut poser le conducteur
p -> couleur_hovered = p -> couleur_autorise;
}
borne_precedente = p;
p -> hovered = true;
p -> update();
}
}
}
/**
Gere le fait qu'on relache la souris sur la Borne.
@param e L'evenement souris correspondant
*/
void Borne::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
setCursor(Qt::ArrowCursor);
borne_precedente = NULL;
couleur_hovered = couleur_neutre;
// verifie que la scene est bien un Schema
if (Schema *s = qobject_cast<Schema *>(scene())) {
// on arrete de dessiner l'apercu du conducteur
s -> poseConducteur(false);
// on recupere l'element sous le pointeur lors du MouseReleaseEvent
QGraphicsItem *qgi = s -> itemAt(e -> scenePos());
// s'il n'y a rien, on arrete la
if (!qgi) return;
// idem si l'element obtenu n'est pas une borne
Borne *p = qgraphicsitem_cast<Borne *>(qgi);
if (!p) return;
// on remet la couleur de hover a sa valeur par defaut
p -> couleur_hovered = p -> couleur_neutre;
// idem s'il s'agit de la borne actuelle
if (p == this) return;
// idem s'il s'agit d'une borne de l'element actuel et que l'element n'a pas le droit de relier ses propres bornes
bool cia = ((Element *)parentItem()) -> connexionsInternesAcceptees();
if (!cia) foreach(QGraphicsItem *item, parentItem() -> children()) if (item == p) return;
// derniere verification : verifier que cette borne n'est pas deja reliee a l'autre borne
foreach (Conducteur *f, liste_conducteurs) if (f -> borne1 == p || f -> borne2 == p) return;
// autrement, on pose un conducteur
new Conducteur(this, (Borne *)qgi, 0, scene());
}
}
/**
Met a jour l'eventuel conducteur relie a la Borne.
*/
void Borne::updateConducteur() {
if (scene()) {
foreach (Conducteur *conducteur, liste_conducteurs) if (!conducteur -> isDestroyed()) conducteur -> update(QRectF()/*scene()->sceneRect()*/);
}
}
/**
@return La liste des conducteurs lies a cette borne
*/
QList<Conducteur *> Borne::conducteurs() const {
return(liste_conducteurs);
}
/**
Methode d'export en XML
@param doc Le Document XML a utiliser pour creer l'element XML
@return un QDomElement representant cette borne
*/
QDomElement Borne::toXml(QDomDocument &doc) {
QDomElement qdo = doc.createElement("borne");
qdo.setAttribute("x", amarrage_elmt.x());
qdo.setAttribute("y", amarrage_elmt.y());
qdo.setAttribute("orientation", sens);
return(qdo);
}
/**
Permet de savoir si un element XML represente une borne
@param e Le QDomElement a analyser
@return true si le QDomElement passe en parametre est une borne, false sinon
*/
bool Borne::valideXml(QDomElement &borne) {
// verifie le nom du tag
if (borne.tagName() != "borne") return(false);
// verifie la presence des attributs minimaux
if (!borne.hasAttribute("x")) return(false);
if (!borne.hasAttribute("y")) return(false);
if (!borne.hasAttribute("orientation")) return(false);
bool conv_ok;
// parse l'abscisse
borne.attribute("x").toDouble(&conv_ok);
if (!conv_ok) return(false);
// parse l'ordonnee
borne.attribute("y").toDouble(&conv_ok);
if (!conv_ok) return(false);
// parse l'id
borne.attribute("id").toInt(&conv_ok);
if (!conv_ok) return(false);
// parse l'orientation
int borne_or = borne.attribute("orientation").toInt(&conv_ok);
if (!conv_ok) return(false);
if (borne_or != Borne::Nord && borne_or != Borne::Sud && borne_or != Borne::Est && borne_or != Borne::Ouest) return(false);
// a ce stade, la borne est syntaxiquement correcte
return(true);
}
/**
Permet de savoir si un element XML represente cette borne. Attention, l'element XML n'est pas verifie
@param e Le QDomElement a analyser
@return true si la borne "se reconnait" (memes coordonnes, meme orientation), false sinon
*/
bool Borne::fromXml(QDomElement &borne) {
return (
borne.attribute("x").toDouble() == amarrage_elmt.x() &&\
borne.attribute("y").toDouble() == amarrage_elmt.y() &&\
borne.attribute("orientation").toInt() == sens
);
}

81
borne.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef BORNE_H
#define BORNE_H
#define TAILLE_BORNE 4
#include <QtGui>
#include <QtXml>
class Conducteur;
class Element;
class Schema;
/**
Classe modelisant la « borne » d'un appareil, c'est-a-dire un
branchement possible pour un Conducteur.
*/
class Borne : public QGraphicsItem {
public:
// enum definissant l'orientation de la borne
enum Orientation {Nord, Sud, Est, Ouest};
// permet de caster un QGraphicsItem en Borne avec qgraphicsitem_cast
enum { Type = UserType + 1002 };
virtual int type() const { return Type; }
// constructeurs
Borne();
Borne(QPointF, Borne::Orientation, Element * = 0, Schema * = 0);
Borne(qreal, qreal, Borne::Orientation, Element * = 0, Schema * = 0);
// destructeur
~Borne();
// implementation des methodes virtuelles pures de QGraphicsItem
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
QRectF boundingRect() const;
// methodes de manipulation des conducteurs lies a cette borne
bool addConducteur(Conducteur *);
void removeConducteur(Conducteur *);
inline int nbConducteurs() { return(liste_conducteurs.size()); }
// methodes de lecture
QList<Conducteur *> conducteurs() const;
Borne::Orientation orientation() const;
inline QPointF amarrageConducteur() const { return(mapToScene(amarrage_conducteur)); }
void updateConducteur();
// methodes relatives a l'import/export au format XML
static bool valideXml(QDomElement &);
bool fromXml (QDomElement &);
QDomElement toXml (QDomDocument &);
// methodes de gestion des evenements
void hoverEnterEvent (QGraphicsSceneHoverEvent *);
void hoverMoveEvent (QGraphicsSceneHoverEvent *);
void hoverLeaveEvent (QGraphicsSceneHoverEvent *);
void mousePressEvent (QGraphicsSceneMouseEvent *);
void mouseMoveEvent (QGraphicsSceneMouseEvent *);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
private:
// pointeur vers la QGraphicsScene de type Schema (evite quelques casts en interne)
Schema *schema_scene;
// coordonnees des points d'amarrage
QPointF amarrage_conducteur;
QPointF amarrage_elmt;
// orientation de la borne
Borne::Orientation sens;
// liste des conducteurs lies a cette borne
QList<Conducteur *> liste_conducteurs;
// pointeur vers un rectangle correspondant au bounding rect ; permet de ne calculer le bounding rect qu'une seule fois ; le pointeur c'est parce que le compilo exige une methode const
QRectF *br;
Borne *borne_precedente;
bool hovered;
// methode initialisant les differents membres de la borne
void initialise(QPointF, Borne::Orientation);
// differentes couleurs utilisables pour l'effet "hover"
QColor couleur_hovered;
QColor couleur_neutre;
QColor couleur_autorise;
QColor couleur_prudence;
QColor couleur_interdit;
};
#endif

198
conducteur.cpp Normal file
View File

@@ -0,0 +1,198 @@
#include <QtDebug>
#include "conducteur.h"
#include "element.h"
/**
Constructeur
@param p1 Premiere Borne auquel le conducteur est lie
@param p2 Seconde Borne auquel le conducteur est lie
@param parent Element parent du conducteur (0 par defaut)
@param scene QGraphicsScene auquelle appartient le conducteur
*/
Conducteur::Conducteur(Borne *p1, Borne* p2, Element *parent, QGraphicsScene *scene) : QGraphicsPathItem(parent, scene) {
// bornes que le conducteur relie
borne1 = p1;
borne2 = p2;
// ajout du conducteur a la liste de conducteurs de chacune des deux bornes
bool ajout_p1 = borne1 -> addConducteur(this);
bool ajout_p2 = borne2 -> addConducteur(this);
// en cas d'echec de l'ajout (conducteur deja existant notamment)
if (!ajout_p1 || !ajout_p2) return;
destroyed = false;
// le conducteur est represente par un trait fin
QPen t;
t.setWidthF(1.0);
setPen(t);
// calcul du rendu du conducteur
calculeConducteur();
}
/**
Met a jour la representation graphique du conducteur.
@param rect Rectangle a mettre a jour
*/
void Conducteur::update(const QRectF &rect = QRectF()) {
calculeConducteur();
QGraphicsPathItem::update(rect);
}
/**
Met a jour la representation graphique du conducteur.
@param x abscisse du rectangle a mettre a jour
@param y ordonnee du rectangle a mettre a jour
@param width longueur du rectangle a mettre a jour
@param height hauteur du rectangle a mettre a jour
*/
void Conducteur::update(qreal x, qreal y, qreal width, qreal height) {
calculeConducteur();
QGraphicsPathItem::update(x, y, width, height);
}
/**
Destructeur du Conducteur. Avant d'etre detruit, le conducteur se decroche des bornes
auxquelles il est lie.
*/
/*Conducteur::~Conducteur() {
}*/
/**
Met a jour le QPainterPath constituant le conducteur pour obtenir
un conducteur uniquement compose de droites reliant les deux bornes.
*/
void Conducteur::calculeConducteur() {
QPainterPath t;
QPointF p1 = borne1 -> amarrageConducteur();
QPointF p2 = borne2 -> amarrageConducteur();
QPointF depart, arrivee;
Borne::Orientation ori_depart, ori_arrivee;
// distingue le depart de l'arrivee : le trajet se fait toujours de gauche a droite
if (p1.x() <= p2.x()) {
depart = mapFromScene(p1);
arrivee = mapFromScene(p2);
ori_depart = borne1 -> orientation();
ori_arrivee = borne2 -> orientation();
} else {
depart = mapFromScene(p2);
arrivee = mapFromScene(p1);
ori_depart = borne2 -> orientation();
ori_arrivee = borne1 -> orientation();
}
// debut du trajet
t.moveTo(depart);
if (depart.y() < arrivee.y()) {
// trajet descendant
if ((ori_depart == Borne::Nord && (ori_arrivee == Borne::Sud || ori_arrivee == Borne::Ouest)) || (ori_depart == Borne::Est && ori_arrivee == Borne::Ouest)) {
// cas « 3 »
qreal ligne_inter_x = (depart.x() + arrivee.x()) / 2.0;
t.lineTo(ligne_inter_x, depart.y());
t.lineTo(ligne_inter_x, arrivee.y());
} else if ((ori_depart == Borne::Sud && (ori_arrivee == Borne::Nord || ori_arrivee == Borne::Est)) || (ori_depart == Borne::Ouest && ori_arrivee == Borne::Est)) {
// cas « 4 »
qreal ligne_inter_y = (depart.y() + arrivee.y()) / 2.0;
t.lineTo(depart.x(), ligne_inter_y);
t.lineTo(arrivee.x(), ligne_inter_y);
} else if ((ori_depart == Borne::Nord || ori_depart == Borne::Est) && (ori_arrivee == Borne::Nord || ori_arrivee == Borne::Est)) {
t.lineTo(arrivee.x(), depart.y()); // cas « 2 »
} else t.lineTo(depart.x(), arrivee.y()); // cas « 1 »
} else {
// trajet montant
if ((ori_depart == Borne::Ouest && (ori_arrivee == Borne::Est || ori_arrivee == Borne::Sud)) || (ori_depart == Borne::Nord && ori_arrivee == Borne::Sud)) {
// cas « 3 »
qreal ligne_inter_y = (depart.y() + arrivee.y()) / 2.0;
t.lineTo(depart.x(), ligne_inter_y);
t.lineTo(arrivee.x(), ligne_inter_y);
} else if ((ori_depart == Borne::Est && (ori_arrivee == Borne::Ouest || ori_arrivee == Borne::Nord)) || (ori_depart == Borne::Sud && ori_arrivee == Borne::Nord)) {
// cas « 4 »
qreal ligne_inter_x = (depart.x() + arrivee.x()) / 2.0;
t.lineTo(ligne_inter_x, depart.y());
t.lineTo(ligne_inter_x, arrivee.y());
} else if ((ori_depart == Borne::Ouest || ori_depart == Borne::Nord) && (ori_arrivee == Borne::Ouest || ori_arrivee == Borne::Nord)) {
t.lineTo(depart.x(), arrivee.y()); // cas « 2 »
} else t.lineTo(arrivee.x(), depart.y()); // cas « 1 »
}
// fin du trajet
t.lineTo(arrivee);
setPath(t);
}
/**
Dessine le conducteur sans antialiasing.
@param qp Le QPainter a utiliser pour dessiner le conducteur
@param qsogi Les options de style pour le conducteur
@param qw Le QWidget sur lequel on dessine
*/
void Conducteur::paint(QPainter *qp, const QStyleOptionGraphicsItem *qsogi, QWidget *qw) {
qp -> save();
qp -> setRenderHint(QPainter::Antialiasing, false);
qp -> setRenderHint(QPainter::TextAntialiasing, false);
qp -> setRenderHint(QPainter::SmoothPixmapTransform, false);
QGraphicsPathItem::paint(qp, qsogi, qw);
qp -> restore();
}
/**
Indique si deux orientations de Borne sont sur le meme axe (Vertical / Horizontal).
@param a La premiere orientation de Borne
@param b La seconde orientation de Borne
@return Un booleen a true si les deux orientations de bornes sont sur le meme axe
*/
bool Conducteur::surLeMemeAxe(Borne::Orientation a, Borne::Orientation b) {
if ((a == Borne::Nord || a == Borne::Sud) && (b == Borne::Nord || b == Borne::Sud)) return(true);
else if ((a == Borne::Est || a == Borne::Ouest) && (b == Borne::Est || b == Borne::Ouest)) return(true);
else return(false);
}
/**
Indique si une orientation de borne est horizontale (Est / Ouest).
@param a L'orientation de borne
@return True si l'orientation de borne est horizontale, false sinon
*/
bool Conducteur::estHorizontale(Borne::Orientation a) {
return(a == Borne::Est || a == Borne::Ouest);
}
/**
Indique si une orientation de borne est verticale (Nord / Sud).
@param a L'orientation de borne
@return True si l'orientation de borne est verticale, false sinon
*/
bool Conducteur::estVerticale(Borne::Orientation a) {
return(a == Borne::Nord || a == Borne::Sud);
}
/**
Methode de preparation a la destruction du conducteur ; le conducteur se detache de ses deux bornes
*/
void Conducteur::destroy() {
destroyed = true;
borne1 -> removeConducteur(this);
borne2 -> removeConducteur(this);
}
/**
Methode de validation d'element XML
@param e Un element XML sense represente un Conducteur
@return true si l'element XML represente bien un Conducteur ; false sinon
*/
bool Conducteur::valideXml(QDomElement &e){
// verifie le nom du tag
if (e.tagName() != "conducteur") return(false);
// verifie la presence des attributs minimaux
if (!e.hasAttribute("borne1")) return(false);
if (!e.hasAttribute("borne2")) return(false);
bool conv_ok;
// parse l'abscisse
e.attribute("borne1").toInt(&conv_ok);
if (!conv_ok) return(false);
// parse l'ordonnee
e.attribute("borne2").toInt(&conv_ok);
if (!conv_ok) return(false);
return(true);
}

36
conducteur.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef CONDUCTEUR_H
#define CONDUCTEUR_H
#include <QtGui>
#include "borne.h"
class Element;
/**
Cette classe represente un conducteur. Un conducteur relie deux bornes d'element.
*/
class Conducteur : public QGraphicsPathItem {
public:
enum { Type = UserType + 1001 };
virtual int type() const { return Type; }
Conducteur(Borne *, Borne *, Element * = 0, QGraphicsScene * = 0);
//virtual ~Conducteur();
void destroy();
bool isDestroyed() const { return(destroyed); }
void update(const QRectF & rect);
void update(qreal x, qreal y, qreal width, qreal height);
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
static bool valideXml(QDomElement &);
///Premiere borne a laquelle le fil est rattache
Borne *borne1;
///Deuxieme borne a laquelle le fil est rattache
Borne *borne2;
private:
/// booleen indiquant si le fil est encore valide
bool destroyed;
void calculeConducteur();
bool surLeMemeAxe(Borne::Orientation, Borne::Orientation);
bool estHorizontale(Borne::Orientation a);
bool estVerticale(Borne::Orientation a);
};
#endif

56
contacteur.cpp Normal file
View File

@@ -0,0 +1,56 @@
#include "contacteur.h"
/**
Constructeur
@param parent Le QObject parent de l'element.
@param scene La scene sur laquelle l'element est affiche
*/
Contacteur::Contacteur(QGraphicsItem *parent, Schema *scene) : ElementFixe(parent, scene) {
// taille et hotspot
setSize(15, 70);
setHotspot(QPoint(10, 5));
// ajout de deux bornes a l'element
new Borne(0, 0, Borne::Nord, this, scene);
new Borne(0, 60, Borne::Sud, this, scene);
}
/**
@return Le nombre actuel de bornes de l'element
*/
int Contacteur::nbBornes() const {
return(2);
}
/**
Fonction qui effectue le rendu graphique du contacteur
@param p Le QPainter a utiliser pour dessiner l'element
@param o Les options de dessin
*/
void Contacteur::paint(QPainter *p, const QStyleOptionGraphicsItem *) {
// traits de couleur noire
QPen t;
t.setColor(Qt::black);
t.setWidthF(1.0);
t.setJoinStyle(Qt::MiterJoin);
p -> setPen(t);
// une ligne eventuellement antialiasee
p -> drawLine(-5, 19, 0, 40);
// deux lignes JAMAIS antialiasees (annulation des renderhints)
p -> save();
p -> setRenderHint(QPainter::Antialiasing, false);
p -> setRenderHint(QPainter::TextAntialiasing, false);
p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
p -> drawLine(0, 0, 0, 20);
p -> drawLine(0, 40, 0, 60);
p -> restore();
}
/**
@return l'ID du type "Contacteur"
*/
QString Contacteur::typeId() {
return(QString("0"));
}

16
contacteur.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef CONTACTEUR_H
#define CONTACTEUR_H
#include "elementfixe.h"
/**
Cette classe herite de la classe Element Fixe pour definir un
contacteur
*/
class Contacteur : public ElementFixe {
public:
Contacteur(QGraphicsItem * = 0, Schema * = 0);
virtual int nbBornes() const;
void paint(QPainter *, const QStyleOptionGraphicsItem *);
QString typeId();
QString nom() { return("Contacteur"); }
};
#endif

63
del.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "del.h"
#include <QPen>
#include <QGraphicsTextItem>
/**
Constructeur
@param parent Le QObject parent de l'element.
@param scene La scene sur laquelle l'element est affiche
*/
DEL::DEL(QGraphicsItem *parent, Schema *scene) : ElementFixe(parent, scene) {
// taille et hotspot
setSize(30, 70);
setHotspot(QPoint(15, 5));
// ajout de deux bornes a l'element
new Borne(0, 0, Borne::Nord, this, scene);
new Borne(0, 60, Borne::Sud, this, scene);
peut_relier_ses_propres_bornes = true;
}
/**
@return Le nombre actuel de bornes de l'element
*/
int DEL::nbBornes() const {
return(2);
}
/**
Fonction qui effectue le rendu graphique de la DEL
@param p Le QPainter a utiliser pour dessiner l'element
@param o Les options de dessin
*/
void DEL::paint(QPainter *p, const QStyleOptionGraphicsItem *) {
// traits de couleur noire
QPen t;
t.setColor(Qt::black);
t.setWidthF(1.0);
p -> setPen(t);
// un cercle a fond blanc
p -> setBrush(QBrush(Qt::white, Qt::SolidPattern));
p -> drawEllipse(-10, 20, 20, 20);
p -> setBrush(Qt::NoBrush);
// deux lignes eventuellement antialiasees
p -> drawLine(-7, 23, 7, 37);
p -> drawLine( 7, 23, -7, 37);
// deux lignes JAMAIS antialiasees
p -> save();
p -> setRenderHint(QPainter::Antialiasing, false);
p -> setRenderHint(QPainter::TextAntialiasing, false);
p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
p -> drawLine(0, 0, 0, 20);
p -> drawLine(0, 40, 0, 60);
p -> restore();
}
/**
@return l'ID du type "DEL"
*/
QString DEL::typeId() {
return(QString("1"));
}

16
del.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef DEL_H
#define DEL_H
#include "elementfixe.h"
/**
Cette classe herite de la classe Element Fixe pour definir une
Diode ElectroLuminescente
*/
class DEL : public ElementFixe {
public:
DEL(QGraphicsItem * = 0, Schema * = 0);
virtual int nbBornes() const;
void paint(QPainter *, const QStyleOptionGraphicsItem *);
QString typeId();
QString nom() { return("DEL"); }
};
#endif

276
element.cpp Normal file
View File

@@ -0,0 +1,276 @@
#include "element.h"
#include "schema.h"
#include <QtDebug>
/*** Methodes publiques ***/
/**
Constructeur pour un element sans scene ni parent
*/
Element::Element(QGraphicsItem *parent, Schema *scene) : QGraphicsItem(parent, scene) {
sens = true;
peut_relier_ses_propres_bornes = false;
}
/**
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 *) {
// 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));
}
/**
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 hsx Abscisse du hotspot
@param hsy Ordonnee 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 = hs.x() > dimensions.width() ? dimensions.width() : hs.x();
int hsy = hs.y() > dimensions.height() ? dimensions.height() : hs.y();
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 (apercu.isNull()) updatePixmap(); // on genere la pixmap si ce n'est deja fait
return(apercu);
}
/**
@todo distinguer les bornes avec un cast dynamique
*/
QVariant Element::itemChange(GraphicsItemChange change, const QVariant &value) {
if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemSelectedChange) {
foreach(QGraphicsItem *qgi, children()) {
if (Borne *p = qgraphicsitem_cast<Borne *>(qgi)) p -> updateConducteur();
}
}
return(QGraphicsItem::itemChange(change, value));
}
/**
@return L'orientation en cours de l'element : true pour une orientation verticale, false pour une orientation horizontale
*/
bool Element::orientation() const {
return(sens);
}
/**
Inverse l'orientation de l'element
@return La nouvelle orientation : true pour une orientation verticale, false pour une orientation horizontale
*/
bool Element::invertOrientation() {
// inversion du sens
sens = !sens;
// on cache temporairement l'element pour eviter un bug graphique
hide();
// rotation en consequence et rafraichissement de l'element graphique
rotate(sens ? 90.0 : -90.0);
// on raffiche l'element, on le reselectionne et on le rafraichit
show();
select();
update();
return(sens);
}
/*** 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 *) {
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 qp 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 *) {
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(), 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
apercu = QPixmap(dimensions);
apercu.fill(QColor(255, 255, 255, 0));
// QPainter sur la pixmap, avec antialiasing
QPainter p(&apercu);
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);
}
/**
Change la position de l'element en veillant a ce que l'element
reste sur la grille du Schema auquel il appartient.
@param p Nouvelles coordonnees de l'element
*/
void Element::setPos(const QPointF &p) {
if (p == pos()) return;
// pas la peine de positionner sur la grille si l'element n'est pas sur un Schema
if (scene()) {
// arrondit l'abscisse a 10 px pres
int p_x = qRound(p.x() / 10.0) * 10;
// arrondit l'ordonnee a 10 px pres
int p_y = qRound(p.y() / 10.0) * 10;
QGraphicsItem::setPos(p_x, p_y);
} else QGraphicsItem::setPos(p);
// actualise les bornes / conducteurs
foreach(QGraphicsItem *qgi, children()) {
if (Borne *p = qgraphicsitem_cast<Borne *>(qgi)) p -> updateConducteur();
}
}
/**
Change la position de l'element en veillant a ce que l'element
reste sur la grille du Schema auquel il appartient.
@param x Nouvelle abscisse de l'element
@param y Nouvelle ordonnee de l'element
*/
void Element::setPos(qreal x, qreal y) {
setPos(QPointF(x, y));
}
/**
Gere les mouvements de souris lies a l'element, notamment
*/
void Element::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
/*&& (flags() & ItemIsMovable)*/ // on le sait qu'il est movable
if (e -> buttons() & Qt::LeftButton) {
QPointF oldPos = pos();
setPos(mapToParent(e->pos()) - matrix().map(e->buttonDownPos(Qt::LeftButton)));
QPointF diff = pos() - oldPos;
// Recupere la liste des elements selectionnes
QList<QGraphicsItem *> selectedItems;
if (scene()) {
selectedItems = scene() -> selectedItems();
} else if (QGraphicsItem *parent = parentItem()) {
while (parent && parent->isSelected()) selectedItems << parent;
}
// Deplace tous les elements selectionnes
foreach (QGraphicsItem *item, selectedItems) {
if (!item->parentItem() || !item->parentItem()->isSelected())
if (item != this) item->setPos(item->pos() + diff);
}
} else e -> ignore();
}
/**
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);
}

50
element.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef ELEMENT_H
#define ELEMENT_H
#include <QtGui>
#include "borne.h"
class Schema;
class Element : public QGraphicsItem {
public:
enum { Type = UserType + 1000 };
virtual int type() const { return Type; }
Element(QGraphicsItem * = 0, Schema * = 0);
virtual int nbBornes() const = 0;
virtual int nbBornesMin() const = 0;
virtual int nbBornesMax() const = 0;
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *) = 0;
virtual QString typeId() = 0;
virtual QString nom() = 0;
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
QRectF boundingRect() const;
QSize setSize(int, int);
QPoint setHotspot(QPoint);
QPoint hotspot() const;
void select();
void deselect();
QPixmap pixmap();
QVariant itemChange(GraphicsItemChange, const QVariant &);
bool orientation() const;
bool invertOrientation();
void setPos(const QPointF &);
void setPos(qreal, qreal);
bool connexionsInternesAcceptees() { return(peut_relier_ses_propres_bornes); }
static bool valideXml(QDomElement &);
virtual bool fromXml(QDomElement &, QHash<int, Borne *>&) = 0;
protected:
void drawAxes(QPainter *, const QStyleOptionGraphicsItem *);
void mouseMoveEvent(QGraphicsSceneMouseEvent *);
bool peut_relier_ses_propres_bornes;
private:
void drawSelection(QPainter *, const QStyleOptionGraphicsItem *);
void updatePixmap();
bool sens;
QSize dimensions;
QPoint hotspot_coord;
QPixmap apercu;
QMenu menu;
};
#endif

82
elementfixe.cpp Normal file
View File

@@ -0,0 +1,82 @@
#include "elementfixe.h"
/**
Constructeur
*/
ElementFixe::ElementFixe(QGraphicsItem *parent, Schema *scene) : Element(parent, scene) {
}
/**
@return Le nombre minimal de bornes que l'element peut avoir
*/
int ElementFixe::nbBornesMin() const {
return(nbBornes());
}
/**
@return Le nombre maximal de bornes que l'element peut avoir
*/
int ElementFixe::nbBornesMax() const {
return(nbBornes());
}
/**
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 ElementFixe::fromXml(QDomElement &e, QHash<int, Borne *> &table_id_adr) {
/*
les bornes vont maintenant etre recensees pour associer leurs id à leur adresse reelle
ce recensement servira lors de la mise en place des fils
*/
QList<QDomElement> liste_bornes;
// parcours des enfants de l'element
for (QDomNode enfant = e.firstChild() ; !enfant.isNull() ; enfant = enfant.nextSibling()) {
// on s'interesse a l'element XML "bornes"
QDomElement bornes = enfant.toElement();
if (bornes.isNull() || bornes.tagName() != "bornes") continue;
// parcours des enfants de l'element XML "bornes"
for (QDomNode node_borne = bornes.firstChild() ; !node_borne.isNull() ; node_borne = node_borne.nextSibling()) {
// on s'interesse a l'element XML "borne"
QDomElement borne = node_borne.toElement();
if (!borne.isNull() && Borne::valideXml(borne)) liste_bornes.append(borne);
}
}
QHash<int, Borne *> priv_id_adr;
int bornes_non_trouvees = 0;
foreach(QGraphicsItem *qgi, children()) {
if (Borne *p = qgraphicsitem_cast<Borne *>(qgi)) {
bool borne_trouvee = false;
foreach(QDomElement qde, liste_bornes) {
if (p -> fromXml(qde)) {
priv_id_adr.insert(qde.attribute("id").toInt(), p);
borne_trouvee = true;
break;
}
}
if (!borne_trouvee) ++ bornes_non_trouvees;
}
}
if (bornes_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));
}
}
return(true);
}

15
elementfixe.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef ELEMENTFIXE_H
#define ELEMENTFIXE_H
#include "element.h"
class ElementFixe : public Element {
public:
ElementFixe(QGraphicsItem * = 0, Schema * = 0);
int nbBornesMin() const;
int nbBornesMax() const;
virtual bool fromXml(QDomElement &, QHash<int, Borne *>&);
virtual int nbBornes() const = 0;
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *) = 0;
virtual QString typeId() = 0;
virtual QString nom() = 0;
};
#endif

196
elementperso.cpp Normal file
View File

@@ -0,0 +1,196 @@
#include "elementperso.h"
ElementPerso::ElementPerso(QString &nom_fichier, QGraphicsItem *qgi, Schema *s, int *etat) : ElementFixe(qgi, s) {
nomfichier = nom_fichier;
nb_bornes = 0;
// pessimisme inside : par defaut, ca foire
elmt_etat = -1;
// le fichier doit exister
QString chemin_elements = "elements/";
nomfichier = chemin_elements + nom_fichier;
if (!QFileInfo(nomfichier).exists()) {
if (etat != NULL) *etat = 1;
elmt_etat = 1;
return;
}
// le fichier doit etre lisible
QFile fichier(nomfichier);
if (!fichier.open(QIODevice::ReadOnly)) {
if (etat != NULL) *etat = 2;
elmt_etat = 2;
return;
}
// le fichier doit etre un document XML
QDomDocument document_xml;
if (!document_xml.setContent(&fichier)) {
if (etat != NULL) *etat = 3;
elmt_etat = 3;
return;
}
// la racine est supposee etre une definition d'element
QDomElement racine = document_xml.documentElement();
if (racine.tagName() != "definition" || racine.attribute("type") != "element") {
if (etat != NULL) *etat = 4;
elmt_etat = 4;
return;
}
// ces attributs doivent etre presents et valides
int w, h, hot_x, hot_y;
if (
racine.attribute("nom") == QString("") ||\
!attributeIsAnInteger(racine, QString("width"), &w) ||\
!attributeIsAnInteger(racine, QString("height"), &h) ||\
!attributeIsAnInteger(racine, QString("hotspot_x"), &hot_x) ||\
!attributeIsAnInteger(racine, QString("hotspot_y"), &hot_y)
) {
if (etat != NULL) *etat = 5;
elmt_etat = 5;
return;
}
// on peut d'ores et deja specifier le nom, la taille et le hotspot
priv_nom = racine.attribute("nom");
setSize(w, h);
setHotspot(QPoint(hot_x, hot_y));
// la definition est supposee avoir des enfants
if (racine.firstChild().isNull()) {
if (etat != NULL) *etat = 6;
elmt_etat = 6;
return;
}
// parcours des enfants de la definition
int nb_elements_parses = 0;
QPainter qp;
qp.begin(&dessin);
QPen t;
t.setColor(Qt::black);
t.setWidthF(1.0);
t.setJoinStyle(Qt::MiterJoin);
qp.setPen(t);
for (QDomNode node = racine.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
QDomElement elmts = node.toElement();
if(elmts.isNull()) continue;
if (parseElement(elmts, qp, s)) ++ nb_elements_parses;
else {
if (etat != NULL) *etat = 7;
elmt_etat = 7;
return;
}
}
qp.end();
// il doit y avoir au moins un element charge
if (!nb_elements_parses) {
if (etat != NULL) *etat = 8;
elmt_etat = 8;
return;
}
// fermeture du fichier
fichier.close();
if (etat != NULL) *etat = 0;
elmt_etat = 0;
}
int ElementPerso::nbBornes() const {
return(nb_bornes);
}
void ElementPerso::paint(QPainter *qp, const QStyleOptionGraphicsItem *) {
dessin.play(qp);
}
bool ElementPerso::parseElement(QDomElement &e, QPainter &qp, Schema *s) {
if (e.tagName() == "borne") return(parseBorne(e, s));
else if (e.tagName() == "ligne") return(parseLigne(e, qp));
else if (e.tagName() == "cercle") return(parseCercle(e, qp));
else if (e.tagName() == "polygone") return(parsePolygone(e, qp));
else return(true); // on n'est pas chiant, on ignore l'element inconnu
}
bool ElementPerso::parseLigne(QDomElement &e, QPainter &qp) {
// verifie la presence et la validite des attributs obligatoires
int x1, y1, x2, y2;
if (!attributeIsAnInteger(e, QString("x1"), &x1)) return(false);
if (!attributeIsAnInteger(e, QString("y1"), &y1)) return(false);
if (!attributeIsAnInteger(e, QString("x2"), &x2)) return(false);
if (!attributeIsAnInteger(e, QString("y2"), &y2)) return(false);
/// @todo : gerer l'antialiasing (mieux que ca !) et le type de trait
setQPainterAntiAliasing(&qp, e.attribute("antialias") == "true");
qp.drawLine(x1, y1, x2, y2);
return(true);
}
bool ElementPerso::parseCercle(QDomElement &e, QPainter &qp) {
// verifie la presence des attributs obligatoires
int cercle_x, cercle_y, cercle_r;
if (!attributeIsAnInteger(e, QString("x"), &cercle_x)) return(false);
if (!attributeIsAnInteger(e, QString("y"), &cercle_y)) return(false);
if (!attributeIsAnInteger(e, QString("rayon"), &cercle_r)) return(false);
/// @todo : gerer l'antialiasing (mieux que ca !) et le type de trait
setQPainterAntiAliasing(&qp, e.attribute("antialias") == "true");
qp.drawEllipse(cercle_x, cercle_y, cercle_r, cercle_r);
return(true);
}
bool ElementPerso::parsePolygone(QDomElement &e, QPainter &qp) {
int i = 1;
while(true) {
if (attributeIsAnInteger(e, QString("x%1").arg(i)) && attributeIsAnInteger(e, QString("y%1").arg(i))) ++ i;
else break;
}
if (i < 3) return(false);
QPointF points[i-1];
for (int j = 1 ; j < i ; ++ j) {
points[j-1] = QPointF(
e.attribute(QString("x%1").arg(j)).toDouble(),
e.attribute(QString("y%1").arg(j)).toDouble()
);
}
setQPainterAntiAliasing(&qp, e.attribute("antialias") == "true");
qp.drawPolygon(points, i-1);
return(true);
}
bool ElementPerso::parseBorne(QDomElement &e, Schema *s) {
// verifie la presence et la validite des attributs obligatoires
int bornex, borney;
Borne::Orientation borneo;
if (!attributeIsAnInteger(e, QString("x"), &bornex)) return(false);
if (!attributeIsAnInteger(e, QString("y"), &borney)) return(false);
if (!e.hasAttribute("orientation")) return(false);
if (e.attribute("orientation") == "n") borneo = Borne::Nord;
else if (e.attribute("orientation") == "s") borneo = Borne::Sud;
else if (e.attribute("orientation") == "e") borneo = Borne::Est;
else if (e.attribute("orientation") == "o") borneo = Borne::Ouest;
else return(false);
new Borne(bornex, borney, borneo, this, s);
++ nb_bornes;
return(true);
}
void ElementPerso::setQPainterAntiAliasing(QPainter *qp, bool aa) {
qp -> setRenderHint(QPainter::Antialiasing, aa);
qp -> setRenderHint(QPainter::TextAntialiasing, aa);
qp -> setRenderHint(QPainter::SmoothPixmapTransform, aa);
}
int ElementPerso::attributeIsAnInteger(QDomElement &e, QString nom_attribut, int *entier) {
// verifie la presence de l'attribut
if (!e.hasAttribute(nom_attribut)) return(false);
// verifie la validite de l'attribut
bool ok;
int tmp = e.attribute(nom_attribut).toInt(&ok);
if (!ok) return(false);
if (entier != NULL) *entier = tmp;
return(true);
}

30
elementperso.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef ELEMENTPERSO_H
#define ELEMENTPERSO_H
#include "elementfixe.h"
#include <QtGui>
class ElementPerso : public ElementFixe {
public:
ElementPerso(QString &, QGraphicsItem * = 0, Schema * = 0, int * = NULL);
virtual int nbBornes() const;
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *);
QString typeId() { return(nomfichier); }
QString fichier() { return(nomfichier); }
bool isNull() { return(elmt_etat != 0); }
int etat() { return(elmt_etat); }
QString nom() { return(priv_nom); }
private:
int elmt_etat; // contient le code d'erreur si l'instanciation a echoue ou 0 si l'instanciation s'est bien passe
QString priv_nom;
QString nomfichier;
QPicture dessin;
bool parseElement(QDomElement &, QPainter &, Schema *);
bool parseLigne(QDomElement &, QPainter &);
bool parseCercle(QDomElement &, QPainter &);
bool parsePolygone(QDomElement &, QPainter &);
bool parseBorne(QDomElement &, Schema *);
void setQPainterAntiAliasing(QPainter *, bool);
int attributeIsAnInteger(QDomElement &, QString, int * = NULL);
int nb_bornes;
};
#endif

8
elements/contacteur.elmt Normal file
View File

@@ -0,0 +1,8 @@
<!-- orientation : 4 lettres = nord/sud/est/ouest avec d = default, y = yes et n = no -->
<definition type="element" nom="contacteur" width="15" height="70" hotspot_x="10" hotspot_y="5" orientation="dnny">
<ligne x1="-5" y1="19" x2="0" y2="40" antialias="true" style="normal" />
<ligne x1="0" y1="0" x2="0" y2="20" antialias="false" style="normal" />
<ligne x1="0" y1="40" x2="0" y2="60" antialias="false" style="normal" />
<borne orientation="n" x="0" y="0" />
<borne orientation="s" x="0" y="60" />
</definition>

9
elements/del.elmt Normal file
View File

@@ -0,0 +1,9 @@
<definition type="element" nom="del" width="30" height="70" hotspot_x="15" hotspot_y="5" orientation="dnny">
<cercle x="-10" y="20" rayon="20" antialias="true" style="normal" />
<ligne x1="-7" y1="23" x2="7" y2="37" antialias="true" style="normal" />
<ligne x1="7" y1="23" x2="-7" y2="37" antialias="true" style="normal" />
<ligne x1="0" y1="0" x2="0" y2="20" antialias="false" style="normal" />
<ligne x1="0" y1="40" x2="0" y2="60" antialias="false" style="normal" />
<borne orientation="n" x="0" y="0" />
<borne orientation="s" x="0" y="60" />
</definition>

6
elements/entree.elmt Normal file
View File

@@ -0,0 +1,6 @@
<definition type="element" nom="entree" width="20" height="40" hotspot_x="10" hotspot_y="15" orientation="dnny">
<!-- note : 7.5 a la place de 8 -->
<polygone x1="-8" y1="-13" x2="8" y2="-13" x3="0" y3="0" antialias="true" style="normal" />
<ligne x1="0" y1="0" x2="0" y2="13" antialias="false" style="normal" />
<borne orientation="s" x="0" y="15" />
</definition>

62
entree.cpp Normal file
View File

@@ -0,0 +1,62 @@
#include "entree.h"
/**
Constructeur
@param parent Le QObject parent de l'element.
@param scene La scene sur laquelle l'element est affiche
*/
Entree::Entree(QGraphicsItem *parent, Schema *scene) : ElementFixe(parent, scene) {
// taille et hotspot
setSize(20, 40);
setHotspot(QPoint(10, 15));
// ajout d'une borne a l'element
new Borne(0, 15, Borne::Sud, this, scene);
}
/**
@return Le nombre actuel de bornes de l'element
*/
int Entree::nbBornes() const {
return(1);
}
/**
Fonction qui effectue le rendu graphique du contacteur
@param p Le QPainter a utiliser pour dessiner l'element
@param o Les options de dessin
*/
void Entree::paint(QPainter *p, const QStyleOptionGraphicsItem *) {
// traits de couleur noire
QPen t;
t.setColor(Qt::black);
t.setWidthF(1.0);
t.setJoinStyle(Qt::MiterJoin);
p -> setPen(t);
p -> setBrush(Qt::black);
// Dessin du triangle
static const QPointF points[3] = {
QPointF(-7.5, -13),
QPointF( 7.5, -13),
QPointF( 0.0, 0.0)
};
p -> drawPolygon(points, 3);
p -> setBrush(Qt::NoBrush);
// une ligne JAMAIS antialiasee (annulation des renderhints)
p -> save();
p -> setRenderHint(QPainter::Antialiasing, false);
p -> setRenderHint(QPainter::TextAntialiasing, false);
p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
p -> drawLine(0, 0, 0, 13);
p -> restore();
}
/**
@return l'ID du type "Contacteur"
*/
QString Entree::typeId() {
return(QString("2"));
}

16
entree.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef ENTREE_H
#define ENTREE_H
#include "elementfixe.h"
/**
Cette classe herite de la classe Element Fixe pour definir une
entree.
*/
class Entree : public ElementFixe{
public:
Entree(QGraphicsItem * = 0, Schema * = 0);
virtual int nbBornes() const;
void paint(QPainter *, const QStyleOptionGraphicsItem *);
QString typeId();
QString nom() { return("Entr\351e"); }
};
#endif

342
gnugpl.txt Normal file
View File

@@ -0,0 +1,342 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

BIN
ico/button_cancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

BIN
ico/button_ok.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

BIN
ico/configure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
ico/copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

BIN
ico/cut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

BIN
ico/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

BIN
ico/editdelete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

BIN
ico/entrer_fs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

BIN
ico/exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

BIN
ico/export.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

BIN
ico/fileclose.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
ico/import.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

BIN
ico/masquer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

BIN
ico/move.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

BIN
ico/new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

BIN
ico/open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
ico/paste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

BIN
ico/pivoter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

BIN
ico/print.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

BIN
ico/qelectrotech.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

13
ico/qelectrotech.svg Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256" height="256" stroke="black" stroke-width="5">
<defs>
<mask id="Masque">
<circle cx="128" cy="128" r="50" fill="white" stroke-width="1" />
</mask>
</defs>
<path d="M128,0 128,256" fill="none" />
<circle cx="128" cy="128" r="50" fill="white" />
<path d="M0,0 256,256" fill="none" mask="url(#Masque)" />
<path d="M256,0 0,256" fill="none" mask="url(#Masque)" />
<text x="5" y="140" stroke-width="1" style="font-size:33px;font-weight:100;">QET</text>
</svg>

After

Width:  |  Height:  |  Size: 541 B

BIN
ico/qet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

12
ico/qet.svg Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256" height="256" stroke="black" stroke-width="10">
<defs>
<mask id="Masque">
<circle cx="128" cy="128" r="100" fill="white" stroke-width="1" />
</mask>
</defs>
<path d="M128,0 128,256" fill="none" />
<circle cx="128" cy="128" r="100" fill="white" />
<path d="M0,0 256,256" fill="none" mask="url(#Masque)" />
<path d="M256,0 0,256" fill="none" mask="url(#Masque)" />
</svg>

After

Width:  |  Height:  |  Size: 454 B

BIN
ico/qt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
ico/redo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

BIN
ico/restaurer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

BIN
ico/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

BIN
ico/saveas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
ico/select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

BIN
ico/sortir_fs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

BIN
ico/toolbars.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
ico/undo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

BIN
ico/viewmag+.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

BIN
ico/viewmag-.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

BIN
ico/viewmag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

BIN
ico/viewmagfit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

21
main.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include <QApplication>
#include <QTranslator>
#include "qetapp.h"
/**
Fonction principale du programme QElectroTech
@param argc nombre de parametres
@param argv parametres
*/
int main(int argc, char **argv) {
// Creation de l'application
QApplication app(argc, argv);
// Traducteur
QTranslator trad;
//trad.load("qet_en");
app.installTranslator(&trad);
// Creation et affichage du QETApp : QElectroTechApplication
(new QETApp()) -> show();
// Execution de l'application
return(app.exec());
}

111
panelappareils.cpp Normal file
View File

@@ -0,0 +1,111 @@
#include "panelappareils.h"
#include "contacteur.h"
#include "del.h"
#include "entree.h"
#include "elementperso.h"
/**
Constructeur
@param parent Le QWidget parent du panel d'appareils
@todo : definir une classe heritant de QListWidgetItem et automatiser tout ca
*/
PanelAppareils::PanelAppareils(QWidget *parent) : QListWidget(parent) {
// selection unique
setSelectionMode(QAbstractItemView::SingleSelection);
// drag'n drop autorise
setDragEnabled(true);
setAcceptDrops(false);
setDropIndicatorShown(false);
// style, mouvement et taille des elements
setIconSize(QSize(50, 50));
setMovement(QListView::Free);
setViewMode(QListView::ListMode);
// donnees
/*Element *del = new DEL(0,0);
Element *contacteur = new Contacteur(0,0);
Element *entree = new Entree(0, 0);*/
QListWidgetItem *qlwi;
QString whats_this = tr("Ceci est un \351l\351ment que vous pouvez ins\351rer dans votre sch\351ma par cliquer-d\351placer");
QString tool_tip = tr("Cliquer-d\351posez cet \351l\351ment sur le sch\351ma pour ins\351rer un \351l\351ment ");
// remplissage de la liste
QDir dossier_elements("elements/");
QStringList filtres;
filtres << "*.elmt";
QStringList fichiers = dossier_elements.entryList(filtres, QDir::Files, QDir::Name);
foreach(QString fichier, fichiers) {
int etat;
ElementPerso *elmt_perso = new ElementPerso(fichier, 0, 0, &etat);
if (etat != 0) {
qDebug() << "Le chargement du composant" << fichier << "a echoue avec le code d'erreur" << etat;
continue;
}
qlwi = new QListWidgetItem(QIcon(elmt_perso -> pixmap()), elmt_perso -> nom(), this);
qlwi -> setStatusTip(tool_tip + "\253 " + elmt_perso -> nom() + " \273");
qlwi -> setToolTip(elmt_perso -> nom());
qlwi -> setWhatsThis(whats_this);
qlwi -> setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled);
qlwi -> setData(42, fichier);
}
// force du noir sur une alternance de blanc (comme le schema) et de bleu clair
QPalette qp = palette();
setAlternatingRowColors(true);
qp.setColor(QPalette::Text, Qt::black);
qp.setColor(QPalette::Base, Qt::white);
//qp.setColor(QPalette::AlternateBase, QColor(240, 255, 255));
setPalette(qp);
}
/**
Gere le mouvement lors d'un drag'n drop
*/
void PanelAppareils::dragMoveEvent(QDragMoveEvent */*e*/) {
}
/**
Gere le depot lors d'un drag'n drop
*/
void PanelAppareils::dropEvent(QDropEvent */*e*/) {
}
/**
Gere le debut des drag'n drop
@param supportedActions Les actions supportees
@todo virer les lignes type «if ("tel appareil") construire TelAppareil» => trouver un moyen d'automatiser ca
*/
void PanelAppareils::startDrag(Qt::DropActions /*supportedActions*/) {
// objet QDrag pour realiser le drag'n drop
QDrag *drag = new QDrag(this);
// donnees qui seront transmises par le drag'n drop
QMimeData *mimeData = new QMimeData();
// appareil temporaire pour fournir un apercu
Element *appar;
int etat;
QString nom_fichier = currentItem() -> data(42).toString();
appar = new ElementPerso(nom_fichier, 0, 0, &etat);
if (etat != 0) {
delete appar;
return;
}
mimeData -> setText(nom_fichier);
drag -> setMimeData(mimeData);
// accrochage d'une pixmap representant l'appareil au pointeur
drag -> setPixmap(appar -> pixmap());
drag -> setHotSpot(appar -> hotspot());
// realisation du drag'n drop
drag -> start(Qt::CopyAction);
// suppression de l'appareil temporaire
delete appar;
}

18
panelappareils.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef PANELAPPAREILS_H
#define PANELAPPAREILS_H
#include <QtGui>
/**
Cette classe represente le panel d'appareils (en tant qu'element
graphique) dans lequel l'utilisateur choisit les composants de
son choix et les depose sur le schema par drag'n drop.
*/
class PanelAppareils : public QListWidget {
Q_OBJECT
public:
PanelAppareils(QWidget * = 0);
public slots:
void dragMoveEvent(QDragMoveEvent *);
void dropEvent(QDropEvent *);
void startDrag(Qt::DropActions);
};
#endif

40
qelectrotech.pro Normal file
View File

@@ -0,0 +1,40 @@
######################################################################
# Automatically generated by qmake (2.01a) dim. oct. 8 23:57:43 2006
######################################################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
HEADERS += aboutqet.h \
borne.h \
conducteur.h \
contacteur.h \
del.h \
element.h \
elementfixe.h \
elementperso.h \
entree.h \
panelappareils.h \
qetapp.h \
schema.h \
schemavue.h
SOURCES += aboutqet.cpp \
borne.cpp \
conducteur.cpp \
contacteur.cpp \
del.cpp \
element.cpp \
elementfixe.cpp \
elementperso.cpp \
entree.cpp \
main.cpp \
panelappareils.cpp \
qetapp.cpp \
schema.cpp \
schemavue.cpp
RESOURCES += qelectrotech.qrc
TRANSLATIONS += qet_en.ts
QT += xml

38
qelectrotech.qrc Normal file
View File

@@ -0,0 +1,38 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>ico/qet.png</file>
<file>ico/qelectrotech.png</file>
<file>ico/button_cancel.png</file>
<file>ico/button_ok.png</file>
<file>ico/configure.png</file>
<file>ico/copy.png</file>
<file>ico/cut.png</file>
<file>ico/delete.png</file>
<file>ico/editdelete.png</file>
<file>ico/entrer_fs.png</file>
<file>ico/exit.png</file>
<file>ico/export.png</file>
<file>ico/fileclose.png</file>
<file>ico/import.png</file>
<file>ico/masquer.png</file>
<file>ico/move.png</file>
<file>ico/new.png</file>
<file>ico/open.png</file>
<file>ico/paste.png</file>
<file>ico/pivoter.png</file>
<file>ico/print.png</file>
<file>ico/qt.png</file>
<file>ico/redo.png</file>
<file>ico/restaurer.png</file>
<file>ico/saveas.png</file>
<file>ico/save.png</file>
<file>ico/select.png</file>
<file>ico/sortir_fs.png</file>
<file>ico/toolbars.png</file>
<file>ico/undo.png</file>
<file>ico/viewmagfit.png</file>
<file>ico/viewmag-.png</file>
<file>ico/viewmag.png</file>
<file>ico/viewmag+.png</file>
</qresource>
</RCC>

BIN
qet_en.qm Normal file

Binary file not shown.

500
qet_en.ts Normal file
View File

@@ -0,0 +1,500 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1" language="en">
<context>
<name>AboutQET</name>
<message>
<location filename="aboutqet.cpp" line="18"/>
<source>&#xc0; propos de QElectrotech</source>
<translation>About QElectroTech</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="25"/>
<source>&#xc0; &amp;propos</source>
<translation>&amp;About</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="26"/>
<source>A&amp;uteurs</source>
<translation>A&amp;uthors</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="27"/>
<source>&amp;Accord de licence</source>
<translation>&amp;License Agreement</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="73"/>
<source>QElectroTech, une application de r&#xe9;alisation de sch&#xe9;mas &#xe9;lectriques.
&#xa9; 2006 Les d&#xe9;veloppeurs de QElectroTech
Merde on n&apos;a pas de site web</source>
<translation>QElectroTech is a program to design electric schemas.
© 2006 QElectroTech Developers
Whoops, we have no website</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="87"/>
<source>Id&#xe9;e originale : Beno&#xee;t Ansieau &lt;benoit.ansieau@gmail.com&gt;
Programmation : Xavier Guerrin &lt;xavier.guerrin@gmail.com&gt;</source>
<translation>Original idea : Benoît Ansieau &lt;benoit.ansieau@gmail.com&gt;
Programming : Xavier Guerrin &lt;xavier.guerrin@gmail.com&gt;</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="98"/>
<source>Ce programme est sous licence GNU/GPL.</source>
<translation>This program is under the GNU/GPL license.</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="105"/>
<source>Le fichier texte contenant la licence GNU/GPL est introuvable - bon bah de toute fa&#xe7;on, vous la connaissez par c&amp;oelig;ur non ?</source>
<translation type="unfinished">The text file containing the GNU/GPL license could not be found - however, you know it by heart, don&apos;t you ?</translation>
</message>
<message>
<location filename="aboutqet.cpp" line="109"/>
<source>Le fichier texte contenant la licence GNU/GPL existe mais n&apos;a pas pu &#xea;tre ouvert - bon bah de toute fa&#xe7;on, vous la connaissez par c&amp;oelig;ur non ?</source>
<translation type="unfinished">The text file containing the GNU/GPL license exists but could not be opened - however, you know it by heart, don&apos;t you ?</translation>
</message>
</context>
<context>
<name>PanelAppareils</name>
<message>
<location filename="panelappareils.cpp" line="41"/>
<source>Ceci est un &#xe9;l&#xe9;ment que vous pouvez ins&#xe9;rer dans votre sch&#xe9;ma par cliquer-d&#xe9;placer</source>
<translation>This is a device you can drag&apos;n drop onto your plan</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="42"/>
<source>Cliquer-d&#xe9;posez cet &#xe9;l&#xe9;ment sur le sch&#xe9;ma pour ins&#xe9;rer </source>
<translation>Drag this device to the plan to insert </translation>
</message>
<message>
<location filename="panelappareils.cpp" line="93"/>
<source>Contacteur</source>
<translation>Contact</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="45"/>
<source>un contacteur</source>
<translation>a contact</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="94"/>
<source>Voyant DEL</source>
<translation>LED light</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="51"/>
<source>une DEL</source>
<translation>a LED light</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="52"/>
<source>Diode Electroluminescente</source>
<translation>Light-Emitting Diode</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="95"/>
<source>Entr&#xe9;e</source>
<translation>Input</translation>
</message>
<message>
<location filename="panelappareils.cpp" line="57"/>
<source>une entr&#xe9;e</source>
<translation>an input</translation>
</message>
</context>
<context>
<name>QETApp</name>
<message>
<location filename="qetapp.cpp" line="527"/>
<source>QElectroTech</source>
<translation>QElectroTech</translation>
</message>
<message>
<location filename="qetapp.cpp" line="29"/>
<source>QElectrotech : dimensions de la zone de dessin : </source>
<translation type="obsolete">QElectrotech : Size of the drawing area :</translation>
</message>
<message>
<location filename="qetapp.cpp" line="29"/>
<source>x</source>
<translation type="obsolete">x</translation>
</message>
<message>
<location filename="qetapp.cpp" line="190"/>
<source>&amp;Masquer</source>
<translation>&amp;Hide</translation>
</message>
<message>
<location filename="qetapp.cpp" line="162"/>
<source>&amp;Quitter</source>
<translation>&amp;Quit</translation>
</message>
<message>
<location filename="qetapp.cpp" line="191"/>
<source>&amp;Restaurer</source>
<translation>&amp;Show</translation>
</message>
<message>
<location filename="qetapp.cpp" line="269"/>
<source>&amp;Fichier</source>
<translation>&amp;File</translation>
</message>
<message>
<location filename="qetapp.cpp" line="270"/>
<source>&amp;&#xc9;dition</source>
<translation>&amp;Edit</translation>
</message>
<message>
<location filename="qetapp.cpp" line="271"/>
<source>Afficha&amp;ge</source>
<translation>Displ&amp;ay</translation>
</message>
<message>
<location filename="qetapp.cpp" line="272"/>
<source>O&amp;utils</source>
<translation>&amp;Tools</translation>
</message>
<message>
<location filename="qetapp.cpp" line="273"/>
<source>&amp;Configuration</source>
<translation>&amp;Settings</translation>
</message>
<message>
<location filename="qetapp.cpp" line="274"/>
<source>&amp;Aide</source>
<translation>&amp;Help</translation>
</message>
<message>
<location filename="qetapp.cpp" line="155"/>
<source>&amp;Nouveau</source>
<translation>&amp;New</translation>
</message>
<message>
<location filename="qetapp.cpp" line="156"/>
<source>&amp;Ouvrir</source>
<translation>&amp;Open</translation>
</message>
<message>
<location filename="qetapp.cpp" line="157"/>
<source>&amp;Enregistrer</source>
<translation>&amp;Save</translation>
</message>
<message>
<location filename="qetapp.cpp" line="411"/>
<source>Enregistrer sous</source>
<translation>Save as</translation>
</message>
<message>
<location filename="qetapp.cpp" line="159"/>
<source>&amp;Importer</source>
<translation>&amp;Import</translation>
</message>
<message>
<location filename="qetapp.cpp" line="201"/>
<source>Ctrl+Shift+I</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="160"/>
<source>E&amp;xporter</source>
<translation>&amp;Export</translation>
</message>
<message>
<location filename="qetapp.cpp" line="202"/>
<source>Ctrl+Shift+X</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="204"/>
<source>Ctrl+Q</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="164"/>
<source>Annu&amp;ler</source>
<translation>&amp;Undo</translation>
</message>
<message>
<location filename="qetapp.cpp" line="165"/>
<source>Re&amp;faire</source>
<translation>&amp;Redo</translation>
</message>
<message>
<location filename="qetapp.cpp" line="166"/>
<source>Co&amp;uper</source>
<translation>Cu&amp;t</translation>
</message>
<message>
<location filename="qetapp.cpp" line="167"/>
<source>Cop&amp;ier</source>
<translation>&amp;Copy</translation>
</message>
<message>
<location filename="qetapp.cpp" line="168"/>
<source>C&amp;oller</source>
<translation>&amp;Paste</translation>
</message>
<message>
<location filename="qetapp.cpp" line="169"/>
<source>Tout s&#xe9;lectionner</source>
<translation>Select All</translation>
</message>
<message>
<location filename="qetapp.cpp" line="170"/>
<source>D&#xe9;s&#xe9;lectionner tout</source>
<translation>Select none</translation>
</message>
<message>
<location filename="qetapp.cpp" line="212"/>
<source>Ctrl+Shift+A</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="171"/>
<source>Inverser la s&#xe9;lection</source>
<translation>Invert selection</translation>
</message>
<message>
<location filename="qetapp.cpp" line="213"/>
<source>Ctrl+I</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="185"/>
<source>Cacher &amp;la barre d&apos;outils</source>
<translation type="obsolete">Hide Too&amp;lbar</translation>
</message>
<message>
<location filename="qetapp.cpp" line="186"/>
<source>&amp;Mode plein &#xe9;cran</source>
<translation type="obsolete">&amp;Fullscreen Mode</translation>
</message>
<message>
<location filename="qetapp.cpp" line="223"/>
<source>Ctrl+Shift+F</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qetapp.cpp" line="188"/>
<source>Configurer les &amp;barres d&apos;outils</source>
<translation type="obsolete">Configure tool&amp;bars</translation>
</message>
<message>
<location filename="qetapp.cpp" line="185"/>
<source>&amp;Configurer QElectroTech</source>
<translation type="unfinished">&amp;Configure QElectroTech</translation>
</message>
<message>
<location filename="qetapp.cpp" line="187"/>
<source>&#xc0; &amp;propos de QElectroTech</source>
<translation>A&amp;bout QElectroTech</translation>
</message>
<message>
<location filename="qetapp.cpp" line="188"/>
<source>&#xc0; propos de &amp;Qt</source>
<translation>About &amp;Qt</translation>
</message>
<message>
<location filename="qetapp.cpp" line="320"/>
<source>D&#xe9;sactiver l&apos;&amp;antialiasing</source>
<translation>Render without &amp;Antialiasing</translation>
</message>
<message>
<location filename="qetapp.cpp" line="320"/>
<source>Activer l&apos;&amp;antialiasing</source>
<translation>Render with &amp;Antialiasing</translation>
</message>
<message>
<location filename="qetapp.cpp" line="23"/>
<source>QElectrotech</source>
<translation>QElectrotech</translation>
</message>
<message>
<location filename="qetapp.cpp" line="26"/>
<source>Panel d&apos;appareils</source>
<translation>Elements Panel</translation>
</message>
<message>
<location filename="qetapp.cpp" line="161"/>
<source>Imprimer</source>
<translation>Print</translation>
</message>
<message>
<location filename="qetapp.cpp" line="175"/>
<source>Zoom avant</source>
<translation>Zoom In</translation>
</message>
<message>
<location filename="qetapp.cpp" line="176"/>
<source>Zoom arri&#xe8;re</source>
<translation>Zoom Out</translation>
</message>
<message>
<location filename="qetapp.cpp" line="177"/>
<source>Zoom adapt&#xe9;</source>
<translation>Fit in view</translation>
</message>
<message>
<location filename="qetapp.cpp" line="178"/>
<source>Pas de zoom</source>
<translation>Reset zoom</translation>
</message>
<message>
<location filename="qetapp.cpp" line="180"/>
<source>Mode Selection</source>
<translation>Selection Mode</translation>
</message>
<message>
<location filename="qetapp.cpp" line="183"/>
<source>Passer en &amp;mode plein &#xe9;cran</source>
<translation>F&amp;ullScreen Screen Mode</translation>
</message>
<message>
<location filename="qetapp.cpp" line="184"/>
<source>Sortir du &amp;mode plein &#xe9;cran</source>
<translation>Exit F&amp;ullScreen Screen Mode</translation>
</message>
<message>
<location filename="qetapp.cpp" line="194"/>
<source>Reduire QElectroTech dans le systray</source>
<translation>Minimize QElectroTech to the sytray</translation>
</message>
<message>
<location filename="qetapp.cpp" line="195"/>
<source>Restaurer QElectroTech</source>
<translation>Restore QElectroTech</translation>
</message>
<message>
<location filename="qetapp.cpp" line="215"/>
<source>P</source>
<translation>P</translation>
</message>
<message>
<location filename="qetapp.cpp" line="219"/>
<source>Ctrl+9</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="220"/>
<source>Ctrl+0</source>
<translation></translation>
</message>
<message>
<location filename="qetapp.cpp" line="313"/>
<source>Afficher</source>
<translation>Display</translation>
</message>
<message>
<location filename="qetapp.cpp" line="344"/>
<source>Outils</source>
<translation>Tools</translation>
</message>
<message>
<location filename="qetapp.cpp" line="379"/>
<source>Exporter vers le fichier</source>
<translation>Export to file</translation>
</message>
<message>
<location filename="qetapp.cpp" line="379"/>
<source>Image PNG (*.png)</source>
<translation>PNG Picture (*.png)</translation>
</message>
<message>
<location filename="qetapp.cpp" line="414"/>
<source>Schema QelectroTech (*.qet)</source>
<translation>QElectroTech Schema (*.qet)</translation>
</message>
<message>
<location filename="qetapp.cpp" line="489"/>
<source>Erreur</source>
<translation>Error</translation>
</message>
<message>
<location filename="qetapp.cpp" line="437"/>
<source>Impossible d&apos;ecrire dans ce fichier</source>
<translation>Can&apos;t write to the file</translation>
</message>
<message>
<location filename="qetapp.cpp" line="475"/>
<source>Ouvrir un fichier</source>
<translation>Open a file</translation>
</message>
<message>
<location filename="qetapp.cpp" line="478"/>
<source>Schema QelectroTech (*.qet);;Fichiers XML (*.xml);;Tous les fichiers (*)</source>
<translation>QelectroTech Schema (*.qet);;XML Files (*.xml);;All Files (*)</translation>
</message>
<message>
<location filename="qetapp.cpp" line="484"/>
<source>Impossible de lire ce fichier</source>
<translation>Can&apos;t read that file</translation>
</message>
<message>
<location filename="qetapp.cpp" line="489"/>
<source>Ce fichier n&apos;est pas un document XML valide.</source>
<translation>This file is not a valid XML Document.</translation>
</message>
<message>
<location filename="qetapp.cpp" line="513"/>
<source>Enregistrer le sch&#xe9;ma en cours ?</source>
<translation>Save the current schema ?</translation>
</message>
<message>
<location filename="qetapp.cpp" line="514"/>
<source>Voulez-vous enregistrer le sch&#xe9;ma en cours ?</source>
<translation>Do you wish to save the current schema ?</translation>
</message>
<message>
<location filename="qetapp.cpp" line="172"/>
<source>Supprimer</source>
<translation>Delete</translation>
</message>
<message>
<location filename="qetapp.cpp" line="173"/>
<source>Pivoter</source>
<translation>Rotate</translation>
</message>
<message>
<location filename="qetapp.cpp" line="181"/>
<source>Mode Visualisation</source>
<translation>View Mode</translation>
</message>
</context>
<context>
<name>Schema</name>
<message>
<location filename="schema.cpp" line="124"/>
<source>Contacteur</source>
<translation type="obsolete">Contact</translation>
</message>
<message>
<location filename="schema.cpp" line="126"/>
<source>Voyant DEL</source>
<translation type="obsolete">LED light</translation>
</message>
</context>
<context>
<name>SchemaVue</name>
<message>
<location filename="schemavue.cpp" line="191"/>
<source>Contacteur</source>
<translation>Contact</translation>
</message>
<message>
<location filename="schemavue.cpp" line="192"/>
<source>Voyant DEL</source>
<translation>LED light</translation>
</message>
<message>
<location filename="schemavue.cpp" line="193"/>
<source>Entr&#xe9;e</source>
<translation>Input</translation>
</message>
</context>
</TS>

733
qetapp.cpp Normal file
View File

@@ -0,0 +1,733 @@
#include "qetapp.h"
#include "schemavue.h"
#include "schema.h"
#include "panelappareils.h"
#include "aboutqet.h"
/**
constructeur
@param parent le widget parent de la fenetre principale
*/
QETApp::QETApp(QWidget *parent) : QMainWindow(parent) {
// mise en place de l'interface MDI au centre de l'application
setCentralWidget(&workspace);
// mise en place du signalmapper
connect(&windowMapper, SIGNAL(mapped(QWidget *)), &workspace, SLOT(setActiveWindow(QWidget *)));
// recupere les arguments passes au programme
QStringList args = QCoreApplication::arguments();
// recupere les chemins de fichiers parmi les arguments
QStringList fichiers;
for (int i = 1 ; i < args.size() ; ++ i) {
if (QFileInfo(args.at(i)).exists()) fichiers << args.at(i);
}
// si des chemins de fichiers valides sont passes en arguments
QList<SchemaVue *> schema_vues;
if (fichiers.size()) {
// alors on ouvre ces fichiers
foreach(QString fichier, fichiers) {
SchemaVue *sv = new SchemaVue(this);
if (sv -> ouvrir(fichier)) schema_vues << sv;
else delete sv;
}
}
// si aucun schema n'a ete ouvert jusqu'a maintenant, on ouvre un nouveau schema
if (!schema_vues.size()) schema_vues << new SchemaVue(this);
// ajout de tous les SchemaVue necessaires
foreach (SchemaVue *sv, schema_vues) addSchemaVue(sv);
// titre de la fenetre
setWindowTitle(tr("QElectroTech"));
// icone de la fenetre
setWindowIcon(QIcon(":/ico/qet.png"));
// barre de statut de la fenetre
statusBar() -> showMessage(tr("QElectrotech"));
// ajout du panel d'Appareils en tant que QDockWidget
qdw_pa = new QDockWidget(tr("Panel d'appareils"), this);
qdw_pa -> setAllowedAreas(Qt::AllDockWidgetAreas);
qdw_pa -> setFeatures(QDockWidget::AllDockWidgetFeatures);
qdw_pa -> setMinimumWidth(160);
qdw_pa -> setWidget(pa = new PanelAppareils(qdw_pa));
addDockWidget(Qt::LeftDockWidgetArea, qdw_pa);
// mise en place des actions
actions();
// mise en place de la barre d'outils
toolbar();
// mise en place des menus
menus();
// systray de l'application
if (QSystemTrayIcon::isSystemTrayAvailable()) {
qsti = new QSystemTrayIcon(QIcon(":/ico/qet.png"), this);
qsti -> setToolTip(tr("QElectroTech"));
connect(qsti, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(systray(QSystemTrayIcon::ActivationReason)));
menu_systray = new QMenu(tr("QElectroTech"));
menu_systray -> addAction(masquer_appli);
menu_systray -> addAction(quitter_qet);
qsti -> setContextMenu(menu_systray);
qsti -> show();
}
// la fenetre est maximisee par defaut
setMinimumWidth(500);
setMinimumHeight(350);
setWindowState(Qt::WindowMaximized);
// connexions signaux / slots pour une interface sensee
connect(&workspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(slot_updateActions()));
connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slot_updateActions()));
}
/**
Gere les evenements relatifs au QSystemTrayIcon
@param raison un entier representant l'evenement survenu sur le systray
*/
void QETApp::systray(QSystemTrayIcon::ActivationReason raison) {
if (!QSystemTrayIcon::isSystemTrayAvailable()) return;
switch(raison) {
case QSystemTrayIcon::Context:
// affichage du menu
(qsti -> contextMenu()) -> show();
break;
case QSystemTrayIcon::DoubleClick:
case QSystemTrayIcon::Trigger:
// reduction ou restauration de l'application
if (isVisible()) systrayReduire(); else systrayRestaurer();
break;
case QSystemTrayIcon::Unknown:
default: // ne rien faire
break;
}
}
/**
Reduit l'application dans le systray
*/
void QETApp::systrayReduire() {
// on sauvegarde la position et les dimensions de l'application
wg = saveGeometry();
// on cache l'application
hide();
// on ajoute le menu "Restaurer" et on enlève le menu "Masquer"
menu_systray -> insertAction(masquer_appli, restaurer_appli);
menu_systray -> removeAction(masquer_appli);
}
/**
Restaure l'application reduite dans le systray
*/
void QETApp::systrayRestaurer() {
// on restaure la position et les dimensions de l'application
restoreGeometry(wg);
// on affiche l'application
show();
// on ajoute le menu "Masquer" et on enlève le menu "Restaurer"
menu_systray -> insertAction(restaurer_appli, masquer_appli);
menu_systray -> removeAction(restaurer_appli);
}
/**
Permet de quitter l'application lors de la fermeture de la fenetre principale
*/
void QETApp::closeEvent(QCloseEvent *) {
quitter();
}
/**
Gere la sortie de l'application
@todo gerer les eventuelles fermetures de fichiers
*/
void QETApp::quitter() {
if (!schemaEnCours()) qApp -> quit();
else {
bool peut_quitter = true;
foreach(QWidget *fenetre, workspace.windowList()) {
if (qobject_cast<SchemaVue *>(fenetre)) {
workspace.setActiveWindow(fenetre);
if (!fermer()) {
peut_quitter = false;
break;
}
}
}
if (peut_quitter) qApp -> quit();
}
}
/**
Fait passer la fenetre en mode plein ecran au mode normal et vice-versa
*/
void QETApp::toggleFullScreen() {
setWindowState(windowState() ^ Qt::WindowFullScreen);
}
/**
Active ou desactive l'antialiasing sur le rendu graphique du Schema
*/
void QETApp::toggleAntialiasing() {
SchemaVue *sv = schemaEnCours();
if (!sv) return;
sv -> setAntialiasing(!sv -> antialiased());
toggle_aa -> setText(sv -> antialiased() ? tr("D\351sactiver l'&antialiasing") : tr("Activer l'&antialiasing"));
}
/**
Dialogue « A propos de QElectroTech »
Le dialogue en question est cree lors du premier appel de cette fonction.
En consequence, sa premiere apparition n'est pas immediate. Par la suite,
le dialogue n'a pas a etre recree et il apparait instantanement. Il est
detruit en meme temps que son parent (ici, la QETApp).
*/
void QETApp::aPropos() {
static AboutQET *apqet = new AboutQET(this);
apqet -> exec();
}
/**
Mise en place des actions
*/
void QETApp::actions() {
// icones et labels
nouveau_fichier = new QAction(QIcon(":/ico/new.png"), tr("&Nouveau"), this);
ouvrir_fichier = new QAction(QIcon(":/ico/open.png"), tr("&Ouvrir"), this);
fermer_fichier = new QAction(QIcon(":/ico/fileclose.png"), tr("&Fermer"), this);
enr_fichier = new QAction(QIcon(":/ico/save.png"), tr("&Enregistrer"), this);
enr_fichier_sous = new QAction(QIcon(":/ico/saveas.png"), tr("Enregistrer sous"), this);
importer = new QAction(QIcon(":/ico/import.png"), tr("&Importer"), this);
exporter = new QAction(QIcon(":/ico/export.png"), tr("E&xporter"), this);
imprimer = new QAction(QIcon(":/ico/print.png"), tr("Imprimer"), this);
quitter_qet = new QAction(QIcon(":/ico/exit.png"), tr("&Quitter"), this);
annuler = new QAction(QIcon(":/ico/undo.png"), tr("Annu&ler"), this);
refaire = new QAction(QIcon(":/ico/redo.png"), tr("Re&faire"), this);
couper = new QAction(QIcon(":/ico/cut.png"), tr("Co&uper"), this);
copier = new QAction(QIcon(":/ico/copy.png"), tr("Cop&ier"), this);
coller = new QAction(QIcon(":/ico/paste.png"), tr("C&oller"), this);
sel_tout = new QAction( tr("Tout s\351lectionner"), this);
sel_rien = new QAction( tr("D\351s\351lectionner tout"), this);
sel_inverse = new QAction( tr("Inverser la s\351lection"), this);
supprimer = new QAction(QIcon(":/ico/delete.png"), tr("Supprimer"), this);
pivoter = new QAction(QIcon(":/ico/pivoter.png"), tr("Pivoter"), this);
toggle_aa = new QAction( tr("D\351sactiver l'&antialiasing"), this);
zoom_avant = new QAction(QIcon(":/ico/viewmag+.png"), tr("Zoom avant"), this);
zoom_arriere = new QAction(QIcon(":/ico/viewmag-.png"), tr("Zoom arri\350re"), this);
zoom_adapte = new QAction(QIcon(":/ico/viewmagfit.png"), tr("Zoom adapt\351"), this);
zoom_reset = new QAction(QIcon(":/ico/viewmag.png"), tr("Pas de zoom"), this);
mode_selection = new QAction(QIcon(":/ico/select.png"), tr("Mode Selection"), this);
mode_visualise = new QAction(QIcon(":/ico/move.png"), tr("Mode Visualisation"), this);
entrer_pe = new QAction(QIcon(":/ico/entrer_fs.png"), tr("Passer en &mode plein \351cran"), this);
sortir_pe = new QAction(QIcon(":/ico/sortir_fs.png"), tr("Sortir du &mode plein \351cran"), this);
configurer = new QAction(QIcon(":/ico/configure.png"), tr("&Configurer QElectroTech"), this);
f_mosaique = new QAction( tr("&Mosa\357que"), this);
f_cascade = new QAction( tr("&Cascade"), this);
f_reorganise = new QAction( tr("Arranger les fen\352tres réduites"),this);
f_suiv = new QAction( tr("Fen\352tre suivante"), this);
f_prec = new QAction( tr("Fen\352tre pr\351c\351dente"), this);
a_propos_de_qet = new QAction(QIcon(":/ico/qet.png"), tr("\300 &propos de QElectroTech"), this);
a_propos_de_qt = new QAction(QIcon(":/ico/qt.png"), tr("\300 propos de &Qt"), this);
masquer_appli = new QAction(QIcon(":/ico/masquer.png"), tr("&Masquer"), this);
restaurer_appli = new QAction(QIcon(":/ico/restaurer.png"), tr("&Restaurer"), this);
// info-bulles / indications dans la barre de statut
masquer_appli -> setToolTip(tr("Reduire QElectroTech dans le systray"));
restaurer_appli -> setToolTip(tr("Restaurer QElectroTech"));
// raccourcis clavier
nouveau_fichier -> setShortcut(QKeySequence::New);
ouvrir_fichier -> setShortcut(QKeySequence::Open);
fermer_fichier -> setShortcut(QKeySequence::Close);
enr_fichier -> setShortcut(QKeySequence::Save);
importer -> setShortcut(QKeySequence(tr("Ctrl+Shift+I")));
exporter -> setShortcut(QKeySequence(tr("Ctrl+Shift+X")));
imprimer -> setShortcut(QKeySequence(QKeySequence::Print));
quitter_qet -> setShortcut(QKeySequence(tr("Ctrl+Q")));
annuler -> setShortcut(QKeySequence::Undo);
refaire -> setShortcut(QKeySequence::Redo);
couper -> setShortcut(QKeySequence::Cut);
copier -> setShortcut(QKeySequence::Copy);
coller -> setShortcut(QKeySequence::Paste);
sel_tout -> setShortcut(QKeySequence::SelectAll);
sel_rien -> setShortcut(QKeySequence(tr("Ctrl+Shift+A")));
sel_inverse -> setShortcut(QKeySequence(tr("Ctrl+I")));
supprimer -> setShortcut(QKeySequence::Delete);
pivoter -> setShortcut(QKeySequence(tr("P")));
zoom_avant -> setShortcut(QKeySequence::ZoomIn);
zoom_arriere -> setShortcut(QKeySequence::ZoomOut);
zoom_adapte -> setShortcut(QKeySequence(tr("Ctrl+9")));
zoom_reset -> setShortcut(QKeySequence(tr("Ctrl+0")));
entrer_pe -> setShortcut(QKeySequence(tr("Ctrl+Shift+F")));
sortir_pe -> setShortcut(QKeySequence(tr("Ctrl+Shift+F")));
//
f_mosaique -> setStatusTip(tr("Dispose les fen\352tres en mosa\357que"));
f_cascade -> setStatusTip(tr("Dispose les fen\352tres en cascade"));
f_reorganise -> setStatusTip(tr("Aligne les fen\352tres réduites"));
f_suiv -> setStatusTip(tr("Active la fen\352tre suivante"));
f_prec -> setStatusTip(tr("Active la fen\352tre pr\351c\351dente"));
// traitements speciaux
mode_selection -> setCheckable(true);
mode_visualise -> setCheckable(true);
mode_selection -> setChecked(true);
QActionGroup *grp_visu_sel = new QActionGroup(this);
grp_visu_sel -> addAction(mode_selection);
grp_visu_sel -> addAction(mode_visualise);
grp_visu_sel -> setExclusive(true);
// connexion a des slots
connect(quitter_qet, SIGNAL(triggered()), this, SLOT(quitter()) );
connect(sel_tout, SIGNAL(triggered()), this, SLOT(slot_selectAll()) );
connect(sel_rien, SIGNAL(triggered()), this, SLOT(slot_selectNothing()) );
connect(sel_inverse, SIGNAL(triggered()), this, SLOT(slot_selectInvert()) );
connect(supprimer, SIGNAL(triggered()), this, SLOT(slot_supprimer()) );
connect(pivoter, SIGNAL(triggered()), this, SLOT(slot_pivoter()) );
connect(entrer_pe, SIGNAL(triggered()), this, SLOT(toggleFullScreen()) );
connect(sortir_pe, SIGNAL(triggered()), this, SLOT(toggleFullScreen()) );
connect(mode_selection, SIGNAL(triggered()), this, SLOT(slot_setSelectionMode()) );
connect(mode_visualise, SIGNAL(triggered()), this, SLOT(slot_setVisualisationMode()));
connect(a_propos_de_qet, SIGNAL(triggered()), this, SLOT(aPropos()) );
connect(a_propos_de_qt, SIGNAL(triggered()), qApp, SLOT(aboutQt()) );
connect(masquer_appli, SIGNAL(triggered()), this, SLOT(systrayReduire ()) );
connect(restaurer_appli, SIGNAL(triggered()), this, SLOT(systrayRestaurer()) );
connect(zoom_avant, SIGNAL(triggered()), this, SLOT(slot_zoomPlus()) );
connect(zoom_arriere, SIGNAL(triggered()), this, SLOT(slot_zoomMoins()) );
connect(zoom_adapte, SIGNAL(triggered()), this, SLOT(slot_zoomFit()) );
connect(zoom_reset, SIGNAL(triggered()), this, SLOT(slot_zoomReset()) );
connect(imprimer, SIGNAL(triggered()), this, SLOT(dialogue_imprimer()) );
connect(exporter, SIGNAL(triggered()), this, SLOT(dialogue_exporter()) );
connect(enr_fichier_sous, SIGNAL(triggered()), this, SLOT(dialogue_enregistrer_sous()));
connect(enr_fichier, SIGNAL(triggered()), this, SLOT(enregistrer()) );
connect(nouveau_fichier, SIGNAL(triggered()), this, SLOT(nouveau()) );
connect(ouvrir_fichier, SIGNAL(triggered()), this, SLOT(ouvrir()) );
connect(fermer_fichier, SIGNAL(triggered()), this, SLOT(fermer()) );
connect(couper, SIGNAL(triggered()), this, SLOT(slot_couper()) );
connect(copier, SIGNAL(triggered()), this, SLOT(slot_copier()) );
connect(coller, SIGNAL(triggered()), this, SLOT(slot_coller()) );
connect(toggle_aa, SIGNAL(triggered()), this, SLOT(toggleAntialiasing()) );
connect(f_mosaique, SIGNAL(triggered()), &workspace, SLOT(tile()));
connect(f_cascade, SIGNAL(triggered()), &workspace, SLOT(cascade()));
connect(f_reorganise, SIGNAL(triggered()), &workspace, SLOT(arrangeIcons()));
connect(f_suiv, SIGNAL(triggered()), &workspace, SLOT(activateNextWindow()));
connect(f_prec, SIGNAL(triggered()), &workspace, SLOT(activatePreviousWindow()));
}
/**
Mise en place des menus
*/
void QETApp::menus() {
QMenu *menu_fichier = menuBar() -> addMenu(tr("&Fichier"));
QMenu *menu_edition = menuBar() -> addMenu(tr("&\311dition"));
QMenu *menu_affichage = menuBar() -> addMenu(tr("Afficha&ge"));
QMenu *menu_outils = menuBar() -> addMenu(tr("O&utils"));
QMenu *menu_config = menuBar() -> addMenu(tr("&Configuration"));
menu_fenetres = menuBar() -> addMenu(tr("Fe&n\352tres"));
QMenu *menu_aide = menuBar() -> addMenu(tr("&Aide"));
// tear off feature rulezz... pas ^^ mais bon...
menu_fichier -> setTearOffEnabled(true);
menu_edition -> setTearOffEnabled(true);
menu_affichage -> setTearOffEnabled(true);
menu_outils -> setTearOffEnabled(true);
menu_config -> setTearOffEnabled(true);
menu_aide -> setTearOffEnabled(true);
// menu Fichier
menu_fichier -> addAction(nouveau_fichier);
menu_fichier -> addAction(ouvrir_fichier);
menu_fichier -> addAction(enr_fichier);
menu_fichier -> addAction(enr_fichier_sous);
menu_fichier -> addAction(fermer_fichier);
menu_fichier -> addSeparator();
menu_fichier -> addAction(importer);
menu_fichier -> addAction(exporter);
menu_fichier -> addSeparator();
menu_fichier -> addAction(imprimer);
menu_fichier -> addSeparator();
menu_fichier -> addAction(quitter_qet);
// menu Edition
menu_edition -> addAction(annuler);
menu_edition -> addAction(refaire);
menu_edition -> addSeparator();
menu_edition -> addAction(couper);
menu_edition -> addAction(copier);
menu_edition -> addAction(coller);
menu_edition -> addSeparator();
menu_edition -> addAction(sel_tout);
menu_edition -> addAction(sel_rien);
menu_edition -> addAction(sel_inverse);
menu_edition -> addSeparator();
menu_edition -> addAction(supprimer);
menu_edition -> addAction(pivoter);
// menu Affichage > Afficher
QMenu *menu_aff_aff = new QMenu(tr("Afficher"));
menu_aff_aff -> addAction(barre_outils -> toggleViewAction());
menu_aff_aff -> addAction(qdw_pa -> toggleViewAction());
// menu Affichage
menu_affichage -> addMenu(menu_aff_aff);
menu_affichage -> addSeparator();
menu_affichage -> addAction(toggle_aa);
menu_affichage -> addSeparator();
menu_affichage -> addAction(zoom_avant);
menu_affichage -> addAction(zoom_arriere);
menu_affichage -> addAction(zoom_adapte);
menu_affichage -> addAction(zoom_reset);
// menu Outils
menu_outils -> addAction(mode_selection);
menu_outils -> addAction(mode_visualise);
// menu Configuration
menu_config -> addAction(entrer_pe);
menu_config -> addAction(configurer);
// menu Fenêtres
slot_updateMenuFenetres();
// menu Aide
menu_aide -> addAction(a_propos_de_qet);
menu_aide -> addAction(a_propos_de_qt);
}
/**
Mise en place de la barre d'outils
*/
void QETApp::toolbar() {
barre_outils = new QToolBar(tr("Outils"), this);
// Modes selection / visualisation
barre_outils -> addAction(mode_selection);
barre_outils -> addAction(mode_visualise);
barre_outils -> addSeparator();
barre_outils -> addAction(annuler);
barre_outils -> addAction(refaire);
barre_outils -> addSeparator();
barre_outils -> addAction(couper);
barre_outils -> addAction(copier);
barre_outils -> addAction(coller);
barre_outils -> addSeparator();
barre_outils -> addAction(supprimer);
barre_outils -> addAction(pivoter);
barre_outils -> addSeparator();
barre_outils -> addAction(zoom_avant);
barre_outils -> addAction(zoom_arriere);
barre_outils -> addAction(zoom_adapte);
barre_outils -> addAction(zoom_reset);
// ajout de la barre d'outils a la fenetre principale
addToolBar(Qt::TopToolBarArea, barre_outils);
}
/**
gere l'impression
*/
void QETApp::dialogue_imprimer() {
QPrinter *qprin = new QPrinter();
QPrintDialog *qpd = new QPrintDialog(qprin, this);
qpd -> exec();
}
void QETApp::dialogue_exporter() {
QString nom_fichier = QFileDialog::getSaveFileName(
this,
tr("Exporter vers le fichier"),
QDir::homePath(),
tr("Image PNG (*.png)")
);
if (nom_fichier != "") {
if (!nom_fichier.endsWith(".png", Qt::CaseInsensitive)) nom_fichier += ".png";
QFile fichier(nom_fichier);
QImage image = schemaEnCours() -> scene -> toImage();
image.save(&fichier, "PNG");
fichier.close();
}
}
/**
Methode enregistrant le schema dans le dernier nom de fichier connu.
Si aucun nom de fichier n'est connu, cette methode appelle la methode enregistrer_sous
@return true si l'enregistrement a reussi, false sinon
*/
bool QETApp::enregistrer() {
if (!schemaEnCours()) return(false);
return(schemaEnCours() -> enregistrer());
}
/**
Cette methode demande un nom de fichier a l'utilisateur pour enregistrer le schema
Si aucun nom n'est entre, elle renvoie faux.
Si le nom ne se termine pas par l'extension .qet, celle-ci est ajoutee.
Si l'enregistrement reussit, le nom du fichier est conserve et la fonction renvoie true.
Sinon, faux est renvoye.
@return true si l'enregistrement a reussi, false sinon
@todo detecter le chemin du bureau automatiquement
*/
bool QETApp::dialogue_enregistrer_sous() {
if (!schemaEnCours()) return(false);
return(schemaEnCours() -> enregistrer_sous());
}
/**
Cette methode cree un nouveau schema.
@return true si tout s'est bien passe ; false si vous executez cette fonction dans un univers non cartesien (en fait y'a pas de return(false) :p)
*/
bool QETApp::nouveau() {
addSchemaVue(new SchemaVue(this));
return(true);
}
/**
Cette fonction demande un nom de fichier a ouvrir a l'utilisateur
@return true si l'ouverture a reussi, false sinon
*/
bool QETApp::ouvrir() {
// demande un nom de fichier a ouvrir a l'utilisateur
QString nom_fichier = QFileDialog::getOpenFileName(
this,
tr("Ouvrir un fichier"),
QDir::homePath(),
tr("Schema QelectroTech (*.qet);;Fichiers XML (*.xml);;Tous les fichiers (*)")
);
if (nom_fichier == "") return(false);
// verifie que le fichier n'est pas deja ouvert
QString chemin_fichier = QFileInfo(nom_fichier).canonicalFilePath();
foreach (QWidget *fenetre, workspace.windowList()) {
SchemaVue *fenetre_en_cours = qobject_cast<SchemaVue *>(fenetre);
if (QFileInfo(fenetre_en_cours -> nom_fichier).canonicalFilePath() == chemin_fichier) {
workspace.setActiveWindow(fenetre);
return(false);
}
}
// ouvre le fichier
SchemaVue *sv = new SchemaVue(this);
int code_erreur;
if (sv -> ouvrir(nom_fichier, &code_erreur)) {
addSchemaVue(sv);
return(true);
} else {
QString message_erreur;
switch(code_erreur) {
case 1: message_erreur = tr("Ce fichier n'existe pas."); break;
case 2: message_erreur = tr("Impossible de lire ce fichier."); break;
case 3: message_erreur = tr("Ce fichier n'est pas un document XML valide."); break;
case 4: message_erreur = tr("Une erreur s'est produite lors de l'ouverture du fichier."); break;
}
QMessageBox::warning(this, tr("Erreur"), message_erreur);
delete sv;
return(false);
}
}
/**
Ferme le document courant
@return true si la fermeture du fichier a reussi, false sinon
@todo detecter les modifications et ne demander que si besoin est
*/
bool QETApp::fermer() {
SchemaVue *sv = schemaEnCours();
if (!sv) return(false);
bool fermeture_schema = sv -> close();
if (fermeture_schema) delete sv;
return(fermeture_schema);
}
/**
@return Le SchemaVue qui a le focus dans l'interface MDI
*/
SchemaVue *QETApp::schemaEnCours() {
return(qobject_cast<SchemaVue *>(workspace.activeWindow()));
}
void QETApp::slot_couper() {
if(schemaEnCours()) schemaEnCours() -> couper();
}
void QETApp::slot_copier() {
if(schemaEnCours()) schemaEnCours() -> copier();
}
void QETApp::slot_coller() {
if(schemaEnCours()) schemaEnCours() -> coller();
}
void QETApp::slot_zoomPlus() {
if(schemaEnCours()) schemaEnCours() -> zoomPlus();
}
void QETApp::slot_zoomMoins() {
if(schemaEnCours()) schemaEnCours() -> zoomMoins();
}
void QETApp::slot_zoomFit() {
if(schemaEnCours()) schemaEnCours() -> zoomFit();
}
void QETApp::slot_zoomReset() {
if(schemaEnCours()) schemaEnCours() -> zoomReset();
}
void QETApp::slot_selectAll() {
if(schemaEnCours()) schemaEnCours() -> selectAll();
}
void QETApp::slot_selectNothing() {
if(schemaEnCours()) schemaEnCours() -> selectNothing();
}
void QETApp::slot_selectInvert() {
if(schemaEnCours()) schemaEnCours() -> selectInvert();
}
void QETApp::slot_supprimer() {
if(schemaEnCours()) schemaEnCours() -> supprimer();
}
void QETApp::slot_pivoter() {
if(schemaEnCours()) schemaEnCours() -> pivoter();
}
void QETApp::slot_setSelectionMode() {
if(schemaEnCours()) schemaEnCours() -> setSelectionMode();
}
void QETApp::slot_setVisualisationMode() {
if(schemaEnCours()) schemaEnCours() -> setVisualisationMode();
}
/**
gere les actions ayant besoin d'un document ouvert
*/
void QETApp::slot_updateActions() {
SchemaVue *sv = schemaEnCours();
bool document_ouvert = (sv != 0);
// actions ayant juste besoin d'un document ouvert
fermer_fichier -> setEnabled(document_ouvert);
enr_fichier -> setEnabled(document_ouvert);
enr_fichier_sous -> setEnabled(document_ouvert);
importer -> setEnabled(document_ouvert);
exporter -> setEnabled(document_ouvert);
imprimer -> setEnabled(document_ouvert);
sel_tout -> setEnabled(document_ouvert);
sel_rien -> setEnabled(document_ouvert);
sel_inverse -> setEnabled(document_ouvert);
zoom_avant -> setEnabled(document_ouvert);
zoom_arriere -> setEnabled(document_ouvert);
zoom_adapte -> setEnabled(document_ouvert);
zoom_reset -> setEnabled(document_ouvert);
toggle_aa -> setEnabled(document_ouvert);
// actions ayant aussi besoin d'un historique des actions
annuler -> setEnabled(document_ouvert);
refaire -> setEnabled(document_ouvert);
// actions ayant aussi besoin d'elements selectionnes
bool elements_selectionnes = document_ouvert ? (sv -> scene -> selectedItems().size() > 0) : false;
couper -> setEnabled(elements_selectionnes);
copier -> setEnabled(elements_selectionnes);
supprimer -> setEnabled(elements_selectionnes);
pivoter -> setEnabled(elements_selectionnes);
// action ayant aussi besoin d'un presse-papier plein
bool peut_coller = QApplication::clipboard() -> text() != QString();
coller -> setEnabled(document_ouvert && peut_coller);
// actions ayant aussi besoin d'un document ouvert et de la connaissance de son mode
if (!document_ouvert) {
mode_selection -> setEnabled(false);
mode_visualise -> setEnabled(false);
} else {
switch((int)(sv -> dragMode())) {
case QGraphicsView::NoDrag:
mode_selection -> setEnabled(false);
mode_visualise -> setEnabled(false);
break;
case QGraphicsView::ScrollHandDrag:
mode_selection -> setEnabled(true);
mode_visualise -> setEnabled(true);
mode_selection -> setChecked(false);
mode_visualise -> setChecked(true);
break;
case QGraphicsView::RubberBandDrag:
mode_selection -> setEnabled(true);
mode_visualise -> setEnabled(true);
mode_selection -> setChecked(true);
mode_visualise -> setChecked(false);
break;
}
}
// actions ayant besoin de la connaissance de son mode
if (document_ouvert) toggle_aa -> setText(sv -> antialiased() ? tr("D\351sactiver l'&antialiasing") : tr("Activer l'&antialiasing"));
slot_updateMenuFenetres();
}
void QETApp::addSchemaVue(SchemaVue *sv) {
if (!sv) return;
SchemaVue *s_v = schemaEnCours();
bool maximise = ((!s_v) || (s_v -> windowState() & Qt::WindowMaximized));
QWidget *p = workspace.addWindow(sv);
connect(sv, SIGNAL(selectionChanged()), this, SLOT(slot_updateActions()));
connect(sv, SIGNAL(modeChanged()), this, SLOT(slot_updateActions()));
if (maximise) p -> showMaximized();
else p -> show();
}
void QETApp::slot_updateMenuFenetres() {
// nettoyage du menu
menu_fenetres -> clear();
// actions de fermeture
menu_fenetres -> addAction(fermer_fichier);
//menu_fenetres -> addAction(closeAllAct);
// actions de reorganisation des fenetres
menu_fenetres -> addSeparator();
menu_fenetres -> addAction(f_mosaique);
menu_fenetres -> addAction(f_cascade);
menu_fenetres -> addAction(f_reorganise);
// actiosn de deplacement entre les fenetres
menu_fenetres -> addSeparator();
menu_fenetres -> addAction(f_suiv);
menu_fenetres -> addAction(f_prec);
// liste des fenetres
QList<QWidget *> fenetres = workspace.windowList();
if (!fenetres.isEmpty()) menu_fenetres -> addSeparator();
for (int i = 0 ; i < fenetres.size() ; ++ i) {
SchemaVue *sv = qobject_cast<SchemaVue *>(fenetres.at(i));
QAction *action = menu_fenetres -> addAction(sv -> windowTitle().left(sv -> windowTitle().length()-3));
action -> setCheckable(true);
action -> setChecked(sv == schemaEnCours());
connect(action, SIGNAL(triggered()), &windowMapper, SLOT(map()));
windowMapper.setMapping(action, sv);
}
}

122
qetapp.h Normal file
View File

@@ -0,0 +1,122 @@
#ifndef QETAPP_H
#define QETAPP_H
#include <QtGui>
class SchemaVue;
class PanelAppareils;
/**
Cette classe represente la fenetre principale de QElectroTech et,
ipso facto, la plus grande partie de l'interface graphique de QElectroTech.
Il s'agit d'un objet QMainWindow avec un objet « Schema » en guise de widget central
et un « Panel d'Appareils » en guise de widget « Dock ».
*/
class QETApp : public QMainWindow {
Q_OBJECT
public:
QETApp(QWidget *parent=0);
void closeEvent(QCloseEvent * event );
void addSchemaVue(SchemaVue *);
public slots:
void systray(QSystemTrayIcon::ActivationReason raison);
void systrayReduire();
void systrayRestaurer();
void quitter();
void toggleFullScreen();
void toggleAntialiasing();
void aPropos();
void dialogue_imprimer();
void dialogue_exporter();
bool dialogue_enregistrer_sous();
bool enregistrer();
bool nouveau();
bool ouvrir();
bool fermer();
protected:
// Actions faisables au travers de menus dans l'application QElectroTech
QAction *mode_selection;
QAction *mode_visualise;
QAction *nouveau_fichier;
QAction *ouvrir_fichier;
QAction *fermer_fichier;
QAction *enr_fichier;
QAction *enr_fichier_sous;
QAction *importer;
QAction *exporter;
QAction *imprimer;
QAction *quitter_qet;
QAction *annuler;
QAction *refaire;
QAction *couper;
QAction *copier;
QAction *coller;
QAction *sel_tout;
QAction *sel_rien;
QAction *sel_inverse;
QAction *supprimer;
QAction *selectionner;
QAction *pivoter;
QAction *poser_fil;
QAction *masquer_appli;
QAction *restaurer_appli;
QAction *zoom_avant;
QAction *zoom_arriere;
QAction *zoom_adapte;
QAction *zoom_reset;
QAction *a_propos_de_qet;
QAction *a_propos_de_qt;
QAction *configurer;
QAction *entrer_pe;
QAction *sortir_pe;
QAction *toggle_aa;
QAction *f_mosaique;
QAction *f_cascade;
QAction *f_reorganise;
QAction *f_prec;
QAction *f_suiv;
void actions();
// menus variables
QAction *menu_systray_masquer_restaurer;
private:
QWorkspace workspace;
SchemaVue *schemaEnCours();
QSignalMapper windowMapper;
/// Dock pour le Panel d'Appareils
QDockWidget *qdw_pa;
/// Panel d'Appareils
PanelAppareils *pa;
/// Elements de menus pour l'icone du systray
QMenu *menu_systray;
QAction *systray_masquer;
QAction * config_fullscreen;
QAction *systray_quitter;
QMenu *menu_fenetres;
/// Icone dans le systray
QSystemTrayIcon *qsti;
/// Geometrie de la fenetre principale
QByteArray wg;
void menus();
void toolbar();
QToolBar *barre_outils;
private slots:
void slot_couper();
void slot_copier();
void slot_coller();
void slot_zoomPlus();
void slot_zoomMoins();
void slot_zoomFit();
void slot_zoomReset();
void slot_selectAll();
void slot_selectNothing();
void slot_selectInvert();
void slot_supprimer();
void slot_pivoter();
void slot_setSelectionMode();
void slot_setVisualisationMode();
void slot_updateActions();
void slot_updateMenuFenetres();
};
#endif

322
schema.cpp Normal file
View File

@@ -0,0 +1,322 @@
#include <math.h>
#include "conducteur.h"
#include "contacteur.h"
#include "elementperso.h"
#include "schema.h"
/**
Constructeur
@param parent Le QObject parent du schema
*/
Schema::Schema(QObject *parent) : QGraphicsScene(parent) {
setBackgroundBrush(Qt::white);
poseur_de_conducteur = new QGraphicsLineItem(0, 0);
poseur_de_conducteur -> setZValue(1000000);
QPen t;
t.setColor(Qt::black);
t.setWidthF(1.5);
t.setStyle(Qt::DashLine);
poseur_de_conducteur -> setPen(t);
poseur_de_conducteur -> setLine(QLineF(QPointF(0.0, 0.0), QPointF(0.0, 0.0)));
doit_dessiner_grille = true;
connect(this, SIGNAL(changed(const QList<QRectF> &)), this, SLOT(slot_checkSelectionChange()));
}
/**
Dessine l'arriere-plan du schema, cad la grille.
@param p Le QPainter a utiliser pour dessiner
@param r Le rectangle de la zone a dessiner
*/
void Schema::drawBackground(QPainter *p, const QRectF &r) {
p -> save();
// desactive tout antialiasing
p -> setRenderHint(QPainter::Antialiasing, false);
p -> setRenderHint(QPainter::TextAntialiasing, false);
p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
// dessine un fond blanc
p -> setPen(Qt::NoPen);
p -> setBrush(Qt::white);
p -> drawRect(r);
if (doit_dessiner_grille) {
// dessine les points de la grille
p -> setPen(Qt::black);
p -> setBrush(Qt::NoBrush);
qreal limite_x = r.x() + r.width();
qreal limite_y = r.y() + r.height();
int g_x = (int)ceil(r.x());
while (g_x % GRILLE_X) ++ g_x;
int g_y = (int)ceil(r.y());
while (g_y % GRILLE_Y) ++ g_y;
for (int gx = g_x ; gx < limite_x ; gx += GRILLE_X) {
for (int gy = g_y ; gy < limite_y ; gy += GRILLE_Y) {
p -> drawPoint(gx, gy);
}
}
p -> drawLine(0, 0, 0, 10);
p -> drawLine(0, 0, 10, 0);
}
p -> restore();
}
QImage Schema::toImage() {
QRectF vue = itemsBoundingRect();
// la marge = 5 % de la longueur necessaire
qreal marge = 0.05 * vue.width();
vue.translate(-marge, -marge);
vue.setWidth(vue.width() + 2.0 * marge);
vue.setHeight(vue.height() + 2.0 * marge);
QSize dimensions_image = vue.size().toSize();
QImage pix = QImage(dimensions_image, QImage::Format_RGB32);
QPainter p;
bool painter_ok = p.begin(&pix);
if (!painter_ok) return(QImage());
// rendu antialiase
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::TextAntialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
render(&p, pix.rect(), vue, Qt::KeepAspectRatio);
p.end();
return(pix);
}
/**
Exporte tout ou partie du schema
@param schema Booleen (a vrai par defaut) indiquant si le XML genere doit representer tout le schema ou seulement les elements selectionnes
@return Un Document XML (QDomDocument)
*/
QDomDocument Schema::toXml(bool schema) {
// document
QDomDocument document;
// racine de l'arbre XML
QDomElement racine = document.createElement("schema");
// proprietes du schema
if (schema) {
if (!auteur.isNull()) racine.setAttribute("auteur", auteur);
if (!date.isNull()) racine.setAttribute("date", date.toString("yyyyMMdd"));
if (!titre.isNull()) racine.setAttribute("titre", titre);
}
document.appendChild(racine);
// si le schema ne contient pas d'element (et donc pas de conducteurs), on retourne de suite le document XML
if (items().isEmpty()) return(document);
// creation de deux listes : une qui contient les elements, une qui contient les conducteurs
QList<Element *> liste_elements;
QList<Conducteur *> liste_conducteurs;
// Determine les elements a « XMLiser »
foreach(QGraphicsItem *qgi, items()) {
if (Element *elmt = qgraphicsitem_cast<Element *>(qgi)) {
if (schema) liste_elements << elmt;
else if (elmt -> isSelected()) liste_elements << elmt;
} else if (Conducteur *f = qgraphicsitem_cast<Conducteur *>(qgi)) {
if (schema) liste_conducteurs << f;
// lorsqu'on n'exporte pas tout le schema, il faut retirer les conducteurs non selectionnes
// et pour l'instant, les conducteurs non selectionnes sont les conducteurs dont un des elements n'est pas relie
else if (f -> borne1 -> parentItem() -> isSelected() && f -> borne2 -> parentItem() -> isSelected()) liste_conducteurs << f;
}
}
// enregistrement des elements
if (liste_elements.isEmpty()) return(document);
int id_borne = 0;
// table de correspondance entre les adresses des bornes et leurs ids
QHash<Borne *, int> table_adr_id;
QDomElement elements = document.createElement("elements");
foreach(Element *elmt, liste_elements) {
QDomElement element = document.createElement("element");
// type, position, selection et orientation
element.setAttribute("type", QFileInfo(elmt -> typeId()).fileName());
element.setAttribute("x", elmt -> pos().x());
element.setAttribute("y", elmt -> pos().y());
if (elmt -> isSelected()) element.setAttribute("selected", "selected");
element.setAttribute("sens", elmt -> orientation() ? "true" : "false");
// enregistrements des bornes de chaque appareil
QDomElement bornes = document.createElement("bornes");
// pour chaque enfant de l'element
foreach(QGraphicsItem *child, elmt -> children()) {
// si cet enfant est une borne
if (Borne *p = qgraphicsitem_cast<Borne *>(child)) {
// alors on enregistre la borne
QDomElement borne = p -> toXml(document);
borne.setAttribute("id", id_borne);
table_adr_id.insert(p, id_borne ++);
bornes.appendChild(borne);
}
}
element.appendChild(bornes);
/**
@todo appeler une methode virtuelle de la classe Element qui permettra
aux developpeurs d'elements de personnaliser l'enregistrement des elements
*/
elements.appendChild(element);
}
racine.appendChild(elements);
// enregistrement des conducteurs
if (liste_conducteurs.isEmpty()) return(document);
QDomElement conducteurs = document.createElement("conducteurs");
foreach(Conducteur *f, liste_conducteurs) {
QDomElement conducteur = document.createElement("conducteur");
conducteur.setAttribute("borne1", table_adr_id.value(f -> borne1));
conducteur.setAttribute("borne2", table_adr_id.value(f -> borne2));
conducteurs.appendChild(conducteur);
}
racine.appendChild(conducteurs);
// on retourne le document XML ainsi genere
return(document);
}
void Schema::reset() {
/// @todo implementer cette fonction
}
/**
Importe le schema decrit dans un document XML. Si une position est precisee, les elements importes sont positionnes de maniere a ce que le coin superieur gauche du plus petit rectangle pouvant les entourant tous (le bounding rect) soit a cette position.
@param document Le document XML a analyser
@param position La position du schema importe
@return true si l'import a reussi, false sinon
*/
bool Schema::fromXml(QDomDocument &document, QPointF position) {
QDomElement racine = document.documentElement();
// le premier element doit etre un schema
if (racine.tagName() != "schema") return(false);
// lecture des attributs de ce schema
auteur = racine.attribute("auteur");
titre = racine.attribute("titre");
date = QDate::fromString(racine.attribute("date"), "yyyyMMdd");
// si la racine n'a pas d'enfant : le chargement est fini (schema vide)
if (racine.firstChild().isNull()) return(true);
// chargement de tous les Elements du fichier XML
QList<Element *> elements_ajoutes;
//uint nb_elements = 0;
QHash< int, Borne *> table_adr_id;
QHash< int, Borne *> &ref_table_adr_id = table_adr_id;
for (QDomNode node = racine.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
// on s'interesse a l'element XML "elements" (= groupe d'elements)
QDomElement elmts = node.toElement();
if(elmts.isNull() || elmts.tagName() != "elements") continue;
// parcours des enfants de l'element XML "elements"
for (QDomNode n = elmts.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
// on s'interesse a l'element XML "element" (elements eux-memes)
QDomElement e = n.toElement();
if (e.isNull() || !Element::valideXml(e)) continue;
Element *element_ajoute;
if ((element_ajoute = elementFromXml(e, ref_table_adr_id)) != NULL) elements_ajoutes << element_ajoute;
else qDebug("Le chargement d'un element a echoue");
}
}
// aucun Element n'a ete ajoute - inutile de chercher des conducteurs - le chargement est fini
if (!elements_ajoutes.size()) return(true);
// gere la translation des nouveaux elements si celle-ci est demandee
if (position != QPointF()) {
// determine quel est le coin superieur gauche du rectangle entourant les elements ajoutes
qreal minimum_x = 0, minimum_y = 0;
bool init = false;
foreach (Element *elmt_ajoute, elements_ajoutes) {
QPointF csg = elmt_ajoute -> mapToScene(elmt_ajoute -> boundingRect().topLeft());
qreal px = csg.x();
qreal py = csg.y();
if (!init) {
minimum_x = px;
minimum_y = py;
init = true;
} else {
if (px < minimum_x) minimum_x = px;
if (py < minimum_y) minimum_y = py;
}
}
qreal diff_x = position.x() - minimum_x;
qreal diff_y = position.y() - minimum_y;
foreach (Element *elmt_ajoute, elements_ajoutes) {
elmt_ajoute -> setPos(elmt_ajoute -> pos().x() + diff_x, elmt_ajoute -> pos().y() + diff_y);
}
}
// chargement de tous les Conducteurs du fichier XML
for (QDomNode node = racine.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
// on s'interesse a l'element XML "conducteurs" (= groupe de conducteurs)
QDomElement conducteurs = node.toElement();
if(conducteurs.isNull() || conducteurs.tagName() != "conducteurs") continue;
// parcours des enfants de l'element XML "conducteurs"
for (QDomNode n = conducteurs.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
// on s'interesse a l'element XML "element" (elements eux-memes)
QDomElement f = n.toElement();
if (f.isNull() || !Conducteur::valideXml(f)) continue;
// verifie que les bornes que le conducteur relie sont connues
int id_p1 = f.attribute("borne1").toInt();
int id_p2 = f.attribute("borne2").toInt();
if (table_adr_id.contains(id_p1) && table_adr_id.contains(id_p2)) {
// pose le conducteur... si c'est possible
Borne *p1 = table_adr_id.value(id_p1);
Borne *p2 = table_adr_id.value(id_p2);
if (p1 != p2) {
bool peut_poser_conducteur = true;
bool cia = ((Element *)p2 -> parentItem()) -> connexionsInternesAcceptees();
if (!cia) foreach(QGraphicsItem *item, p2 -> parentItem() -> children()) if (item == p1) peut_poser_conducteur = false;
if (peut_poser_conducteur) new Conducteur(table_adr_id.value(id_p1), table_adr_id.value(id_p2), 0, this);
}
} else qDebug() << "Le chargement du conducteur" << id_p1 << id_p2 << "a echoue";
}
}
return(true);
}
/**
Ajoute au schema l'Element correspondant au QDomElement passe en parametre
@param e QDomElement a analyser
@param table_id_adr Table de correspondance entre les entiers et les bornes
@return true si l'ajout a parfaitement reussi, false sinon
*/
Element *Schema::elementFromXml(QDomElement &e, QHash<int, Borne *> &table_id_adr) {
// cree un element dont le type correspond à l'id type
QString type = e.attribute("type");
int etat;
Element *nvel_elmt = new ElementPerso(type, 0, 0, &etat);
/*switch(e.attribute("type").toInt()) {
case 0: nvel_elmt = new Contacteur(); break;
case 1: nvel_elmt = new DEL(); break;
case 2: nvel_elmt = new Entree(); break;
}*/
if (etat != 0) return(false);
bool retour = nvel_elmt -> fromXml(e, table_id_adr);
if (!retour) {
delete nvel_elmt;
} else {
// ajout de l'element au schema
addItem(nvel_elmt);
nvel_elmt -> setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
nvel_elmt -> setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
if (e.attribute("sens") == "false") nvel_elmt -> invertOrientation();
nvel_elmt -> setSelected(e.attribute("selected") == "selected");
}
return(retour ? nvel_elmt : NULL);
}
void Schema::slot_checkSelectionChange() {
static QList<QGraphicsItem *> cache_selecteditems = QList<QGraphicsItem *>();
QList<QGraphicsItem *> selecteditems = selectedItems();
if (cache_selecteditems != selecteditems) emit(selectionChanged());
cache_selecteditems = selecteditems;
}

46
schema.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef SCHEMA_H
#define SCHEMA_H
#define GRILLE_X 10
#define GRILLE_Y 10
#include <QtGui>
#include <QtXml>
class Element;
class Borne;
class Schema : public QGraphicsScene {
Q_OBJECT
public:
Schema(QObject * = 0);
void drawBackground(QPainter *, const QRectF &);
inline void poseConducteur(bool pf) {
if (pf) {
if (!poseur_de_conducteur -> scene()) addItem(poseur_de_conducteur);
} else {
if (poseur_de_conducteur -> scene()) removeItem(poseur_de_conducteur);
}
}
inline void setDepart (QPointF d) { poseur_de_conducteur -> setLine(QLineF(d, poseur_de_conducteur -> line().p2())); }
inline void setArrivee(QPointF a) { poseur_de_conducteur -> setLine(QLineF(poseur_de_conducteur -> line().p1(), a)); }
QImage toImage();
QDomDocument toXml(bool = true);
bool fromXml(QDomDocument &, QPointF = QPointF());
void reset();
QGraphicsItem *getElementById(uint id);
private:
QGraphicsLineItem *poseur_de_conducteur;
bool doit_dessiner_grille;
// elements du cartouche
QString auteur;
QDate date;
QString titre;
QString folio; // vraiment necessaire ce truc ?
QString nom_fichier; // meme remarque
Element *elementFromXml(QDomElement &e, QHash<int, Borne *> &);
private slots:
void slot_checkSelectionChange();
signals:
void selectionChanged();
};
#endif

431
schemavue.cpp Normal file
View File

@@ -0,0 +1,431 @@
#include "schemavue.h"
#include "schema.h"
#include "elementperso.h"
#include "contacteur.h"
#include "del.h"
#include "entree.h"
/**
Initialise le SchemaVue
*/
void SchemaVue::initialise() {
setInteractive(true);
setAntialiasing(true);
setScene(scene = new Schema(this));
setDragMode(RubberBandDrag);
setAcceptDrops(true);
setWindowTitle(tr("Nouveau sch\351ma") + "[*]");
connect(scene, SIGNAL(selectionChanged()), this, SLOT(slot_selectionChanged()));
}
/**
Constructeur par defaut
*/
SchemaVue::SchemaVue() : QGraphicsView() {
initialise();
}
/**
Constructeur
@param parent Le QWidegt parent de cette vue de schema
*/
SchemaVue::SchemaVue(QWidget *parent) : QGraphicsView(parent) {
initialise();
}
/**
Permet de savoir si le rendu graphique du SchemaVue est antialiase ou non.
@return Un booleen indiquant si le SchemaVue est antialiase
*/
bool SchemaVue::antialiased() const {
return(antialiasing);
}
/**
Active ou desactive l'antialiasing pour le rendu graphique du SchemaVue.
@param aa un booleen indiquant si le SchemaVue doit etre antialiase ou non
*/
void SchemaVue::setAntialiasing(bool aa) {
antialiasing = aa;
setRenderHint(QPainter::Antialiasing, aa);
setRenderHint(QPainter::TextAntialiasing, aa);
setRenderHint(QPainter::SmoothPixmapTransform, aa);
repaint();
}
/**
appelle la methode select sur tous les elements de la liste d'elements
@todo modifier selectAll pour l'integration des conducteurs
*/
void SchemaVue::selectAll() {
if (scene -> items().isEmpty()) return;
foreach (QGraphicsItem *item, scene -> items()) item -> setSelected(true);
}
/**
appelle la methode deselect sur tous les elements de la liste d'elements
@todo modifier selectNothing pour l'integration des conducteurs
*/
void SchemaVue::selectNothing() {
if (scene -> items().isEmpty()) return;
foreach (QGraphicsItem *item, scene -> items()) item -> setSelected(false);
}
/**
Inverse l'etat de selection de tous les elements de la liste d'elements
@todo modifier selectInvert pour l'integration des conducteurs
*/
void SchemaVue::selectInvert() {
if (scene -> items().isEmpty()) return;
foreach (QGraphicsItem *item, scene -> items()) item -> setSelected(!item -> isSelected());
}
/**
Supprime les composants selectionnes
*/
void SchemaVue::supprimer() {
QList<QGraphicsItem *> garbage_elmt;
QList<QGraphicsItem *> garbage_conducteurs;
// useless but careful : creating two lists : one for wires, one for elements
foreach (QGraphicsItem *qgi, scene -> selectedItems()) {
if (!garbage_elmt.contains(qgi)) garbage_elmt.append(qgi);
// pour chaque enfant de l'element
foreach (QGraphicsItem *child, qgi -> children()) {
// si cet enfant est une borne
if (Borne *p = qgraphicsitem_cast<Borne *>(child)) {
// alors chaque conducteur de la borne est recense
foreach (Conducteur *f, p -> conducteurs()) {
if (!garbage_conducteurs.contains(f)) garbage_conducteurs.append(f);
}
}
}
}
scene -> clearSelection();
// "destroying" the wires, removing them from the scene and stocking them into the « garbage »
foreach (QGraphicsItem *qgi, garbage_conducteurs) {
if (Conducteur *f = qgraphicsitem_cast<Conducteur *>(qgi)) {
f -> destroy();
scene -> removeItem(f);
throwToGarbage(f);
}
}
// removing the elements from the scene and stocking them into the « garbage »
foreach (QGraphicsItem *qgi, garbage_elmt) {
scene -> removeItem(qgi);
throwToGarbage(qgi);
}
resetCachedContent();
QTimer::singleShot(5000, this, SLOT(flushGarbage()));
}
/**
Envoie un item vers le "garbage" pour qu'il soit supprime plus tard
@param qgi L'item a supprimer
*/
void SchemaVue::throwToGarbage(QGraphicsItem *qgi) {
// pas de doublon dans le garbage (sinon ca va sentir la segfault)
bool qgi_deja_dans_le_garbage = false;
foreach(QGraphicsItem *gbg_qgi, garbage) {
if ((void *)gbg_qgi == (void *)qgi) {
qgi_deja_dans_le_garbage = true;
break;
}
}
if (!qgi_deja_dans_le_garbage) garbage.append(qgi);
}
/**
Supprime tous les elements du "garbage"
*/
void SchemaVue::flushGarbage() {
foreach(QGraphicsItem *qgi, garbage) {
delete(qgi);
garbage.removeAll(qgi);
}
}
/**
Pivote les composants selectionnes
*/
void SchemaVue::pivoter() {
if (scene -> selectedItems().isEmpty()) return;
foreach (QGraphicsItem *item, scene -> selectedItems()) {
if (Element *elt = qgraphicsitem_cast<Element *>(item)) {
elt -> invertOrientation();
elt -> update();
}
}
}
/**
accepte ou refuse le drag'n drop en fonction du type de donnees entrant
@param e le QDragEnterEvent correspondant au drag'n drop tente
@todo trouver un MIME Type plus adapte
*/
void SchemaVue::dragEnterEvent(QDragEnterEvent *e) {
if (e -> mimeData() -> hasFormat("text/plain")) e -> acceptProposedAction();
else e-> ignore();
}
/**
gere les dragleaveevent
@param e le QDragEnterEvent correspondant au drag'n drop sortant
*/
void SchemaVue::dragLeaveEvent(QDragLeaveEvent *) {}
/**
accepte ou refuse le drag'n drop en fonction du type de donnees entrant
@param e le QDragMoveEvent correspondant au drag'n drop tente
*/
void SchemaVue::dragMoveEvent(QDragMoveEvent *e) {
if (e -> mimeData() -> hasFormat("text/plain")) e -> acceptProposedAction();
else e-> ignore();
}
/**
gere les depots (drop) acceptes sur le Schema
@param e le QDropEvent correspondant au drag'n drop effectue
@todo Ajouter directement l'objet Element a la scene lorsque le drag'n drop aura ete ameliore
*/
void SchemaVue::dropEvent(QDropEvent *e) {
QString fichier = e -> mimeData() -> text();
int etat;
Element *el = new ElementPerso(fichier, 0, 0, &etat);
if (etat != 0) delete el;
else {
scene -> addItem(el);
el -> setPos(mapToScene(e -> pos().x(), e -> pos().y()));
el -> setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
}
}
/**
Passe le Schema en mode visualisation
*/
void SchemaVue::setVisualisationMode() {
setDragMode(ScrollHandDrag);
emit(modeChanged());
}
/**
Passe le Schema en mode Selection
*/
void SchemaVue::setSelectionMode() {
setDragMode(RubberBandDrag);
setCursor(Qt::ArrowCursor);
emit(modeChanged());
}
/**
Agrandit le schema (+33% = inverse des -25 % de zoomMoins())
*/
void SchemaVue::zoomPlus() {
scale(4.0/3.0, 4.0/3.0);
}
/**
Retrecit le schema (-25% = inverse des +33 % de zoomPlus())
*/
void SchemaVue::zoomMoins() {
scale(0.75, 0.75);
}
/**
Agrandit ou rectrecit le schema de facon a ce que tous les elements du
schema soient visibles a l'ecran. S'il n'y a aucun element sur le schema,
le zoom est reinitialise
*/
void SchemaVue::zoomFit() {
if (scene -> items().isEmpty()) {
zoomReset();
return;
}
QRectF vue = scene -> itemsBoundingRect();
// la marge = 5 % de la longueur necessaire
qreal marge = 0.05 * vue.width();
vue.translate(-marge, -marge);
vue.setWidth(vue.width() + 2.0 * marge);
vue.setHeight(vue.height() + 2.0 * marge);
fitInView(vue, Qt::KeepAspectRatio);
}
/**
Reinitialise le zoom
*/
void SchemaVue::zoomReset() {
resetMatrix();
}
/**
copie les elements selectionnes du schema dans le presse-papier puis les supprime
*/
void SchemaVue::couper() {
copier();
supprimer();
}
/**
copie les elements selectionnes du schema dans le presse-papier
*/
void SchemaVue::copier() {
QClipboard *presse_papier = QApplication::clipboard();
QString contenu_presse_papier = scene -> toXml(false).toString(4);
if (presse_papier -> supportsSelection()) presse_papier -> setText(contenu_presse_papier, QClipboard::Selection);
presse_papier -> setText(contenu_presse_papier);
}
/**
importe les elements contenus dans le presse-papier dans le schema
*/
void SchemaVue::coller() {
QString texte_presse_papier;
QDomDocument document_xml;
if ((texte_presse_papier = QApplication::clipboard() -> text()) == QString()) return;
if (!document_xml.setContent(texte_presse_papier)) return;
scene -> fromXml(document_xml);
}
/**
gere les clics et plus particulierement le clic du milieu (= coller pour X11)
*/
void SchemaVue::mousePressEvent(QMouseEvent *e) {
if (e -> buttons() == Qt::MidButton) {
QString texte_presse_papier;
QDomDocument document_xml;
if ((texte_presse_papier = QApplication::clipboard() -> text(QClipboard::Selection)) == QString()) return;
if (!document_xml.setContent(texte_presse_papier)) return;
scene -> fromXml(document_xml, mapToScene(e -> pos()));
}
QGraphicsView::mousePressEvent(e);
}
/**
Ouvre un fichier *.qet dans cette SchemaVue
@param nom_fichier Nom du fichier a ouvrir
@param erreur Si le pointeur est specifie, cet entier est mis a 0 en cas de reussite de l'ouverture, 1 si le fichier n'existe pas, 2 si le fichier n'est pas lisible, 3 si le fichier n'est pas un element XML, 4 si l'ouverture du fichier a echoue pour une autre raison (c'est pas ca qui manque ^^)
@return true si l'ouverture a reussi, false sinon
*/
bool SchemaVue::ouvrir(QString n_fichier, int *erreur) {
// verifie l'existence du fichier
if (!QFileInfo(n_fichier).exists()) {
if (erreur != NULL) *erreur = 1;
return(false);
}
// ouvre le fichier
QFile fichier(n_fichier);
if (!fichier.open(QIODevice::ReadOnly)) {
if (erreur != NULL) *erreur = 2;
return(false);
}
// lit son contenu dans un QDomDocument
QDomDocument document;
if (!document.setContent(&fichier)) {
if (erreur != NULL) *erreur = 3;
fichier.close();
return(false);
}
fichier.close();
// construit le schema a partir du QDomDocument
QDomDocument &doc = document;
if (scene -> fromXml(doc)) {
if (erreur != NULL) *erreur = 0;
nom_fichier = n_fichier;
setWindowTitle(nom_fichier + "[*]");
return(true);
} else {
if (erreur != NULL) *erreur = 4;
return(false);
}
}
void SchemaVue::slot_selectionChanged() {
emit(selectionChanged());
}
void SchemaVue::closeEvent(QCloseEvent *event) {
// demande d'abord a l'utilisateur s'il veut enregistrer le schema en cours
QMessageBox::StandardButton reponse = QMessageBox::question(
this,
tr("Enregistrer le sch\351ma en cours ?"),
tr("Voulez-vous enregistrer le sch\351ma en cours ?"),
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,
QMessageBox::Cancel
);
bool retour;
switch(reponse) {
case QMessageBox::Cancel: retour = false; break; // l'utilisateur annule : echec de la fermeture
case QMessageBox::Yes: retour = enregistrer(); break; // l'utilisateur dit oui : la reussite depend de l'enregistrement
default: retour = true; // l'utilisateur dit non ou ferme le dialogue: c'est reussi
}
if (retour) event -> accept();
else event -> ignore();
}
/**
Methode enregistrant le schema dans le dernier nom de fichier connu.
Si aucun nom de fichier n'est connu, cette methode appelle la methode enregistrer_sous
@return true si l'enregistrement a reussi, false sinon
*/
bool SchemaVue::enregistrer() {
if (nom_fichier == QString()) return(enregistrer_sous());
else return(private_enregistrer(nom_fichier));
}
/**
Cette methode demande un nom de fichier a l'utilisateur pour enregistrer le schema
Si aucun nom n'est entre, elle renvoie faux.
Si le nom ne se termine pas par l'extension .qet, celle-ci est ajoutee.
Si l'enregistrement reussit, le nom du fichier est conserve et la fonction renvoie true.
Sinon, faux est renvoye.
@return true si l'enregistrement a reussi, false sinon
@todo detecter le chemin du bureau automatiquement
*/
bool SchemaVue::enregistrer_sous() {
// demande un nom de fichier a l'utilisateur pour enregistrer le schema
QString n_fichier = QFileDialog::getSaveFileName(
this,
tr("Enregistrer sous"),
QDir::homePath(),
tr("Schema QelectroTech (*.qet)")
);
// si aucun nom n'est entre, renvoie faux.
if (n_fichier == "") return(false);
// si le nom ne se termine pas par l'extension .qet, celle-ci est ajoutee
if (!n_fichier.endsWith(".qet", Qt::CaseInsensitive)) n_fichier += ".qet";
// tente d'enregistrer le fichier
bool resultat_enregistrement = private_enregistrer(n_fichier);
// si l'enregistrement reussit, le nom du fichier est conserve
if (resultat_enregistrement) {
nom_fichier = n_fichier;
setWindowTitle(nom_fichier + "[*]");
}
// retourne un booleen representatif de la reussite de l'enregistrement
return(resultat_enregistrement);
}
/**
Methode privee gerant l'enregistrement du fichier XML. S'il n'est pas possible
d'ecrire dans le fichier, cette fonction affiche un message d'erreur et renvoie false.
Autrement, elle renvoie true.
@param nom_fichier Nom du fichier dans lequel l'arbre XML doit etre ecrit
@return true si l'enregistrement a reussi, false sinon
*/
bool SchemaVue::private_enregistrer(QString &n_fichier) {
QFile fichier(n_fichier);
if (!fichier.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("Erreur"), tr("Impossible d'ecrire dans ce fichier"));
return(false);
}
QTextStream out(&fichier);
out.setCodec("UTF-8");
out << scene -> toXml().toString(4);
fichier.close();
return(true);
}

69
schemavue.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef SCHEMAVUE_H
#define SCHEMAVUE_H
#include <QtGui>
class Schema;
#include "element.h"
#include "conducteur.h"
#define TAILLE_GRILLE 10
/**
Classe representant un SchemaVue electrique
@todo creer une structure capable de retenir les differents composants du SchemaVue : elements, fils, indications eventuelles => revoir les SchemaVues
*/
class SchemaVue : public QGraphicsView {
Q_OBJECT
public:
// constructeurs
SchemaVue();
SchemaVue(QWidget * = 0);
// nouveaux attributs
Schema *scene;
// methodes publiques
bool antialiased() const;
void setAntialiasing(bool);
bool ouvrir(QString, int * = NULL);
void closeEvent(QCloseEvent *);
QString nom_fichier;
bool enregistrer();
bool enregistrer_sous();
private:
bool private_enregistrer(QString &);
void initialise();
bool antialiasing; // booleen indiquant s'il faut effectuer un antialiasing sur le rendu graphique du SchemaVue
QList<QGraphicsItem *> garbage;
void throwToGarbage(QGraphicsItem *);
void mousePressEvent(QMouseEvent *);
void dragEnterEvent(QDragEnterEvent *);
void dragLeaveEvent(QDragLeaveEvent *);
void dragMoveEvent(QDragMoveEvent *);
void dropEvent(QDropEvent *);
signals:
void selectionChanged();
void antialiasingChanged();
void modeChanged();
public slots:
void selectNothing();
void selectAll();
void selectInvert();
void supprimer();
void pivoter();
void setVisualisationMode();
void setSelectionMode();
void zoomPlus();
void zoomMoins();
void zoomFit();
void zoomReset();
void couper();
void copier();
void coller();
private slots:
void flushGarbage();
void slot_selectionChanged();
};
#endif