[qet] [2613] Merge sources dir branch devel to trunk

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


Revision: 2613
Author:   scorpio810
Date:     2013-11-14 11:11:22 +0100 (Thu, 14 Nov 2013)
Log Message:
-----------
Merge sources dir branch devel to trunk

Modified Paths:
--------------
    trunk/qelectrotech.pro

Added Paths:
-----------
    trunk/sources/
    trunk/sources/aboutqet.cpp
    trunk/sources/aboutqet.h
    trunk/sources/autonumerotation.cpp
    trunk/sources/autonumerotation.h
    trunk/sources/basicmoveelementshandler.cpp
    trunk/sources/basicmoveelementshandler.h
    trunk/sources/borderproperties.cpp
    trunk/sources/borderproperties.h
    trunk/sources/borderpropertieswidget.cpp
    trunk/sources/borderpropertieswidget.h
    trunk/sources/bordertitleblock.cpp
    trunk/sources/bordertitleblock.h
    trunk/sources/closediagramsdialog.cpp
    trunk/sources/closediagramsdialog.h
    trunk/sources/conductorautonumerotation.cpp
    trunk/sources/conductorautonumerotation.h
    trunk/sources/conductorautonumerotationwidget.cpp
    trunk/sources/conductorautonumerotationwidget.h
    trunk/sources/conductorprofile.cpp
    trunk/sources/conductorprofile.h
    trunk/sources/conductorproperties.cpp
    trunk/sources/conductorproperties.h
    trunk/sources/conductorpropertieswidget.cpp
    trunk/sources/conductorpropertieswidget.h
    trunk/sources/conductorsegment.cpp
    trunk/sources/conductorsegment.h
    trunk/sources/conductorsegmentprofile.h
    trunk/sources/configdialog.cpp
    trunk/sources/configdialog.h
    trunk/sources/configpage.h
    trunk/sources/configpages.cpp
    trunk/sources/configpages.h
    trunk/sources/diagram.cpp
    trunk/sources/diagram.h
    trunk/sources/diagramcommands.cpp
    trunk/sources/diagramcommands.h
    trunk/sources/diagramcontent.cpp
    trunk/sources/diagramcontent.h
    trunk/sources/diagramcontext.cpp
    trunk/sources/diagramcontext.h
    trunk/sources/diagramcontextwidget.cpp
    trunk/sources/diagramcontextwidget.h
    trunk/sources/diagramposition.cpp
    trunk/sources/diagramposition.h
    trunk/sources/diagramprintdialog.cpp
    trunk/sources/diagramprintdialog.h
    trunk/sources/diagramschooser.cpp
    trunk/sources/diagramschooser.h
    trunk/sources/diagramview.cpp
    trunk/sources/diagramview.h
    trunk/sources/editor/
    trunk/sources/editor/arceditor.cpp
    trunk/sources/editor/arceditor.h
    trunk/sources/editor/customelementgraphicpart.cpp
    trunk/sources/editor/customelementgraphicpart.h
    trunk/sources/editor/customelementpart.cpp
    trunk/sources/editor/customelementpart.h
    trunk/sources/editor/editorcommands.cpp
    trunk/sources/editor/editorcommands.h
    trunk/sources/editor/elementcontent.h
    trunk/sources/editor/elementitemeditor.cpp
    trunk/sources/editor/elementitemeditor.h
    trunk/sources/editor/elementprimitivedecorator.cpp
    trunk/sources/editor/elementprimitivedecorator.h
    trunk/sources/editor/elementscene.cpp
    trunk/sources/editor/elementscene.h
    trunk/sources/editor/elementview.cpp
    trunk/sources/editor/elementview.h
    trunk/sources/editor/ellipseeditor.cpp
    trunk/sources/editor/ellipseeditor.h
    trunk/sources/editor/lineeditor.cpp
    trunk/sources/editor/lineeditor.h
    trunk/sources/editor/partarc.cpp
    trunk/sources/editor/partarc.h
    trunk/sources/editor/partellipse.cpp
    trunk/sources/editor/partellipse.h
    trunk/sources/editor/partline.cpp
    trunk/sources/editor/partline.h
    trunk/sources/editor/partpolygon.cpp
    trunk/sources/editor/partpolygon.h
    trunk/sources/editor/partrectangle.cpp
    trunk/sources/editor/partrectangle.h
    trunk/sources/editor/partterminal.cpp
    trunk/sources/editor/partterminal.h
    trunk/sources/editor/parttext.cpp
    trunk/sources/editor/parttext.h
    trunk/sources/editor/parttextfield.cpp
    trunk/sources/editor/parttextfield.h
    trunk/sources/editor/polygoneditor.cpp
    trunk/sources/editor/polygoneditor.h
    trunk/sources/editor/qetelementeditor.cpp
    trunk/sources/editor/qetelementeditor.h
    trunk/sources/editor/rectangleeditor.cpp
    trunk/sources/editor/rectangleeditor.h
    trunk/sources/editor/styleeditor.cpp
    trunk/sources/editor/styleeditor.h
    trunk/sources/editor/terminaleditor.cpp
    trunk/sources/editor/terminaleditor.h
    trunk/sources/editor/texteditor.cpp
    trunk/sources/editor/texteditor.h
    trunk/sources/editor/textfieldeditor.cpp
    trunk/sources/editor/textfieldeditor.h
    trunk/sources/elementdefinition.cpp
    trunk/sources/elementdefinition.h
    trunk/sources/elementdeleter.cpp
    trunk/sources/elementdeleter.h
    trunk/sources/elementdialog.cpp
    trunk/sources/elementdialog.h
    trunk/sources/elementscategorieslist.cpp
    trunk/sources/elementscategorieslist.h
    trunk/sources/elementscategorieswidget.cpp
    trunk/sources/elementscategorieswidget.h
    trunk/sources/elementscategory.cpp
    trunk/sources/elementscategory.h
    trunk/sources/elementscategorydeleter.cpp
    trunk/sources/elementscategorydeleter.h
    trunk/sources/elementscategoryeditor.cpp
    trunk/sources/elementscategoryeditor.h
    trunk/sources/elementscollection.cpp
    trunk/sources/elementscollection.h
    trunk/sources/elementscollectioncache.cpp
    trunk/sources/elementscollectioncache.h
    trunk/sources/elementscollectionitem.h
    trunk/sources/elementslocation.cpp
    trunk/sources/elementslocation.h
    trunk/sources/elementsmover.cpp
    trunk/sources/elementsmover.h
    trunk/sources/elementspanel.cpp
    trunk/sources/elementspanel.h
    trunk/sources/elementspanelwidget.cpp
    trunk/sources/elementspanelwidget.h
    trunk/sources/elementtextsmover.cpp
    trunk/sources/elementtextsmover.h
    trunk/sources/exportdialog.cpp
    trunk/sources/exportdialog.h
    trunk/sources/exportproperties.cpp
    trunk/sources/exportproperties.h
    trunk/sources/exportpropertieswidget.cpp
    trunk/sources/exportpropertieswidget.h
    trunk/sources/fileelementdefinition.cpp
    trunk/sources/fileelementdefinition.h
    trunk/sources/fileelementscategory.cpp
    trunk/sources/fileelementscategory.h
    trunk/sources/fileelementscollection.cpp
    trunk/sources/fileelementscollection.h
    trunk/sources/genericpanel.cpp
    trunk/sources/genericpanel.h
    trunk/sources/integrationmoveelementshandler.cpp
    trunk/sources/integrationmoveelementshandler.h
    trunk/sources/interactivemoveelementshandler.cpp
    trunk/sources/interactivemoveelementshandler.h
    trunk/sources/main.cpp
    trunk/sources/moveelementsdescription.cpp
    trunk/sources/moveelementsdescription.h
    trunk/sources/moveelementshandler.h
    trunk/sources/nameslist.cpp
    trunk/sources/nameslist.h
    trunk/sources/nameslistwidget.cpp
    trunk/sources/nameslistwidget.h
    trunk/sources/newelementwizard.cpp
    trunk/sources/newelementwizard.h
    trunk/sources/nomenclature.cpp
    trunk/sources/nomenclature.h
    trunk/sources/numerotationcontext.cpp
    trunk/sources/numerotationcontext.h
    trunk/sources/numerotationcontextcommands.cpp
    trunk/sources/numerotationcontextcommands.h
    trunk/sources/orientationset.cpp
    trunk/sources/orientationset.h
    trunk/sources/projectconfigpages.cpp
    trunk/sources/projectconfigpages.h
    trunk/sources/projectview.cpp
    trunk/sources/projectview.h
    trunk/sources/qet.cpp
    trunk/sources/qet.h
    trunk/sources/qetapp.cpp
    trunk/sources/qetapp.h
    trunk/sources/qetarguments.cpp
    trunk/sources/qetarguments.h
    trunk/sources/qetdiagrameditor.cpp
    trunk/sources/qetdiagrameditor.h
    trunk/sources/qetgraphicsitem/
    trunk/sources/qetgraphicsitem/conductor.cpp
    trunk/sources/qetgraphicsitem/conductor.h
    trunk/sources/qetgraphicsitem/conductortextitem.cpp
    trunk/sources/qetgraphicsitem/conductortextitem.h
    trunk/sources/qetgraphicsitem/customelement.cpp
    trunk/sources/qetgraphicsitem/customelement.h
    trunk/sources/qetgraphicsitem/diagramimageitem.cpp
    trunk/sources/qetgraphicsitem/diagramimageitem.h
    trunk/sources/qetgraphicsitem/diagramtextitem.cpp
    trunk/sources/qetgraphicsitem/diagramtextitem.h
    trunk/sources/qetgraphicsitem/element.cpp
    trunk/sources/qetgraphicsitem/element.h
    trunk/sources/qetgraphicsitem/elementtextitem.cpp
    trunk/sources/qetgraphicsitem/elementtextitem.h
    trunk/sources/qetgraphicsitem/fixedelement.cpp
    trunk/sources/qetgraphicsitem/fixedelement.h
    trunk/sources/qetgraphicsitem/ghostelement.cpp
    trunk/sources/qetgraphicsitem/ghostelement.h
    trunk/sources/qetgraphicsitem/independenttextitem.cpp
    trunk/sources/qetgraphicsitem/independenttextitem.h
    trunk/sources/qetgraphicsitem/qetgraphicsitem.cpp
    trunk/sources/qetgraphicsitem/qetgraphicsitem.h
    trunk/sources/qeticons.cpp
    trunk/sources/qeticons.h
    trunk/sources/qetmainwindow.cpp
    trunk/sources/qetmainwindow.h
    trunk/sources/qetmessagebox.cpp
    trunk/sources/qetmessagebox.h
    trunk/sources/qetprintpreviewdialog.cpp
    trunk/sources/qetprintpreviewdialog.h
    trunk/sources/qetproject.cpp
    trunk/sources/qetproject.h
    trunk/sources/qetregexpvalidator.cpp
    trunk/sources/qetregexpvalidator.h
    trunk/sources/qetresult.cpp
    trunk/sources/qetresult.h
    trunk/sources/qetsingleapplication.cpp
    trunk/sources/qetsingleapplication.h
    trunk/sources/qettabbar.cpp
    trunk/sources/qettabbar.h
    trunk/sources/qettabwidget.cpp
    trunk/sources/qettabwidget.h
    trunk/sources/qfilenameedit.cpp
    trunk/sources/qfilenameedit.h
    trunk/sources/qgimanager.cpp
    trunk/sources/qgimanager.h
    trunk/sources/qtextorientationspinboxwidget.cpp
    trunk/sources/qtextorientationspinboxwidget.h
    trunk/sources/qtextorientationwidget.cpp
    trunk/sources/qtextorientationwidget.h
    trunk/sources/recentfiles.cpp
    trunk/sources/recentfiles.h
    trunk/sources/richtext/
    trunk/sources/richtext/README.txt
    trunk/sources/richtext/addlinkdialog.ui
    trunk/sources/richtext/richtexteditor.cpp
    trunk/sources/richtext/richtexteditor_p.h
    trunk/sources/richtext/ui_addlinkdialog.h
    trunk/sources/terminal.cpp
    trunk/sources/terminal.h
    trunk/sources/titleblock/
    trunk/sources/titleblock/dimension.cpp
    trunk/sources/titleblock/dimension.h
    trunk/sources/titleblock/dimensionwidget.cpp
    trunk/sources/titleblock/dimensionwidget.h
    trunk/sources/titleblock/gridlayoutanimation.cpp
    trunk/sources/titleblock/gridlayoutanimation.h
    trunk/sources/titleblock/helpercell.cpp
    trunk/sources/titleblock/helpercell.h
    trunk/sources/titleblock/integrationmovetemplateshandler.cpp
    trunk/sources/titleblock/integrationmovetemplateshandler.h
    trunk/sources/titleblock/movetemplateshandler.h
    trunk/sources/titleblock/qettemplateeditor.cpp
    trunk/sources/titleblock/qettemplateeditor.h
    trunk/sources/titleblock/splittedhelpercell.cpp
    trunk/sources/titleblock/splittedhelpercell.h
    trunk/sources/titleblock/templatecellsset.cpp
    trunk/sources/titleblock/templatecellsset.h
    trunk/sources/titleblock/templatecellwidget.cpp
    trunk/sources/titleblock/templatecellwidget.h
    trunk/sources/titleblock/templatecommands.cpp
    trunk/sources/titleblock/templatecommands.h
    trunk/sources/titleblock/templatedeleter.cpp
    trunk/sources/titleblock/templatedeleter.h
    trunk/sources/titleblock/templatelocation.cpp
    trunk/sources/titleblock/templatelocation.h
    trunk/sources/titleblock/templatelocationchooser.cpp
    trunk/sources/titleblock/templatelocationchooser.h
    trunk/sources/titleblock/templatelocationsaver.cpp
    trunk/sources/titleblock/templatelocationsaver.h
    trunk/sources/titleblock/templatelogomanager.cpp
    trunk/sources/titleblock/templatelogomanager.h
    trunk/sources/titleblock/templatescollection.cpp
    trunk/sources/titleblock/templatescollection.h
    trunk/sources/titleblock/templateview.cpp
    trunk/sources/titleblock/templateview.h
    trunk/sources/titleblock/templatevisualcell.cpp
    trunk/sources/titleblock/templatevisualcell.h
    trunk/sources/titleblockcell.cpp
    trunk/sources/titleblockcell.h
    trunk/sources/titleblockproperties.cpp
    trunk/sources/titleblockproperties.h
    trunk/sources/titleblockpropertieswidget.cpp
    trunk/sources/titleblockpropertieswidget.h
    trunk/sources/titleblocktemplate.cpp
    trunk/sources/titleblocktemplate.h
    trunk/sources/titleblocktemplaterenderer.cpp
    trunk/sources/titleblocktemplaterenderer.h
    trunk/sources/treecoloranimation.cpp
    trunk/sources/treecoloranimation.h
    trunk/sources/ui/
    trunk/sources/ui/diagramselection.cpp
    trunk/sources/ui/diagramselection.h
    trunk/sources/ui/diagramselection.ui
    trunk/sources/ui/dialogautonum.cpp
    trunk/sources/ui/dialogautonum.h
    trunk/sources/ui/dialogautonum.ui
    trunk/sources/ui/dialogwaiting.cpp
    trunk/sources/ui/dialogwaiting.h
    trunk/sources/ui/dialogwaiting.ui
    trunk/sources/ui/numparteditorw.cpp
    trunk/sources/ui/numparteditorw.h
    trunk/sources/ui/numparteditorw.ui
    trunk/sources/ui/selectautonumw.cpp
    trunk/sources/ui/selectautonumw.h
    trunk/sources/ui/selectautonumw.ui
    trunk/sources/xmlelementdefinition.cpp
    trunk/sources/xmlelementdefinition.h
    trunk/sources/xmlelementscategory.cpp
    trunk/sources/xmlelementscategory.h
    trunk/sources/xmlelementscollection.cpp
    trunk/sources/xmlelementscollection.h

Removed Paths:
-------------
    trunk/sources/

Modified: trunk/qelectrotech.pro
===================================================================
--- trunk/qelectrotech.pro	2013-11-14 09:55:08 UTC (rev 2612)
+++ trunk/qelectrotech.pro	2013-11-14 10:11:22 UTC (rev 2613)
@@ -64,8 +64,8 @@
 INCLUDEPATH += sources sources/editor sources/titleblock
 
 # Fichiers sources
-HEADERS += sources/*.h   sources/ui/*.h   sources/editor/*.h   sources/titleblock/*.h  sources/richtext/*.h
-SOURCES += sources/*.cpp sources/editor/*.cpp sources/titleblock/*.cpp sources/richtext/*.cpp sources/ui/*.cpp
+HEADERS += sources/*.h   sources/ui/*.h   sources/editor/*.h   sources/titleblock/*.h  sources/richtext/*.h sources/qetgraphicsitem/*.h
+SOURCES += sources/*.cpp sources/editor/*.cpp sources/titleblock/*.cpp sources/richtext/*.cpp sources/ui/*.cpp sources/qetgraphicsitem/*.cpp
 
 # Liste des fichiers qui seront incorpores au binaire en tant que ressources Qt
 RESOURCES += qelectrotech.qrc

Added: trunk/sources/aboutqet.cpp
===================================================================
--- trunk/sources/aboutqet.cpp	                        (rev 0)
+++ trunk/sources/aboutqet.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,222 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <QtGui>
+#include "qettabwidget.h"
+#include "aboutqet.h"
+#include "qet.h"
+#include "qeticons.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", "window title"));
+	setMinimumWidth(680);
+	setMinimumHeight(690);
+	setModal(true);
+	
+	// Trois onglets
+	QETTabWidget *tabs = new QETTabWidget(this);
+	tabs -> addTab(aboutTab(),        tr("\300 &propos",       "tab title"));
+	tabs -> addTab(authorsTab(),      tr("A&uteurs",           "tab title"));
+	tabs -> addTab(translatorsTab(),  tr("&Traducteurs",       "tab title"));
+	tabs -> addTab(contributorsTab(), tr("&Contributeurs",     "tab title"));
+	tabs -> addTab(licenseTab(),      tr("&Accord de licence", "tab title"));
+	
+	// Un bouton pour fermer la boite de dialogue
+	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close);
+	connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
+	connect(buttons, SIGNAL(rejected()), this, SLOT(accept()));
+	
+	// Le tout dans une disposition verticale
+	QVBoxLayout *vlayout = new QVBoxLayout();
+	vlayout -> addWidget(title());
+	vlayout -> addWidget(tabs);
+	vlayout -> addWidget(buttons);
+	setLayout(vlayout);
+}
+
+/**
+	Destructeur
+*/
+AboutQET::~AboutQET() {
+}
+
+/**
+	@return Le titre QElectroTech avec son icone
+*/
+QWidget *AboutQET::title() const {
+	QWidget *icon_and_title = new QWidget();
+	// icone
+	QLabel *icon = new QLabel();
+	icon -> setPixmap(QET::Icons::QETOxygenLogo.pixmap(48, 48));
+	// label "QElectroTech"
+	QLabel *title = new QLabel("<span style=\"font-weight:0;font-size:16pt;\">QElectroTech v" + QET::displayedVersion + "</span>");
+	QString compilation_info = "<br />" + tr("Compilation : ") +  __DATE__ + " "  + __TIME__;
+#ifdef __GNUC__
+	compilation_info += " - GCC " + QString(__VERSION__);
+	compilation_info += " - built with Qt " + QString(QT_VERSION_STR);
+	compilation_info += " - run with Qt "+ QString(qVersion());
+#endif
+	title -> setText(title->text() + compilation_info);
+	title -> setTextFormat(Qt::RichText);
+	
+	QHBoxLayout *hlayout = new QHBoxLayout();
+	hlayout -> addWidget(icon);
+	hlayout -> addWidget(title);
+	hlayout -> addStretch();
+	icon_and_title -> setLayout(hlayout);
+	return(icon_and_title);
+}
+
+/**
+	@return Le widget contenu par l'onglet "A propos"
+*/
+QWidget *AboutQET::aboutTab() const {
+	QLabel *about = new QLabel(
+		tr("QElectroTech, une application de r\351alisation de sch\351mas \351lectriques.", "about tab, description line") +
+		"<br><br>" +
+		tr("\251 2006-2013 Les d\351veloppeurs de QElectroTech", "about tab, developers line") +
+		"<br><br>"
+		"<a href=\"http://qelectrotech.org/\";>http://qelectrotech.org/</a>"
+		"<br><br>" +
+		tr("Contact\240: <a href=\"mailto:qet@xxxxxxxxxxxxxxxxxxx\";>qet@xxxxxxxxxxxxxxxxxxx</a>", "about tab, contact line")
+	);
+	about -> setAlignment(Qt::AlignCenter);
+	about -> setOpenExternalLinks(true);
+	about -> setTextFormat(Qt::RichText);
+	return(about);
+}
+
+/**
+	@return Le widget contenu par l'onglet "Auteurs"
+*/
+QWidget *AboutQET::authorsTab() const {
+	QLabel *authors = new QLabel();
+	addAuthor(authors, "Beno\356t Ansieau",  "benoit@xxxxxxxxxxxxxxxx",     tr("Id\351e originale"));
+	addAuthor(authors, "Laurent Trinques",   "scorpio@xxxxxxxxxxxxxxxx",    tr("Collection d'\351l\351ments & D\351veloppement"));
+	addAuthor(authors, "Cyril Frausti",      "cyril@xxxxxxxxxxxxxxxx",      tr("D\351veloppement"));
+	addAuthor(authors, "Joshua Claveau",     "Joshua@xxxxxxxxxxxxxxxx",     tr("D\351veloppement"));
+
+	authors -> setAlignment(Qt::AlignCenter);
+	authors -> setOpenExternalLinks(true);
+	authors -> setTextFormat(Qt::RichText);
+	return(authors);
+}
+
+/**
+	@return Le widget contenu par l'onglet "Traducteurs"
+*/
+QWidget *AboutQET::translatorsTab() const {
+	QLabel *translators = new QLabel();
+	
+	addAuthor(translators, "Alfredo Carreto",            "electronicos_mx@xxxxxxxxxxxx",tr("Traduction en espagnol"));
+	addAuthor(translators, "Yuriy Litkevich",            "yuriy@xxxxxxxxxxxxxxxx",      tr("Traduction en russe"));
+	addAuthor(translators, "Jos\351 Carlos Martins",     "jose@xxxxxxxxxxxxxxxx",       tr("Traduction en portugais"));
+	addAuthor(translators, "Pavel Fric",                 "pavelfric@xxxxxxxxx",         tr("Traduction en tch\350que"));
+	addAuthor(translators, "Pawe&#x0142; &#x015A;miech", "pawel32640@xxxxxxxxx",        tr("Traduction en polonais"));
+	addAuthor(translators, "Markus Budde",               "markus.budde@xxxxxxx",        tr("Traduction en allemand"));
+	addAuthor(translators, "Jonas Stein",                "news@xxxxxxxxxxxxx",          tr("Traduction en allemand"));
+	addAuthor(translators, "Noah Braden",                "",                            tr("Traduction en allemand"));
+	addAuthor(translators, "Gabi Mandoc",                "gabriel.mandoc@xxxxxx",       tr("Traduction en roumain"));
+	addAuthor(translators, "Alessandro Conti",           "dr.slump@xxxxxxxxxxxx",       tr("Traduction en italien"));
+	addAuthor(translators, "Silvio",                     "silvio@xxxxxxxxxxxxxxxx",     tr("Traduction en italien"));
+	addAuthor(translators, "Mohamed Souabni",            "souabnimohamed@xxxxxxxx",     tr("Traduction en arabe"));
+	addAuthor(translators, "Antun Marakovi&#x0107;",     "antun.marakovic@xxxxxxxxxxxx",tr("Traduction en croate"));
+	addAuthor(translators, "Eduard Amor\363s",           "amoros@xxxxxxxxxxxxx",        tr("Traduction en catalan"));
+	addAuthor(translators, "Nikos Papadopoylos",         "231036448@xxxxxxxxxxx",       tr("Traduction en grec"));
+	addAuthor(translators, "Yannis Gyftomitros",         "yang@xxxxxxxxx",              tr("Traduction en grec"));
+	addAuthor(translators, "Eduard Amoros",              "",                            tr("Traduction en catalan"));
+
+
+
+	
+	translators -> setOpenExternalLinks(true);
+	translators -> setTextFormat(Qt::RichText);
+	
+	QWidget *translators_widget = new QWidget();
+	QHBoxLayout *translators_layout = new QHBoxLayout(translators_widget);
+	translators_layout -> addWidget(translators, 0, Qt::AlignCenter);
+	return(translators_widget);
+}
+
+/**
+	@return Le widget contenu par l'onglet "Contributeurs"
+*/
+QWidget *AboutQET::contributorsTab() const {
+	QLabel *contributors = new QLabel();
+	
+	addAuthor(contributors, "Remi Collet",         "remi@xxxxxxxxxxxxxxxxx",      tr("Paquets Fedora et Red Hat"));
+	addAuthor(contributors, "Trem",                "trem@xxxxxxxxxx",             tr("Paquets Mageia"));
+	addAuthor(contributors, "Laurent Trinques",    "scorpio@xxxxxxxxxxxxxxxx",    tr("Paquets Debian"));
+	addAuthor(contributors, "Markos Chandras",     "hwoarang@xxxxxxxxxx.",        tr("Paquets Gentoo"));
+	addAuthor(contributors, "Mbit",                 "",                           tr("Paquets Gentoo"));
+	addAuthor(contributors, "Elbert",               "",                           tr("Paquets OS/2"));
+	addAuthor(contributors, "zloidemon",            "",                           tr("Paquets FreeBSD"));
+	addAuthor(contributors, "Chipsterjulien",       "",                           tr("Paquets Archlinux AUR"));
+	addAuthor(contributors, "Nuno Pinheiro",       "nuno@xxxxxxxxxxxxxx",         tr("Ic\364nes"));
+
+	
+	contributors -> setOpenExternalLinks(true);
+	contributors -> setTextFormat(Qt::RichText);
+	
+	QWidget *contributors_widget = new QWidget();
+	QHBoxLayout *contributors_layout = new QHBoxLayout(contributors_widget);
+	contributors_layout -> addWidget(contributors, 0, Qt::AlignCenter);
+	return(contributors_widget);
+}
+
+/**
+	@return Le widget contenu par l'onglet "Accord de Licence"
+*/
+QWidget *AboutQET::licenseTab() const {
+	QWidget *license = new QWidget();
+	// label
+	QLabel *title_license = new QLabel(tr("Ce programme est sous licence GNU/GPL."));
+	
+	// texte de la GNU/GPL dans une zone de texte scrollable non editable
+	QTextEdit *text_license = new QTextEdit();
+	text_license -> setPlainText(QET::license());
+	text_license -> setReadOnly(true);
+	
+	// le tout dans une disposition verticale
+	QVBoxLayout *license_layout = new QVBoxLayout();
+	license_layout -> addWidget(title_license);
+	license_layout -> addWidget(text_license);
+	license -> setLayout(license_layout);
+	return(license);
+}
+
+/**
+	Ajoute une personne a la liste des auteurs
+	@param label QLabel auquel sera ajoute la personne
+	@param name  Nom de la personne
+	@param email Adresse e-mail de la personne
+	@param work  Fonction / travail effectue par la personne
+*/
+void AboutQET::addAuthor(QLabel *label, const QString &name, const QString &email, const QString &work) const {
+	QString new_text = label -> text();
+	
+	QString author_template = "<span style=\"text-decoration: underline;\">%1</span> : %2 &lt;<a href=\"mailto:%3\";>%3</a>&gt;&lrm;<br/><br/>";
+	
+	// ajoute la fonction de la personne
+	new_text += author_template.arg(work).arg(name).arg(email);
+	label -> setText(new_text);
+}

Added: trunk/sources/aboutqet.h
===================================================================
--- trunk/sources/aboutqet.h	                        (rev 0)
+++ trunk/sources/aboutqet.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,46 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ABOUTQET_H
+#define ABOUTQET_H
+#include <QDialog>
+class QLabel;
+/**
+	This class represents the "About QElectroTech" dialog.
+*/
+class AboutQET : public QDialog {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	AboutQET(QWidget * = 0);
+	virtual ~AboutQET();
+	
+	private:
+	AboutQET(AboutQET &);
+	
+	// methods
+	private:
+	QWidget *title() const;
+	QWidget *aboutTab() const;
+	QWidget *authorsTab() const;
+	QWidget *translatorsTab() const;
+	QWidget *contributorsTab() const;
+	QWidget *licenseTab() const;
+	void addAuthor(QLabel *, const QString &, const QString &, const QString &) const;
+};
+#endif

Added: trunk/sources/autonumerotation.cpp
===================================================================
--- trunk/sources/autonumerotation.cpp	                        (rev 0)
+++ trunk/sources/autonumerotation.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,5 @@
+#include "autonumerotation.h"
+
+AutoNumerotation::AutoNumerotation(Diagram *d):
+	diagram_ (d)
+{}

Added: trunk/sources/autonumerotation.h
===================================================================
--- trunk/sources/autonumerotation.h	                        (rev 0)
+++ trunk/sources/autonumerotation.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,22 @@
+#ifndef AUTONUMEROTATION_H
+#define AUTONUMEROTATION_H
+
+#include "diagram.h"
+
+class AutoNumerotation: public QObject
+{
+	Q_OBJECT
+
+	public:
+	AutoNumerotation(Diagram *);
+	virtual void numerate() = 0;
+
+	public slots:
+	virtual void applyText(QString) = 0;
+
+	protected:
+	Diagram *diagram_;
+	NumerotationContext num_context;
+};
+
+#endif // AUTONUMEROTATION_H

Added: trunk/sources/basicmoveelementshandler.cpp
===================================================================
--- trunk/sources/basicmoveelementshandler.cpp	                        (rev 0)
+++ trunk/sources/basicmoveelementshandler.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,143 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "basicmoveelementshandler.h"
+
+/**
+	Constructeur
+	@param parent QObject parent
+*/
+BasicMoveElementsHandler::BasicMoveElementsHandler(QObject *parent) :
+	MoveElementsHandler(parent),
+	already_exists_(QET::Erase),
+	not_readable_(QET::Ignore),
+	not_writable_(QET::Ignore),
+	error_(QET::Ignore),
+	rename_("renamed")
+{
+}
+
+/**
+	Destructeur
+*/
+BasicMoveElementsHandler::~BasicMoveElementsHandler() {
+}
+
+/**
+	@param action Action a renvoyer si un item existe deja
+*/
+void BasicMoveElementsHandler::setActionIfItemAlreadyExists(QET::Action action) {
+	already_exists_ = action;
+}
+
+/**
+	@param action Action a renvoyer si un item n'est pas lisible
+*/
+void BasicMoveElementsHandler::setActionIfItemIsNotReadable(QET::Action action) {
+	not_readable_ = action;
+}
+
+/**
+	@param action Action a renvoyer si un item n'est pas accessible en ecriture
+*/
+void BasicMoveElementsHandler::setActionIfItemIsNotWritable(QET::Action action) {
+	not_writable_ = action;
+}
+
+/**
+	@param action Action a renvoyer si un item provoque une erreur
+*/
+void BasicMoveElementsHandler::setActionIfItemTriggersAnError(QET::Action action) {
+	error_ = action;
+}
+
+/**
+	@param name Nom a renvoyer pour une eventuelle operation de renommage
+	Il est toutefois deconseille de proceder a un renommage systematique, vu que
+	cette propriete est invariable.
+*/
+void BasicMoveElementsHandler::setNameForRenamingOperation(const QString &name) {
+	rename_ = name;
+}
+
+/**
+	@return l'action a effectuer si la categorie cible existe deja
+*/
+QET::Action BasicMoveElementsHandler::categoryAlreadyExists(ElementsCategory *, ElementsCategory  *) {
+	return(already_exists_);
+}
+
+/**
+	@return l'action a effectuer si l'element cible existe deja
+*/
+QET::Action BasicMoveElementsHandler::elementAlreadyExists(ElementDefinition *, ElementDefinition *) {
+	return(already_exists_);
+}
+
+/**
+	@return l'action a effectuer si la categorie existe deja
+*/
+QET::Action BasicMoveElementsHandler::categoryIsNotReadable(ElementsCategory *) {
+	return(not_readable_);
+}
+
+/**
+	@return l'action a effectuer si l'element existe deja
+*/
+QET::Action BasicMoveElementsHandler::elementIsNotReadable(ElementDefinition *) {
+	return(not_readable_);
+}
+
+/**
+	@return l'action a effectuer si la categorie cible n'est pas accessible
+	en ecriture
+*/
+QET::Action BasicMoveElementsHandler::categoryIsNotWritable(ElementsCategory *) {
+	return(not_writable_);
+}
+
+/**
+	@return l'action a effectuer si l'element cible n'est pas accessible
+	en ecriture
+*/
+QET::Action BasicMoveElementsHandler::elementIsNotWritable(ElementDefinition *) {
+	return(not_writable_);
+}
+
+/**
+	@return l'action a effectuer lorsque l'erreur decrite dans la QString
+	s'est produite avec la categorie indiquee
+*/
+QET::Action BasicMoveElementsHandler::errorWithACategory(ElementsCategory *, const QString &) {
+	return(error_);
+}
+
+/**
+	@return l'action a effectuer lorsque l'erreur decrite dans la QString
+	s'est produite avec l'element indique
+*/
+QET::Action BasicMoveElementsHandler::errorWithAnElement(ElementDefinition *, const QString &) {
+	return(error_);
+}
+
+/**
+	@return le nom a utiliser pour le renommage si une methode de cet objet
+	a precedemment renvoye QET::Rename.
+*/
+QString BasicMoveElementsHandler::nameForRenamingOperation() {
+	return(rename_);
+}

Added: trunk/sources/basicmoveelementshandler.h
===================================================================
--- trunk/sources/basicmoveelementshandler.h	                        (rev 0)
+++ trunk/sources/basicmoveelementshandler.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,62 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BASIC_MOVE_ELEMENTS_HANDLER
+#define BASIC_MOVE_ELEMENTS_HANDLER
+#include "moveelementshandler.h"
+/**
+	This class implements the MoveElementsHandler Strategy class in a baasic way.
+	It always returns the same (configurable) action or value for a particular
+	question.
+*/
+class BasicMoveElementsHandler : public MoveElementsHandler {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	BasicMoveElementsHandler(QObject * = 0);
+	virtual ~BasicMoveElementsHandler();
+	private:
+	BasicMoveElementsHandler(const BasicMoveElementsHandler &);
+	
+	// methods
+	public:
+	virtual void setActionIfItemAlreadyExists(QET::Action);
+	virtual void setActionIfItemIsNotReadable(QET::Action);
+	virtual void setActionIfItemIsNotWritable(QET::Action);
+	virtual void setActionIfItemTriggersAnError(QET::Action);
+	virtual void setNameForRenamingOperation(const QString &);
+	
+	virtual QET::Action categoryAlreadyExists(ElementsCategory *src, ElementsCategory  *dst);
+	virtual QET::Action elementAlreadyExists(ElementDefinition *src, ElementDefinition *dst);
+	virtual QET::Action categoryIsNotReadable(ElementsCategory *);
+	virtual QET::Action elementIsNotReadable(ElementDefinition *);
+	virtual QET::Action categoryIsNotWritable(ElementsCategory *);
+	virtual QET::Action elementIsNotWritable(ElementDefinition *);
+	virtual QET::Action errorWithACategory(ElementsCategory *, const QString &);
+	virtual QET::Action errorWithAnElement(ElementDefinition *, const QString &);
+	virtual QString nameForRenamingOperation();
+	
+	// attributes
+	private:
+	QET::Action already_exists_;
+	QET::Action not_readable_;
+	QET::Action not_writable_;
+	QET::Action error_;
+	QString rename_;
+};
+#endif

Added: trunk/sources/borderproperties.cpp
===================================================================
--- trunk/sources/borderproperties.cpp	                        (rev 0)
+++ trunk/sources/borderproperties.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,122 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "borderproperties.h"
+
+/**
+	Constructeur. Initialise un objet BorderProperties avec les proprietes par
+	defaut suivantes :
+	  * 17 colonnes affichees de 60.0 px de large pour 20.0px de haut
+	  * 8 lignes affichees de 80.0 px de haut pour 20.0px de large
+*/
+BorderProperties::BorderProperties() :
+	columns_count(17),
+	columns_width(60.0),
+	columns_header_height(20.0),
+	display_columns(true),
+	rows_count(8),
+	rows_height(80.0),
+	rows_header_width(20.0),
+	display_rows(true)
+{
+}
+
+/**
+	Destructeur
+*/
+BorderProperties::~BorderProperties() {
+}
+
+/**
+	@param bp autre conteneur BorderProperties
+	@return true si ip et ce conteneur sont identiques, false sinon
+*/
+bool BorderProperties::operator==(const BorderProperties &bp) {
+	return(
+		bp.columns_count == columns_count &&\
+		bp.columns_width == columns_width &&\
+		bp.columns_header_height == columns_header_height &&\
+		bp.display_columns == display_columns &&\
+		bp.rows_count == rows_count &&\
+		bp.rows_height == rows_height &&\
+		bp.rows_header_width == rows_header_width &&\
+		bp.display_rows == display_rows
+	);
+}
+
+/**
+	@param bp autre conteneur BorderProperties
+	@return false si bp et ce conteneur sont identiques, true sinon
+*/
+bool BorderProperties::operator!=(const BorderProperties &bp) {
+	return(!(*this == bp));
+}
+
+/**
+	Exporte les dimensions sous formes d'attributs XML ajoutes a l'element e.
+	@param e Element XML auquel seront ajoutes des attributs
+*/
+void BorderProperties::toXml(QDomElement &e) const {
+	e.setAttribute("cols",        columns_count);
+	e.setAttribute("colsize",     QString("%1").arg(columns_width));
+	e.setAttribute("rows",        rows_count);
+	e.setAttribute("rowsize",     QString("%1").arg(rows_height));
+	e.setAttribute("displaycols", display_columns ? "true" : "false");
+	e.setAttribute("displayrows", display_rows    ? "true" : "false");
+}
+
+/**
+	Importe les dimensions a partir des attributs XML de l'element e
+	@param e Element XML dont les attributs seront lus
+*/
+void BorderProperties::fromXml(QDomElement &e) {
+	if (e.hasAttribute("cols"))        columns_count   = e.attribute("cols").toInt();
+	if (e.hasAttribute("colsize"))     columns_width   = e.attribute("colsize").toInt();
+	if (e.hasAttribute("rows"))        rows_count      = e.attribute("rows").toInt();
+	if (e.hasAttribute("rowsize"))     rows_height     = e.attribute("rowsize").toInt();
+	if (e.hasAttribute("displaycols")) display_columns = e.attribute("displaycols") == "true";
+	if (e.hasAttribute("displayrows")) display_rows    = e.attribute("displayrows") == "true";
+}
+
+/**
+	Exporte les dimensions dans une configuration.
+	@param settings Parametres a ecrire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void BorderProperties::toSettings(QSettings &settings, const QString &prefix) const {
+	settings.setValue(prefix + "cols",        columns_count);
+	settings.setValue(prefix + "colsize",     columns_width);
+	settings.setValue(prefix + "displaycols", display_columns);
+	settings.setValue(prefix + "rows",        rows_count);
+	settings.setValue(prefix + "rowsize",     rows_height);
+	settings.setValue(prefix + "displayrows", display_rows);
+}
+
+/**
+	Importe les dimensions depuis une configuration.
+	@param settings Parametres a lire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void BorderProperties::fromSettings(QSettings &settings, const QString &prefix) {
+	columns_count   = settings.value(prefix + "cols",            columns_count).toInt();
+	columns_width   = qRound(settings.value(prefix + "colsize",  columns_width).toDouble());
+	display_columns = settings.value(prefix + "displaycols",     display_columns).toBool();
+	
+	rows_count      = settings.value(prefix + "rows",            rows_count).toInt();
+	rows_height     = qRound(settings.value(prefix + "rowsize",  rows_height).toDouble());
+	display_rows    = settings.value(prefix + "displayrows",     display_rows).toBool();
+}

Added: trunk/sources/borderproperties.h
===================================================================
--- trunk/sources/borderproperties.h	                        (rev 0)
+++ trunk/sources/borderproperties.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,51 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BORDER_PROPERTIES_H
+#define BORDER_PROPERTIES_H
+#include <QtCore>
+#include <QtXml>
+/**
+	This class is a container for dimensions and display properties of a
+	diagram.
+*/
+class BorderProperties {
+	public:
+	// constructor, destructor, operators
+	BorderProperties();
+	virtual ~BorderProperties();
+	
+	bool operator==(const BorderProperties &);
+	bool operator!=(const BorderProperties &);
+	
+	void toXml(QDomElement &) const;
+	void fromXml(QDomElement &);
+	void toSettings(QSettings &, const QString & = QString()) const;
+	void fromSettings(QSettings &, const QString & = QString());
+	
+	// attributes
+	int columns_count;            ///< Columns count
+	qreal columns_width;          ///< Columns width
+	qreal columns_header_height;  ///< Column headers height
+	bool display_columns;         ///< Whether to display column headers
+	
+	int rows_count;               ///< Rows count
+	qreal rows_height;            ///< Rows height
+	qreal rows_header_width;      ///< Row headers width
+	bool display_rows;            ///< Whether to display row headers
+};
+#endif

Added: trunk/sources/borderpropertieswidget.cpp
===================================================================
--- trunk/sources/borderpropertieswidget.cpp	                        (rev 0)
+++ trunk/sources/borderpropertieswidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,142 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "borderpropertieswidget.h"
+#include <QtGui>
+#include "qetapp.h"
+#include "bordertitleblock.h"
+
+/**
+	Constructeur
+	Construit un widget editant les proprietes d'une bordure
+	@param bp proprietes a editer
+	@param parent QWidget parent
+*/
+BorderPropertiesWidget::BorderPropertiesWidget(const BorderProperties &bp, QWidget *parent) :
+	QWidget(parent)
+{
+	build();
+	setEditedBorder(bp);
+}
+
+/**
+	Destructeur
+*/
+BorderPropertiesWidget::~BorderPropertiesWidget() {
+}
+
+/**
+	@return Les proprietes editees par ce widget
+*/
+const BorderProperties &BorderPropertiesWidget::borderProperties() {
+	border_.columns_count   = columns_count   -> value();
+	border_.columns_width   = columns_width   -> value();
+	border_.display_columns = display_columns -> isChecked();
+	border_.rows_count      = rows_count      -> value();
+	border_.rows_height     = rows_height     -> value();
+	border_.display_rows    = display_rows    -> isChecked();
+	return(border_);
+}
+
+/**
+	@return true si ce widget est en lecture seule, false sinon
+*/
+bool BorderPropertiesWidget::isReadOnly() const {
+	return(columns_count -> isReadOnly());
+}
+
+/**
+	@param ro true pour passer ce widget en lecture seule, false sinon
+*/
+void BorderPropertiesWidget::setReadOnly(bool ro) {
+	columns_count   -> setReadOnly(ro);
+	columns_width   -> setReadOnly(ro);
+	display_columns -> setDisabled(ro);
+	rows_count      -> setReadOnly(ro);
+	rows_height     -> setReadOnly(ro);
+	display_rows    -> setDisabled(ro);
+}
+
+/**
+	Definit les proprietes a editer
+	@param bp Nouvelles proprietes
+*/
+void BorderPropertiesWidget::setEditedBorder(const BorderProperties &bp) {
+	border_ = bp;
+	columns_count   -> setValue(border_.columns_count);
+	columns_width   -> setValue(qRound(border_.columns_width));
+	display_columns -> setChecked(border_.display_columns);
+	rows_count      -> setValue(border_.rows_count);
+	rows_height     -> setValue(qRound(border_.rows_height));
+	display_rows    -> setChecked(border_.display_rows);
+}
+
+/**
+	Construit le widget
+*/
+void BorderPropertiesWidget::build() {
+	QVBoxLayout *widget_layout = new QVBoxLayout();
+	widget_layout -> setContentsMargins(0, 0, 0, 0);
+	
+	QGroupBox *diagram_size_box = new QGroupBox(tr("Dimensions du sch\351ma"));
+	QGridLayout *diagram_size_box_layout = new QGridLayout(diagram_size_box);
+	
+	// colonnes : nombre et largeur
+	QLabel *ds1 = new QLabel(tr("Colonnes :"));
+	
+	columns_count = new QSpinBox(diagram_size_box);
+	columns_count -> setMinimum(BorderTitleBlock::minNbColumns());
+	columns_count -> setMaximum(10000); // valeur arbitraire
+	
+	columns_width = new QSpinBox(diagram_size_box);
+	columns_width -> setMinimum(qRound(BorderTitleBlock::minColumnsWidth()));
+	columns_width -> setMaximum(10000);
+	columns_width -> setSingleStep(10);
+	columns_width -> setPrefix(tr("\327", "multiplication symbol"));
+	columns_width -> setSuffix(tr("px",   "unit for cols width"));
+	
+	display_columns = new QCheckBox(tr("Afficher les en-t\352tes"), diagram_size_box);
+	
+	// lignes : nombre et largeur
+	QLabel *ds2 = new QLabel(tr("Lignes :"));
+	
+	rows_count = new QSpinBox(diagram_size_box);
+	rows_count -> setMinimum(BorderTitleBlock::minNbRows());
+	rows_count -> setMaximum(10000); // valeur arbitraire
+	
+	rows_height  = new QSpinBox(diagram_size_box);
+	rows_height -> setMinimum(qRound(BorderTitleBlock::minRowsHeight()));
+	rows_height -> setMaximum(10000);
+	rows_height -> setSingleStep(10);
+	rows_height -> setPrefix(tr("\327", "multiplication symbol"));
+	rows_height -> setSuffix(tr("px",   "unit for rows height"));
+	
+	display_rows = new QCheckBox(tr("Afficher les en-t\352tes"), diagram_size_box);
+	
+	// layout
+	diagram_size_box_layout -> addWidget(ds1,            0, 0);
+	diagram_size_box_layout -> addWidget(columns_count,  0, 1);
+	diagram_size_box_layout -> addWidget(columns_width,  0, 2);
+	diagram_size_box_layout -> addWidget(display_columns,0, 3);
+	diagram_size_box_layout -> addWidget(ds2,            1, 0);
+	diagram_size_box_layout -> addWidget(rows_count,     1, 1);
+	diagram_size_box_layout -> addWidget(rows_height,    1, 2);
+	diagram_size_box_layout -> addWidget(display_rows,   1, 3);
+	
+	widget_layout -> addWidget(diagram_size_box);
+	setLayout(widget_layout);
+}

Added: trunk/sources/borderpropertieswidget.h
===================================================================
--- trunk/sources/borderpropertieswidget.h	                        (rev 0)
+++ trunk/sources/borderpropertieswidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,59 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BORDER_PROPERTIES_WIDGET_H
+#define BORDER_PROPERTIES_WIDGET_H
+#include <QWidget>
+#include "borderproperties.h"
+class QCheckBox;
+class QSpinBox;
+/**
+	This class provides a widget to edit dimensions and display properties of a
+	diagram, title block excluded.
+	@see TitleBlockPropertiesWidget
+*/
+class BorderPropertiesWidget : public QWidget {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	BorderPropertiesWidget(const BorderProperties &, QWidget * = 0);
+	virtual ~BorderPropertiesWidget();
+	private:
+	BorderPropertiesWidget(const BorderPropertiesWidget &);
+	
+	// methods
+	public:
+	const BorderProperties &borderProperties();
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	void setEditedBorder(const BorderProperties &);
+	
+	private:
+	void build();
+	
+	// attributes
+	private:
+	BorderProperties border_;       ///< Edited properties
+	QSpinBox *columns_count;        ///< Widget to edit the columns count
+	QSpinBox *columns_width;        ///< Widget to edit the columns width
+	QCheckBox *display_columns;     ///< Checkbox stating whether to display column headers
+	QSpinBox *rows_count;           ///< Widget to edit the rows count
+	QSpinBox *rows_height;          ///< Widget to edit the rows height
+	QCheckBox *display_rows;        ///< Checkbox stating whether to display row headers
+};
+#endif

Added: trunk/sources/bordertitleblock.cpp
===================================================================
--- trunk/sources/bordertitleblock.cpp	                        (rev 0)
+++ trunk/sources/bordertitleblock.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,633 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <QPainter>
+#include "titleblocktemplate.h"
+#include "titleblocktemplaterenderer.h"
+#include "bordertitleblock.h"
+#include "diagramposition.h"
+#include "qetapp.h"
+#include "math.h"
+
+/**
+	Constructeur simple : construit une bordure en recuperant les dimensions
+	dans la configuration de l'application.
+	@param parent QObject parent de ce BorderTitleBlock
+*/
+BorderTitleBlock::BorderTitleBlock(QObject *parent) :
+	QObject(parent)
+{
+	// at first, the internal titleblock template renderer uses the default titleblock template
+	titleblock_template_renderer_ = new TitleBlockTemplateRenderer(this);
+	titleblock_template_renderer_ -> setTitleBlockTemplate(QETApp::defaultTitleBlockTemplate());
+	
+	// disable the QPicture-based cache from Qt 4.8 to avoid rendering errors and crashes
+	if (!QRegExp("4\\.[0-7]\\.").exactMatch(qVersion())) {
+		titleblock_template_renderer_ -> setUseCache(false);
+	}
+	
+	// dimensions par defaut du schema
+	importBorder(BorderProperties());
+	
+	// contenu par defaut du cartouche
+	importTitleBlock(TitleBlockProperties());
+	
+	display_titleblock_         = true;
+	display_border_        = true;
+	setFolioData(1, 1);
+	updateRectangles();
+}
+
+/**
+	Destructeur - ne fait rien
+*/
+BorderTitleBlock::~BorderTitleBlock() {
+}
+
+/**
+	@return la hauteur du cartouche
+*/
+qreal BorderTitleBlock::titleBlockHeight() const {
+	return(titleblock_template_renderer_ -> height());
+}
+
+/**
+	@return Le nombre minimum de colonnes qu'un schema doit comporter
+*/
+int BorderTitleBlock::minNbColumns() {
+	return(3);
+}
+
+/**
+	@return la largeur minimale d'une colonne de schema
+*/
+qreal BorderTitleBlock::minColumnsWidth() {
+	return(5.0);
+}
+
+/**
+	@return Le nombre minimum de lignes qu'un schema doit comporter
+*/
+int BorderTitleBlock::minNbRows() {
+	return(2);
+}
+
+/**
+	@return la hauteur minimale d'une ligne de schema
+*/
+qreal BorderTitleBlock::minRowsHeight() {
+	return(5.0);
+}
+
+/**
+	Exports the title block current values to XML.
+	@param xml_elmt the XML element attributes will be added to
+*/
+void BorderTitleBlock::titleBlockToXml(QDomElement &xml_elmt) {
+	exportTitleBlock().toXml(xml_elmt);
+}
+
+/**
+	Reads the title block values from XML.
+	@param xml_elmt the XML element values will be read from
+*/
+void BorderTitleBlock::titleBlockFromXml(const QDomElement &xml_elmt) {
+	TitleBlockProperties tbp;
+	tbp.fromXml(xml_elmt);
+	importTitleBlock(tbp);
+}
+
+/**
+	Exports the border current settings to XML.
+	@param xml_elmt the XML element attributes will be added to
+*/
+void BorderTitleBlock::borderToXml(QDomElement &xml_elmt) {
+	xml_elmt.setAttribute("cols",        columnsCount());
+	xml_elmt.setAttribute("colsize",     QString("%1").arg(columnsWidth()));
+	xml_elmt.setAttribute("displaycols", columnsAreDisplayed() ? "true" : "false");
+	
+	xml_elmt.setAttribute("rows",        rowsCount());
+	xml_elmt.setAttribute("rowsize",     QString("%1").arg(rowsHeight()));
+	xml_elmt.setAttribute("displayrows", rowsAreDisplayed() ? "true" : "false");
+	
+	// attribut datant de la version 0.1 - laisse pour retrocompatibilite
+	xml_elmt.setAttribute("height", QString("%1").arg(diagramHeight()));
+}
+
+/**
+	Reads the border settings from XML.
+	@param xml_elmt the XML element values will be read from
+*/
+void BorderTitleBlock::borderFromXml(const QDomElement &xml_elmt) {
+	bool ok;
+	// columns count
+	int cols_count = xml_elmt.attribute("cols").toInt(&ok);
+	if (ok) setColumnsCount(cols_count);
+	
+	// columns width
+	double cols_width = xml_elmt.attribute("colsize").toDouble(&ok);
+	if (ok) setColumnsWidth(cols_width);
+	
+	// backward compatibility: diagrams saved with 0.1 version have a "height" attribute
+	if (xml_elmt.hasAttribute("rows") && xml_elmt.hasAttribute("rowsize")) {
+		// rows counts
+		int rows_count = xml_elmt.attribute("rows").toInt(&ok);
+		if (ok) setRowsCount(rows_count);
+		
+		// taille des lignes
+		double rows_size = xml_elmt.attribute("rowsize").toDouble(&ok);
+		if (ok) setRowsHeight(rows_size);
+	} else {
+		// hauteur du schema
+		double height = xml_elmt.attribute("height").toDouble(&ok);
+		if (ok) setDiagramHeight(height);
+	}
+	
+	// rows and columns display
+	displayColumns(xml_elmt.attribute("displaycols") != "false");
+	displayRows(xml_elmt.attribute("displayrows") != "false");
+	
+	adjustTitleBlockToColumns();
+}
+
+/**
+	@return les proprietes du cartouches
+*/
+TitleBlockProperties BorderTitleBlock::exportTitleBlock() {
+	TitleBlockProperties ip;
+	
+	ip.author = author();
+	ip.date = date();
+	ip.title = title();
+	ip.filename = fileName();
+	ip.folio = folio();
+	ip.template_name = titleBlockTemplateName();
+	ip.context = additional_fields_;
+	
+	return(ip);
+}
+
+/**
+	@param ip les nouvelles proprietes du cartouche
+*/
+void BorderTitleBlock::importTitleBlock(const TitleBlockProperties &ip) {
+	setAuthor(ip.author);
+	setDate(ip.date);
+	setTitle(ip.title);
+	setFileName(ip.filename);
+	setFolio(ip.folio);
+	additional_fields_ = ip.context;
+	
+	emit(needFolioData()); // Note: we expect additional data to be provided
+	// through setFolioData(), which in turn calls updateDiagramContextForTitleBlock().
+	emit(needTitleBlockTemplate(ip.template_name));
+}
+
+/**
+	@return les proprietes de la bordure
+*/
+BorderProperties BorderTitleBlock::exportBorder() {
+	BorderProperties bp;
+	bp.columns_count = columnsCount();
+	bp.columns_width = columnsWidth();
+	bp.columns_header_height = columnsHeaderHeight();
+	bp.display_columns = columnsAreDisplayed();
+	bp.rows_count = rowsCount();
+	bp.rows_height = rowsHeight();
+	bp.rows_header_width = rowsHeaderWidth();
+	bp.display_rows = rowsAreDisplayed();
+	return(bp);
+}
+
+/**
+	@param bp les nouvelles proprietes de la bordure
+*/
+void BorderTitleBlock::importBorder(const BorderProperties &bp) {
+	setColumnsHeaderHeight(bp.columns_header_height);
+	setColumnsCount(bp.columns_count);
+	setColumnsWidth(bp.columns_width);
+	displayColumns(bp.display_columns);
+	setRowsHeaderWidth(bp.rows_header_width);
+	setRowsCount(bp.rows_count);
+	setRowsHeight(bp.rows_height);
+	displayRows(bp.display_rows);
+}
+
+/**
+	@return the titleblock template used to render the titleblock
+	@see TitleBlockTemplateRenderer::titleBlockTemplate()
+*/
+const TitleBlockTemplate *BorderTitleBlock::titleBlockTemplate() {
+	return(titleblock_template_renderer_ -> titleBlockTemplate());
+}
+
+/**
+	@param titleblock_template The new titleblock template to use to render the titleblock
+	@see TitleBlockTemplateRenderer::setTitleBlockTemplate()
+*/
+void BorderTitleBlock::setTitleBlockTemplate(const TitleBlockTemplate *titleblock_template) {
+	titleblock_template_renderer_ -> setTitleBlockTemplate(titleblock_template);
+}
+
+/**
+	@return The name of the template used to render the titleblock.
+*/
+QString BorderTitleBlock::titleBlockTemplateName() const {
+	QString tbt_name = titleblock_template_renderer_ -> titleBlockTemplate() -> name();
+	return((tbt_name == "default") ? "" : tbt_name);
+}
+
+/**
+	This slot may be used to inform this class that the given title block
+	template has changed. The title block-dedicated rendering cache will thus be
+	flushed.
+	@param template_name Name of the title block template that has changed
+*/
+void BorderTitleBlock::titleBlockTemplateChanged(const QString &template_name) {
+	if (titleBlockTemplateName() != template_name) return;
+	titleblock_template_renderer_ -> invalidateRenderedTemplate();
+}
+
+/**
+	This slot has to be used to inform this class that the given title block
+	template is about to be removed and is no longer accessible. This class
+	will either use the provided optional TitleBlockTemplate or the default
+	title block provided by QETApp::defaultTitleBlockTemplate()
+	@param template_name Name of the title block template that has changed
+	@param new_template (Optional) title block template to use instead
+*/
+void BorderTitleBlock::titleBlockTemplateRemoved(const QString &removed_template_name, const TitleBlockTemplate *new_template) {
+	if (titleBlockTemplateName() != removed_template_name) return;
+	
+	if (new_template) {
+		setTitleBlockTemplate(new_template);
+	} else {
+		setTitleBlockTemplate(QETApp::defaultTitleBlockTemplate());
+	}
+}
+
+/**
+	@param di true pour afficher le cartouche, false sinon
+*/
+void BorderTitleBlock::displayTitleBlock(bool di) {
+	bool change = (di != display_titleblock_);
+	display_titleblock_ = di;
+	if (change) emit(displayChanged());
+}
+
+/**
+	@param dc true pour afficher les entetes des colonnes, false sinon
+*/
+void BorderTitleBlock::displayColumns(bool dc) {
+	bool change = (dc != display_columns_);
+	display_columns_ = dc;
+	if (change) emit(displayChanged());
+}
+
+/**
+	@param dr true pour afficher les entetes des lignes, false sinon
+*/
+void BorderTitleBlock::displayRows(bool dr) {
+	bool change = (dr != display_rows_);
+	display_rows_ = dr;
+	if (change) emit(displayChanged());
+}
+
+/**
+	@param db true pour afficher la bordure du schema, false sinon
+	Note : si l'affichage de la bordure est ainsi desactivee, les lignes et
+	colonnes ne seront pas dessinees.
+*/
+void BorderTitleBlock::displayBorder(bool db) {
+	bool change = (db != display_border_);
+	display_border_  = db;
+	if (change) emit(displayChanged());
+}
+
+/**
+	Methode recalculant les rectangles composant le cadre et le cartouche en
+	fonction des attributs de taille
+*/
+void BorderTitleBlock::updateRectangles() {
+	// rectangle delimitant le schema
+	QRectF previous_diagram = diagram_rect_;
+	diagram_rect_ = QRectF(0, 0, diagramWidth(), diagramHeight());
+	if (diagram_rect_ != previous_diagram) emit(borderChanged(previous_diagram, diagram_rect_));
+	
+	// rectangles relatifs au cartouche
+	titleblock_rect_ = QRectF(diagram_rect_.bottomLeft().x(), diagram_rect_.bottomLeft().y(), titleBlockWidth(), titleBlockHeight());
+}
+
+/**
+	Dessine le cadre et le cartouche
+	@param qp QPainter a utiliser pour dessiner le cadre et le cartouche
+	@param x  Abscisse du cadre
+	@param y  Ordonnee du cadre
+*/
+void BorderTitleBlock::draw(QPainter *qp, qreal x, qreal y) {
+	// translate tous les rectangles
+	diagram_rect_     .translate(x, y);
+	titleblock_rect_       .translate(x, y);
+	
+	// prepare le QPainter
+	qp -> save();
+	qp -> setPen(Qt::black);
+	qp -> setBrush(Qt::NoBrush);
+	
+	// dessine le cadre
+	if (display_border_) qp -> drawRect(diagram_rect_);
+	
+	qp -> setFont(QETApp::diagramTextsFont());
+	
+	// dessine la case vide qui apparait des qu'il y a un entete
+	if (display_border_ && (display_columns_ || display_rows_)) {
+		qp -> setBrush(Qt::white);
+		QRectF first_rectangle(
+			diagram_rect_.topLeft().x(),
+			diagram_rect_.topLeft().y(),
+			rows_header_width_,
+			columns_header_height_
+		);
+		qp -> drawRect(first_rectangle);
+	}
+	
+	// dessine la numerotation des colonnes
+	if (display_border_ && display_columns_) {
+		for (int i = 1 ; i <= columns_count_ ; ++ i) {
+			QRectF numbered_rectangle = QRectF(
+				diagram_rect_.topLeft().x() + (rows_header_width_ + ((i - 1) * columns_width_)),
+				diagram_rect_.topLeft().y(),
+				columns_width_,
+				columns_header_height_
+			);
+			qp -> drawRect(numbered_rectangle);
+			qp -> drawText(numbered_rectangle, Qt::AlignVCenter | Qt::AlignCenter, QString("%1").arg(i));
+		}
+	}
+	
+	// dessine la numerotation des lignes
+	if (display_border_ && display_rows_) {
+		QString row_string("A");
+		for (int i = 1 ; i <= rows_count_ ; ++ i) {
+			QRectF lettered_rectangle = QRectF(
+				diagram_rect_.topLeft().x(),
+				diagram_rect_.topLeft().y() + (columns_header_height_ + ((i - 1) * rows_height_)),
+				rows_header_width_,
+				rows_height_
+			);
+			qp -> drawRect(lettered_rectangle);
+			qp -> drawText(lettered_rectangle, Qt::AlignVCenter | Qt::AlignCenter, row_string);
+			row_string = incrementLetters(row_string);
+		}
+	}
+	
+	// render the titleblock, using the TitleBlockTemplate object
+	if (display_titleblock_) {
+		qp -> translate(titleblock_rect_.topLeft());
+		titleblock_template_renderer_ -> render(qp, titleblock_rect_.width());
+		qp -> translate(-titleblock_rect_.topLeft());
+	}
+	
+	qp -> restore();
+	
+	// annule la translation des rectangles
+	diagram_rect_     .translate(-x, -y);
+	titleblock_rect_       .translate(-x, -y);
+}
+
+/**
+	Ajoute une colonne.
+*/
+void BorderTitleBlock::addColumn() {
+	setColumnsCount(columnsCount() + 1);
+}
+
+/**
+	Enleve une colonne sans passer sous le minimum requis.
+	@see minNbColumns()
+*/
+void BorderTitleBlock::removeColumn() {
+	setColumnsCount(columnsCount() - 1);
+}
+
+/**
+	Ajoute une ligne.
+*/
+void BorderTitleBlock::addRow() {
+	setRowsCount(rowsCount() + 1);
+}
+
+/**
+	Enleve une ligne sans passer sous le minimum requis.
+	@see minNbRows()
+*/
+void BorderTitleBlock::removeRow() {
+	setRowsCount(rowsCount() - 1);
+}
+
+/**
+	Permet de changer le nombre de colonnes.
+	Si ce nombre de colonnes est inferieur au minimum requis, c'est ce minimum
+	qui est utilise.
+	@param nb_c nouveau nombre de colonnes
+	@see minNbColumns()
+*/
+void BorderTitleBlock::setColumnsCount(int nb_c) {
+	if (nb_c == columnsCount()) return;
+	columns_count_ = qMax(minNbColumns(), nb_c);
+	setTitleBlockWidth(diagramWidth());
+}
+
+/**
+	Change la largeur des colonnes.
+	Si la largeur indiquee est inferieure au minimum requis, c'est ce minimum
+	qui est utilise.
+	@param new_cw nouvelle largeur des colonnes
+	@see minColumnsWidth()
+*/
+void BorderTitleBlock::setColumnsWidth(const qreal &new_cw) {
+	if (new_cw == columnsWidth()) return;
+	columns_width_ = qMax(minColumnsWidth(), new_cw);
+	setTitleBlockWidth(diagramWidth());
+}
+
+/**
+	Change la hauteur des en-tetes contenant les numeros de colonnes. Celle-ci
+	doit rester comprise entre 5 et 50 px.
+	@param new_chh nouvelle hauteur des en-tetes de colonnes
+*/
+void BorderTitleBlock::setColumnsHeaderHeight(const qreal &new_chh) {
+	columns_header_height_ = qBound(qreal(5.0), new_chh, qreal(50.0));
+	updateRectangles();
+}
+
+/**
+	Permet de changer le nombre de lignes.
+	Si ce nombre de lignes est inferieur au minimum requis, cette fonction ne
+	fait rien
+	@param nb_r nouveau nombre de lignes
+	@see minNbRows()
+*/
+void BorderTitleBlock::setRowsCount(int nb_r) {
+	if (nb_r == rowsCount()) return;
+	rows_count_ = qMax(minNbRows(), nb_r);
+	setTitleBlockWidth(diagramWidth());
+	updateRectangles();
+}
+
+/**
+	Change la hauteur des lignes.
+	Si la hauteur indiquee est inferieure au minimum requis, c'est ce minimum
+	qui est utilise.
+	@param new_rh nouvelle hauteur des lignes
+	@see minRowsHeight()
+*/
+void BorderTitleBlock::setRowsHeight(const qreal &new_rh) {
+	if (new_rh == rowsHeight()) return;
+	rows_height_ = qMax(minRowsHeight(), new_rh);
+	updateRectangles();
+}
+
+/**
+	Change la largeur des en-tetes contenant les numeros de lignes. Celle-ci
+	doit rester comprise entre 5 et 50 px.
+	@param new_rhw nouvelle largeur des en-tetes des lignes
+*/
+void BorderTitleBlock::setRowsHeaderWidth(const qreal &new_rhw) {
+	rows_header_width_ = qBound(qreal(5.0), new_rhw, qreal(50.0));
+	updateRectangles();
+}
+
+/**
+	Cette methode essaye de se rapprocher le plus possible de la hauteur donnee
+	en parametre en modifiant le nombre de lignes en cours.
+*/
+void BorderTitleBlock::setDiagramHeight(const qreal &height) {
+	// taille des lignes a utiliser = rows_height
+	setRowsCount(qRound(ceil(height / rows_height_)));
+}
+
+/**
+	Change la largeur du cartouche. Cette largeur sera restreinte a celle du
+	schema.
+*/
+void BorderTitleBlock::setTitleBlockWidth(const qreal &new_iw) {
+	titleblock_width_ = qMin(diagramWidth(), new_iw);
+	updateRectangles();
+}
+
+
+/**
+	Ajuste la largeur du cartouche de facon a ce que celui-ci soit aussi large
+	que le schema
+*/
+void BorderTitleBlock::adjustTitleBlockToColumns() {
+	setTitleBlockWidth(diagramWidth());
+}
+
+/**
+	@param pos Position cartesienne (ex : 10.3, 45.2) a transformer en position
+	dans la grille (ex : B2)
+	@return la position dans la grille correspondant a pos
+*/
+DiagramPosition BorderTitleBlock::convertPosition(const QPointF &pos) {
+	// recupere le rectangle quadrille par les en-tetes
+	QRectF grid_rect(
+		rowsHeaderWidth(),
+		columnsHeaderHeight(),
+		diagramWidth(),
+		diagramHeight()
+	);
+	
+	if (!grid_rect.contains(pos)) {
+		return(DiagramPosition("", 0));
+	}
+	
+	QPointF relative_pos = pos - grid_rect.topLeft();
+	int row_number    = int(ceil(relative_pos.x() / columnsWidth()));
+	int column_number = int(ceil(relative_pos.y() / rowsHeight()));
+	
+	QString letter = "A";
+	for (int i = 1 ; i < column_number ; ++ i) {
+		letter = incrementLetters(letter);
+	}
+	
+	return(DiagramPosition(letter, row_number));
+}
+
+/**
+	Update the informations given to the titleblock template by regenerating a
+	DiagramContext object.
+	@param initial_context Base diagram context that will be overridden by
+	diagram-wide values
+*/
+void BorderTitleBlock::updateDiagramContextForTitleBlock(const DiagramContext &initial_context) {
+	// Our final DiagramContext is the initial one (which is supposed to bring
+	// project-wide properties), overridden by the "additional fields" one...
+	DiagramContext context = initial_context;
+	foreach (QString key, additional_fields_.keys()) {
+		context.addValue(key, additional_fields_[key]);
+	}
+	
+	// ... overridden by the historical and/or dynamically generated fields
+	context.addValue("author",      btb_author_);
+	context.addValue("date",        btb_date_.toString("dd/MM/yyyy"));
+	context.addValue("title",       btb_title_);
+	context.addValue("filename",    btb_filename_);
+	context.addValue("folio",       btb_final_folio_);
+	context.addValue("folio-id",    folio_index_);
+	context.addValue("folio-total", folio_total_);
+	
+	titleblock_template_renderer_ -> setContext(context);
+}
+
+QString BorderTitleBlock::incrementLetters(const QString &string) {
+	if (string.isEmpty()) {
+		return("A");
+	} else {
+		// separe les digits precedents du dernier digit
+		QString first_digits(string.left(string.count() - 1));
+		QChar last_digit(string.at(string.count() - 1));
+		if (last_digit != 'Z') {
+			// incremente le dernier digit
+			last_digit = last_digit.toAscii() + 1;
+			return(first_digits + QString(last_digit));
+		} else {
+			return(incrementLetters(first_digits) + "A");
+		}
+	}
+}
+
+/**
+	@param index numero du schema (de 1 a total)
+	@param total nombre total de schemas dans le projet
+	@param project_properties Project-wide properties, to be merged with diagram-wide ones.
+*/
+void BorderTitleBlock::setFolioData(int index, int total, const DiagramContext &project_properties) {
+	if (index < 1 || total < 1 || index > total) return;
+	
+	// memorise les informations
+	folio_index_ = index;
+	folio_total_ = total;
+	
+	// regenere le contenu du champ folio
+	btb_final_folio_ = btb_folio_;
+	btb_final_folio_.replace("%id",    QString::number(folio_index_));
+	btb_final_folio_.replace("%total", QString::number(folio_total_));
+	
+	updateDiagramContextForTitleBlock(project_properties);
+}

Added: trunk/sources/bordertitleblock.h
===================================================================
--- trunk/sources/bordertitleblock.h	                        (rev 0)
+++ trunk/sources/bordertitleblock.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,247 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BORDERTITLEBLOCK_H
+#define BORDERTITLEBLOCK_H
+#include "diagramcontext.h"
+#include "titleblockproperties.h"
+#include "borderproperties.h"
+#include <QObject>
+#include <QRectF>
+#include <QDate>
+class QPainter;
+class DiagramPosition;
+class TitleBlockTemplate;
+class TitleBlockTemplateRenderer;
+/**
+	This class represents the border and the titleblock which frame a
+	particular electric diagram.
+*/
+class BorderTitleBlock : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	BorderTitleBlock(QObject * = 0);
+	virtual ~BorderTitleBlock();
+	
+	private:
+	BorderTitleBlock(const BorderTitleBlock &);
+	
+	// methods
+	public:
+	static int   minNbColumns();
+	static qreal minColumnsWidth();
+	static int   minNbRows();
+	static qreal minRowsHeight();
+	
+	void draw(QPainter *, qreal = 0.0, qreal = 0.0);
+	
+	// methods to get dimensions
+	// columns
+	/// @return the number of columns
+	int   columnsCount() const { return(columns_count_); }
+	/// @return the columns width, in pixels
+	qreal columnsWidth() const { return(columns_width_); }
+	/// @return the total width of all columns, headers excluded
+	qreal columnsTotalWidth() const { return(columns_count_ * columns_width_); }
+	/// @return the column headers height, in pixels
+	qreal columnsHeaderHeight() const { return(columns_header_height_); }
+	
+	// rows
+	/// @return the number of rows
+	int rowsCount() const { return(rows_count_); }
+	/// @return the rows height, in pixels
+	qreal rowsHeight() const { return(rows_height_); }
+	/// @return the total height of all rows, headers excluded
+	qreal rowsTotalHeight() const { return(rows_count_ * rows_height_); }
+	/// @return la rows header width, in pixels
+	qreal rowsHeaderWidth() const { return(rows_header_width_); }
+	
+	// border - title block = diagram
+	/// @return the diagram width, i.e. the width of the border without title block
+	qreal diagramWidth() const { return(columnsTotalWidth() + rowsHeaderWidth()); }
+	/// @return the diagram height, i.e. the height of the border without title block
+	qreal diagramHeight() const { return(rowsTotalHeight() + columnsHeaderHeight()); }
+	
+	// title block
+	/// @return the title block width
+	qreal titleBlockWidth()  const { return(titleblock_width_); }
+	qreal titleBlockHeight() const;
+	
+	// border + title block
+	/// @return the border width
+	qreal borderWidth()  const { return(diagramWidth()); }
+	/// @return the border height
+	qreal borderHeight() const { return(diagramHeight() + titleBlockHeight()); }
+	
+	// methods to get title block basic data
+	/// @return the value of the title block "Author" field
+	QString author() const { return(btb_author_); }
+	/// @return the value of the title block "Date" field
+	QDate date() const { return(btb_date_); }
+	/// @return the value of the title block "Title" field
+	QString title() const { return(btb_title_); }
+	/// @return the value of the title block "Folio" field
+	QString folio() const { return(btb_folio_); }
+	/// @return the value of the title block "File" field
+	QString fileName() const { return(btb_filename_); }
+	
+	// methods to get display options
+	/// @return true si le cartouche est affiche, false sinon
+	bool titleBlockIsDisplayed() const { return(display_titleblock_); }
+	/// @return true si les entetes des colonnes sont affiches, false sinon
+	bool columnsAreDisplayed() const { return(display_columns_); }
+	/// @return true si les entetes des lignes sont affiches, false sinon
+	bool rowsAreDisplayed() const { return(display_rows_); }
+	/// @return true si la bordure est affichee, false sinon
+	bool borderIsDisplayed() const { return(display_border_); }
+	
+	// methods to set dimensions
+	void addColumn();
+	void addRow();
+	void removeColumn();
+	void removeRow();
+	void setColumnsCount(int);
+	void setRowsCount(int);
+	void setColumnsWidth(const qreal &);
+	void setRowsHeight(const qreal &);
+	void setColumnsHeaderHeight(const qreal &);
+	void setRowsHeaderWidth(const qreal &);
+	void setDiagramHeight(const qreal &);
+	void setTitleBlockWidth(const qreal &);
+	void adjustTitleBlockToColumns();
+	
+	DiagramPosition convertPosition(const QPointF &);
+	
+	// methods to set title block basic data
+	/// @param author the new value of the "Author" field
+	void setAuthor(const QString &author) { btb_author_ = author; }
+	/// @param author the new value of the "Date" field
+	void setDate(const QDate &date) { btb_date_ = date; }
+	/// @param author the new value of the "Title" field
+	void setTitle(const QString &title) {
+		if (btb_title_ != title) {
+			btb_title_ = title;
+			emit(diagramTitleChanged(title));
+		}
+	}
+	/// @param author the new value of the "Folio" field
+	void setFolio(const QString &folio) { btb_folio_ = folio; }
+	void setFolioData(int, int, const DiagramContext & = DiagramContext());
+	/// @param author the new value of the "File" field
+	void setFileName(const QString &filename) { btb_filename_ = filename; }
+	
+	void titleBlockToXml(QDomElement &);
+	void titleBlockFromXml(const QDomElement &);
+	void borderToXml(QDomElement &);
+	void borderFromXml(const QDomElement &);
+	
+	TitleBlockProperties exportTitleBlock();
+	void importTitleBlock(const TitleBlockProperties &);
+	BorderProperties exportBorder();
+	void importBorder(const BorderProperties &);
+	
+	const TitleBlockTemplate *titleBlockTemplate();
+	void setTitleBlockTemplate(const TitleBlockTemplate *);
+	QString titleBlockTemplateName() const;
+	
+	public slots:
+	void titleBlockTemplateChanged(const QString &);
+	void titleBlockTemplateRemoved(const QString &, const TitleBlockTemplate * = 0);
+	
+	// methods to set display options
+	void displayTitleBlock(bool);
+	void displayColumns(bool);
+	void displayRows(bool);
+	void displayBorder(bool);
+	
+	private:
+	void updateRectangles();
+	void updateDiagramContextForTitleBlock(const DiagramContext & = DiagramContext());
+	QString incrementLetters(const QString &);
+	
+	signals:
+	/**
+		Signal emitted after the border has changed
+		@param old_border Former border
+		@param new_border New border
+	*/
+	void borderChanged(QRectF old_border, QRectF new_border);
+	/**
+		Signal emitted after display options have changed
+	*/
+	void displayChanged();
+	
+	/**
+		Signal emitted after the title has changed
+	*/
+	void diagramTitleChanged(const QString &);
+	
+	/**
+		Signal emitted when the title block requires its data to be updated in order
+		to generate the folio field.
+	*/
+	void needFolioData();
+	
+	/**
+		Signal emitted when this object needs to set a specific title block
+		template. This object cannot handle the job since it does not know of
+		its parent project.
+	*/
+	void needTitleBlockTemplate(const QString &);
+	
+	// attributes
+	private:
+	// titleblock basic data
+	QString btb_author_;
+	QDate   btb_date_;
+	QString btb_title_;
+	QString btb_folio_;
+	QString btb_final_folio_;
+	int folio_index_;
+	int folio_total_;
+	QString btb_filename_;
+	DiagramContext additional_fields_;
+	
+	// border dimensions (rows and columns)
+	// columns: number and dimensions
+	int columns_count_;
+	qreal columns_width_;
+	qreal columns_header_height_;
+	
+	// rows: number and dimensions
+	int rows_count_;
+	qreal rows_height_;
+	qreal rows_header_width_;
+	
+	// title block dimensions
+	qreal titleblock_width_;
+	qreal titleblock_height_;
+	
+	// rectangles used for drawing operations
+	QRectF diagram_rect_;
+	QRectF titleblock_rect_;
+	
+	// display options
+	bool display_titleblock_;
+	bool display_columns_;
+	bool display_rows_;
+	bool display_border_;
+	TitleBlockTemplateRenderer *titleblock_template_renderer_;
+};
+#endif

Added: trunk/sources/closediagramsdialog.cpp
===================================================================
--- trunk/sources/closediagramsdialog.cpp	                        (rev 0)
+++ trunk/sources/closediagramsdialog.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,321 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "closediagramsdialog.h"
+#include "diagram.h"
+#include "qeticons.h"
+
+/**
+	Construct a dialog showing \a diagrams.
+	@param parent Parent QWidget
+*/
+CloseDiagramsDialog::CloseDiagramsDialog(const QList<Diagram *> &diagrams, QWidget *parent) :
+	QDialog(parent),
+	diagrams_list_(diagrams),
+	answer_(-1)
+{
+	initWidgets();
+	initLayout();
+}
+
+/**
+	Destructor
+*/
+CloseDiagramsDialog::~CloseDiagramsDialog() {
+}
+
+/**
+	@return the user answer once the dialog has been executed.
+*/
+int CloseDiagramsDialog::answer() const {
+	return(answer_);
+}
+
+/**
+	@return what the user wants to do with \a diagram
+	@see CloseDiagramsDialog::Actions
+*/
+int CloseDiagramsDialog::actionForDiagram(Diagram *diagram) {
+	if (QCheckBox *checkbox = getCheckBoxForDiagram(diagram)) {
+		if (!diagram -> undoStack().isClean()) {
+			return(checkbox -> isChecked() ? Save : DoNotSave);
+		} else if (!diagram -> wasWritten()) {
+			return(checkbox -> isChecked() ? Save : Remove);
+		}
+	}
+	return(Unknown);
+}
+
+/**
+	@return the list of diagrams for which users have chosen the \a action
+	action.
+*/
+QList<Diagram *> CloseDiagramsDialog::diagramsByAction(Actions action) {
+	QList<Diagram *> diagrams;
+	foreach (Diagram *diagram, diagrams_list_) {
+		if (actionForDiagram(diagram) == action) {
+			diagrams << diagram;
+		}
+	}
+	return(diagrams);
+}
+
+/**
+	Initialize widgets.
+*/
+void CloseDiagramsDialog::initWidgets() {
+	setWindowTitle(tr("Fermer un projet", "window title"));
+	
+	connect(&show_mapper_, SIGNAL(mapped(int)), this, SLOT(requireShowDiagram(int)));
+	
+	// label when diagrams were modified
+	informative_label1_ = new QLabel(
+		tr(
+			"Les sch\351mas ci-dessous contiennent des modifications non "
+			"enregistr\351es. Faut-il les sauvegarder ?",
+			"informative label"
+		)
+	);
+	informative_label1_ -> setWordWrap(true);
+	
+	// label when no diagrams were modified
+	informative_label2_ = new QLabel(tr("Voulez-vous enregistrer le projet ?", "informative label"));
+	informative_label2_ -> setWordWrap(true);
+	
+	// header labels
+	QLabel *state_label = new QLabel(tr("\311tat", "column header"));
+	QLabel *title_label = new QLabel(tr("Sch\351ma", "column header"));
+	
+	// header checkbox in order to check/uncheck all diagrams
+	QString action_label_text = tr("Action", "column header");
+	QLabel *action_label = new QLabel(action_label_text);
+	all_checkbox_ = new QCheckBox(action_label_text);
+	all_checkbox_ -> setToolTip(tr("Cocher ou d\351cocher toutes les cases \340 cocher", "checbox tooltip"));
+	all_checkbox_ -> setChecked(true);
+	connect(all_checkbox_, SIGNAL(stateChanged(int)), this, SLOT(topCheckBoxChangedState(int)));
+	QWidget *header_widget = diagrams_list_.count() > 1 ? static_cast<QWidget *>(all_checkbox_) : static_cast<QWidget *>(action_label);
+	Qt::Alignment header_alignment = diagrams_list_.count() > 1 ? Qt::AlignLeft : Qt::AlignCenter;
+	
+	// spacers inserted in the header row
+	QSpacerItem *spacer1 = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);
+	QSpacerItem *spacer2 = new QSpacerItem(25, 10, QSizePolicy::Preferred, QSizePolicy::Minimum);
+	
+	buttons_ = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel);
+	connect(buttons_, SIGNAL(clicked(QAbstractButton *)), this, SLOT(storeAnswer(QAbstractButton *)));
+	
+	// widget layout
+	diagrams_list_layout_ = new QGridLayout();
+	diagrams_list_layout_ -> addWidget(title_label, 0, 1, 1, 1, Qt::AlignCenter);
+	diagrams_list_layout_ -> addItem(spacer1, 0, 2);
+	diagrams_list_layout_ -> addWidget(state_label, 0, 3, 1, 1, Qt::AlignCenter);
+	diagrams_list_layout_ -> addItem(spacer2, 0, 4);
+	diagrams_list_layout_ -> addWidget(header_widget, 0, 5, 1, 1, header_alignment);
+	
+	// widget
+	diagrams_list_widget_ = new QWidget();
+	diagrams_list_widget_ -> setLayout(diagrams_list_layout_);
+	diagrams_list_widget_ -> setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+	
+	// scroll area
+	diagrams_list_area_ = new QScrollArea();
+	diagrams_list_area_ -> setWidgetResizable(true);
+	diagrams_list_area_ -> setFrameStyle(QFrame::Plain | QFrame::NoFrame);
+	
+	loadDiagramsList();
+	diagrams_list_area_ -> setWidget(diagrams_list_widget_);
+}
+
+/**
+	Initialize layout.
+*/
+void CloseDiagramsDialog::initLayout() {
+	if (diagrams_list_.count()) {
+		setMinimumSize(650, 340);
+	}
+	
+	QVBoxLayout *vlayout0 = new QVBoxLayout();
+	vlayout0 -> addWidget(informative_label1_);
+	vlayout0 -> addWidget(informative_label2_);
+	vlayout0 -> addWidget(diagrams_list_area_);
+	vlayout0 -> addWidget(buttons_);
+	setLayout(vlayout0);
+}
+
+/**
+	Create a visual list of all modified diagrams.
+*/
+void CloseDiagramsDialog::loadDiagramsList() {
+	if (diagrams_list_.count()) {
+		int row_id = 1;
+		foreach (Diagram *diagram, diagrams_list_) {
+			addDiagram(diagram, row_id);
+			++ row_id;
+		}
+		informative_label2_ -> setVisible(false);
+	} else {
+		informative_label1_ -> setVisible(false);
+		diagrams_list_area_ -> setVisible(false);
+	}
+}
+
+/**
+	Add \a diagram to the \a row_id row of the visual list.
+*/
+void CloseDiagramsDialog::addDiagram(Diagram *diagram, int row_id) {
+	QLabel *diagram_title = new QLabel(diagramTitle(diagram));
+	QPushButton *diagram_show = new QPushButton(QET::Icons::ZoomOriginal, "");
+	diagram_show -> setToolTip(tr("Afficher ce sch\351ma", "button tooltip"));
+	show_mapper_.setMapping(diagram_show, row_id - 1);
+	connect(diagram_show, SIGNAL(released()), &show_mapper_, SLOT(map()));
+	QLabel *diagram_status = new QLabel(diagramStatus(diagram));
+	QCheckBox *diagram_checkbox = new QCheckBox(diagramAction(diagram));
+	diagram_checkbox -> setChecked(true);
+	connect(diagram_checkbox, SIGNAL(stateChanged(int)), this, SLOT(lambdaCheckBoxChangedState(int)));
+	
+	diagrams_list_layout_ -> addWidget(diagram_show,       row_id, 0, 1, 1, Qt::AlignCenter);
+	diagrams_list_layout_ -> addWidget(diagram_title,      row_id, 1, 1, 1, Qt::AlignCenter);
+	diagrams_list_layout_ -> addWidget(diagram_status,     row_id, 3, 1, 1, Qt::AlignCenter);
+	diagrams_list_layout_ -> addWidget(diagram_checkbox,   row_id, 5, 1, 1, Qt::AlignLeft);
+}
+
+/**
+	@return the action checkbox for \a diagram, or 0 if no adequate checkbox could be found.
+*/
+QCheckBox *CloseDiagramsDialog::getCheckBoxForDiagram(Diagram *diagram) {
+	int diagram_index = diagrams_list_.indexOf(diagram);
+	if (diagram_index == -1) return(0);
+	
+	// We add 1 because there is one row dedicated to column headers;
+	// 4 is the column containing checkboxes, see initWidgets().
+	QLayoutItem *item = diagrams_list_layout_ -> itemAtPosition(diagram_index + 1, 5);
+	if (!item) return(0);
+	
+	QWidget *widget = item -> widget();
+	if (!widget) return(0);
+	
+	return(static_cast<QCheckBox *>(widget));
+}
+
+/**
+	@return the title for \a diagram
+*/
+QString CloseDiagramsDialog::diagramTitle(Diagram *diagram) {
+	if (!diagram -> title().isEmpty()) {
+		return(diagram -> title());
+	}
+	return(tr("Sch\351ma sans titre", "fallback diagram title"));
+}
+
+/**
+	@return a string describing the status of \a diagram
+*/
+QString CloseDiagramsDialog::diagramStatus(Diagram *diagram) {
+	if (!diagram) return(QString());
+	if (!diagram -> undoStack().isClean()) {
+		return(tr("Modifi\351", "diagram status"));
+	} else if (!diagram -> wasWritten()) {
+		return(tr("Ajout\351, non modifi\351", "diagram status"));
+	} else {
+		return(QString());
+	}
+}
+
+/**
+	@return a string describing the effect of saving \a diagram (e.g. "Save" or
+	"Keep").
+*/
+QString CloseDiagramsDialog::diagramAction(Diagram *diagram) {
+	if (!diagram) return(QString());
+	if (!diagram -> undoStack().isClean()) {
+		return(tr("Enregistrer", "diagram action"));
+	} else if (!diagram -> wasWritten()) {
+		return(tr("Conserver", "diagram action"));
+	} else {
+		return(QString());
+	}
+}
+
+/**
+	Adjust the state of the header checkbox when a diagram checkbox was
+	switched to \a new_state.
+*/
+void CloseDiagramsDialog::lambdaCheckBoxChangedState(int new_state) {
+	bool state = new_state;
+	
+	bool all_same_state = true;
+	foreach (Diagram *diagram, diagrams_list_) {
+		if (QCheckBox *checkbox = getCheckBoxForDiagram(diagram)) {
+			if (checkbox -> isChecked() != state) {
+				all_same_state = false;
+				break;
+			}
+		}
+	}
+	
+	all_checkbox_ -> blockSignals(true);
+	if (all_same_state) {
+		all_checkbox_ -> setTristate(false);
+		all_checkbox_ -> setChecked(state);
+	} else {
+		all_checkbox_ -> setTristate(true);
+		all_checkbox_ -> setCheckState(Qt::PartiallyChecked);
+	}
+	all_checkbox_ -> blockSignals(false);
+	all_checkbox_ -> update();
+}
+
+/**
+	Adjust diagram checkboxes when the header checkbox was switched to \a
+	new_state.
+*/
+void CloseDiagramsDialog::topCheckBoxChangedState(int new_state) {
+	setCheckedAll((bool)new_state);
+}
+
+/**
+	Set all diagram checkboxes to the checked (true) or unchecked (false)
+	state.
+*/
+void CloseDiagramsDialog::setCheckedAll(bool checked) {
+	foreach (Diagram *diagram, diagrams_list_) {
+		if (QCheckBox *checkbox = getCheckBoxForDiagram(diagram)) {
+			if (checkbox -> isChecked() != checked) {
+				checkbox -> blockSignals(true);
+				checkbox -> setChecked(checked);
+				checkbox -> blockSignals(false);
+			}
+		}
+	}
+}
+
+/**
+	Find the diagram at \a diagram_index and emts the showDiagram() signal with
+	it.
+*/
+void CloseDiagramsDialog::requireShowDiagram(int diagram_index) {
+	Diagram *diagram = diagrams_list_.value(diagram_index);
+	if (!diagram) return;
+	emit(showDiagram(diagram));
+}
+
+/**
+	Store the user answer when the dialog is validated or rejected.
+*/
+void CloseDiagramsDialog::storeAnswer(QAbstractButton *button) {
+	answer_ = buttons_ -> buttonRole(button);
+	accept();
+}

Added: trunk/sources/closediagramsdialog.h
===================================================================
--- trunk/sources/closediagramsdialog.h	                        (rev 0)
+++ trunk/sources/closediagramsdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,92 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CLOSE_DIAGRAMS_DIALOG_H
+#define CLOSE_DIAGRAMS_DIALOG_H
+#include <QDialog>
+#include <QSignalMapper>
+class QAbstractButton;
+class QDialogButtonBox;
+class QCheckBox;
+class QLabel;
+class QPushButton;
+class Diagram;
+class QGridLayout;
+class QScrollArea;
+
+/**
+	This class represents a dialog asking users whether they want to save
+	their modified project when it is being closed and what they wish to save
+	in it.
+*/
+class CloseDiagramsDialog : public QDialog {
+	Q_OBJECT
+	public:
+	enum Actions {
+		Unknown,
+		Save,
+		DoNotSave,
+		Remove
+	};
+	
+	// Constructors, destructor
+	public:
+	CloseDiagramsDialog(const QList<Diagram *> &diagrams, QWidget *parent = 0);
+	virtual ~CloseDiagramsDialog();
+	private:
+	CloseDiagramsDialog(const CloseDiagramsDialog &);
+	
+	// methods
+	public:
+	int answer() const;
+	int actionForDiagram(Diagram *);
+	QList<Diagram *> diagramsByAction(Actions);
+	
+	private:
+	void initWidgets();
+	void initLayout();
+	void loadDiagramsList();
+	void addDiagram(Diagram *, int);
+	QCheckBox *getCheckBoxForDiagram(Diagram *);
+	QString diagramTitle(Diagram *);
+	QString diagramStatus(Diagram *);
+	QString diagramAction(Diagram *);
+	
+	signals:
+	void showDiagram(Diagram *);
+	
+	private slots:
+	void lambdaCheckBoxChangedState(int);
+	void topCheckBoxChangedState(int);
+	void setCheckedAll(bool);
+	void requireShowDiagram(int);
+	void storeAnswer(QAbstractButton *);
+	
+	// attributes
+	private:
+	QList<Diagram *> diagrams_list_;     ///< List of (modified or newly added) diagrams displayed by the dialog
+	QLabel *informative_label1_;         ///< Informative label when there are modified diagrams
+	QLabel *informative_label2_;         ///< Informative label when there is no modified diagram
+	QCheckBox *all_checkbox_;            ///< Header checkbox to check/uncheck all other checkboxes
+	QScrollArea *diagrams_list_area_;    ///< Scroll area to make the diagrams visual list fit in the dialog
+	QWidget *diagrams_list_widget_;      ///< Scrolled widget
+	QGridLayout *diagrams_list_layout_;  ///< Layout used to list diagrams
+	QDialogButtonBox *buttons_;          ///< Buttons for users to input their final choice
+	int answer_;                         ///< Reflects the user answer once the diagram has been executed
+	QSignalMapper show_mapper_;          ///< Signal mapper for the "show diagram" buttons to work
+};
+#endif

Added: trunk/sources/conductorautonumerotation.cpp
===================================================================
--- trunk/sources/conductorautonumerotation.cpp	                        (rev 0)
+++ trunk/sources/conductorautonumerotation.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,174 @@
+/*
+	Copyright 2006-2013 The QElectroTech team
+	This file is part of QElectroTech.
+
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "conductorautonumerotation.h"
+#include "conductorautonumerotationwidget.h"
+#include "diagramcommands.h"
+#include "numerotationcontextcommands.h"
+
+/**
+ *Constructor
+ * @param c the conductor to apply automatic numerotation
+ */
+ConductorAutoNumerotation::ConductorAutoNumerotation(Conductor *c) :
+	AutoNumerotation (c -> diagram()),
+	conductor_ (c),
+	conductor_list(c -> relatedPotentialConductors())
+{
+	num_context = diagram_ -> getNumerotation(Diagram::Conductors);
+}
+
+/**
+ * Constructor
+ * @param d a diagram to apply automatic numerotation
+ */
+ConductorAutoNumerotation::ConductorAutoNumerotation(Diagram *d) :
+	AutoNumerotation (d),
+	conductor_ (NULL)
+{}
+
+/**
+ * @param c the conductor to apply automatic numerotation
+ */
+void ConductorAutoNumerotation::setConductor(Conductor *c) {
+	conductor_ = c;
+	diagram_ = c -> diagram();
+	conductor_list = c -> relatedPotentialConductors();
+	num_context = diagram_ -> getNumerotation(Diagram::Conductors);
+}
+
+/**
+ * @brief ConductorAutoNumerotation::numerate
+ * execute the automatic numerotation
+ */
+void ConductorAutoNumerotation::numerate() {
+	if (!conductor_) return;
+	if (conductor_list.size() >= 1 ) numeratePotential();
+	else if (conductor_ -> properties().type == ConductorProperties::Multi) numerateNewConductor();
+	else return;
+}
+
+/**
+ * @brief ConductorAutoNumerotation::numerateDiagram
+ * Numerate all conductor in diagram
+ */
+void ConductorAutoNumerotation::numerateDiagram() {
+	if (!diagram_) return;
+	//Get all potentials presents in diagram
+	QList <QSet <Conductor *> > potential_list = diagram_ -> potentials();
+	//Browse all potentials and set new numerotation
+	for (int i=0; i < potential_list.size(); ++i) {
+		setConductor (potential_list.at(i).toList().first());
+		NumerotationContextCommands ncc(diagram_, num_context);
+		applyText(ncc.toRepresentedString());
+		diagram_ -> setNumerotation(Diagram::Conductors, ncc.next());
+	}
+}
+
+/**
+ * @brief ConductorAutoNumerotation::applyText
+ * apply the text @t to @conductor_ and all conductors at the same potential
+ */
+void ConductorAutoNumerotation::applyText(QString t) {
+	if (!conductor_) return;
+	if (conductor_list.empty()) {
+		//initialize the corresponding UndoCommand object
+		ChangeConductorPropertiesCommand *ccpc = new ChangeConductorPropertiesCommand (conductor_);
+		ccpc -> setOldSettings (conductor_ -> properties());
+		ConductorProperties cp = conductor_ -> properties();
+		cp.text = t;
+		ccpc -> setNewSettings(cp);
+		diagram_ -> undoStack().push(ccpc);
+	}
+	else {
+		QSet <Conductor *> clist = conductor_list;
+		clist << conductor_;
+		QList <ConductorProperties> old_properties, new_properties;
+		ConductorProperties cp;
+
+		foreach (Conductor *c, clist) {
+			old_properties << c -> properties();
+			cp = c -> properties();
+			cp.text = t;
+			new_properties << cp;
+		}
+		//initialize the corresponding UndoCommand object
+		ChangeSeveralConductorsPropertiesCommand *cscpc = new ChangeSeveralConductorsPropertiesCommand(clist);
+		cscpc -> setOldSettings(old_properties);
+		cscpc -> setNewSettings(new_properties);
+		diagram_ -> undoStack().push(cscpc);
+	}
+}
+
+/**
+ * @brief Set the default text to all potentials of the diagram
+ */
+void ConductorAutoNumerotation::removeNumOfDiagram() {
+	if (!diagram_) return;
+	//Get all potentials presents in diagram
+	QList <QSet <Conductor *> > potential_list = diagram_ -> potentials();
+	//Browse all potentials and set the default text
+	for (int i=0; i < potential_list.size(); i++) {
+		setConductor (potential_list.at(i).toList().first());
+		applyText (diagram_ -> defaultConductorProperties.text);
+	}
+}
+
+/**
+ * @brief ConductorAutoNumerotation::numeratePotential
+ * Numerate a conductor on an existing potential
+ */
+void ConductorAutoNumerotation::numeratePotential() {
+	QStringList strl;
+	foreach (const Conductor *cc, conductor_list) strl<<(cc->text());
+	//the texts is identicals
+	if (eachIsEqual(strl)) {
+		ConductorProperties cp = conductor_ -> properties();
+		cp.text = strl.at(0);
+		conductor_ -> setProperties(cp);
+		conductor_ -> setText(strl.at(0));
+	}
+	//the texts isn't identicals
+	else {
+		ConductorAutoNumerotationWidget *canw = new ConductorAutoNumerotationWidget(conductor_, conductor_list, conductor_ -> diagramEditor());
+		connect(canw, SIGNAL(textIsSelected(QString)),
+				this, SLOT(applyText(QString)));
+		canw -> exec();
+	}
+}
+
+/**
+ * @brief ConductorAutoNumerotation::numerateNewConductor
+ * create and apply a new numerotation to @conductor_
+ */
+void ConductorAutoNumerotation::numerateNewConductor() {
+	if (!conductor_ || num_context.isEmpty()) return;
+
+	NumerotationContextCommands ncc (diagram_, num_context);
+	applyText(ncc.toRepresentedString());
+	diagram_-> setNumerotation(Diagram::Conductors, ncc.next());
+}
+
+/**
+ * @return true if every text of qsl is identical, else false.
+ */
+bool eachIsEqual (const QStringList &qsl) {
+	foreach (const QString t, qsl) {
+		if (qsl.at(0) != t) return false;
+	}
+	return true;
+}

Added: trunk/sources/conductorautonumerotation.h
===================================================================
--- trunk/sources/conductorautonumerotation.h	                        (rev 0)
+++ trunk/sources/conductorautonumerotation.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,53 @@
+/*
+	Copyright 2006-2013 The QElectroTech team
+	This file is part of QElectroTech.
+
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONDUCTORAUTONUMEROTATION_H
+#define CONDUCTORAUTONUMEROTATION_H
+
+#include "qetgraphicsitem/conductor.h"
+#include "numerotationcontext.h"
+#include "autonumerotation.h"
+
+class ConductorAutoNumerotation: public AutoNumerotation
+{
+	public:
+	//constructors & destructor
+	ConductorAutoNumerotation (Conductor *);
+	ConductorAutoNumerotation (Diagram *);
+
+	//methods
+	void setConductor(Conductor *);
+	void numerate();
+	void numerateDiagram();
+	void removeNumOfDiagram();
+
+	public slots:
+	void applyText(QString);
+
+	private:
+	//methods
+	void numeratePotential ();
+	void numerateNewConductor ();
+
+	//attributes
+	Conductor *conductor_;
+	QSet <Conductor *> conductor_list;
+};
+
+bool eachIsEqual (const QStringList &);
+
+#endif // CONDUCTORAUTONUMEROTATION_H

Added: trunk/sources/conductorautonumerotationwidget.cpp
===================================================================
--- trunk/sources/conductorautonumerotationwidget.cpp	                        (rev 0)
+++ trunk/sources/conductorautonumerotationwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,120 @@
+#include "conductorautonumerotationwidget.h"
+
+/**
+ * constructor
+ */
+ConductorAutoNumerotationWidget::ConductorAutoNumerotationWidget(Conductor *c, QSet<Conductor *> cl, QWidget *parent) :
+	QDialog (parent),
+	conductor_(c),
+	c_list(cl),
+	diagram_(c -> diagram())
+{
+#ifdef Q_WS_MAC
+	setWindowFlags(Qt::Sheet);
+#endif
+	buildInterface();
+}
+
+/**
+ * @brief ConductorAutoNumerotationWidget::buildInterface
+ */
+void ConductorAutoNumerotationWidget::buildInterface() {
+	QVBoxLayout *mainlayout = new QVBoxLayout;
+	QGroupBox *potential_groupbox = new QGroupBox(tr("Textes de potentiel"), this);
+	QVBoxLayout *vlayout = new QVBoxLayout;
+
+	QLabel *label= new QLabel(tr("Les textes de ce potentiel \351lectrique ne sont pas identiques.\n"
+								 "Appliquer un texte \340 l'ensemble de ces conducteurs?"), this);
+	vlayout -> addWidget(label);
+
+	//map the signal for each radio button create in buildRadioList
+	sm_ = new QSignalMapper(this);
+	connect(sm_, SIGNAL(mapped(QString)), this, SLOT(setText(QString)));
+	vlayout -> addLayout(buildRadioList());
+
+	potential_groupbox -> setLayout(vlayout);
+	mainlayout -> addWidget(potential_groupbox);
+
+	QDialogButtonBox *dbb = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Yes, Qt::Horizontal, this);
+	connect(dbb, SIGNAL(rejected()),
+			this, SLOT(reject()));
+	connect(dbb, SIGNAL(accepted()),
+			this, SLOT(accept()));
+
+	mainlayout->addWidget(dbb);
+	setLayout(mainlayout);
+}
+
+/**
+ * @brief ConductorAutoNumerotationWidget::buildRadioList
+ *construit toute la partie de l'interface contenant les boutons radio permetant le choix du texte a appliquer
+ * @return un layout contenant les boutons radio
+ */
+QVBoxLayout* ConductorAutoNumerotationWidget::buildRadioList() {
+	QVBoxLayout *radioLayout = new QVBoxLayout;
+	QHBoxLayout *otherLayout = new QHBoxLayout;
+
+	//create a new radio button for each text of @conductorList
+	QMultiMap<int, QString> conductorlist = conductorsTextToMap(c_list);
+	for (QMultiMap<int, QString>::ConstIterator it = conductorlist.constEnd()-1; it != conductorlist.constBegin()-1; --it) {
+		QRadioButton *rb= new QRadioButton(it.value() + tr("  : est pr\351sent ") + QString::number(it.key()) + tr(" fois."), this);
+		if (it == conductorlist.constEnd()-1) {
+			rb -> setChecked(true);
+			text_ = it.value();
+		}
+		//connect the button to mapper @sm_
+		connect(rb, SIGNAL(clicked()),
+				sm_, SLOT(map()));
+		sm_ -> setMapping(rb, it.value());
+		radioLayout -> addWidget(rb);
+	}
+
+	//create the "other" radio button and is text field
+	QRadioButton *other= new QRadioButton(tr("Autre"), this);
+	text_field = new QLineEdit(this);
+	text_field -> setEnabled(false);
+	connect(other, SIGNAL(toggled(bool)), text_field, SLOT(setEnabled(bool)));
+	otherLayout -> addWidget(other);
+	otherLayout -> addWidget(text_field);
+	radioLayout -> addLayout(otherLayout);
+	return radioLayout;
+}
+
+/**
+ * @param csl liste des conducteurs a analyser
+ * @return QMultiMap avec le nombre de conducteurs possedant le même texte en clee et le texte en question comme valeur
+ */
+QMultiMap <int, QString> ConductorAutoNumerotationWidget::conductorsTextToMap(QSet<Conductor *> csl) {
+	QStringList textList;
+	foreach(Conductor *c, csl) textList << c -> text();
+
+	QMultiMap<int, QString> conductorlist;
+	while (!textList.size() == 0) {
+		QString t = textList.at(0);
+		int n = textList.count(t);
+		textList.removeAll(t);
+		conductorlist.insert(n, t);
+	}
+	return conductorlist;
+}
+
+/**
+ * @brief ConductorAutoNumerotationWidget::setText
+ * enregistre le texte @t passé en parametre
+ */
+void ConductorAutoNumerotationWidget::setText(QString t) {
+	text_ = t;
+}
+
+/**
+ * @brief ConductorAutoNumerotationWidget::accept
+ *action executé lors de l'appuis sur le bouton 'oui'
+ */
+void ConductorAutoNumerotationWidget::accept() {
+	if (text_field -> isEnabled()) {
+		emit textIsSelected(text_field -> text());
+		}
+	else
+		emit textIsSelected(text_);
+	close();
+}

Added: trunk/sources/conductorautonumerotationwidget.h
===================================================================
--- trunk/sources/conductorautonumerotationwidget.h	                        (rev 0)
+++ trunk/sources/conductorautonumerotationwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,42 @@
+#ifndef CONDUCTORAUTONUMEROTATIONWIDGET_H
+#define CONDUCTORAUTONUMEROTATIONWIDGET_H
+
+#include <QtGui>
+#include <QList>
+#include <QSet>
+#include <QMultiMap>
+#include <QString>
+#include "qetgraphicsitem/conductor.h"
+#include "diagram.h"
+
+class ConductorAutoNumerotationWidget : public QDialog
+{
+	Q_OBJECT
+	public:
+	explicit ConductorAutoNumerotationWidget(Conductor *, QSet <Conductor *>, QWidget *parent = 0);
+	QMultiMap <int, QString> conductorsTextToMap (QSet <Conductor *>);
+	
+	public slots:
+	void setText (QString);
+	void accept();
+
+	signals:
+	void textIsSelected (QString);
+
+	private:
+	//methods
+	void buildInterface();
+	QVBoxLayout* buildRadioList();
+
+	//attributes
+	Conductor *conductor_;
+	QSet<Conductor *> c_list; //liste des conducteurs au même potentiel
+	Diagram *diagram_;
+	QList <QRadioButton *> *radio_List;
+	QLineEdit *text_field;
+	QString text_;
+	QSignalMapper *sm_;
+	
+};
+
+#endif // CONDUCTORAUTONUMEROTATIONWIDGET_H

Added: trunk/sources/conductorprofile.cpp
===================================================================
--- trunk/sources/conductorprofile.cpp	                        (rev 0)
+++ trunk/sources/conductorprofile.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,157 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "conductorprofile.h"
+#include "qetgraphicsitem/conductor.h"
+#include "conductorsegmentprofile.h"
+
+/// Constructeur
+ConductorProfile::ConductorProfile() {
+}
+
+/**
+	Constructeur
+	@param conductor conducteur dont il faut extraire le profil
+*/
+ConductorProfile::ConductorProfile(Conductor *conductor) {
+	fromConductor(conductor);
+}
+
+/**
+	Constructeur de copie
+	@param c autre conducteur
+*/
+ConductorProfile::ConductorProfile(const ConductorProfile &c) {
+	beginOrientation = c.beginOrientation;
+	endOrientation   = c.endOrientation;
+	foreach(ConductorSegmentProfile *csp, c.segments) {
+		segments << new ConductorSegmentProfile(*csp);
+	}
+}
+
+/**
+	Operateur =
+	@param c autre conducteur
+*/
+ConductorProfile &ConductorProfile::operator=(const ConductorProfile &c) {
+	if (&c == this) return(*this);
+	
+	// supprime ses informations
+	setNull();
+	
+	// copie les informations de l'autre profil de conducteur
+	beginOrientation = c.beginOrientation;
+	endOrientation   = c.endOrientation;
+	foreach(ConductorSegmentProfile *csp, c.segments) {
+		segments << new ConductorSegmentProfile(*csp);
+	}
+	return(*this);
+}
+
+/// destructeur
+ConductorProfile::~ConductorProfile() {
+	setNull();
+}
+
+/// @return true si le profil est nul
+bool ConductorProfile::isNull() const {
+	return(segments.isEmpty());
+}
+
+/// supprime les segments du profil de conducteur
+void ConductorProfile::setNull() {
+	foreach(ConductorSegmentProfile *csp, segments) delete csp;
+	segments.clear();
+}
+
+/// @return la largeur occupee par le conducteur
+qreal ConductorProfile::width() const {
+	qreal width = 0.0;
+	foreach(ConductorSegmentProfile *csp, segments) {
+		if (csp -> isHorizontal) width += csp -> length;
+	}
+	return(width);
+}
+
+/// @return la hauteur occupee par le conducteur
+qreal ConductorProfile::height() const{
+	qreal height = 0.0;
+	foreach(ConductorSegmentProfile *csp, segments) {
+		if (!csp -> isHorizontal) height += csp -> length;
+	}
+	return(height);
+}
+
+/**
+	@param type Type de Segments
+	@return Le nombre de segments composant le conducteur.
+*/
+uint ConductorProfile::segmentsCount(QET::ConductorSegmentType type) const {
+	if (type == QET::Both) return(segments.count());
+	uint nb_seg = 0;
+	foreach(ConductorSegmentProfile *csp, segments) {
+		if (type == QET::Horizontal && csp -> isHorizontal) ++ nb_seg;
+		else if (type == QET::Vertical && !csp -> isHorizontal) ++ nb_seg;
+	}
+	return(nb_seg);
+}
+
+/// @return les segments horizontaux de ce profil
+QList<ConductorSegmentProfile *> ConductorProfile::horizontalSegments() {
+	QList<ConductorSegmentProfile *> segments_list;
+	foreach(ConductorSegmentProfile *csp, segments) {
+		if (csp -> isHorizontal) segments_list << csp;
+	}
+	return(segments_list);
+}
+
+/// @return les segments verticaux de ce profil
+QList<ConductorSegmentProfile *> ConductorProfile::verticalSegments() {
+	QList<ConductorSegmentProfile *> segments_list;
+	foreach(ConductorSegmentProfile *csp, segments) {
+		if (!csp -> isHorizontal) segments_list << csp;
+	}
+	return(segments_list);
+}
+
+/**
+	Reconstruit le profil a partir d'un conducteur existant
+*/
+void ConductorProfile::fromConductor(Conductor *conductor) {
+	// supprime les segments precedents
+	setNull();
+	
+	foreach(ConductorSegment *conductor_segment, conductor -> segmentsList()) {
+		segments << new ConductorSegmentProfile(conductor_segment);
+	}
+	beginOrientation = conductor -> terminal1 -> orientation();
+	endOrientation   = conductor -> terminal2 -> orientation();
+}
+
+/**
+	Permet de debugger un profil de conducteur
+	@param d Object QDebug a utiliser pour l'affichage des informations de debug
+	@param t Profil de conducteur a debugger
+*/
+QDebug &operator<<(QDebug d, ConductorProfile &t) {
+	d << "ConductorProfile {";
+	foreach(ConductorSegmentProfile *csp, t.segments) {
+		d << "CSP" << (csp -> isHorizontal ? "horizontal" : "vertical") << ":" << csp -> length << ",";
+	}
+	d << "}";
+	return(d.space());
+}

Added: trunk/sources/conductorprofile.h
===================================================================
--- trunk/sources/conductorprofile.h	                        (rev 0)
+++ trunk/sources/conductorprofile.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,58 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONDUCTOR_PROFILE_H
+#define CONDUCTOR_PROFILE_H
+#include <QList>
+#include "qet.h"
+class Conductor;
+class ConductorSegmentProfile;
+/**
+	This class represents the profile of a conductor, i.e. its primary
+	characteristics.
+*/
+class ConductorProfile {
+	public:
+	// constructors, destructor
+	ConductorProfile();
+	ConductorProfile(Conductor *conductor);
+	ConductorProfile(const ConductorProfile &);
+	ConductorProfile &operator=(const ConductorProfile &);
+	virtual ~ConductorProfile();
+	
+	// attributes
+	public:
+	/// Segments composing the conductor
+	QList<ConductorSegmentProfile *> segments;
+	/// Orientation of the start terminal
+	QET::Orientation beginOrientation;
+	/// Orientation of the end terminal.
+	QET::Orientation endOrientation;
+	
+	// methods
+	public:
+	bool isNull() const;
+	void setNull();
+	qreal width() const;
+	qreal height() const;
+	uint segmentsCount(QET::ConductorSegmentType) const;
+	QList<ConductorSegmentProfile *> horizontalSegments();
+	QList<ConductorSegmentProfile *> verticalSegments();
+	void fromConductor(Conductor *);
+};
+QDebug &operator<<(QDebug d, ConductorProfile &);
+#endif

Added: trunk/sources/conductorproperties.cpp
===================================================================
--- trunk/sources/conductorproperties.cpp	                        (rev 0)
+++ trunk/sources/conductorproperties.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,462 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "conductorproperties.h"
+
+/**
+	Constructeur par defaut
+*/
+SingleLineProperties::SingleLineProperties() :
+	hasGround(true),
+	hasNeutral(true),
+	is_pen(false),
+	phases(1)
+{
+}
+
+/// Destructeur
+SingleLineProperties::~SingleLineProperties() {
+}
+
+/**
+	Definit le nombre de phases (0, 1, 2, ou 3)
+	@param n Nombre de phases
+*/
+void SingleLineProperties::setPhasesCount(int n) {
+	phases = qBound(0, n, 3);
+}
+
+/// @return le nombre de phases (0, 1, 2, ou 3)
+unsigned short int SingleLineProperties::phasesCount() {
+	return(phases);
+}
+
+/**
+	@return true if the singleline conductor should be drawn using the PEN
+	(Protective Earth Neutral) representation and if it features the ground and
+	the neutral.
+*/
+bool SingleLineProperties::isPen() const {
+	return(hasNeutral && hasGround && is_pen);
+}
+
+/**
+	Dessine les symboles propres a un conducteur unifilaire
+	@param painter QPainter a utiliser pour dessiner les symboles
+	@param direction direction du segment sur lequel les symboles apparaitront
+	@param rect rectangle englobant le dessin ; utilise pour specifier a la fois la position et la taille du dessin
+*/
+void SingleLineProperties::draw(QPainter *painter, QET::ConductorSegmentType direction, const QRectF &rect) {
+	// s'il n'y a rien a dessiner, on retourne immediatement
+	if (!hasNeutral && !hasGround && !phases) return;
+	
+	// prepare le QPainter
+	painter -> save();
+	QPen pen(painter -> pen());
+	pen.setCapStyle(Qt::FlatCap);
+	pen.setJoinStyle(Qt::MiterJoin);
+	pen.setStyle(Qt::SolidLine);
+	painter -> setPen(pen);
+	painter -> setRenderHint(QPainter::Antialiasing, true);
+	
+	uint symbols_count = (hasNeutral ? 1 : 0) + (hasGround ? 1 : 0) - (isPen() ? 1 : 0) + phases;
+	qreal interleave_base = (direction == QET::Horizontal ? rect.width() : rect.height());
+	qreal interleave = interleave_base / (symbols_count + 1);;
+	qreal symbol_width = interleave_base / 12;
+	
+	for (uint i = 1 ; i <= symbols_count ; ++ i) {
+		// dessine le tronc du symbole
+		QPointF symbol_p1, symbol_p2;
+		if (direction == QET::Horizontal) {
+			symbol_p1 = QPointF(rect.x() + (i * interleave) + symbol_width, rect.y() + rect.height() * 0.75);
+			symbol_p2 = QPointF(rect.x() + (i * interleave) - symbol_width, rect.y() + rect.height() * 0.25);
+		} else {
+			symbol_p2 = QPointF(rect.x() + rect.width() * 0.75, rect.y() + (i * interleave) - symbol_width);
+			symbol_p1 = QPointF(rect.x() + rect.width() * 0.25, rect.y() + (i * interleave) + symbol_width);
+		}
+		painter -> drawLine(QLineF(symbol_p1, symbol_p2));
+		
+		// dessine le reste des symboles terre et neutre
+		if (isPen()) {
+			if (i == 1) {
+				drawPen(painter, direction, symbol_p2, symbol_width);
+			}
+		} else {
+			if (hasGround && i == 1) {
+				drawGround(painter, direction, symbol_p2, symbol_width * 2.0);
+			} else if (hasNeutral && ((i == 1 && !hasGround) || (i == 2 && hasGround))) {
+				drawNeutral(painter, direction, symbol_p2, symbol_width * 1.5);
+			}
+		}
+	}
+	painter -> restore();
+}
+
+/**
+	Dessine le segment correspondant au symbole de la terre sur un conducteur unifilaire
+	@param painter QPainter a utiliser pour dessiner le segment
+	@param direction direction du segment sur lequel le symbole apparaitra
+	@param center centre du segment
+	@param size taille du segment
+*/
+void SingleLineProperties::drawGround(QPainter *painter, QET::ConductorSegmentType direction, QPointF center, qreal size) {
+	painter -> save();
+	
+	// prepare le QPainter
+	painter -> setRenderHint(QPainter::Antialiasing, false);
+	QPen pen2(painter -> pen());
+	pen2.setCapStyle(Qt::SquareCap);
+	painter -> setPen(pen2);
+	
+	// dessine le segment representant la terre
+	qreal half_size = size / 2.0;
+	QPointF offset_point(
+		(direction == QET::Horizontal) ? half_size : 0.0,
+		(direction == QET::Horizontal) ? 0.0 : half_size
+	);
+	painter -> drawLine(
+		QLineF(
+			center + offset_point,
+			center - offset_point
+		)
+	);
+	
+	painter -> restore();
+}
+
+/**
+	Dessine le cercle correspondant au symbole du neutre sur un conducteur unifilaire
+	@param painter QPainter a utiliser pour dessiner le segment
+	@param direction direction du segment sur lequel le symbole apparaitra
+	@param center centre du cercle
+	@param size diametre du cercle
+*/
+void SingleLineProperties::drawNeutral(QPainter *painter, QET::ConductorSegmentType direction, QPointF center, qreal size) {
+	Q_UNUSED(direction);
+	painter -> save();
+	
+	// prepare le QPainter
+	if (painter -> brush() == Qt::NoBrush) painter -> setBrush(Qt::black);
+	painter -> setPen(Qt::NoPen);
+	
+	// desine le cercle representant le neutre
+	painter -> drawEllipse(
+		QRectF(
+			center - QPointF(size / 2.0, size / 2.0),
+			QSizeF(size, size)
+		)
+	);
+	
+	painter -> restore();
+}
+
+/**
+	Draw the PEN (Protective Earth Neutral) symbol using \a painter at position \a
+	center, using a size hint of \a size.
+	@param direction Indicate the direction of the underlying conductor segment
+*/
+void SingleLineProperties::drawPen(QPainter *painter, QET::ConductorSegmentType direction, QPointF center, qreal size) {
+	painter -> save();
+	
+	//painter -> setBrush(Qt::white);
+	// desine le cercle representant le neutre
+	//painter -> drawEllipse(
+	//	QRectF(
+	//		center - QPointF(size * 1.5 / 2.0, size * 1.5 / 2.0),
+	//		QSizeF(size * 1.5, size * 1.5)
+	//	)
+	//);
+	drawNeutral(painter, direction, center, size * 1.5);
+	
+	int offset = (size * 1.5 / 2.0);
+	QPointF pos = center + (direction == QET::Horizontal ? QPointF(0.0, -offset - 0.5) : QPointF(offset + 0.5, 0.0));
+	drawGround(painter, direction, pos, 2.0 * size);
+	painter -> restore();
+}
+
+/**
+	Exporte les parametres du conducteur unifilaire sous formes d'attributs XML
+	ajoutes a l'element e.
+	@param e Element XML auquel seront ajoutes des attributs
+*/
+void SingleLineProperties::toXml(QDomElement &e) const {
+	e.setAttribute("ground",  hasGround  ? "true" : "false");
+	e.setAttribute("neutral", hasNeutral ? "true" : "false");
+	e.setAttribute("phase",   phases);
+	if (isPen()) e.setAttribute("pen", "true");
+}
+
+/**
+	Importe les parametres du conducteur unifilaire a partir des attributs XML
+	de l'element e
+	@param e Element XML dont les attributs seront lus
+*/
+void SingleLineProperties::fromXml(QDomElement &e) {
+	hasGround  = e.attribute("ground")  == "true";
+	hasNeutral = e.attribute("neutral") == "true";
+	setPhasesCount(e.attribute("phase").toInt());
+	is_pen = (hasGround && hasNeutral && e.attribute("pen", "false") == "true");
+}
+
+/**
+	Constructeur : par defaut, les proprietes font un conducteur
+	multifilaire noir dont le texte est "_"
+*/
+ConductorProperties::ConductorProperties() :
+	type(Multi),
+	color(Qt::black),
+	text("_"),
+	verti_rotate_text(270),
+	horiz_rotate_text(0),
+	style(Qt::SolidLine)
+{
+}
+
+/**
+	Destructeur
+*/
+ConductorProperties::~ConductorProperties() {
+}
+
+/**
+	Exporte les parametres du conducteur sous formes d'attributs XML
+	ajoutes a l'element e.
+	@param e Element XML auquel seront ajoutes des attributs
+*/
+void ConductorProperties::toXml(QDomElement &e) const {
+	e.setAttribute("type", typeToString(type));
+	
+	if (color != QColor(Qt::black)) {
+		e.setAttribute("color", color.name());
+	}
+	
+	if (type == Single) {
+		singleLineProperties.toXml(e);
+	} else if (type == Multi) {
+		e.setAttribute("num", text);
+		e.setAttribute("vertirotatetext", verti_rotate_text);
+		e.setAttribute("horizrotatetext", horiz_rotate_text);
+	}
+	
+	QString conductor_style = writeStyle();
+	if (!conductor_style.isEmpty()) {
+		e.setAttribute("style", conductor_style);
+	}
+}
+
+/**
+	Importe les parametres du conducteur unifilaire a partir des attributs XML
+	de l'element e
+	@param e Element XML dont les attributs seront lus
+*/
+void ConductorProperties::fromXml(QDomElement &e) {
+	// recupere la couleur du conducteur
+	QColor xml_color= QColor(e.attribute("color"));
+	if (xml_color.isValid()) {
+		color = xml_color;
+	} else {
+		color = QColor(Qt::black);
+	}
+	
+	// lit le style du conducteur
+	readStyle(e.attribute("style"));
+	
+	if (e.attribute("type") == typeToString(Single)) {
+		// recupere les parametres specifiques a un conducteur unifilaire
+		singleLineProperties.fromXml(e);
+		type = Single;
+	} else if (e.attribute("type") == typeToString(Simple)) {
+		type = Simple;
+	} else {
+		// recupere le champ de texte
+		text = e.attribute("num");
+		verti_rotate_text = e.attribute("vertirotatetext").toDouble();
+		horiz_rotate_text = e.attribute("horizrotatetext").toDouble();
+		type = Multi;
+	}
+}
+
+/**
+	@param settings Parametres a ecrire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void ConductorProperties::toSettings(QSettings &settings, const QString &prefix) const {
+	settings.setValue(prefix + "color", color.name());
+	settings.setValue(prefix + "style", writeStyle());
+	settings.setValue(prefix + "type", typeToString(type));
+	settings.setValue(prefix + "text", text);
+	settings.setValue(prefix + "vertirotatetext", QString::number(verti_rotate_text));
+	settings.setValue(prefix + "horizrotatetext", QString::number(horiz_rotate_text));
+	singleLineProperties.toSettings(settings, prefix);
+}
+
+/**
+	@param settings Parametres a lire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void ConductorProperties::fromSettings(QSettings &settings, const QString &prefix) {
+	// recupere la couleur dans les parametres
+	QColor settings_color = QColor(settings.value(prefix + "color").toString());
+	if (settings_color.isValid()) {
+		color = settings_color;
+	} else {
+		color = QColor(Qt::black);
+	}
+	
+	QString setting_type = settings.value(prefix + "type", typeToString(Multi)).toString();
+	if (setting_type == typeToString(Single)) {
+		type = Single;
+	} else if (setting_type == typeToString(Simple)) {
+		type = Simple;
+	} else {
+		type = Multi;
+	}
+	singleLineProperties.fromSettings(settings, prefix);
+	text = settings.value(prefix + "text", "_").toString();
+	verti_rotate_text = settings.value((prefix + "vertirotatetext"), "270").toDouble();
+	horiz_rotate_text = settings.value((prefix + "horizrotatetext"), "0").toDouble();
+
+	// lit le style du conducteur
+	readStyle(settings.value(prefix + "style").toString());
+}
+
+/**
+	@param t type du conducteur
+*/
+QString ConductorProperties::typeToString(ConductorType t) {
+	switch(t) {
+		case Simple: return("simple");
+		case Single: return("single");
+		case Multi:  return("multi");
+		default: return(QString());
+	}
+}
+
+/**
+	@param other l'autre ensemble de proprietes avec lequel il faut effectuer la comparaison
+	@return true si les deux ensembles de proprietes sont identiques, false sinon
+*/
+int ConductorProperties::operator==(const ConductorProperties &other) {
+	return(
+		other.type == type &&\
+		other.color == color &&\
+		other.style == style &&\
+		other.text == text &&\
+		other.verti_rotate_text == verti_rotate_text &&\
+		other.horiz_rotate_text == horiz_rotate_text &&\
+		other.singleLineProperties == singleLineProperties
+	);
+}
+
+/**
+	@param other l'autre ensemble de proprietes avec lequel il faut effectuer la comparaison
+	@return true si les deux ensembles de proprietes sont differents, false sinon
+*/
+int ConductorProperties::operator!=(const ConductorProperties &other) {
+	return(
+		other.type != type ||\
+		other.color != color ||\
+		other.style != style ||\
+		other.text != text ||\
+		other.verti_rotate_text != verti_rotate_text ||\
+		other.horiz_rotate_text != horiz_rotate_text ||\
+		other.singleLineProperties != singleLineProperties
+	);
+}
+
+/**
+	Applique les styles passes en parametre dans cet objet
+	@param style_string Chaine decrivant le style du conducteur
+*/
+void ConductorProperties::readStyle(const QString &style_string) {
+	style = Qt::SolidLine; // style par defaut
+	
+	if (style_string.isEmpty()) return;
+	
+	// recupere la liste des couples style / valeur
+	QStringList styles = style_string.split(";", QString::SkipEmptyParts);
+	
+	QRegExp rx("^\\s*([a-z-]+)\\s*:\\s*([a-z-]+)\\s*$");
+	foreach (QString style_str, styles) {
+		if (rx.exactMatch(style_str)) {
+			QString style_name = rx.cap(1);
+			QString style_value = rx.cap(2);
+			if (style_name == "line-style") {
+				if (style_value == "dashed") style = Qt::DashLine;
+				else if (style_value == "dashdotted") style = Qt::DashDotLine;
+				else if (style_value == "normal") style = Qt::SolidLine;
+			}
+		}
+	}
+}
+
+/**
+	Exporte le style du conducteur sous forme d'une chaine de caracteres
+	@return une chaine de caracteres decrivant le style du conducteur
+*/
+QString ConductorProperties::writeStyle() const {
+	if (style == Qt::DashLine) {
+		return("line-style: dashed;");
+	} else if (style == Qt::DashDotLine) {
+		return("line-style: dashdotted");
+	} else {
+		return(QString());
+	}
+}
+
+/**
+	@param other l'autre ensemble de proprietes avec lequel il faut effectuer la comparaison
+	@return true si les deux ensembles de proprietes sont identiques, false sinon
+*/
+int SingleLineProperties::operator==(const SingleLineProperties &other) const {
+	return(
+		other.hasGround == hasGround &&\
+		other.hasNeutral == hasNeutral &&\
+		other.is_pen == is_pen &&\
+		other.phases == phases
+	);
+}
+
+/**
+	@param other l'autre ensemble de proprietes avec lequel il faut effectuer la comparaison
+	@return true si les deux ensembles de proprietes sont differents, false sinon
+*/
+int SingleLineProperties::operator!=(const SingleLineProperties &other) const {
+	return(!(other == (*this)));
+}
+
+/**
+	@param settings Parametres a ecrire
+	@param prefix prefix a ajouter devant les noms des parametres
+*/
+void SingleLineProperties::toSettings(QSettings &settings, const QString &prefix) const {
+	settings.setValue(prefix + "hasGround",  hasGround);
+	settings.setValue(prefix + "hasNeutral", hasNeutral);
+	settings.setValue(prefix + "phases",     phases);
+	settings.setValue(prefix + "pen",        is_pen);
+}
+
+/**
+	@param settings Parametres a lire
+	@param prefix prefix a ajouter devant les noms des parametres
+*/
+void SingleLineProperties::fromSettings(QSettings &settings, const QString &prefix) {
+	hasGround  = settings.value(prefix + "hasGround",  true).toBool();
+	hasNeutral = settings.value(prefix + "hasNeutral", true).toBool();
+	phases     = settings.value(prefix + "phases",     1).toInt();
+	is_pen     = settings.value(prefix + "pen",        false).toBool();
+}

Added: trunk/sources/conductorproperties.h
===================================================================
--- trunk/sources/conductorproperties.h	                        (rev 0)
+++ trunk/sources/conductorproperties.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,106 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONDUCTOR_PROPERTIES_H
+#define CONDUCTOR_PROPERTIES_H
+#include "qet.h"
+#include <QtGui>
+#include <QtXml>
+/**
+	This class represents the properties of a singleline conductor.
+*/
+class SingleLineProperties {
+	public:
+	SingleLineProperties();
+	virtual ~SingleLineProperties();
+	
+	void setPhasesCount(int);
+	unsigned short int phasesCount();
+	bool isPen() const;
+	void draw(QPainter *, QET::ConductorSegmentType, const QRectF &);
+	void toXml(QDomElement &) const;
+	void fromXml(QDomElement &);
+	void toSettings(QSettings &, const QString & = QString()) const;
+	void fromSettings(QSettings &, const QString & = QString());
+	
+	/// Whether the singleline conductor should display the ground symbol
+	bool hasGround;
+	/// Whether the singleline conductor should display the neutral symbol
+	bool hasNeutral;
+	/// Protective Earth Neutral: visually merge neutral and ground
+	bool is_pen;
+	
+	int operator==(const SingleLineProperties &) const;
+	int operator!=(const SingleLineProperties &) const;
+	
+	private:
+	unsigned short int phases;
+	void drawGround (QPainter *, QET::ConductorSegmentType, QPointF, qreal);
+	void drawNeutral(QPainter *, QET::ConductorSegmentType, QPointF, qreal);
+	void drawPen(QPainter *, QET::ConductorSegmentType, QPointF, qreal);
+};
+
+/**
+	This class represents the functional properties of a particular conductor,
+	i.e. properties other than path and terminals.
+*/
+class ConductorProperties {
+	// constructors, destructor
+	public:
+	ConductorProperties();
+	virtual ~ConductorProperties();
+	
+	/**
+		Represents the kind of a particular conductor:
+		 * Simple: no symbols, no text input
+		 * Single: singleline symbols, no text input
+		 * Multi: text input, no symbol
+	*/
+	enum ConductorType { Simple, Single, Multi };
+	
+	// attributes
+	/// Conductor type
+	ConductorType type;
+	/// Conductor color
+	QColor color;
+	/// Texte displayed for multiline conductors
+	QString text;
+	/// rotation angle texte
+	double verti_rotate_text;
+	double horiz_rotate_text;
+	/// conducteur style (Qt::SolidLine or Qt::DashLine)
+	Qt::PenStyle style;
+	
+	/// properties for singleline conductors
+	SingleLineProperties singleLineProperties;
+	
+	// methods
+	void toXml(QDomElement &) const;
+	void fromXml(QDomElement &);
+	void toSettings(QSettings &, const QString & = QString()) const;
+	void fromSettings(QSettings &, const QString & = QString());
+	static QString typeToString(ConductorType);
+	
+	// operators
+	int operator==(const ConductorProperties &);
+	int operator!=(const ConductorProperties &);
+	
+	private:
+	void readStyle(const QString &);
+	QString writeStyle() const;
+};
+#endif

Added: trunk/sources/conductorpropertieswidget.cpp
===================================================================
--- trunk/sources/conductorpropertieswidget.cpp	                        (rev 0)
+++ trunk/sources/conductorpropertieswidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,366 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "conductorpropertieswidget.h"
+#include <QtGui>
+#include "qetgraphicsitem/conductor.h"
+#include "qeticons.h"
+#include "qetapp.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+ConductorPropertiesWidget::ConductorPropertiesWidget(QWidget *parent) :
+	QWidget(parent)
+{
+	buildInterface();
+}
+
+/**
+	Constructeur
+	@param parent QWidget parent
+	@param cp Proprietes a editer
+*/
+ConductorPropertiesWidget::ConductorPropertiesWidget(const ConductorProperties &cp, QWidget *parent) :
+	QWidget(parent)
+{
+	buildInterface();
+	setConductorProperties(cp);
+}
+
+/// construit l'interface du widget
+void ConductorPropertiesWidget::buildInterface() {
+	
+	setFocusPolicy(Qt::StrongFocus);
+	
+	QVBoxLayout *main_layout = new QVBoxLayout(this);
+	main_layout -> setContentsMargins(0, 0, 0, 0);
+	
+	QGroupBox *groupbox = new QGroupBox(tr("Type de conducteur"));
+	main_layout -> addWidget(groupbox);
+	
+	QVBoxLayout *groupbox_layout = new QVBoxLayout();
+	groupbox -> setLayout(groupbox_layout);
+	
+	simple = new QRadioButton(tr("Simple"));
+	multiline = new QRadioButton(tr("Multifilaire"));
+	
+	QHBoxLayout *multiline_layout = new QHBoxLayout();
+	QLabel *text = new QLabel(tr("Texte :"));
+	text_field = new QLineEdit();
+	multiline_layout -> addWidget(text);
+	multiline_layout -> addWidget(text_field);
+
+	QGridLayout *rotate_text_layout = new QGridLayout;
+	QLabel *rotate_label = new QLabel(tr("Rotation du texte sur conducteur :"));
+	rotate_text_layout -> addWidget(rotate_label, 0, 0);
+
+	QLabel *verti_text = new QLabel(tr("Vertical"));
+	verti_select = QETApp::createTextOrientationSpinBoxWidget();
+	rotate_text_layout -> addWidget(verti_text, 1, 0);
+	rotate_text_layout -> setAlignment(verti_text, Qt::AlignCenter);
+	rotate_text_layout -> addWidget(verti_select, 2, 0);
+
+	QLabel *horiz_text = new QLabel(tr("Horizontal"));
+	horiz_select = QETApp::createTextOrientationSpinBoxWidget();
+	rotate_text_layout -> addWidget(horiz_text, 1, 1);
+	rotate_text_layout -> setAlignment(horiz_text, Qt::AlignCenter);
+	rotate_text_layout -> addWidget(horiz_select, 2, 1);
+	
+	singleline = new QRadioButton(tr("Unifilaire"));
+	
+	ground_checkbox = new QCheckBox(tr("terre"));
+	ground_checkbox -> setIcon(QET::Icons::Ground);
+	neutral_checkbox = new QCheckBox(tr("neutre"));
+	neutral_checkbox -> setIcon(QET::Icons::Neutral);
+	merge_checkbox = new QCheckBox(tr("PEN", "Protective Earth Neutral"));
+	merge_checkbox -> setToolTip(tr("Protective Earth Neutral", "Tooltip displaying the meaning of the 'PEN' acronym"));
+	
+	QVBoxLayout *singleline_layout5 = new QVBoxLayout();
+	singleline_layout5 -> addWidget(ground_checkbox);
+	singleline_layout5 -> addWidget(neutral_checkbox);
+	
+	QHBoxLayout *singleline_layout4 = new QHBoxLayout();
+	singleline_layout4 -> addLayout(singleline_layout5);
+	singleline_layout4 -> addWidget(merge_checkbox, 4, Qt::AlignVCenter | Qt::AlignLeft);
+	
+	QHBoxLayout *singleline_layout3 = new QHBoxLayout();
+	phase_checkbox = new QCheckBox(tr("phase"));
+	phase_checkbox -> setIcon(QET::Icons::Phase);
+	phase_slider = new QSlider(Qt::Horizontal);
+	phase_slider -> setRange(1, 3);
+	phase_spinbox = new QSpinBox();
+	phase_spinbox -> setRange(1, 3);
+	singleline_layout3 -> addWidget(phase_checkbox);
+	singleline_layout3 -> addWidget(phase_slider);
+	singleline_layout3 -> addWidget(phase_spinbox);
+	
+	QVBoxLayout *singleline_layout2 = new QVBoxLayout();
+	singleline_layout2 -> addLayout(singleline_layout4);
+	singleline_layout2 -> addLayout(singleline_layout3);
+	
+	QHBoxLayout *singleline_layout1 = new QHBoxLayout();
+	preview = new QLabel();
+	preview -> setFixedSize(150, 150);
+	singleline_layout1 -> addWidget(preview);
+	singleline_layout1 -> addLayout(singleline_layout2);
+	
+	QGroupBox *groupbox2 = new QGroupBox(tr("Apparence du conducteur"));
+	main_layout -> addWidget(groupbox2);
+	
+	QVBoxLayout *groupbox2_layout = new QVBoxLayout();
+	groupbox2 -> setLayout(groupbox2_layout);
+	
+	QHBoxLayout *color_layout = new QHBoxLayout();
+	QLabel *text1 = new QLabel(tr("Couleur :"));
+	color_button = new QPushButton("");
+	color_layout -> addWidget(text1);
+	color_layout -> addWidget(color_button);
+	
+	QHBoxLayout *style_layout = new QHBoxLayout();
+	QLabel *text2 = new QLabel(tr("Style :", "conductor line style"));
+	line_style = new QComboBox();
+	line_style -> addItem(tr("Trait plein", "conductor style: solid line"), Qt::SolidLine);
+	line_style -> addItem(tr("Trait en pointill\351s", "conductor style: dashed line"), Qt::DashLine);
+	line_style -> addItem(tr("Traits et points", "conductor style: dashed and dotted line"), Qt::DashDotLine);
+	style_layout -> addWidget(text2);
+	style_layout -> addWidget(line_style);
+	
+	setColorButton(properties_.color);
+	int index = line_style -> findData(properties_.style);
+	if (index != -1) line_style -> setCurrentIndex(index);
+	
+	groupbox_layout -> addWidget(simple);
+	groupbox_layout -> addWidget(multiline);
+	groupbox_layout -> addLayout(multiline_layout);
+	groupbox_layout -> addLayout(rotate_text_layout);
+	groupbox_layout -> addWidget(singleline);
+	groupbox_layout -> addLayout(singleline_layout1);
+	
+	groupbox2_layout -> addLayout(color_layout);
+	groupbox2_layout -> addLayout(style_layout);
+
+	radio_buttons = new QButtonGroup(this);
+	radio_buttons -> addButton(simple,     ConductorProperties::Simple);
+	radio_buttons -> addButton(multiline,  ConductorProperties::Multi);
+	radio_buttons -> addButton(singleline, ConductorProperties::Single);
+	
+	buildConnections();
+	setConductorType(ConductorProperties::Multi);
+}
+
+/// Met en place les connexions signaux/slots
+void ConductorPropertiesWidget::buildConnections() {
+	connect(phase_slider,      SIGNAL(valueChanged(int)),            phase_spinbox, SLOT(setValue(int)));
+	connect(phase_spinbox,     SIGNAL(valueChanged(int)),            phase_slider,  SLOT(setValue(int)));
+	connect(ground_checkbox,   SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	connect(neutral_checkbox,  SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	connect(merge_checkbox,    SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	connect(phase_checkbox,    SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	connect(phase_slider,      SIGNAL(valueChanged(int)),            this,          SLOT(updateConfig()));
+	connect(radio_buttons,     SIGNAL(buttonClicked(int)),           this,          SLOT(updateConfig()));
+	connect(text_field,        SIGNAL(textChanged(const QString &)), this,          SLOT(updateConfig()));
+	connect(line_style,        SIGNAL(currentIndexChanged(int)),     this,          SLOT(updateConfig()));
+	connect(color_button,      SIGNAL(clicked()),                    this,          SLOT(chooseColor()));
+	connect(verti_select,      SIGNAL(editingFinished(double)),      this,          SLOT(updateConfig()));
+	connect(horiz_select,      SIGNAL(editingFinished(double)),      this,          SLOT(updateConfig()));
+}
+
+/**
+	Demande a l'utilisateur de choisir une couleur via un dialogue approprie.
+*/
+void ConductorPropertiesWidget::chooseColor() {
+	QColor user_chosen_color = QColorDialog::getColor(properties_.color);
+	if (user_chosen_color.isValid()) {
+		setColorButton(user_chosen_color);
+		updateConfig();
+	}
+}
+
+/**
+	@return la couleur actuelle du bouton permettant de choisir la couleur du
+	conducteur
+*/
+QColor ConductorPropertiesWidget::colorButton() const {
+	return(color_button -> palette().color(QPalette::Button));
+}
+
+/**
+	Change la couleur du bouton permettant de choisir la couleur du conducteur
+	@param color Nouvelle couleur a afficher
+*/
+void ConductorPropertiesWidget::setColorButton(const QColor &color) {
+	QPalette palette;
+	palette.setColor(QPalette::Button, color);
+	color_button -> setPalette(palette);
+}
+
+/// Enleve les connexions signaux/slots
+void ConductorPropertiesWidget::destroyConnections() {
+	disconnect(phase_slider,      SIGNAL(valueChanged(int)),            phase_spinbox, SLOT(setValue(int)));
+	disconnect(phase_spinbox,     SIGNAL(valueChanged(int)),            phase_slider,  SLOT(setValue(int)));
+	disconnect(ground_checkbox,   SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	disconnect(neutral_checkbox,  SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	disconnect(merge_checkbox,    SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	disconnect(phase_checkbox,    SIGNAL(toggled(bool)),                this,          SLOT(updateConfig()));
+	disconnect(phase_slider,      SIGNAL(valueChanged(int)),            this,          SLOT(updateConfig()));
+	disconnect(radio_buttons,     SIGNAL(buttonClicked(int)),           this,          SLOT(updateConfig()));
+	disconnect(text_field,        SIGNAL(textChanged(const QString &)), this,          SLOT(updateConfig()));
+	disconnect(color_button,      SIGNAL(clicked()),                    this,          SLOT(chooseColor()));
+	disconnect(line_style,        SIGNAL(currentIndexChanged(int)),     this,          SLOT(updateConfig()));
+	disconnect(verti_select,      SIGNAL(editingFinished(double)),      this,          SLOT(updateConfig()));
+	disconnect(horiz_select,      SIGNAL(editingFinished(double)),      this,          SLOT(updateConfig()));
+}
+
+/// Destructeur
+ConductorPropertiesWidget::~ConductorPropertiesWidget() {
+	delete verti_select;
+	delete horiz_select;
+}
+
+/// Met a jour les proprietes
+void ConductorPropertiesWidget::updateConfig() {
+	properties_.type = static_cast<ConductorProperties::ConductorType>(radio_buttons -> checkedId());
+	properties_.color = colorButton();
+	properties_.style = static_cast<Qt::PenStyle>(line_style -> itemData(line_style -> currentIndex()).toInt());
+	properties_.text = text_field -> text();
+	properties_.verti_rotate_text = verti_select -> value();
+	properties_.horiz_rotate_text = horiz_select -> value();
+	properties_.singleLineProperties.hasGround = ground_checkbox -> isChecked();
+	properties_.singleLineProperties.hasNeutral = neutral_checkbox -> isChecked();
+	properties_.singleLineProperties.is_pen = merge_checkbox -> isChecked();
+	properties_.singleLineProperties.setPhasesCount(phase_checkbox -> isChecked() ? phase_spinbox -> value() : 0);
+	
+	updateDisplay();
+}
+
+/// Met a jour l'affichage des proprietes
+void ConductorPropertiesWidget::updateDisplay() {
+	destroyConnections();
+	
+	setConductorType(properties_.type);
+	setColorButton(properties_.color);
+	int index = line_style -> findData(properties_.style);
+	if (index != -1) line_style -> setCurrentIndex(index);
+	text_field -> setText(properties_.text);
+	ground_checkbox -> setChecked(properties_.singleLineProperties.hasGround);
+	neutral_checkbox -> setChecked(properties_.singleLineProperties.hasNeutral);
+	merge_checkbox -> setChecked(properties_.singleLineProperties.is_pen);
+	merge_checkbox -> setEnabled(!isReadOnly() && properties_.type == ConductorProperties::Single && ground_checkbox -> isChecked() && neutral_checkbox -> isChecked());
+	phase_spinbox -> setValue(properties_.singleLineProperties.phasesCount());
+	phase_slider -> setValue(properties_.singleLineProperties.phasesCount());
+	phase_checkbox -> setChecked(properties_.singleLineProperties.phasesCount());
+	verti_select -> setValue(properties_.verti_rotate_text);
+	horiz_select -> setValue(properties_.horiz_rotate_text);
+	
+	buildConnections();
+	updatePreview();
+}
+
+/// Met a jour la previsualisation des attributs unifilaires
+void ConductorPropertiesWidget::updatePreview() {
+	const QRect pixmap_rect(0, 0, 96, 96);
+	QPixmap pixmap(pixmap_rect.width(), pixmap_rect.height());
+	QPainter painter;
+	painter.begin(&pixmap);
+	painter.eraseRect(pixmap_rect);
+	painter.drawRect(pixmap_rect.adjusted(0,0,-1,-1));
+	painter.drawLine(QLineF(0, pixmap_rect.height() / 2, pixmap_rect.width(), pixmap_rect.height() / 2));
+	properties_.singleLineProperties.draw(&painter, QET::Horizontal, pixmap_rect);
+	painter.end();
+	preview -> setPixmap(pixmap);
+}
+
+/**
+	Passe le widget en mode simple, unifilaire ou multifilaire
+	@param t le type de conducteur
+*/
+void ConductorPropertiesWidget::setConductorType(ConductorProperties::ConductorType t) {
+	
+	// widgets lies au simple
+	simple        -> setChecked(t == ConductorProperties::Simple);
+	
+	// widgets lies au mode multifilaire
+	multiline        -> setChecked(t == ConductorProperties::Multi);
+	text_field       -> setEnabled(t == ConductorProperties::Multi);
+	
+	// widgets lies au mode unifilaire
+	bool sl = (t == ConductorProperties::Single);
+	singleline       -> setChecked(sl);
+	preview          -> setEnabled(sl);
+	phase_checkbox   -> setEnabled(sl);
+	phase_slider     -> setEnabled(sl);
+	phase_spinbox    -> setEnabled(sl);
+	ground_checkbox  -> setEnabled(sl);
+	neutral_checkbox -> setEnabled(sl);
+	merge_checkbox   -> setEnabled(sl);
+}
+
+/// @param p les nouvelles proprietes
+void ConductorPropertiesWidget::setConductorProperties(const ConductorProperties &p) {
+	properties_ = p;
+	updateDisplay();
+}
+
+/// @return les proprietes editees
+ConductorProperties ConductorPropertiesWidget::conductorProperties() const {
+	return(properties_);
+}
+
+/**
+	@return true si ce widget est en lecture seule, false sinon
+*/
+bool ConductorPropertiesWidget::isReadOnly() const {
+	return(text_field -> isReadOnly());
+}
+
+/**
+	@param ro true pour passer ce widget en lecture seule, false sinon
+*/
+void ConductorPropertiesWidget::setReadOnly(bool ro) {
+	// enable or disable all child widgets according to the read only state
+	simple    -> setDisabled(ro);
+	multiline -> setDisabled(ro);
+	singleline -> setDisabled(ro);
+	text_field -> setReadOnly(ro);
+	phase_checkbox -> setDisabled(ro);
+	phase_spinbox -> setReadOnly(ro);
+	ground_checkbox -> setDisabled(ro);
+	neutral_checkbox -> setDisabled(ro);
+	merge_checkbox -> setDisabled(ro);
+	color_button -> setDisabled(ro);
+	line_style -> setDisabled(ro);
+	// if the widget is not read-only, we still need to disable some widgets for consistency
+	if (!ro) {
+		updateDisplay();
+	}
+}
+
+/**
+	Gere le focus de ce widget
+*/
+void ConductorPropertiesWidget::focusInEvent(QFocusEvent *event) {
+	if (properties_.type == ConductorProperties::Multi) {
+		text_field -> setFocus(Qt::ActiveWindowFocusReason);
+		text_field -> selectAll();
+	} else if (properties_.type == ConductorProperties::Single) {
+		phase_spinbox -> setFocus(Qt::ActiveWindowFocusReason);
+		phase_spinbox -> selectAll();
+	}
+	QWidget::focusInEvent(event);
+}

Added: trunk/sources/conductorpropertieswidget.h
===================================================================
--- trunk/sources/conductorpropertieswidget.h	                        (rev 0)
+++ trunk/sources/conductorpropertieswidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,86 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONDUCTOR_PROPERTIES_WIDGET_H
+#define CONDUCTOR_PROPERTIES_WIDGET_H
+#include "conductorproperties.h"
+#include "qtextorientationspinboxwidget.h"
+#include <QWidget>
+/**
+	This widget enables users to change the properties of a particular
+	conductor; these properties include singleline symbols, style, color, and
+	conductor type but exclude the path it draws.
+*/
+class ConductorPropertiesWidget : public QWidget {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ConductorPropertiesWidget(QWidget * = 0);
+	ConductorPropertiesWidget(const ConductorProperties &, QWidget * = 0);
+	virtual ~ConductorPropertiesWidget();
+	
+	private:
+	ConductorPropertiesWidget(const ConductorPropertiesWidget  &);
+	
+	// methods
+	public:
+	void setConductorProperties(const ConductorProperties &);
+	ConductorProperties conductorProperties() const;
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	
+	private:
+	void setConductorType(ConductorProperties::ConductorType);
+	void setColorButton(const QColor &);
+	QColor colorButton() const;
+	
+	public slots:
+	void updatePreview();
+	void updateConfig();
+	void updateDisplay();
+	void chooseColor();
+	
+	protected:
+	void focusInEvent(QFocusEvent *);
+	
+	// private attributes
+	private:
+	QButtonGroup *radio_buttons;
+	QRadioButton *simple;
+	QRadioButton *multiline;
+	QLineEdit *text_field;
+	QTextOrientationSpinBoxWidget *verti_select;
+	QTextOrientationSpinBoxWidget *horiz_select;
+	QRadioButton *singleline;
+	QCheckBox *phase_checkbox;
+	QSlider *phase_slider;
+	QSpinBox *phase_spinbox;
+	QCheckBox *ground_checkbox;
+	QCheckBox *neutral_checkbox;
+	QLabel *preview;
+	QPushButton *color_button;
+	QComboBox *line_style;
+	QCheckBox *merge_checkbox;
+	
+	ConductorProperties properties_;
+	
+	// private methods
+	void buildInterface();
+	void buildConnections();
+	void destroyConnections();
+};
+#endif

Added: trunk/sources/conductorsegment.cpp
===================================================================
--- trunk/sources/conductorsegment.cpp	                        (rev 0)
+++ trunk/sources/conductorsegment.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,521 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "conductorsegment.h"
+#include <QDebug>
+
+/**
+	Constructeur
+	@param p1 Le point 
+	@param p2 
+	@param cs1 Le segment precedent
+	@param cs2 Le segment suivant
+*/
+ConductorSegment::ConductorSegment(
+	const QPointF &p1,
+	const QPointF &p2,
+	ConductorSegment *cs1,
+	ConductorSegment *cs2
+) :
+	point1(p1),
+	point2(p2)
+{
+	setPreviousSegment(cs1);
+	setNextSegment(cs2);
+}
+
+/**
+	Destructeur - Relie le segment precedent au suivant
+*/
+ConductorSegment::~ConductorSegment() {
+	if (hasPreviousSegment()) previousSegment() -> setNextSegment(nextSegment());
+	if (hasNextSegment()) nextSegment() -> setPreviousSegment(previousSegment());
+}
+
+/**
+	Permet de savoir s'il est possible de deplacer le premier point du segment
+	sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire
+	sans incoherence est stockee dans le second parametre.
+	@param asked_dx La valeur du mouvement demande
+	@param possible_dx La valeur du mouvement possible (au maximum)
+	@return true si le mouvement est possible ; false s'il doit etre limite
+*/
+bool ConductorSegment::canMove1stPointX(const qreal &asked_dx, qreal &possible_dx) const {
+	
+	Q_ASSERT_X(isVertical(), "ConductorSegment::canMove1stPointX", "segment non vertical");
+	
+	/// On ne bouge jamais le premier point d'un segment statique.
+	if (isStatic()) {
+		possible_dx = 0.0;
+		return(false);
+	}
+	// a ce stade, on a forcement un segment precedent
+	
+	/// Si le segment precedent n'est pas statique, le mouvement est possible.
+	if (previous_segment -> hasPreviousSegment()) {
+		possible_dx = asked_dx;
+		return(true);
+	}
+	// a ce stade, le segment precedent est forcement statique
+	
+	/// Si le segment precedent est vertical, le mouvement est possible :
+	/// il induira la creation d'un segment horizontal supplementaire.
+	if (previous_segment -> isVertical()) {
+		possible_dx = asked_dx;
+		return(true);
+	}
+	// a ce stade, le segment precedent est forcement horizontal
+	
+	// recupere quelques donnees
+	qreal prev_segment_first_x = previous_segment -> point1.x();
+	qreal first_x              = point1.x();
+	
+	/// Il se peut que le mouvement doive etre limite de facon a ce
+	/// que le segment statique conserve une taille minimale.
+	if (previous_segment -> length() > 0.0) {
+		if (first_x + asked_dx < prev_segment_first_x + 12.0) {
+			possible_dx = -first_x + prev_segment_first_x + 12.0;
+			return(false);
+		} else {
+			possible_dx = asked_dx;
+			return(true);
+		}
+	} else {
+		if (first_x + asked_dx >= prev_segment_first_x - 12.0) {
+			possible_dx = prev_segment_first_x - 12.0 - first_x;
+			return(false);
+		} else {
+			possible_dx = asked_dx;
+			return(true);
+		}
+	}
+}
+
+/**
+	Permet de savoir s'il est possible de deplacer le second point du segment
+	sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire
+	sans incoherence est stockee dans le second parametre.
+	@param asked_dx La valeur du mouvement demande
+	@param possible_dx La valeur du mouvement possible (au maximum)
+	@return true si le mouvement est possible ; false s'il doit etre limite
+*/
+bool ConductorSegment::canMove2ndPointX(const qreal &asked_dx, qreal &possible_dx) const {
+	
+	Q_ASSERT_X(isVertical(), "ConductorSegment::canMove2ndPointX", "segment non vertical");
+	
+	/// On ne modifie jamais l'abscisse du second point d'un segment statique.
+	if (isStatic()) {
+		possible_dx = 0.0;
+		return(false);
+	}
+	// a ce stade, on a forcement un segment suivant
+	
+	/// Si le segment suivant n'est pas statique, le mouvement est possible.
+	if (next_segment -> hasNextSegment()) {
+		possible_dx = asked_dx;
+		return(true);
+	}
+	// a ce stade, le segment suivant est forcement statique
+	
+	/// Si le segment suivant est vertical, le mouvement est possible :
+	/// il induira la creation d'un segment horizontal supplementaire.
+	if (next_segment -> isVertical()) {
+		possible_dx = asked_dx;
+		return(true);
+	}
+	// a ce stade, le segment suivant est forcement horizontal
+	
+	// recupere quelques donnees
+	qreal next_segment_second_x = next_segment -> point2.x();
+	qreal second_x              = point2.x();
+	
+	/// Il se peut que le mouvement doive etre limite de facon a ce
+	/// que le segment statique conserve une taille minimale.
+	if (next_segment -> length() < 0.0) {
+		if (second_x + asked_dx < next_segment_second_x + 12.0) {
+			possible_dx = -second_x + next_segment_second_x + 12.0;
+			return(false);
+		} else {
+			possible_dx = asked_dx;
+			return(true);
+		}
+	} else {
+		if (second_x + asked_dx >= next_segment_second_x - 12.0) {
+			possible_dx = next_segment_second_x - 12.0 - second_x;
+			return(false);
+		} else {
+			possible_dx = asked_dx;
+			return(true);
+		}
+	}
+}
+
+/**
+	Permet de savoir s'il est possible de deplacer le premier point du segment
+	sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire
+	sans incoherence est stockee dans le second parametre.
+	@param asked_dy La valeur du mouvement demande
+	@param possible_dy La valeur du mouvement possible (au maximum)
+	@return true si le mouvement est possible ; false s'il doit etre limite
+*/
+bool ConductorSegment::canMove1stPointY(const qreal &asked_dy, qreal &possible_dy) const {
+	
+	Q_ASSERT_X(isHorizontal(), "ConductorSegment::canMove1stPointY", "segment non horizontal");
+	
+	/// On ne bouge jamais le premier point d'un segment statique.
+	if (isStatic()) {
+		possible_dy = 0.0;
+		return(false);
+	}
+	// a ce stade, on a forcement un segment precedent
+	
+	/// Si le segment precedent n'est pas statique, le mouvement est possible.
+	if (previous_segment -> hasPreviousSegment()) {
+		possible_dy = asked_dy;
+		return(true);
+	}
+	// a ce stade, le segment precedent est forcement statique
+	
+	/// Si le segment precedent est horizontal, le mouvement est possible :
+	/// il induira la creation d'un segment vertical supplementaire.
+	if (previous_segment -> isHorizontal()) {
+		possible_dy = asked_dy;
+		return(true);
+	}
+	// a ce stade, le segment precedent est forcement vertical
+	
+	// recupere quelques donnees
+	qreal prev_segment_first_y = previous_segment -> point1.y();
+	qreal first_y              = point1.y();
+	
+	/// Il se peut que le mouvement doive etre limite de facon a ce
+	/// que le segment statique conserve une taille minimale.
+	if (previous_segment -> length() > 0.0) {
+		if (first_y + asked_dy < prev_segment_first_y + 12.0) {
+			possible_dy = -first_y + prev_segment_first_y + 12.0;
+			return(false);
+		} else {
+			possible_dy = asked_dy;
+			return(true);
+		}
+	} else {
+		if (first_y + asked_dy >= prev_segment_first_y - 12.0) {
+			possible_dy = prev_segment_first_y - 12.0 - first_y;
+			return(false);
+		} else {
+			possible_dy = asked_dy;
+			return(true);
+		}
+	}
+}
+
+/**
+	Permet de savoir s'il est possible de deplacer le second point du segment
+	sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire
+	sans incoherence est stockee dans le second parametre.
+	@param asked_dy La valeur du mouvement demande
+	@param possible_dy La valeur du mouvement possible (au maximum)
+	@return true si le mouvement est possible ; false s'il doit etre limite
+*/
+bool ConductorSegment::canMove2ndPointY(const qreal &asked_dy, qreal &possible_dy) const {
+	
+	Q_ASSERT_X(isHorizontal(), "ConductorSegment::canMove2ndPointY", "segment non horizontal");
+	
+	/// On ne modifie jamais l'abscisse du second point d'un segment statique.
+	if (isStatic()) {
+		possible_dy = 0.0;
+		return(false);
+	}
+	// a ce stade, on a forcement un segment suivant
+	
+	/// Si le segment suivant n'est pas statique, le mouvement est possible.
+	if (next_segment -> hasNextSegment()) {
+		possible_dy = asked_dy;
+		return(true);
+	}
+	// a ce stade, le segment suivant est forcement statique
+	
+	/// Si le segment suivant est horizontal, le mouvement est possible :
+	/// il induira la creation d'un segment vertical supplementaire.
+	if (next_segment -> isHorizontal()) {
+		possible_dy = asked_dy;
+		return(true);
+	}
+	// a ce stade, le segment suivant est forcement vertical
+	
+	// recupere quelques donnees
+	qreal next_segment_second_y = next_segment -> point2.y();
+	qreal second_y              = point2.y();
+	
+	/// Il se peut que le mouvement doive etre limite de facon a ce
+	/// que le segment statique conserve une taille minimale.
+	if (next_segment -> length() < 0.0) {
+		if (second_y + asked_dy < next_segment_second_y + 12.0) {
+			possible_dy = -second_y + next_segment_second_y + 12.0;
+			return(false);
+		} else {
+			possible_dy = asked_dy;
+			return(true);
+		}
+	} else {
+		if (second_y + asked_dy >= next_segment_second_y - 12.0) {
+			possible_dy = next_segment_second_y - 12.0 - second_y;
+			return(false);
+		} else {
+			possible_dy = asked_dy;
+			return(true);
+		}
+	}
+}
+
+/**
+	Gere les mouvements sur l'axe horizontal
+	@param dx taille du deplacement en pixels
+*/
+void ConductorSegment::moveX(const qreal &dx) {
+	if (isHorizontal()) return;
+	Q_ASSERT_X(isVertical(), "ConductorSegment::moveX", "segment non vertical");
+	
+	bool has_prev_segment = hasPreviousSegment();
+	bool has_next_segment = hasNextSegment();
+	
+	if (isStatic()) return;
+	
+	// determine si le mouvement demande doit etre limite et, le cas echeant, a quelle valeur il faut le limiter
+	qreal real_dx_for_1st_point = 0.0;
+	qreal real_dx_for_2nd_point = 0.0;
+	canMove1stPointX(dx, real_dx_for_1st_point);
+	canMove2ndPointX(dx, real_dx_for_2nd_point);
+	
+	qreal final_movement = (dx <= 0.0) ? qMax(real_dx_for_1st_point, real_dx_for_2nd_point) : qMin(real_dx_for_1st_point, real_dx_for_2nd_point);
+	
+	// applique le mouvement au premier point
+	if (has_prev_segment) {
+		point1.rx() += final_movement;
+		if (previous_segment -> isFirstSegment()) {
+			new ConductorSegment(
+				previous_segment -> point2,
+				point1,
+				previous_segment,
+				this
+			);
+		} else previous_segment -> setSecondPoint(point1);
+	}
+	
+	// applique le mouvement au second point
+	if (has_next_segment) {
+		point2.rx() += final_movement;
+		if (next_segment -> isLastSegment()) {
+			new ConductorSegment(
+				point2,
+				next_segment -> point1,
+				this,
+				next_segment
+			);
+		} else next_segment -> setFirstPoint(point2);
+	}
+}
+
+/**
+	Gere les mouvements sur l'axe vertical
+	@param dy taille du deplacement en pixels
+*/
+void ConductorSegment::moveY(const qreal &dy) {
+	if (isVertical()) return;
+	Q_ASSERT_X(isHorizontal(), "ConductorSegment::moveY", "segment non horizontal");
+	
+	bool has_prev_segment = hasPreviousSegment();
+	bool has_next_segment = hasNextSegment();
+	
+	if (isStatic()) return;
+	
+	// determine si le mouvement demande doit etre limite et, le cas echeant, a quelle valeur il faut le limiter
+	qreal real_dy_for_1st_point = 0.0;
+	qreal real_dy_for_2nd_point = 0.0;
+	canMove1stPointY(dy, real_dy_for_1st_point);
+	canMove2ndPointY(dy, real_dy_for_2nd_point);
+	
+	qreal final_movement = (dy <= 0.0) ? qMax(real_dy_for_1st_point, real_dy_for_2nd_point) : qMin(real_dy_for_1st_point, real_dy_for_2nd_point);
+	
+	// applique le mouvement au premier point
+	if (has_prev_segment) {
+		point1.ry() += final_movement;
+		if (previous_segment -> isFirstSegment()) {
+			new ConductorSegment(
+				previous_segment -> point2,
+				point1,
+				previous_segment,
+				this
+			);
+		} else previous_segment -> setSecondPoint(point1);
+	}
+	
+	// applique le mouvement au second point
+	if (has_next_segment) {
+		point2.ry() += final_movement;
+		if (next_segment -> isLastSegment()) {
+			new ConductorSegment(
+				point2,
+				next_segment -> point1,
+				this,
+				next_segment
+			);
+		} else next_segment -> setFirstPoint(point2);
+	}
+}
+
+/**
+	Change le segment precedent
+	@param ps Le nouveau segment precedent
+*/
+void ConductorSegment::setPreviousSegment(ConductorSegment *ps) {
+	previous_segment = ps;
+	if (hasPreviousSegment()) {
+		if (previousSegment() -> nextSegment() != this) previousSegment() -> setNextSegment(this);
+	}
+}
+
+/**
+	Change le segment suivant
+	@param ns Le nouveau segment suivant
+*/
+void ConductorSegment::setNextSegment(ConductorSegment *ns) {
+	next_segment = ns;
+	if (hasNextSegment()) {
+		if (nextSegment() -> previousSegment() != this) nextSegment() -> setPreviousSegment(this);
+	}
+}
+
+/// @return true si ce segment est un segment statique, cad un segment relie a une borne
+bool ConductorSegment::isStatic() const {
+	return(isFirstSegment() || isLastSegment());
+}
+
+/// @return true si ce segment est le premier du conducteur
+bool ConductorSegment::isFirstSegment() const {
+	return(!hasPreviousSegment());
+}
+
+/// @return true si ce segment est le dernier du conducteur
+bool ConductorSegment::isLastSegment() const {
+	return(!hasNextSegment());
+}
+
+/**
+	@return Le segment precedent
+*/
+ConductorSegment *ConductorSegment::previousSegment()  const {
+	return(previous_segment);
+}
+
+/**
+	@return Le segment suivant
+*/
+ConductorSegment *ConductorSegment::nextSegment()  const {
+	return(next_segment);
+}
+
+/**
+	@return true si le segment est vertical, false sinon
+*/
+bool ConductorSegment::isVertical() const {
+	return(point1.x() == point2.x());
+}
+
+/**
+	@return true si le segment est horizontal, false sinon
+*/
+bool ConductorSegment::isHorizontal() const {
+	return(point1.y() == point2.y());
+}
+
+/**
+	@return le premier point du segment
+*/
+QPointF ConductorSegment::firstPoint() const {
+	return(point1);
+}
+
+/**
+	@return le second point du segment
+*/
+QPointF ConductorSegment::secondPoint() const {
+	return(point2);
+}
+
+/**
+	Permet de changer la position du premier point du segment
+	@param p La nouvelle position du premier point
+*/
+void ConductorSegment::setFirstPoint(const QPointF &p) {
+	point1 = p;
+}
+
+/**
+	Permet de changer la position du second point du segment
+	@param p La nouvelle position du second point
+*/
+void ConductorSegment::setSecondPoint(const QPointF &p) {
+	point2 = p;
+}
+
+/**
+	@return true si le segment a un segment precedent, false sinon
+*/
+bool ConductorSegment::hasPreviousSegment() const {
+	return(previous_segment != NULL);
+}
+
+/**
+	@return true si le segment a un segment suivant, false sinon
+*/
+bool ConductorSegment::hasNextSegment() const {
+	return(next_segment != NULL);
+}
+
+/**
+	@return Le centre du rectangle delimitant le conducteur
+*/
+QPointF ConductorSegment::middle() const {
+	return(
+		QPointF(
+			(point1.x() + point2.x()) / 2.0,
+			(point1.y() + point2.y()) / 2.0
+		)
+	);
+}
+
+/**
+	@return La longueur du conducteur
+*/
+qreal ConductorSegment::length() const {
+	if (isHorizontal()) {
+		return(secondPoint().x() - firstPoint().x());
+	} else {
+		return(secondPoint().y() - firstPoint().y());
+	}
+}
+
+/// @return QET::Horizontal si le segment est horizontal, QET::Vertical sinon
+QET::ConductorSegmentType ConductorSegment::type() const {
+	return(isHorizontal() ? QET::Horizontal : QET::Vertical);
+}
+
+/// @return true si les deux points constituant le segment sont egaux
+bool ConductorSegment::isPoint() const {
+	return(point1 == point2);
+}

Added: trunk/sources/conductorsegment.h
===================================================================
--- trunk/sources/conductorsegment.h	                        (rev 0)
+++ trunk/sources/conductorsegment.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,70 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONDUCTOR_SEGMENT_H
+#define CONDUCTOR_SEGMENT_H
+#include <QPointF>
+#include "qet.h"
+/**
+	This class represents a conductor segment.
+*/
+class ConductorSegment {
+	
+	// constructors, destructor
+	public:
+	ConductorSegment(const QPointF &, const QPointF &, ConductorSegment * = NULL, ConductorSegment * = NULL);
+	virtual ~ConductorSegment();
+	
+	private:
+	ConductorSegment(const ConductorSegment &);
+	
+	// attributes
+	private:
+	ConductorSegment *previous_segment;
+	ConductorSegment *next_segment;
+	QPointF point1;
+	QPointF point2;
+	
+	// methods
+	public:
+	void moveX(const qreal &);
+	void moveY(const qreal &);
+	ConductorSegment *previousSegment() const;
+	ConductorSegment *nextSegment()  const;
+	bool hasPreviousSegment() const;
+	bool hasNextSegment() const;
+	void setPreviousSegment(ConductorSegment *);
+	void setNextSegment(ConductorSegment *);
+	bool isStatic() const;
+	bool isFirstSegment() const;
+	bool isLastSegment() const;
+	QPointF firstPoint() const;
+	QPointF secondPoint() const;
+	void setFirstPoint(const QPointF &);
+	void setSecondPoint(const QPointF &);
+	QPointF middle() const;
+	bool isHorizontal() const;
+	bool isVertical() const;
+	QET::ConductorSegmentType type() const;
+	qreal length() const;
+	bool isPoint() const;
+	bool canMove1stPointX(const qreal &, qreal &) const;
+	bool canMove2ndPointX(const qreal &, qreal &) const;
+	bool canMove1stPointY(const qreal &, qreal &) const;
+	bool canMove2ndPointY(const qreal &, qreal &) const;
+};
+#endif

Added: trunk/sources/conductorsegmentprofile.h
===================================================================
--- trunk/sources/conductorsegmentprofile.h	                        (rev 0)
+++ trunk/sources/conductorsegmentprofile.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,61 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONDUCTOR_SEGMENT_PROFILE_H
+#define CONDUCTOR_SEGMENT_PROFILE_H
+#include <QtCore>
+#include "conductorsegment.h"
+/**
+	This class embeds the profile (i.e. main characteristics) of a conductor
+	segment.
+*/
+class ConductorSegmentProfile {
+	// constructors, destructor
+	public:
+	/**
+		Constructor
+		@param l segment length
+		@param ori true if the segment is horizontal, false if it is vertical
+	*/
+	ConductorSegmentProfile(qreal l, bool ori = true) :
+		length(l),
+		isHorizontal(ori)
+	{
+	}
+	
+	/**
+		Constructor
+		@param segment Segment the profile should be copied from.
+	*/
+	ConductorSegmentProfile(ConductorSegment *segment) :
+		length(segment -> length()),
+		isHorizontal(segment -> isHorizontal())
+	{
+	}
+	
+	/// Destructor
+	virtual ~ConductorSegmentProfile() {
+	}
+	
+	// attributes
+	public:
+	/// segment length
+	qreal length;
+	/// segment orientation
+	bool isHorizontal;
+};
+#endif

Added: trunk/sources/configdialog.cpp
===================================================================
--- trunk/sources/configdialog.cpp	                        (rev 0)
+++ trunk/sources/configdialog.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,115 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "configdialog.h"
+#include "configpages.h"
+#include "qetapp.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+ConfigDialog::ConfigDialog(QWidget *parent) : QDialog(parent) {
+	// liste des pages
+	pages_list = new QListWidget();
+	pages_list -> setViewMode(QListView::IconMode);
+	pages_list -> setIconSize(QSize(110, 110));
+	pages_list -> setMovement(QListView::Static);
+	pages_list -> setMinimumWidth(150);
+	pages_list -> setMaximumWidth(150);
+	pages_list -> setSpacing(4);
+	
+	// pages
+	pages_widget = new QStackedWidget();
+	
+	// boutons
+	buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
+	
+	// layouts
+	QHBoxLayout *hlayout1 = new QHBoxLayout();
+	hlayout1 -> addWidget(pages_list);
+	hlayout1 -> addWidget(pages_widget);
+	
+	QVBoxLayout *vlayout1 = new QVBoxLayout();
+	vlayout1 -> addLayout(hlayout1);
+	vlayout1 -> addWidget(buttons);
+	setLayout(vlayout1);
+	
+	// connexion signaux / slots
+	connect(buttons, SIGNAL(accepted()), this, SLOT(applyConf()));
+	connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
+	connect(pages_list, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*)));
+	
+#ifdef Q_WS_MAC
+	if (parent) {
+		setWindowFlags(Qt::Sheet);
+	}
+#endif
+}
+
+/// Destructeur
+ConfigDialog::~ConfigDialog() {
+}
+
+/**
+	Gere les changements de page dans le dialogue de configuration
+*/
+void ConfigDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) {
+	if (!current) current = previous;
+	pages_widget -> setCurrentIndex(pages_list -> row(current));
+}
+
+/**
+	Construit la liste des pages sur la gauche
+*/
+void ConfigDialog::buildPagesList() {
+	pages_list -> clear();
+	foreach(ConfigPage *page, pages) {
+		addPageToList(page);
+	}
+}
+
+/**
+	Add the \a page ConfigPage to this configuration dialog.
+*/
+void ConfigDialog::addPageToList(ConfigPage *page) {
+	QListWidgetItem *new_button = new QListWidgetItem(pages_list);
+	new_button -> setIcon(page -> icon());
+	new_button -> setText(page -> title());
+	new_button -> setTextAlignment(Qt::AlignHCenter);
+	new_button -> setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+}
+
+/**
+	Applique la configuration de toutes les pages
+*/
+void ConfigDialog::applyConf() {
+	foreach(ConfigPage *page, pages) {
+		page -> applyConf();
+	}
+	accept();
+}
+
+/**
+	Ajoute une page au dialogue de configuration
+*/
+void ConfigDialog::addPage(ConfigPage *page) {
+	if (!page || pages.contains(page)) return;
+	pages << page;
+	pages_widget -> addWidget(page);
+	addPageToList(page);
+}

Added: trunk/sources/configdialog.h
===================================================================
--- trunk/sources/configdialog.h	                        (rev 0)
+++ trunk/sources/configdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,57 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONFIG_DIALOG_H
+#define CONFIG_DIALOG_H
+#include <QDialog>
+class ConfigPage;
+class QListWidget;
+class QListWidgetItem;
+class QStackedWidget;
+class QDialogButtonBox;
+/**
+	This class represents the configuration dialog for QElectroTech.
+	It displays "configuration pages", each page having to provide an icon and
+	a title.
+*/
+class ConfigDialog : public QDialog {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ConfigDialog(QWidget * = 0);
+	virtual ~ConfigDialog();
+	private:
+	ConfigDialog(const ConfigDialog &);
+	
+	// methods
+	public slots:
+	void changePage(QListWidgetItem *, QListWidgetItem *);
+	void applyConf();
+	void addPage(ConfigPage *);
+	
+	private:
+	void buildPagesList();
+	void addPageToList(ConfigPage *);
+	
+	// attributes
+	private:
+	QListWidget *pages_list;
+	QStackedWidget *pages_widget;
+	QDialogButtonBox *buttons;
+	QList<ConfigPage *> pages;
+};
+#endif

Added: trunk/sources/configpage.h
===================================================================
--- trunk/sources/configpage.h	                        (rev 0)
+++ trunk/sources/configpage.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,28 @@
+#ifndef CONFIGPAGE_H
+#define CONFIGPAGE_H
+
+#include <QWidget>
+
+/**
+	This abstract class specifies methods all derived classes should
+	implement.
+*/
+class ConfigPage : public QWidget {
+	Q_OBJECT
+	public:
+	/**
+		Constructor
+		@param parent Parent QWidget
+	*/
+	ConfigPage(QWidget *parent) : QWidget(parent) {};
+	/// Destructor
+	virtual ~ConfigPage() {};
+	/// Apply the configuration after user input
+	virtual void applyConf() = 0;
+	/// @return the configuration page title
+	virtual QString title() const = 0;
+	/// @return the configuration page icon
+	virtual QIcon icon() const = 0;
+};
+
+#endif

Added: trunk/sources/configpages.cpp
===================================================================
--- trunk/sources/configpages.cpp	                        (rev 0)
+++ trunk/sources/configpages.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,311 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "configpages.h"
+#include "borderpropertieswidget.h"
+#include "conductorpropertieswidget.h"
+#include "titleblockpropertieswidget.h"
+#include "qetapp.h"
+#include "qetdiagrameditor.h"
+#include "bordertitleblock.h"
+#include "qeticons.h"
+#include "exportpropertieswidget.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+NewDiagramPage::NewDiagramPage(QWidget *parent) : ConfigPage(parent) {
+	// dimensions par defaut d'un schema
+	bpw = new BorderPropertiesWidget(QETDiagramEditor::defaultBorderProperties());
+	
+	// proprietes par defaut d'un cartouche
+	ipw = new TitleBlockPropertiesWidget(QETDiagramEditor::defaultTitleBlockProperties(), true);
+	
+	// proprietes par defaut des conducteurs
+	cpw = new ConductorPropertiesWidget(QETDiagramEditor::defaultConductorProperties());
+	cpw -> setContentsMargins(0, 0, 0, 0);
+	
+	QVBoxLayout *vlayout1 = new QVBoxLayout();
+	
+	QLabel *title = new QLabel(this -> title());
+	vlayout1 -> addWidget(title);
+	
+	QFrame *horiz_line = new QFrame();
+	horiz_line -> setFrameShape(QFrame::HLine);
+	vlayout1 -> addWidget(horiz_line);
+	
+	QHBoxLayout *hlayout1 = new QHBoxLayout();
+	QVBoxLayout *vlayout2 = new QVBoxLayout();
+	
+	vlayout2 -> addWidget(bpw);
+	vlayout2 -> addWidget(ipw);
+	vlayout2 -> setSpacing(5);
+	hlayout1 -> addLayout(vlayout2);
+	hlayout1 -> addWidget(cpw);
+	vlayout1 -> addLayout(hlayout1);
+	vlayout1 -> addStretch(1);
+	hlayout1 -> setAlignment(cpw, Qt::AlignTop);
+	setLayout(vlayout1);
+}
+
+/// Destructeur
+NewDiagramPage::~NewDiagramPage() {
+}
+
+/**
+	Applique la configuration de cette page
+*/
+void NewDiagramPage::applyConf() {
+	QSettings &settings = QETApp::settings();
+	
+	// dimensions des nouveaux schemas
+	bpw -> borderProperties().toSettings(settings, "diagrameditor/default");
+	
+	// proprietes du cartouche
+	ipw-> titleBlockProperties().toSettings(settings, "diagrameditor/default");
+	
+	// proprietes par defaut des conducteurs
+	cpw -> conductorProperties().toSettings(settings, "diagrameditor/defaultconductor");
+}
+
+/// @return l'icone de cette page
+QIcon NewDiagramPage::icon() const {
+	return(QET::Icons::NewDiagram);
+}
+
+/// @return le titre de cette page
+QString NewDiagramPage::title() const {
+	return(tr("Nouveau sch\351ma", "configuration page title"));
+}
+
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) : ConfigPage(parent) {
+	
+	// acces a la configuration de QElectroTech
+	QSettings &settings = QETApp::settings();
+	bool use_system_colors = settings.value("usesystemcolors", "true").toBool();
+	bool tabbed = settings.value("diagrameditor/viewmode", "tabbed") == "tabbed";
+	bool integrate_elements = settings.value("diagrameditor/integrate-elements", true).toBool();
+	bool highlight_integrated_elements = settings.value("diagrameditor/highlight-integrated-elements", true).toBool();
+	QString default_element_informations = settings.value("elementeditor/default-informations", "").toString();
+	
+	appearance_ = new QGroupBox(tr("Apparence"), this);
+	use_system_colors_ = new QCheckBox(tr("Utiliser les couleurs du syst\350me"), appearance_);
+	
+	projects_view_mode_ = new QGroupBox(tr("Projets"), this);
+	windowed_mode_ = new QRadioButton(tr("Utiliser des fen\352tres"), projects_view_mode_);
+	tabbed_mode_ = new QRadioButton(tr("Utiliser des onglets"), projects_view_mode_);
+	warning_view_mode_ = new QLabel(tr("Ces param\350tres s'appliqueront d\350s la prochaine ouverture d'un \351diteur de sch\351mas."));
+	
+	elements_management_ = new QGroupBox(tr("Gestion des \351l\351ments"), this);
+	integrate_elements_ = new QCheckBox(tr("Int\351grer automatiquement les \351l\351ments dans les projets (recommand\351)"));
+	highlight_integrated_elements_ = new QCheckBox(tr("Mettre en valeur dans le panel les \351l\351ments fra\356chement int\351gr\351s", "configuration option"));
+	default_element_infos_label_ = new QLabel(
+		tr(
+			"Chaque \351l\351ment embarque des informations sur ses auteurs, sa licence, ou tout autre renseignement que vous jugerez utile dans un champ libre. "
+			"Vous pouvez sp\351cifier ici la valeur par d\351faut de ce champ pour les \351l\351ments que vous cr\351erez :"
+		)
+	);
+	default_element_infos_label_ -> setWordWrap(true);
+	default_element_infos_textfield_ = new QTextEdit();
+	default_element_infos_textfield_ ->  setAcceptRichText(false);
+	
+	use_system_colors_ -> setChecked(use_system_colors);
+	
+	if (tabbed) {
+		tabbed_mode_ -> setChecked(true);
+	} else {
+		windowed_mode_ -> setChecked(true);
+	}
+	
+	integrate_elements_ -> setChecked(integrate_elements);
+	highlight_integrated_elements_ -> setChecked(highlight_integrated_elements);
+	default_element_infos_textfield_ -> setPlainText(default_element_informations);
+	
+	QVBoxLayout *appearance_layout = new QVBoxLayout();
+	appearance_layout -> addWidget(use_system_colors_);
+	appearance_ -> setLayout(appearance_layout);
+	
+	QVBoxLayout *projects_view_mode_layout = new QVBoxLayout();
+	projects_view_mode_layout -> addWidget(windowed_mode_);
+	projects_view_mode_layout -> addWidget(tabbed_mode_);
+	projects_view_mode_layout -> addWidget(warning_view_mode_);
+	projects_view_mode_ -> setLayout(projects_view_mode_layout);
+	
+	QVBoxLayout *elements_management_layout = new QVBoxLayout();
+	elements_management_layout -> addWidget(integrate_elements_);
+	elements_management_layout -> addWidget(highlight_integrated_elements_);
+	elements_management_layout -> addWidget(default_element_infos_label_);
+	elements_management_layout -> addWidget(default_element_infos_textfield_);
+	elements_management_ -> setLayout(elements_management_layout);
+	
+	QVBoxLayout *vlayout1 = new QVBoxLayout();
+	
+	QLabel *title_label_ = new QLabel(title());
+	vlayout1 -> addWidget(title_label_);
+	
+	QFrame *horiz_line_ = new QFrame();
+	horiz_line_ -> setFrameShape(QFrame::HLine);
+	vlayout1 -> addWidget(horiz_line_);
+	
+	vlayout1 -> addWidget(appearance_);
+	vlayout1 -> addWidget(projects_view_mode_);
+	vlayout1 -> addWidget(elements_management_);
+	vlayout1 -> addStretch();
+	
+	setLayout(vlayout1);
+}
+
+/// Destructeur
+GeneralConfigurationPage::~GeneralConfigurationPage() {
+}
+
+/**
+	Applique la configuration de cette page
+*/
+void GeneralConfigurationPage::applyConf() {
+	QSettings &settings = QETApp::settings();
+	
+	bool was_using_system_colors = settings.value("usesystemcolors", "true").toBool();
+	bool must_use_system_colors  = use_system_colors_ -> isChecked();
+	settings.setValue("usesystemcolors", must_use_system_colors);
+	if (was_using_system_colors != must_use_system_colors) {
+		QETApp::instance() -> useSystemPalette(must_use_system_colors);
+	}
+	
+	QString view_mode = tabbed_mode_ -> isChecked() ? "tabbed" : "windowed";
+	settings.setValue("diagrameditor/viewmode", view_mode) ;
+	
+	settings.setValue("diagrameditor/integrate-elements", integrate_elements_ -> isChecked());
+	settings.setValue("diagrameditor/highlight-integrated-elements", highlight_integrated_elements_ -> isChecked());
+	settings.setValue("elementeditor/default-informations", default_element_infos_textfield_ -> toPlainText());
+}
+
+/// @return l'icone de cette page
+QIcon GeneralConfigurationPage::icon() const {
+	return(QET::Icons::Settings);
+}
+
+/// @return le titre de cette page
+QString GeneralConfigurationPage::title() const {
+	return(tr("G\351n\351ral", "configuration page title"));
+}
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+ExportConfigPage::ExportConfigPage(QWidget *parent) : ConfigPage(parent) {
+	// epw contient les options d'export
+	epw = new ExportPropertiesWidget(QETDiagramEditor::defaultExportProperties());
+	
+	// layout vertical contenant le titre, une ligne horizontale et epw
+	QVBoxLayout *vlayout1 = new QVBoxLayout();
+	
+	QLabel *title = new QLabel(this -> title());
+	vlayout1 -> addWidget(title);
+	
+	QFrame *horiz_line = new QFrame();
+	horiz_line -> setFrameShape(QFrame::HLine);
+	vlayout1 -> addWidget(horiz_line);
+	vlayout1 -> addWidget(epw);
+	vlayout1 -> addStretch();
+
+	// activation du layout
+	setLayout(vlayout1);
+}
+
+/// Destructeur
+ExportConfigPage::~ExportConfigPage() {
+}
+
+/**
+	Applique la configuration de cette page
+*/
+void ExportConfigPage::applyConf() {
+	QSettings &settings = QETApp::settings();
+	epw -> exportProperties().toSettings(settings, "export/default");
+}
+
+/// @return l'icone de cette page
+QIcon ExportConfigPage::icon() const {
+	return(QET::Icons::DocumentExport);
+}
+
+/// @return le titre de cette page
+QString ExportConfigPage::title() const {
+	return(tr("Export", "configuration page title"));
+}
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+PrintConfigPage::PrintConfigPage(QWidget *parent) : ConfigPage(parent) {
+	// epw contient les options d'export
+	epw = new ExportPropertiesWidget(QETDiagramEditor::defaultPrintProperties());
+	epw -> setPrintingMode(true);
+	
+	// layout vertical contenant le titre, une ligne horizontale et epw
+	QVBoxLayout *vlayout1 = new QVBoxLayout();
+	
+	QLabel *title = new QLabel(this -> title());
+	vlayout1 -> addWidget(title);
+	
+	QFrame *horiz_line = new QFrame();
+	horiz_line -> setFrameShape(QFrame::HLine);
+	vlayout1 -> addWidget(horiz_line);
+	vlayout1 -> addWidget(epw);
+	vlayout1 -> addStretch();
+
+	// activation du layout
+	setLayout(vlayout1);
+}
+
+/// Destructeur
+PrintConfigPage::~PrintConfigPage() {
+}
+
+/**
+	Applique la configuration de cette page
+*/
+void PrintConfigPage::applyConf() {
+	QString prefix = "print/default";
+	
+	QSettings &settings = QETApp::settings();
+	epw -> exportProperties().toSettings(settings, prefix);
+	
+	// annule l'enregistrement de certaines proprietes non pertinentes
+	settings.remove(prefix + "path");
+	settings.remove(prefix + "format");
+	settings.remove(prefix + "area");
+}
+
+/// @return l'icone de cette page
+QIcon PrintConfigPage::icon() const {
+	return(QET::Icons::Printer);
+}
+
+/// @return le titre de cette page
+QString PrintConfigPage::title() const {
+	return(tr("Impression", "configuration page title"));
+}

Added: trunk/sources/configpages.h
===================================================================
--- trunk/sources/configpages.h	                        (rev 0)
+++ trunk/sources/configpages.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,134 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CONFIG_PAGES_H
+#define CONFIG_PAGES_H
+#include <QtGui>
+#include "configpage.h"
+class BorderPropertiesWidget;
+class ConductorPropertiesWidget;
+class TitleBlockPropertiesWidget;
+class ExportPropertiesWidget;
+
+/**
+	This configuration page enables users to define the properties of new
+	diagrams to come.
+*/
+class NewDiagramPage : public ConfigPage {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	NewDiagramPage(QWidget * = 0);
+	virtual ~NewDiagramPage();
+	private:
+	NewDiagramPage(const NewDiagramPage &);
+	
+	// methods
+	public:
+	void applyConf();
+	QString title() const;
+	QIcon icon() const;
+	
+	// attributes
+	public:
+	BorderPropertiesWidget *bpw;     ///< Widget to edit default diagram dimensions
+	TitleBlockPropertiesWidget *ipw; ///< Widget to edit default title block properties
+	ConductorPropertiesWidget *cpw;  ///< Widget to edit default conductor properties
+};
+
+/**
+	This configuration page enables users to specify various options,most of
+	them applying to the whole application.
+*/
+class GeneralConfigurationPage : public ConfigPage {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	GeneralConfigurationPage(QWidget * = 0);
+	virtual ~GeneralConfigurationPage();
+	private:
+	GeneralConfigurationPage(const GeneralConfigurationPage &);
+	
+	// methods
+	public:
+	void applyConf();
+	QString title() const;
+	QIcon icon() const;
+	
+	// attributes
+	public:
+	QLabel *title_label_;
+	QFrame *horiz_line_;
+	QGroupBox *appearance_;
+	QCheckBox *use_system_colors_;
+	QGroupBox *projects_view_mode_;
+	QRadioButton *windowed_mode_;
+	QRadioButton *tabbed_mode_;
+	QLabel *warning_view_mode_;
+	QGroupBox *elements_management_;
+	QCheckBox *integrate_elements_;
+	QCheckBox *highlight_integrated_elements_;
+	QLabel    *default_element_infos_label_;
+	QTextEdit *default_element_infos_textfield_;
+};
+
+/**
+	This configuration page enables users to set default export options.
+*/
+class ExportConfigPage : public ConfigPage {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ExportConfigPage(QWidget * = 0);
+	virtual ~ExportConfigPage();
+	private:
+	ExportConfigPage(const ExportConfigPage &);
+	
+	// methods
+	public:
+	void applyConf();
+	QString title() const;
+	QIcon icon() const;
+	
+	// attributes
+	public:
+	ExportPropertiesWidget *epw;
+};
+
+/**
+	This configuration page enables users to set default printing options.
+*/
+class PrintConfigPage : public ConfigPage {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	PrintConfigPage(QWidget * = 0);
+	virtual ~PrintConfigPage();
+	private:
+	PrintConfigPage(const PrintConfigPage &);
+	
+	// methods
+	public:
+	void applyConf();
+	QString title() const;
+	QIcon icon() const;
+	
+	// attributes
+	public:
+	ExportPropertiesWidget *epw;
+};
+#endif

Added: trunk/sources/diagram.cpp
===================================================================
--- trunk/sources/diagram.cpp	                        (rev 0)
+++ trunk/sources/diagram.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1266 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <math.h>
+#include "qetgraphicsitem/conductor.h"
+#include "qetgraphicsitem/conductortextitem.h"
+#include "qetgraphicsitem/customelement.h"
+#include "diagram.h"
+#include "diagramcommands.h"
+#include "diagramcontent.h"
+#include "diagramposition.h"
+#include "qetgraphicsitem/elementtextitem.h"
+#include "elementsmover.h"
+#include "elementtextsmover.h"
+#include "exportdialog.h"
+#include "qetgraphicsitem/ghostelement.h"
+#include "qetgraphicsitem/independenttextitem.h"
+#include "qetapp.h"
+#include "qetgraphicsitem/diagramimageitem.h"
+
+const int   Diagram::xGrid  = 10;
+const int   Diagram::yGrid  = 10;
+const qreal Diagram::margin = 5.0;
+
+/**
+	Constructeur
+	@param parent Le QObject parent du schema
+*/
+Diagram::Diagram(QObject *parent) :
+	QGraphicsScene(parent),
+	draw_grid_(true),
+	use_border_(true),
+	draw_terminals_(true),
+	draw_colored_conductors_(true),
+	project_(0),
+	read_only_(false),
+	diagram_qet_version_(-1)
+{
+	undo_stack_ = new QUndoStack();
+	qgi_manager_ = new QGIManager(this);
+	setBackgroundBrush(Qt::white);
+	conductor_setter_ = new QGraphicsLineItem(0, 0);
+	conductor_setter_ -> setZValue(1000000);
+	QPen t;
+	t.setColor(Qt::black);
+	t.setWidthF(1.5);
+	t.setStyle(Qt::DashLine);
+	conductor_setter_ -> setPen(t);
+	conductor_setter_ -> setLine(QLineF(QPointF(0.0, 0.0), QPointF(0.0, 0.0)));
+	
+	// initialise les objets gerant les deplacements
+	elements_mover_ = new ElementsMover();           // deplacements d'elements/conducteurs/textes
+	element_texts_mover_ = new ElementTextsMover();  // deplacements d'ElementTextItem
+	
+	connect(
+		&border_and_titleblock, SIGNAL(needTitleBlockTemplate(const QString &)),
+		this, SLOT(setTitleBlockTemplate(const QString &))
+	);
+	connect(
+		&border_and_titleblock, SIGNAL(diagramTitleChanged(const QString &)),
+		this, SLOT(titleChanged(const QString &))
+	);
+}
+
+/**
+	Destructeur
+*/
+Diagram::~Diagram() {
+	// suppression de la liste des annulations - l'undo stack fait appel au qgimanager pour supprimer certains elements
+	delete undo_stack_;
+	// suppression du QGIManager - tous les elements qu'il connait sont supprimes
+	delete qgi_manager_;
+	// remove of conductor setter
+	delete conductor_setter_;
+	
+	// suppression des objets gerant les deplacements
+	delete elements_mover_;
+	delete element_texts_mover_;
+	
+	// recense les items supprimables
+	QList<QGraphicsItem *> deletable_items;
+	foreach(QGraphicsItem *qgi, items()) {
+		if (qgi -> parentItem()) continue;
+		if (qgraphicsitem_cast<Conductor *>(qgi)) continue;
+		deletable_items << qgi;
+	}
+	
+	// suppression des items supprimables
+	foreach(QGraphicsItem *qgi_d, deletable_items) {
+		delete qgi_d;
+	}
+}
+
+/**
+	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 Diagram::drawBackground(QPainter *p, const QRectF &r) {
+	p -> save();
+	
+	// desactive tout antialiasing, sauf pour le texte
+	p -> setRenderHint(QPainter::Antialiasing, false);
+	p -> setRenderHint(QPainter::TextAntialiasing, true);
+	p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
+	
+	// dessine un fond blanc
+	p -> setPen(Qt::NoPen);
+	p -> setBrush(Qt::white);
+	p -> drawRect(r);
+	
+	if (draw_grid_) {
+		// 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 % xGrid) ++ g_x;
+		int g_y = (int)ceil(r.y());
+		while (g_y % yGrid) ++ g_y;
+		
+		QPolygon points;
+		for (int gx = g_x ; gx < limite_x ; gx += xGrid) {
+			for (int gy = g_y ; gy < limite_y ; gy += yGrid) {
+				points << QPoint(gx, gy);
+			}
+		}
+		p -> drawPoints(points);
+	}
+	
+	if (use_border_) border_and_titleblock.draw(p, margin, margin);
+	p -> restore();
+}
+
+/**
+	Gere les enfoncements de touches du clavier
+	@param e QKeyEvent decrivant l'evenement clavier
+*/
+void Diagram::keyPressEvent(QKeyEvent *e) {
+	bool transmit_event = true;
+	if (!isReadOnly()) {
+		QPointF movement;
+		switch(e -> key()) {
+			case Qt::Key_Left:  movement = QPointF(-xGrid, 0.0); break;
+			case Qt::Key_Right: movement = QPointF(+xGrid, 0.0); break;
+			case Qt::Key_Up:    movement = QPointF(0.0, -yGrid); break;
+			case Qt::Key_Down:  movement = QPointF(0.0, +yGrid); break;
+		}
+		if (!movement.isNull() && !focusItem()) {
+			beginMoveElements();
+			continueMoveElements(movement);
+			e -> accept();
+			transmit_event = false;
+		}
+	}
+	if (transmit_event) {
+		QGraphicsScene::keyPressEvent(e);
+	}
+}
+
+/**
+	Gere les relachements de touches du clavier
+	@param e QKeyEvent decrivant l'evenement clavier
+*/
+void Diagram::keyReleaseEvent(QKeyEvent *e) {
+	bool transmit_event = true;
+	if (!isReadOnly()) {
+		// detecte le relachement d'une touche de direction ( = deplacement d'elements)
+		if (
+			(e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right  ||
+			 e -> key() == Qt::Key_Up   || e -> key() == Qt::Key_Down)  &&
+			!e -> isAutoRepeat()
+		) {
+			endMoveElements();
+			e -> accept();
+			transmit_event = false;
+		}
+	}
+	if (transmit_event) {
+		QGraphicsScene::keyReleaseEvent(e);
+	}
+}
+
+/**
+	Exporte le schema vers une image
+	@return Une QImage representant le schema
+*/
+bool Diagram::toPaintDevice(QPaintDevice &pix, int width, int height, Qt::AspectRatioMode aspectRatioMode) {
+	// determine la zone source =  contenu du schema + marges
+	QRectF source_area;
+	if (!use_border_) {
+		source_area = itemsBoundingRect();
+		source_area.translate(-margin, -margin);
+		source_area.setWidth (source_area.width () + 2.0 * margin);
+		source_area.setHeight(source_area.height() + 2.0 * margin);
+	} else {
+		source_area = QRectF(
+			0.0,
+			0.0,
+			border_and_titleblock.borderWidth () + 2.0 * margin,
+			border_and_titleblock.borderHeight() + 2.0 * margin
+		);
+	}
+	
+	// si les dimensions ne sont pas precisees, l'image est exportee a l'echelle 1:1
+	QSize image_size = (width == -1 && height == -1) ? source_area.size().toSize() : QSize(width, height);
+	
+	// prepare le rendu
+	QPainter p;
+	if (!p.begin(&pix)) return(false);
+	
+	// rendu antialiase
+	p.setRenderHint(QPainter::Antialiasing, true);
+	p.setRenderHint(QPainter::TextAntialiasing, true);
+	p.setRenderHint(QPainter::SmoothPixmapTransform, true);
+	
+	// deselectionne tous les elements
+	QList<QGraphicsItem *> selected_elmts = selectedItems();
+	foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(false);
+	
+	// effectue le rendu lui-meme
+	render(&p, QRect(QPoint(0, 0), image_size), source_area, aspectRatioMode);
+	p.end();
+	
+	// restaure les elements selectionnes
+	foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(true);
+	
+	return(true);
+}
+
+/**
+	Permet de connaitre les dimensions qu'aura l'image generee par la methode toImage()
+	@return La taille de l'image generee par toImage()
+*/
+QSize Diagram::imageSize() const {
+	// determine la zone source =  contenu du schema + marges
+	qreal image_width, image_height;
+	if (!use_border_) {
+		QRectF items_rect = itemsBoundingRect();
+		image_width  = items_rect.width();
+		image_height = items_rect.height();
+	} else {
+		image_width  = border_and_titleblock.borderWidth();
+		image_height = border_and_titleblock.borderHeight();
+	}
+	
+	image_width  += 2.0 * margin;
+	image_height += 2.0 * margin;
+	
+	// renvoie la taille de la zone source
+	return(QSizeF(image_width, image_height).toSize());
+}
+
+/**
+	@return true si le schema est considere comme vide, false sinon.
+	Un schema vide ne contient ni element, ni conducteur, ni champ de texte
+*/
+bool Diagram::isEmpty() const {
+	return(!items().count());
+}
+
+/**
+ * @brief Diagram::potential
+ * @return all potential in the diagram
+ *each potential are in the QList and each conductors of one potential are in the QSet
+ */
+QList < QSet <Conductor *> > Diagram::potentials() {
+	QList < QSet <Conductor *> > potential_List;
+	if (content().conductors().size() == 0) return (potential_List); //return an empty potential
+	QList <Conductor *> conductors_list = content().conductors();
+
+	do {
+		QSet <Conductor *> one_potential = conductors_list.first() -> relatedPotentialConductors();
+		one_potential << conductors_list.takeFirst();
+		foreach (Conductor *c, one_potential) conductors_list.removeOne(c);
+		potential_List << one_potential;
+	} while (!conductors_list.empty());
+
+	return (potential_List);
+}
+
+/**
+	Exporte tout ou partie du schema 
+	@param whole_content Booleen (a vrai par defaut) indiquant si le XML genere doit
+	representer l'integralite du schema ou seulement le contenu selectionne
+	@return Un Document XML (QDomDocument)
+*/
+QDomDocument Diagram::toXml(bool whole_content) {
+	// document
+	QDomDocument document;
+	
+	// racine de l'arbre XML
+	QDomElement racine = document.createElement("diagram");
+	
+	// add the application version number
+	racine.setAttribute("version", QET::version);
+	
+	// proprietes du schema
+	if (whole_content) {
+		border_and_titleblock.titleBlockToXml(racine);
+		border_and_titleblock.borderToXml(racine);
+		
+		// type de conducteur par defaut
+		QDomElement default_conductor = document.createElement("defaultconductor");
+		defaultConductorProperties.toXml(default_conductor);
+		racine.appendChild(default_conductor);
+
+		//autonumerotation of conductor
+		if (!getNumerotation(Diagram::Conductors).isEmpty()) {
+			QDomElement autonum = document.createElement("autonum");
+			autonum.appendChild(getNumerotation(Diagram::Conductors).toXml(document, "conductor"));
+			racine.appendChild(autonum);
+		}
+	}
+	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 trois listes : une qui contient les elements, une qui contient les conducteurs, une qui contient les champs de texte
+	QList<Element *> list_elements;
+	QList<Conductor *> list_conductors;
+	QList<DiagramTextItem *> list_texts;
+	QList<DiagramImageItem *> list_images;
+	
+	// Determine les elements a "XMLiser"
+	foreach(QGraphicsItem *qgi, items()) {
+		if (Element *elmt = qgraphicsitem_cast<Element *>(qgi)) {
+			if (whole_content) list_elements << elmt;
+			else if (elmt -> isSelected()) list_elements << elmt;
+		} else if (Conductor *f = qgraphicsitem_cast<Conductor *>(qgi)) {
+			if (whole_content) list_conductors << f;
+			// lorsqu'on n'exporte pas tout le diagram, 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 selectionne
+			else if (f -> terminal1 -> parentItem() -> isSelected() && f -> terminal2 -> parentItem() -> isSelected()) {
+				list_conductors << f;
+			}
+		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(qgi)) {
+			if (whole_content) list_texts << iti;
+			else if (iti -> isSelected()) list_texts << iti;
+		} else if (DiagramImageItem *dii = qgraphicsitem_cast<DiagramImageItem *>(qgi)) {
+			if (whole_content) list_images << dii;
+			else if (dii -> isSelected()) list_images << dii;
+		}
+	}
+	
+	// table de correspondance entre les adresses des bornes et leurs ids
+	QHash<Terminal *, int> table_adr_id;
+	
+	// enregistrement des elements
+	if (!list_elements.isEmpty()) {
+		QDomElement elements = document.createElement("elements");
+		foreach(Element *elmt, list_elements) {
+			elements.appendChild(elmt -> toXml(document, table_adr_id));
+		}
+		racine.appendChild(elements);
+	}
+	
+	// enregistrement des conducteurs
+	if (!list_conductors.isEmpty()) {
+		QDomElement conductors = document.createElement("conductors");
+		foreach(Conductor *cond, list_conductors) {
+			conductors.appendChild(cond -> toXml(document, table_adr_id));
+		}
+		racine.appendChild(conductors);
+	}
+	
+	// enregistrement des champs de texte
+	if (!list_texts.isEmpty()) {
+		QDomElement inputs = document.createElement("inputs");
+		foreach(DiagramTextItem *dti, list_texts) {
+			inputs.appendChild(dti -> toXml(document));
+		}
+		racine.appendChild(inputs);
+	}
+
+	// save of images
+	if (!list_images.isEmpty()) {
+		QDomElement images = document.createElement("images");
+		foreach (DiagramImageItem *dii, list_images) {
+			images.appendChild(dii -> toXml(document));
+		}
+		racine.appendChild(images);
+	}
+	
+	// on retourne le document XML ainsi genere
+	return(document);
+}
+
+/**
+	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
+	@param consider_informations Si vrai, les informations complementaires
+	(auteur, titre, ...) seront prises en compte
+	@param content_ptr si ce pointeur vers un DiagramContent est different de 0,
+	il sera rempli avec le contenu ajoute au schema par le fromXml
+	@return true si l'import a reussi, false sinon
+*/
+bool Diagram::fromXml(QDomDocument &document, QPointF position, bool consider_informations, DiagramContent *content_ptr) {
+	QDomElement root = document.documentElement();
+	return(fromXml(root, position, consider_informations, content_ptr));
+}
+
+/**
+	Importe le schema decrit dans un element XML. Cette methode delegue son travail a Diagram::fromXml
+	Si l'import reussit, cette methode initialise egalement le document XML
+	interne permettant de bien gerer l'enregistrement de ce schema dans le
+	projet auquel il appartient.
+	@see Diagram::fromXml
+	@param document Le document XML a analyser
+	@param position La position du schema importe
+	@param consider_informations Si vrai, les informations complementaires
+	(auteur, titre, ...) seront prises en compte
+	@param content_ptr si ce pointeur vers un DiagramContent est different de 0,
+	il sera rempli avec le contenu ajoute au schema par le fromXml
+	@return true si l'import a reussi, false sinon
+	
+*/
+bool Diagram::initFromXml(QDomElement &document, QPointF position, bool consider_informations, DiagramContent *content_ptr) {
+	// import le contenu et les proprietes du schema depuis l'element XML fourni en parametre
+	bool from_xml = fromXml(document, position, consider_informations, content_ptr);
+	
+	// initialise le document XML interne a partir de l'element XML fourni en parametre
+	if (from_xml) {
+		xml_document_.clear();
+		xml_document_.appendChild(xml_document_.importNode(document, true));
+		// a ce stade, le document XML interne contient le code XML qui a ete importe, et non pas une version re-exporte par la methode toXml()
+	}
+	return(from_xml);
+}
+
+/**
+	Importe le schema decrit dans un element 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
+	@param consider_informations Si vrai, les informations complementaires
+	(auteur, titre, ...) seront prises en compte
+	@param content_ptr si ce pointeur vers un DiagramContent est different de 0,
+	il sera rempli avec le contenu ajoute au schema par le fromXml
+	@return true si l'import a reussi, false sinon
+*/
+bool Diagram::fromXml(QDomElement &document, QPointF position, bool consider_informations, DiagramContent *content_ptr) {
+	QDomElement root = document;
+	// le premier element doit etre un schema
+	if (root.tagName() != "diagram") return(false);
+	
+	// lecture des attributs de ce schema
+	if (consider_informations) {
+		bool conv_ok;
+		qreal version_value = root.attribute("version").toDouble(&conv_ok);
+		if (conv_ok) {
+			diagram_qet_version_ = version_value;
+		}
+		
+		border_and_titleblock.titleBlockFromXml(root);
+		border_and_titleblock.borderFromXml(root);
+		
+		// repere le permier element "defaultconductor"
+		QDomElement default_conductor_elmt = root.firstChildElement("defaultconductor");
+		if (!default_conductor_elmt.isNull()) {
+			defaultConductorProperties.fromXml(default_conductor_elmt);
+		}
+		// find the first element autonum
+		QDomElement num_auto = root.firstChildElement("autonum");
+		if (!num_auto.isNull()) {
+			QDomElement num_conductor = num_auto.firstChildElement("conductor");
+			//set the auto-numerotation of conductor
+			if (!num_conductor.isNull()) {
+				NumerotationContext nc(num_conductor);
+				setNumerotation(Diagram::Conductors, nc);
+			}
+		}
+	}
+	
+	// si la racine n'a pas d'enfant : le chargement est fini (schema vide)
+	if (root.firstChild().isNull()) {
+		write(document);
+		return(true);
+	}
+	
+	// Backward compatibility: prior to version 0.3, we need to compensate, at
+	// diagram-opening time, the rotation of the element for each of its
+	// textfields having the "FollowParentRotation" option disabled.
+	// After 0.3, elements textfields get userx, usery and userrotation attributes
+	// that explicitly specify their position and orientation.
+	qreal project_qet_version = declaredQElectroTechVersion(true);
+	bool handle_inputs_rotation = (
+		project_qet_version != -1 && project_qet_version < 0.3 &&
+		project_ -> state() == QETProject::ProjectParsingRunning
+	);
+	
+	// chargement de tous les elements du fichier XML
+	QList<Element *> added_elements;
+	QHash<int, Terminal *> table_adr_id;
+	foreach (QDomElement element_xml, QET::findInDomElement(root, "elements", "element")) {
+		if (!Element::valideXml(element_xml)) continue;
+		
+		// cree un element dont le type correspond a l'id type
+		QString type_id = element_xml.attribute("type");
+		ElementsLocation element_location = ElementsLocation(type_id);
+		if (type_id.startsWith("embed://")) element_location.setProject(project_);
+		
+		CustomElement *nvel_elmt = new CustomElement(element_location);
+		if (nvel_elmt -> isNull()) {
+			QString debug_message = QString("Diagram::fromXml() : Le chargement de la description de l'element %1 a echoue avec le code d'erreur %2").arg(element_location.path()).arg(nvel_elmt -> state());
+			qDebug() << qPrintable(debug_message);
+			delete nvel_elmt;
+			
+			qDebug() << "Diagram::fromXml() : Utilisation d'un GhostElement en lieu et place de cet element.";
+			nvel_elmt = new GhostElement(element_location);
+		}
+		
+		// charge les caracteristiques de l'element
+		if (nvel_elmt -> fromXml(element_xml, table_adr_id, handle_inputs_rotation)) {
+			// ajout de l'element au schema et a la liste des elements ajoutes
+			addElement(nvel_elmt);
+			added_elements << nvel_elmt;
+		} else {
+			delete nvel_elmt;
+			qDebug() << "Diagram::fromXml() : Le chargement des parametres d'un element a echoue";
+		}
+	}
+	
+	// chargement de tous les textes du fichiers XML
+	QList<IndependentTextItem *> added_texts;
+	foreach (QDomElement text_xml, QET::findInDomElement(root, "inputs", "input")) {
+        	IndependentTextItem *iti = new IndependentTextItem(this);
+		iti -> fromXml(text_xml);
+		addIndependentTextItem(iti);
+		added_texts << iti;
+	}
+
+	QList<DiagramImageItem *> added_images;
+	foreach (QDomElement image_xml, QET::findInDomElement(root, "images", "image")) {
+		DiagramImageItem *dii = new DiagramImageItem ();
+		dii -> fromXml(image_xml);
+		addItem(dii);
+		added_images << dii;
+	}
+	
+	// gere la translation des nouveaux elements et texte 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;
+		QList<QGraphicsItem *> added_items;
+		foreach (Element *added_element, added_elements) added_items << added_element;
+		foreach (DiagramTextItem *added_text, added_texts) added_items << added_text;
+		foreach (DiagramImageItem *added_image, added_images) added_items << added_image;
+		foreach (QGraphicsItem *item, added_items) {
+			QPointF csg = item -> mapToScene(item -> boundingRect()).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 *added_element, added_elements) {
+			added_element -> setPos(added_element -> pos().x() + diff_x, added_element -> pos().y() + diff_y);
+		}
+		foreach (DiagramTextItem *added_text, added_texts) {
+			added_text -> setPos(added_text -> pos().x() + diff_x, added_text -> pos().y() + diff_y);
+		}
+		foreach (DiagramImageItem *added_image, added_images) {
+			added_image -> setPos(added_image -> pos().x() + diff_x, added_image -> pos().y() + diff_y);
+		}
+	}
+	
+	// chargement de tous les Conducteurs du fichier XML
+	QList<Conductor *> added_conductors;
+	foreach (QDomElement f, QET::findInDomElement(root, "conductors", "conductor")) {
+		if (!Conductor::valideXml(f)) continue;
+		// verifie que les bornes que le conducteur relie sont connues
+		int id_p1 = f.attribute("terminal1").toInt();
+		int id_p2 = f.attribute("terminal2").toInt();
+		if (table_adr_id.contains(id_p1) && table_adr_id.contains(id_p2)) {
+			// pose le conducteur... si c'est possible
+			Terminal *p1 = table_adr_id.value(id_p1);
+			Terminal *p2 = table_adr_id.value(id_p2);
+			if (p1 != p2) {
+				bool can_add_conductor = true;
+				bool cia = ((Element *)p2 -> parentItem()) -> internalConnections();
+				if (!cia) {
+					foreach(QGraphicsItem *item, p2 -> parentItem() -> children()) {
+						if (item == p1) can_add_conductor = false;
+					}
+				}
+				if (can_add_conductor) {
+					Conductor *c = new Conductor(table_adr_id.value(id_p1), table_adr_id.value(id_p2), this);
+					c -> fromXml(f);
+					added_conductors << c;
+				}
+			}
+		} else qDebug() << "Diagram::fromXml() : Le chargement du conducteur" << id_p1 << id_p2 << "a echoue";
+	}
+	
+	// remplissage des listes facultatives
+	if (content_ptr) {
+		content_ptr -> elements         = added_elements.toSet();
+		content_ptr -> conductorsToMove = added_conductors.toSet();
+		content_ptr -> textFields       = added_texts.toSet();
+		content_ptr -> images			= added_images.toSet();
+	}
+	
+	return(true);
+}
+
+/**
+	Enregistre le schema XML dans son document XML interne et emet le signal
+	written().
+*/
+void Diagram::write() {
+	qDebug() << qPrintable(QString("Diagram::write() : saving changes from diagram \"%1\" [%2]").arg(title()).arg(QET::pointerString(this)));
+	write(toXml().documentElement());
+	undoStack().setClean();
+}
+
+/**
+	Enregistre un element XML dans son document XML interne et emet le signal
+	written().
+	@param element xml a enregistrer
+*/
+void Diagram::write(const QDomElement &element) {
+	xml_document_.clear();
+	xml_document_.appendChild(xml_document_.importNode(element, true));
+	emit(written());
+}
+
+/**
+	@return true si la fonction write a deja ete appele (pour etre plus exact :
+	si le document XML utilise en interne n'est pas vide), false sinon
+*/
+bool Diagram::wasWritten() const {
+	return(!xml_document_.isNull());
+}
+
+/**
+	@return le schema en XML tel qu'il doit etre enregistre dans le fichier projet
+	@param xml_doc document XML a utiliser pour creer l'element
+*/
+QDomElement Diagram::writeXml(QDomDocument &xml_doc) const {
+	// si le schema n'a pas ete enregistre explicitement, on n'ecrit rien
+	if (!wasWritten()) return(QDomElement());
+	
+	QDomElement diagram_elmt = xml_document_.documentElement();
+	QDomNode new_node = xml_doc.importNode(diagram_elmt, true);
+	return(new_node.toElement());
+}
+
+/**
+	Ajoute un element sur le schema
+	@param element Element a ajouter
+*/
+void Diagram::addElement(Element *element) {
+	if (!element || isReadOnly()) return;
+	
+	// ajoute l'element au schema
+	if (element -> scene() != this) {
+		addItem(element);
+	}
+	
+	// surveille les modifications de ses champs de texte
+	foreach(ElementTextItem *eti, element -> texts()) {
+		connect(
+			eti,
+			SIGNAL(diagramTextChanged(DiagramTextItem *, const QString &, const QString &)),
+			this,
+			SLOT(diagramTextChanged(DiagramTextItem *, const QString &, const QString &))
+		);
+	}
+}
+
+/**
+	Ajoute un conducteur sur le schema
+	@param conductor Conducteur a ajouter
+*/
+void Diagram::addConductor(Conductor *conductor) {
+	if (!conductor || isReadOnly()) return;
+	
+	// ajoute le conducteur au schema
+	if (conductor -> scene() != this) {
+		addItem(conductor);
+		conductor -> terminal1 -> addConductor(conductor);
+		conductor -> terminal2 -> addConductor(conductor);
+	}
+}
+
+/**
+	Aoute un champ de texte independant sur le schema
+	@param iti Champ de texte a ajouter
+*/
+void Diagram::addIndependentTextItem(IndependentTextItem *iti) {
+	if (!iti || isReadOnly()) return;
+	
+	// ajoute le champ de texte au schema
+	if (iti -> scene() != this) {
+		addItem(iti);
+	}
+	
+	// surveille les modifications apportees au champ de texte
+	connect(
+		iti,
+		SIGNAL(diagramTextChanged(DiagramTextItem *, const QString &, const QString &)),
+		this,
+		SLOT(diagramTextChanged(DiagramTextItem *, const QString &, const QString &))
+	);
+}
+
+void Diagram::addDiagramImageItem(DiagramImageItem *dii) {
+	if (!dii || isReadOnly()) return;
+
+	//add image at diagram
+	if (dii -> scene() != this) {
+		addItem(dii);
+	}
+}
+
+/**
+	Enleve un element du schema
+	@param element Element a enlever
+*/
+void Diagram::removeElement(Element *element) {
+	if (!element || isReadOnly()) return;
+	
+	// enleve l'element au schema
+	removeItem(element);
+	
+	// arrete la surveillance des modifications de ses champs de texte
+	foreach(ElementTextItem *eti, element -> texts()) {
+		disconnect(
+			eti,
+			SIGNAL(diagramTextChanged(DiagramTextItem *, const QString &, const QString &)),
+			this,
+			SLOT(diagramTextChanged(DiagramTextItem *, const QString &, const QString &))
+		);
+	}
+}
+
+/**
+	Enleve un conducteur du schema
+	@param conductor Conducteur a enlever
+*/
+void Diagram::removeConductor(Conductor *conductor) {
+	if (!conductor || isReadOnly()) return;
+	
+	// detache le conducteur sans le detruire
+	conductor -> terminal1 -> removeConductor(conductor);
+	conductor -> terminal2 -> removeConductor(conductor);
+	
+	// enleve le conducteur du schema
+	removeItem(conductor);
+}
+
+/**
+	Enleve un champ de texte independant du schema
+	@param iti Champ de texte a enlever
+*/
+void Diagram::removeIndependentTextItem(IndependentTextItem *iti) {
+	if (!iti || isReadOnly()) return;
+	
+	// enleve le champ de texte au schema
+	removeItem(iti);
+	
+	// arrete la surveillance des modifications apportees au champ de texte
+	disconnect(
+		iti,
+		SIGNAL(diagramTextChanged(DiagramTextItem *, const QString &, const QString &)),
+		this,
+		SLOT(diagramTextChanged(DiagramTextItem *, const QString &, const QString &))
+	);
+}
+
+void Diagram::titleChanged(const QString &title) {
+	emit(diagramTitleChanged(this, title));
+}
+
+/**
+	Gere le fait qu'un texte du schema ait ete modifie
+	@param text_item Texte modifie
+	@param old_text Ancien texte
+	@param new_text Nouveau texte
+*/
+void Diagram::diagramTextChanged(DiagramTextItem *text_item, const QString &old_text, const QString &new_text) {
+	if (!text_item) return;
+	undo_stack_ -> push(new ChangeDiagramTextCommand(text_item, old_text, new_text));
+}
+
+/**
+	This slot may be used to inform the diagram object that the given title
+	block template has changed. The diagram will thus flush its title
+	block-dedicated rendering cache.
+	@param template_name Name of the title block template that has changed
+*/
+void Diagram::titleBlockTemplateChanged(const QString &template_name) {
+	if (border_and_titleblock.titleBlockTemplateName() != template_name) return;
+	
+	border_and_titleblock.titleBlockTemplateChanged(template_name);
+	update();
+}
+
+/**
+	This slot has to be be used to inform this class that the given title block
+	template is about to be removed and is no longer accessible. This class
+	will either use the provided  optional TitleBlockTemplate or the default
+	title block provided by QETApp::defaultTitleBlockTemplate()
+	@param template_name Name of the title block template that has changed
+	@param new_template (Optional) Name of the title block template to use instead
+*/
+void Diagram::titleBlockTemplateRemoved(const QString &template_name, const QString &new_template) {
+	if (border_and_titleblock.titleBlockTemplateName() != template_name) return;
+	
+	const TitleBlockTemplate *final_template = project_ -> getTemplateByName(new_template);
+	border_and_titleblock.titleBlockTemplateRemoved(template_name, final_template);
+	update();
+}
+
+/**
+	Set the template to use to render the title block of this diagram.
+	@param template_name Name of the title block template.
+*/
+void Diagram::setTitleBlockTemplate(const QString &template_name) {
+	if (!project_) return;
+	
+	QString current_name = border_and_titleblock.titleBlockTemplateName();
+	const TitleBlockTemplate *titleblock_template = project_ -> getTemplateByName(template_name);
+	border_and_titleblock.titleBlockTemplateRemoved(current_name, titleblock_template);
+	
+	if (template_name != current_name) {
+		emit(usedTitleBlockTemplateChanged(template_name));
+	}
+}
+
+/**
+	Selectionne tous les objets du schema
+*/
+void Diagram::selectAll() {
+	if (items().isEmpty()) return;
+	
+	blockSignals(true);
+	foreach(QGraphicsItem *qgi, items()) qgi -> setSelected(true);
+	blockSignals(false);
+	emit(selectionChanged());
+}
+
+/**
+	Deslectionne tous les objets selectionnes
+*/
+void Diagram::deselectAll() {
+	if (items().isEmpty()) return;
+	
+	clearSelection();
+}
+
+/**
+	Inverse l'etat de selection de tous les objets du schema
+*/
+void Diagram::invertSelection() {
+	if (items().isEmpty()) return;
+	
+	blockSignals(true);
+	foreach (QGraphicsItem *item, items()) item -> setSelected(!item -> isSelected());
+	blockSignals(false);
+	emit(selectionChanged());
+}
+
+/**
+	@return Le rectangle (coordonnees par rapport a la scene) delimitant le bord du schema
+*/
+QRectF Diagram::border() const {
+	return(
+		QRectF(
+			margin,
+			margin,
+			border_and_titleblock.borderWidth(),
+			border_and_titleblock.borderHeight()
+		)
+	);
+}
+
+/**
+	@return le titre du cartouche
+*/
+QString Diagram::title() const {
+	return(border_and_titleblock.title());
+}
+
+/**
+	@return la liste des elements de ce schema
+*/
+QList<CustomElement *> Diagram::customElements() const {
+	QList<CustomElement *> elements_list;
+	foreach(QGraphicsItem *qgi, items()) {
+		if (CustomElement *elmt = qgraphicsitem_cast<CustomElement *>(qgi)) {
+			elements_list << elmt;
+		}
+	}
+	return(elements_list);
+}
+
+/**
+	Initialise un deplacement d'elements, conducteurs et champs de texte sur le
+	schema.
+	@param driver_item Item deplace par la souris et ne necessitant donc pas
+	d'etre deplace lors des appels a continueMovement.
+	@see ElementsMover
+*/
+int Diagram::beginMoveElements(QGraphicsItem *driver_item) {
+	return(elements_mover_ -> beginMovement(this, driver_item));
+}
+
+/**
+	Prend en compte un mouvement composant un deplacement d'elements,
+	conducteurs et champs de texte
+	@param movement mouvement a ajouter au deplacement en cours
+	@see ElementsMover
+*/
+void Diagram::continueMoveElements(const QPointF &movement) {
+	elements_mover_ -> continueMovement(movement);
+}
+
+/**
+	Finalise un deplacement d'elements, conducteurs et champs de texte
+	@see ElementsMover
+*/
+void Diagram::endMoveElements() {
+	elements_mover_ -> endMovement();
+}
+
+/**
+	Initialise un deplacement d'ElementTextItems
+	@param driver_item Item deplace par la souris et ne necessitant donc pas
+	d'etre deplace lors des appels a continueMovement.
+	@see ElementTextsMover
+*/
+int Diagram::beginMoveElementTexts(QGraphicsItem *driver_item) {
+	return(element_texts_mover_ -> beginMovement(this, driver_item));
+}
+
+/**
+	Prend en compte un mouvement composant un deplacement d'ElementTextItems
+	@param movement mouvement a ajouter au deplacement en cours
+	@see ElementTextsMover
+*/
+void Diagram::continueMoveElementTexts(const QPointF &movement) {
+	element_texts_mover_ -> continueMovement(movement);
+}
+
+/**
+	Finalise un deplacement d'ElementTextItems
+	@see ElementTextsMover
+*/
+void Diagram::endMoveElementTexts() {
+	element_texts_mover_ -> endMovement();
+}
+
+/**
+	Permet de savoir si un element est utilise sur un schema
+	@param location Emplacement d'un element
+	@return true si l'element location est utilise sur ce schema, false sinon
+*/
+bool Diagram::usesElement(const ElementsLocation &location) {
+	foreach(CustomElement *element, customElements()) {
+		if (element -> location() == location) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	@param a title block template name
+	@return true if the provided template is used by this diagram, false
+	otherwise.
+*/
+bool Diagram::usesTitleBlockTemplate(const QString &name) {
+	return(name == border_and_titleblock.titleBlockTemplateName());
+}
+
+/**
+	Cette methode permet d'appliquer de nouvelles options de rendu tout en
+	accedant aux proprietes de rendu en cours.
+	@param new_properties Nouvelles options de rendu a appliquer
+	@return les options de rendu avant l'application de new_properties
+*/
+ExportProperties Diagram::applyProperties(const ExportProperties &new_properties) {
+	// exporte les options de rendu en cours
+	ExportProperties old_properties;
+	old_properties.draw_grid               = displayGrid();
+	old_properties.draw_border             = border_and_titleblock.borderIsDisplayed();
+	old_properties.draw_titleblock         = border_and_titleblock.titleBlockIsDisplayed();
+	old_properties.draw_terminals          = drawTerminals();
+	old_properties.draw_colored_conductors = drawColoredConductors();
+	old_properties.exported_area           = useBorder() ? QET::BorderArea : QET::ElementsArea;
+	
+	// applique les nouvelles options de rendu
+	setUseBorder                  (new_properties.exported_area == QET::BorderArea);
+	setDrawTerminals              (new_properties.draw_terminals);
+	setDrawColoredConductors      (new_properties.draw_colored_conductors);
+	setDisplayGrid                (new_properties.draw_grid);
+	border_and_titleblock.displayBorder(new_properties.draw_border);
+	border_and_titleblock.displayTitleBlock (new_properties.draw_titleblock);
+	
+	// retourne les anciennes options de rendu
+	return(old_properties);
+}
+
+/**
+	@param pos Position cartesienne (ex : 10.3, 45.2) a transformer en position
+	dans la grille (ex : B2)
+	@return la position dans la grille correspondant a pos
+*/
+DiagramPosition Diagram::convertPosition(const QPointF &pos) {
+	// decale la position pour prendre en compte les marges en haut a gauche du schema
+	QPointF final_pos = pos - QPointF(margin, margin);
+	
+	// delegue le calcul au BorderTitleBlock
+	DiagramPosition diagram_position = border_and_titleblock.convertPosition(final_pos);
+	
+	// embarque la position cartesienne
+	diagram_position.setPosition(pos);
+	
+	return(diagram_position);
+}
+
+/**
+	Definit s'il faut afficher ou non les bornes
+	@param dt true pour afficher les bornes, false sinon
+*/
+void Diagram::setDrawTerminals(bool dt) {
+	foreach(QGraphicsItem *qgi, items()) {
+		if (Terminal *t = qgraphicsitem_cast<Terminal *>(qgi)) {
+			t -> setVisible(dt);
+		}
+	}
+}
+
+/**
+	Definit s'il faut respecter ou non les couleurs des conducteurs.
+	Si non, les conducteurs sont tous dessines en noir.
+	@param dcc true pour respecter les couleurs, false sinon
+*/
+void Diagram::setDrawColoredConductors(bool dcc) {
+	draw_colored_conductors_ = dcc;
+}
+
+/**
+	@return la liste des conducteurs selectionnes sur le schema
+*/
+QSet<Conductor *> Diagram::selectedConductors() const {
+	QSet<Conductor *> conductors_set;
+	foreach(QGraphicsItem *qgi, selectedItems()) {
+		if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
+			conductors_set << c;
+		}
+	}
+	return(conductors_set);
+}
+
+/**
+	@return la liste de tous les textes selectionnes : les textes independants,
+	mais aussi ceux rattaches a des conducteurs ou des elements
+*/
+QSet<DiagramTextItem *> Diagram::selectedTexts() const {
+	QSet<DiagramTextItem *> selected_texts;
+	foreach(QGraphicsItem *item, selectedItems()) {
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(item)) {
+			selected_texts << cti;
+		} else if (ElementTextItem *eti = qgraphicsitem_cast<ElementTextItem *>(item)) {
+			selected_texts << eti;
+		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
+			selected_texts << iti;
+		}
+	}
+	return(selected_texts);
+}
+
+/**
+ * @brief Diagram::selectedConductorTexts
+ * @return the list of conductor texts selected
+ */
+QSet<ConductorTextItem *> Diagram::selectedConductorTexts() const {
+	QSet<ConductorTextItem *> selected_texts;
+	foreach(QGraphicsItem *item, selectedItems()) {
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(item)) {
+			selected_texts << cti;
+		}
+	}
+	return(selected_texts);
+}
+
+/// @return true si le presse-papier semble contenir un schema
+bool Diagram::clipboardMayContainDiagram() {
+	QString clipboard_text = QApplication::clipboard() -> text().trimmed();
+	bool may_be_diagram = clipboard_text.startsWith("<diagram") && clipboard_text.endsWith("</diagram>");
+	return(may_be_diagram);
+}
+
+/**
+	@return le projet auquel ce schema appartient ou 0 s'il s'agit d'un schema
+	independant.
+*/
+QETProject *Diagram::project() const {
+	return(project_);
+}
+
+/**
+	@param project le nouveau projet auquel ce schema appartient ou 0 s'il
+	s'agit d'un schema independant. Indiquer 0 pour rendre ce schema independant.
+*/
+void Diagram::setProject(QETProject *project) {
+	project_ = project;
+}
+
+/**
+	@return the folio number of this diagram within its parent project, or -1
+	if it is has no parent project
+*/
+int Diagram::folioIndex() const {
+	if (!project_) return(-1);
+	return(project_ -> folioIndex(this));
+}
+
+/**
+	@param fallback_to_project When a diagram does not have a declared version,
+	this method will use the one declared by its parent project only if
+	fallback_to_project is true.
+	@return the declared QElectroTech version of this diagram
+*/
+qreal Diagram::declaredQElectroTechVersion(bool fallback_to_project) const {
+	if (diagram_qet_version_ != -1) {
+		return diagram_qet_version_;
+	}
+	if (fallback_to_project && project_) {
+		return(project_ -> declaredQElectroTechVersion());
+	}
+	return(-1);
+}
+
+/**
+	@return true si le schema est en lecture seule
+*/
+bool Diagram::isReadOnly() const {
+	return(read_only_);
+}
+
+/**
+	@param read_only true pour passer le schema en lecture seule, false sinon
+*/
+void Diagram::setReadOnly(bool read_only) {
+	if (read_only_ != read_only) {
+		read_only_ = read_only;
+		emit(readOnlyChanged(read_only_));
+	}
+}
+
+/**
+	@return Le contenu du schema. Les conducteurs sont tous places dans
+	conductorsToMove.
+*/
+DiagramContent Diagram::content() const {
+	DiagramContent dc;
+	foreach(QGraphicsItem *qgi, items()) {
+		if (Element *e = qgraphicsitem_cast<Element *>(qgi)) {
+			dc.elements << e;
+		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(qgi)) {
+			dc.textFields << iti;
+		} else if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
+			dc.conductorsToMove << c;
+		}
+	}
+	return(dc);
+}
+
+/**
+	@return le contenu selectionne du schema.
+*/
+DiagramContent Diagram::selectedContent() {
+	DiagramContent dc;
+	
+	// recupere les elements deplaces
+	foreach (QGraphicsItem *item, selectedItems()) {
+		if (Element *elmt = qgraphicsitem_cast<Element *>(item)) {
+			dc.elements << elmt;
+		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
+			dc.textFields << iti;
+		} else if (Conductor *c = qgraphicsitem_cast<Conductor *>(item)) {
+			// recupere les conducteurs selectionnes isoles (= non deplacables mais supprimables)
+			if (
+				!c -> terminal1 -> parentItem() -> isSelected() &&\
+				!c -> terminal2 -> parentItem() -> isSelected()
+			) {
+				dc.otherConductors << c;
+			}
+		} else if (DiagramImageItem *dii = qgraphicsitem_cast<DiagramImageItem *>(item)) {
+			dc.images << dii;
+		}
+	}
+	
+	// pour chaque element deplace, determine les conducteurs qui seront modifies
+	foreach(Element *elmt, dc.elements) {
+		foreach(Terminal *terminal, elmt -> terminals()) {
+			foreach(Conductor *conductor, terminal -> conductors()) {
+				Terminal *other_terminal;
+				if (conductor -> terminal1 == terminal) {
+					other_terminal = conductor -> terminal2;
+				} else {
+					other_terminal = conductor -> terminal1;
+				}
+				// si les deux elements du conducteur sont deplaces
+				if (dc.elements.contains(other_terminal -> parentElement())) {
+					dc.conductorsToMove << conductor;
+				} else {
+					dc.conductorsToUpdate << conductor;
+				}
+			}
+		}
+	}
+	
+	return(dc);
+}
+
+/**
+	@return true s'il est possible de tourner les elements selectionnes.
+	Concretement, cette methode retourne true s'il y a des elements selectionnes
+	et qu'au moins l'un d'entre eux peut etre pivote.
+*/
+bool Diagram::canRotateSelection() const {
+	foreach(QGraphicsItem * qgi, selectedItems()) {
+		if (qgraphicsitem_cast<IndependentTextItem *>(qgi) ||
+		qgraphicsitem_cast<ConductorTextItem *>(qgi) ||
+		qgraphicsitem_cast<DiagramImageItem *>(qgi) ||
+		qgraphicsitem_cast<ElementTextItem *>(qgi) ||
+		qgraphicsitem_cast<Element *>(qgi)) return (true);
+	}
+	return(false);
+}

Added: trunk/sources/diagram.h
===================================================================
--- trunk/sources/diagram.h	                        (rev 0)
+++ trunk/sources/diagram.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,337 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAM_H
+#define DIAGRAM_H
+#include <QtGui>
+#include <QtXml>
+#include <QHash>
+#include "bordertitleblock.h"
+#include "conductorproperties.h"
+#include "exportproperties.h"
+#include "qgimanager.h"
+#include "numerotationcontext.h"
+
+class Conductor;
+class CustomElement;
+class DiagramContent;
+class DiagramPosition;
+class DiagramTextItem;
+class Element;
+class ElementsLocation;
+class ElementsMover;
+class ElementTextItem;
+class ElementTextsMover;
+class IndependentTextItem;
+class QETProject;
+class Terminal;
+class ConductorTextItem;
+class DiagramImageItem;
+/**
+	This class represents an electric diagram. It manages its various child
+	elements, conductors and texts and handles their graphic rendering.
+*/
+class Diagram : public QGraphicsScene {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	Diagram(QObject * = 0);
+	virtual ~Diagram();
+	
+	private:
+	Diagram(const Diagram &diagram);
+	
+	// attributes
+	public:
+	/**
+		Represents available options when rendering a particular diagram:
+		 * EmptyBorder: display border only
+		 * TitleBlock: display title block
+		 * Columns: display columns
+	*/
+	enum BorderOptions { EmptyBorder, TitleBlock, Columns };
+	/// Represents available option of Numerotation type.
+	enum NumerotationType { Conductors };
+	/// Default properties for new conductors
+	ConductorProperties defaultConductorProperties;
+	/// Diagram dimensions and title block
+	BorderTitleBlock border_and_titleblock;
+	/// abscissa grid step size
+	static const int xGrid;
+	/// ordinate grid step size
+	static const int yGrid;
+	/// margin around the diagram
+	static const qreal margin;
+	
+	private:
+	QGraphicsLineItem *conductor_setter_;
+	ElementsMover *elements_mover_;
+	ElementTextsMover *element_texts_mover_;
+	bool draw_grid_;
+	bool use_border_;
+	QGIManager *qgi_manager_;
+	QUndoStack *undo_stack_;
+	bool draw_terminals_;
+	bool draw_colored_conductors_;
+	QDomDocument xml_document_;
+	QETProject *project_;
+	bool read_only_;
+	qreal diagram_qet_version_;
+	QHash <NumerotationType, NumerotationContext > numerotation_;
+	
+	// methods
+	protected:
+	virtual void drawBackground(QPainter *, const QRectF &);
+	virtual void keyPressEvent(QKeyEvent *);
+	virtual void keyReleaseEvent(QKeyEvent *);
+	
+	public:
+	static bool clipboardMayContainDiagram();
+	bool setNumerotation (NumerotationType, NumerotationContext);
+	NumerotationContext getNumerotation (NumerotationType) const;
+	
+	// methods related to parent project
+	QETProject *project() const;
+	void setProject(QETProject *);
+	int folioIndex() const;
+	qreal declaredQElectroTechVersion(bool = true) const;
+	
+	// methods related to read only mode
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	
+	// methods related to conductor creation
+	void setConductor(bool);
+	void setConductorStart (QPointF);
+	void setConductorStop(QPointF);
+	QList < QSet <Conductor *> > potentials();
+	
+	// methods related to XML import/export
+	QDomDocument toXml(bool = true);
+	bool initFromXml(QDomElement &, QPointF = QPointF(), bool = true, DiagramContent * = 0);
+	bool fromXml(QDomDocument &, QPointF = QPointF(), bool = true, DiagramContent * = 0);
+	bool fromXml(QDomElement &, QPointF = QPointF(), bool = true, DiagramContent * = 0);
+	void write();
+	void write(const QDomElement &);
+	bool wasWritten() const;
+	QDomElement writeXml(QDomDocument &) const;
+	
+	// methods related to graphics items addition/removal on the diagram
+	void addElement(Element *);
+	void addConductor(Conductor *);
+	void addIndependentTextItem(IndependentTextItem *);
+	void addDiagramImageItem(DiagramImageItem *);
+	
+	void removeElement(Element *);
+	void removeConductor(Conductor *);
+	void removeIndependentTextItem(IndependentTextItem *);
+	
+	// methods related to graphics options
+	ExportProperties applyProperties(const ExportProperties &);
+	void setDisplayGrid(bool);
+	bool displayGrid();
+	void setUseBorder(bool);
+	bool useBorder();
+	void setBorderOptions(BorderOptions);
+	BorderOptions borderOptions();
+	DiagramPosition convertPosition(const QPointF &);
+	
+	bool drawTerminals() const;
+	void setDrawTerminals(bool);
+	bool drawColoredConductors() const;
+	void setDrawColoredConductors(bool);
+	
+	QRectF border() const;
+	QString title() const;
+	bool toPaintDevice(QPaintDevice &, int = -1, int = -1, Qt::AspectRatioMode = Qt::KeepAspectRatio);
+	QSize imageSize() const;
+	
+	bool isEmpty() const;
+	
+	QList<CustomElement *> customElements() const;
+	QSet<DiagramTextItem *> selectedTexts() const;
+	QSet<ConductorTextItem *> selectedConductorTexts() const;
+	QSet<Conductor *> selectedConductors() const;
+	DiagramContent content() const;
+	DiagramContent selectedContent();
+	bool canRotateSelection() const;
+	int  beginMoveElements(QGraphicsItem * = 0);
+	void continueMoveElements(const QPointF &);
+	void endMoveElements();
+	int  beginMoveElementTexts(QGraphicsItem * = 0);
+	void continueMoveElementTexts(const QPointF &);
+	void endMoveElementTexts();
+	bool usesElement(const ElementsLocation &);
+	bool usesTitleBlockTemplate(const QString &);
+	
+	QUndoStack &undoStack();
+	QGIManager &qgiManager();
+	
+	public slots:
+	void titleChanged(const QString &);
+	void diagramTextChanged(DiagramTextItem *, const QString &, const QString &);
+	void titleBlockTemplateChanged(const QString &);
+	void titleBlockTemplateRemoved(const QString &, const QString & = QString());
+	void setTitleBlockTemplate(const QString &);
+	
+	// methods related to graphics items selection
+	void selectAll();
+	void deselectAll();
+	void invertSelection();
+	
+	signals:
+	void written();
+	void readOnlyChanged(bool);
+	void usedTitleBlockTemplateChanged(const QString &);
+	void diagramTitleChanged(Diagram *, const QString &);
+};
+Q_DECLARE_METATYPE(Diagram *)
+
+/**
+ * @brief Diagram::setNumerotation, store a numerotation type
+ * @return true if storage is available
+ */
+inline bool Diagram::setNumerotation(Diagram::NumerotationType type, NumerotationContext context) {
+	switch (type) {
+		case Conductors:
+			numerotation_.insert(type, context);
+			return true;
+			break;
+		default:
+			return false;
+			break;
+	}
+}
+
+/**
+ * @brief Diagram::getNumerotation
+ * @return the NumerotationContext associated with the key.
+ * If numerotation_ contains no item with the key, the function returns a default-constructed NumerotationContext
+ */
+inline NumerotationContext Diagram::getNumerotation(Diagram::NumerotationType type) const {
+	return numerotation_.value(type);
+}
+
+/**
+	Display or hide the conductor setter, i.e. a dashed conductor stub which appears when creating a conductor between two terminals.
+	@param pf true pour ajouter le poseur de conducteur, false pour l'enlever
+*/
+inline void Diagram::setConductor(bool adding) {
+	if (adding) {
+		if (!conductor_setter_ -> scene()) addItem(conductor_setter_);
+	} else {
+		if (conductor_setter_ -> scene()) removeItem(conductor_setter_);
+	}
+}
+
+/**
+	Set the start point of the conductor setter.
+	@param start the point (in scene coordinates) which the newly created
+	conductor should start from.
+*/
+inline void Diagram::setConductorStart(QPointF start) {
+	conductor_setter_ -> setLine(QLineF(start, conductor_setter_ -> line().p2()));
+}
+
+/**
+	Set the end point of the conductor setter.
+	@param end the point (in scene coordinates) upon to which the newly created
+	conductor should be drawn.
+*/
+inline void Diagram::setConductorStop(QPointF end) {
+	conductor_setter_ -> setLine(QLineF(conductor_setter_ -> line().p1(), end));
+}
+
+/**
+	Set whether the diagram grid should be drawn.
+	@param dg true to render the grid, false otherwise.
+*/
+inline void Diagram::setDisplayGrid(bool dg) {
+	draw_grid_ = dg;
+}
+
+/**
+	@return true if the grid is drawn, false otherwise.
+*/
+inline bool Diagram::displayGrid() {
+	return(draw_grid_);
+}
+
+/**
+	Set whether the diagram border (including rows/colums headers and the title
+	block) should be rendered along with the diagram. When set to false, the size
+	of the smallest rectangle containing all items is considered as the diagram
+	size.
+	@param ub true to take the border into account, false otherwise
+*/
+inline void Diagram::setUseBorder(bool ub) {
+	use_border_ = ub;
+}
+
+/**
+	@return true if the border is rendered and take into account, false
+	otherwise.
+*/
+inline bool Diagram::useBorder() {
+	return(use_border_);
+}
+
+/**
+	Set the rendering options for the diagram border (including rows/colums
+	headers and the title block)
+	@param bo Enabled options ORed together
+	@see BorderOptions
+*/
+inline void Diagram::setBorderOptions(Diagram::BorderOptions bo) {
+	border_and_titleblock.displayBorder(!(bo & EmptyBorder));
+	border_and_titleblock.displayColumns(bo & Columns);
+	border_and_titleblock.displayTitleBlock(bo & TitleBlock);
+}
+
+/**
+	@return The rendering optios for the diagram border
+	@see setBorderOptions
+*/
+inline Diagram::BorderOptions Diagram::borderOptions() {
+	BorderOptions options = EmptyBorder;
+	if (border_and_titleblock.titleBlockIsDisplayed()) options = (BorderOptions)(options|TitleBlock);
+	if (border_and_titleblock.columnsAreDisplayed()) options = (BorderOptions)(options|Columns);
+	return(options);
+}
+
+/// @return the diagram undo stack
+inline QUndoStack &Diagram::undoStack() {
+	return(*undo_stack_);
+}
+
+/// @return the diagram graphics item manager
+inline QGIManager &Diagram::qgiManager() {
+	return(*qgi_manager_);
+}
+
+/// @return true if terminals are rendered, false otherwise
+inline bool Diagram::drawTerminals() const {
+	return(draw_terminals_);
+}
+
+/// @return true if conductors colors are rendered, false otherwise.
+inline bool Diagram::drawColoredConductors() const {
+	return(draw_colored_conductors_);
+}
+
+#endif

Added: trunk/sources/diagramcommands.cpp
===================================================================
--- trunk/sources/diagramcommands.cpp	                        (rev 0)
+++ trunk/sources/diagramcommands.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1064 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramcommands.h"
+#include "qetgraphicsitem/element.h"
+#include "qetgraphicsitem/conductor.h"
+#include "qetgraphicsitem/conductortextitem.h"
+#include "diagram.h"
+#include "qetgraphicsitem/elementtextitem.h"
+#include "qetgraphicsitem/independenttextitem.h"
+#include "qgimanager.h"
+#include "diagram.h"
+#include "qetgraphicsitem/diagramtextitem.h"
+#include "qetgraphicsitem/diagramimageitem.h"
+
+/**
+	Constructeur
+	@param d Schema auquel on ajoute un element
+	@param elmt Element ajoute
+	@param p Position a laquelle l'element est ajoute
+	@param parent QUndoCommand parent
+*/
+AddElementCommand::AddElementCommand(
+	Diagram *d,
+	Element *elmt,
+	const QPointF &p,
+	QUndoCommand *parent
+) :
+	QUndoCommand(QString(QObject::tr("ajouter 1 %1", "undo caption - %1 is an element name")).arg(elmt -> name()), parent),
+	element(elmt),
+	diagram(d),
+	position(p)
+{
+	diagram -> qgiManager().manage(element);
+}
+
+/// Destructeur
+AddElementCommand::~AddElementCommand() {
+	diagram -> qgiManager().release(element);
+}
+
+/// Annule l'ajout
+void AddElementCommand::undo() {
+	diagram -> removeElement(element);
+}
+
+/// Refait l'ajout
+void AddElementCommand::redo() {
+	diagram -> addElement(element);
+	element -> setPos(position);
+	element -> setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
+}
+
+/**
+	Constructeur
+	@param dia Schema auquel on ajoute du texte
+	@param text Texte ajoute
+	@param pos Position a laquelle le texte est ajoute
+	@param parent QUndoCommand parent
+*/
+AddTextCommand::AddTextCommand(Diagram *dia, IndependentTextItem *text, const QPointF &pos, QUndoCommand *parent) :
+	QUndoCommand(QObject::tr("Ajouter un champ de texte", "undo caption"), parent),
+	textitem(text),
+	diagram(dia),
+	position(pos)
+{
+	diagram -> qgiManager().manage(textitem);
+}
+
+/// Destructeur
+AddTextCommand::~AddTextCommand() {
+	diagram -> qgiManager().release(textitem);
+}
+
+/// Annule l'ajout
+void AddTextCommand::undo() {
+	diagram -> removeIndependentTextItem(textitem);
+}
+
+/// Refait l'ajout
+void AddTextCommand::redo() {
+	diagram -> addIndependentTextItem(textitem);
+	textitem -> setPos(position);
+}
+
+/**
+	Constructeur
+	@param dia Schema auquel on ajoute une image
+	@param image Image ajoute
+	@param pos Position a laquelle l'image est ajoute
+	@param parent QUndoCommand parent
+ */
+AddImageCommand::AddImageCommand(Diagram *dia, DiagramImageItem *image, const QPointF &pos, QUndoCommand *parent):
+	QUndoCommand(QObject::tr("Ajouter une image", "undo caption"), parent),
+	imageitem(image),
+	diagram(dia),
+	position(pos)
+{
+	diagram -> qgiManager().manage(imageitem);
+}
+
+///Destructor
+AddImageCommand::~AddImageCommand() {
+	diagram -> qgiManager().release(imageitem);
+}
+
+///Annule l'ajout
+void AddImageCommand::undo() {
+	diagram -> removeItem(imageitem);
+}
+
+///Refait l'ajout
+void AddImageCommand::redo() {
+	diagram -> addDiagramImageItem(imageitem);
+	imageitem -> setPos(position - imageitem -> boundingRect().center());
+}
+
+/**
+	Constructeur
+	@param d Schema auquel on ajoute un conducteur
+	@param c Conducteur ajoute
+	@param parent QUndoCommand parent
+*/
+AddConductorCommand::AddConductorCommand(
+	Diagram *d,
+	Conductor *c,
+	QUndoCommand *parent
+) :
+	QUndoCommand(QObject::tr("ajouter un conducteur", "undo caption"), parent),
+	conductor(c),
+	diagram(d)
+{
+	diagram -> qgiManager().manage(conductor);
+}
+
+/// Destructeur
+AddConductorCommand::~AddConductorCommand() {
+	diagram -> qgiManager().release(conductor);
+}
+
+/// Annule l'ajout
+void AddConductorCommand::undo() {
+	diagram -> removeConductor(conductor);
+}
+
+/// Refait l'ajout
+void AddConductorCommand::redo() {
+	diagram -> addConductor(conductor);
+}
+
+/**
+	Constructeur
+	@param dia Schema dont on supprime des elements et conducteurs
+	@param content Contenu supprime
+	@param parent QUndoCommand parent
+*/
+DeleteElementsCommand::DeleteElementsCommand(
+	Diagram *dia,
+	const DiagramContent &content,
+	QUndoCommand *parent
+) :
+	QUndoCommand(parent),
+	removed_content(content),
+	diagram(dia)
+{
+	setText(
+		QString(
+			QObject::tr(
+				"supprimer %1",
+				"undo caption - %1 is a sentence listing the removed content"
+			)
+		).arg(removed_content.sentence(DiagramContent::All))
+	);
+	diagram -> qgiManager().manage(removed_content.items(DiagramContent::All));
+}
+
+/// Destructeur
+DeleteElementsCommand::~DeleteElementsCommand() {
+	diagram -> qgiManager().release(removed_content.items(DiagramContent::All));
+}
+
+/// annule les suppressions
+void DeleteElementsCommand::undo() {
+	// remet les elements
+	foreach(Element *e, removed_content.elements) {
+		diagram -> addElement(e);
+	}
+	
+	// remet les conducteurs
+	foreach(Conductor *c, removed_content.conductors(DiagramContent::AnyConductor)) {
+		diagram -> addConductor(c);
+	}
+	
+	// remet les textes
+	foreach(IndependentTextItem *t, removed_content.textFields) {
+		diagram -> addIndependentTextItem(t);
+	}
+
+	foreach(DiagramImageItem *dii, removed_content.images) {
+		diagram -> addItem(dii);
+	}
+}
+
+/// refait les suppressions
+void DeleteElementsCommand::redo() {
+	// enleve les conducteurs
+	foreach(Conductor *c, removed_content.conductors(DiagramContent::AnyConductor)) {
+		diagram -> removeConductor(c);
+	}
+	
+	// enleve les elements
+	foreach(Element *e, removed_content.elements) {
+		diagram -> removeElement(e);
+	}
+	
+	// enleve les textes
+	foreach(IndependentTextItem *t, removed_content.textFields) {
+		diagram -> removeIndependentTextItem(t);
+	}
+
+	//enleve les images
+	foreach(DiagramImageItem *dii, removed_content.images) {
+		diagram -> removeItem(dii);
+	}
+}
+
+/**
+	Constructeur
+	@param dia Schema sur lequel on colle les elements et conducteurs
+	@param c Contenu a coller sur le schema
+	@param parent QUndoCommand parent
+*/
+PasteDiagramCommand::PasteDiagramCommand(
+	Diagram *dia,
+	const DiagramContent &c,
+	QUndoCommand *parent
+) :
+	QUndoCommand(parent),
+	content(c),
+	diagram(dia),
+	filter(DiagramContent::Elements|DiagramContent::TextFields|DiagramContent::Images|DiagramContent::ConductorsToMove),
+	first_redo(true)
+{
+	
+	setText(
+		QString(
+			QObject::tr(
+				"coller %1",
+				"undo caption - %1 is a sentence listing the content to paste"
+			).arg(content.sentence(filter))
+		)
+	);
+	diagram -> qgiManager().manage(content.items(filter));
+}
+
+/// Destructeur
+PasteDiagramCommand::~PasteDiagramCommand() {
+	diagram -> qgiManager().release(content.items(filter));
+}
+
+/// annule le coller
+void PasteDiagramCommand::undo() {
+	// remove the conductors
+	foreach(Conductor *c, content.conductorsToMove) diagram -> removeConductor(c);
+	
+	// remove the elements
+	foreach(Element *e, content.elements) diagram -> removeElement(e);
+	
+	// remove the texts
+	foreach(IndependentTextItem *t, content.textFields) diagram -> removeIndependentTextItem(t);
+
+	// remove the images
+	foreach(DiagramImageItem *dii, content.images) diagram -> removeItem(dii);
+}
+
+/// refait le coller
+void PasteDiagramCommand::redo() {
+	if (first_redo) first_redo = false;
+	else {
+		// paste the elements
+		foreach(Element *e, content.elements)  diagram -> addElement(e);
+		
+		// paste the conductors
+		foreach(Conductor *c, content.conductorsToMove) diagram -> addConductor(c);
+		
+		// paste the texts
+		foreach(IndependentTextItem *t, content.textFields) diagram -> addIndependentTextItem(t);
+
+		// paste the images
+		foreach(DiagramImageItem *dii, content.images) diagram -> addDiagramImageItem(dii);
+	}
+	foreach(Element *e, content.elements) e -> setSelected(true);
+	foreach(Conductor *c, content.conductorsToMove) c -> setSelected(true);
+	foreach(IndependentTextItem *t, content.textFields) t -> setSelected(true);
+	foreach(DiagramImageItem *dii, content.images) dii -> setSelected(true);
+}
+
+/**
+	Constructeur
+	@param dia Schema dont on coupe des elements et conducteurs
+	@param content Contenu coupe
+	@param parent QUndoCommand parent
+*/
+CutDiagramCommand::CutDiagramCommand(
+	Diagram *dia,
+	const DiagramContent &content,
+	QUndoCommand *parent
+) : 
+	DeleteElementsCommand(dia, content, parent)
+{
+	setText(
+		QString(
+			QObject::tr(
+				"couper %1",
+				"undo caption - %1 is a sentence listing the content to cut"
+			).arg(content.sentence(DiagramContent::All))
+		)
+	);
+}
+
+/// Destructeur
+CutDiagramCommand::~CutDiagramCommand() {
+}
+
+/**
+	Constructeur
+	@param dia Schema sur lequel on deplace des elements
+	@param diagram_content Contenu a deplacer
+	@param m translation subie par les elements
+	@param parent QUndoCommand parent
+*/
+MoveElementsCommand::MoveElementsCommand(
+	Diagram *dia,
+	const DiagramContent &diagram_content,
+	const QPointF &m,
+	QUndoCommand *parent
+) :
+	QUndoCommand(parent),
+	diagram(dia),
+	content_to_move(diagram_content),
+	movement(m),
+	first_redo(true)
+{
+	QString moved_content_sentence = content_to_move.sentence(
+		DiagramContent::Elements |
+		DiagramContent::TextFields |
+		DiagramContent::ConductorsToUpdate |
+		DiagramContent::ConductorsToMove |
+		DiagramContent::Images
+	);
+	
+	setText(
+		QString(
+			QObject::tr(
+				"d\351placer %1",
+				"undo caption - %1 is a sentence listing the moved content"
+			).arg(moved_content_sentence)
+		)
+	);
+}
+
+/// Destructeur
+MoveElementsCommand::~MoveElementsCommand() {
+}
+
+/// annule le deplacement
+void MoveElementsCommand::undo() {
+	move(-movement);
+}
+
+/// refait le deplacement
+void MoveElementsCommand::redo() {
+	if (first_redo) first_redo = false;
+	else move(movement);
+}
+
+/**
+	deplace les elements et conducteurs
+	@param actual_movement translation a effectuer sur les elements et conducteurs
+*/
+void MoveElementsCommand::move(const QPointF &actual_movement) {
+	// deplace les elements
+	foreach(Element *element, content_to_move.elements) {
+		element -> setPos(element -> pos() + actual_movement);
+	}
+	
+	// deplace certains conducteurs
+	foreach(Conductor *conductor, content_to_move.conductorsToMove) {
+		conductor -> setPos(conductor -> pos() + actual_movement);
+	}
+	
+	// recalcule les autres conducteurs
+	foreach(Conductor *conductor, content_to_move.conductorsToUpdate) {
+		conductor -> updatePath();
+	}
+	
+	// repositionne les textes des conducteurs mis a jour
+	foreach(ConductorTextItem *text_item, moved_conductor_texts_.keys()) {
+		// determine s'il s'agit d'un undo ou d'un redo
+		qreal coef = actual_movement.x() / movement.x();
+		// -1 : undo, 1 : redo
+		QPointF desired_pos = coef > 0 ? moved_conductor_texts_[text_item].second : moved_conductor_texts_[text_item].first;
+		text_item -> setPos(desired_pos);
+	}
+	
+	// deplace les textes
+	foreach(DiagramTextItem *text, content_to_move.textFields) {
+		text -> setPos(text -> pos() + actual_movement);
+	}
+
+	// deplace les images
+	foreach (DiagramImageItem *dii, content_to_move.images) {
+		dii -> setPos(dii -> pos() + actual_movement);
+	}
+}
+
+/**
+	Ajoute un champ de texte de conducteur a deplacer
+	@param text_item Champ de texte a deplacer
+	@param old_pos Position du champ de texte avant le deplacement
+	@param new_pos Position du champ de texte apres le deplacement
+*/
+void MoveElementsCommand::addConductorTextItemMovement(ConductorTextItem *text_item, const QPointF &old_pos, const QPointF &new_pos) {
+	if (moved_conductor_texts_.contains(text_item)) return;
+	if (!text_item -> wasMovedByUser()) return;
+	if (new_pos == old_pos) return;
+	moved_conductor_texts_.insert(
+		text_item,
+		qMakePair(old_pos, new_pos)
+	);
+}
+
+/**
+	Constructeur
+	@param diagram Schema sur lequel on deplace des champs de texte
+	@param texts Liste des textes deplaces
+	@param m translation subie par les elements
+	@param parent QUndoCommand parent
+*/
+MoveElementsTextsCommand::MoveElementsTextsCommand(
+	Diagram *diagram,
+	const QSet<ElementTextItem *> &texts,
+	const QPointF &m,
+	QUndoCommand *parent
+) :
+	QUndoCommand(parent),
+	diagram(diagram),
+	texts_to_move(texts),
+	movement(m),
+	first_redo(true)
+{
+	QString moved_content_sentence = QET::ElementsAndConductorsSentence(0, 0, texts_to_move.count());
+	
+	setText(
+		QString(
+			QObject::tr(
+				"d\351placer %1",
+				"undo caption - %1 is a sentence listing the moved content"
+			).arg(moved_content_sentence)
+		)
+	);
+}
+
+/// Destructeur
+MoveElementsTextsCommand::~MoveElementsTextsCommand() {
+}
+
+/// annule le deplacement
+void MoveElementsTextsCommand::undo() {
+	move(-movement);
+}
+
+/// refait le deplacement
+void MoveElementsTextsCommand::redo() {
+	if (first_redo) first_redo = false;
+	else move(movement);
+}
+
+/**
+	deplace les elements et conducteurs
+	@param actual_movement translation a effectuer sur les elements et conducteurs
+*/
+void MoveElementsTextsCommand::move(const QPointF &actual_movement) {
+	// deplace les textes
+	foreach(ElementTextItem *text, texts_to_move) {
+		QPointF applied_movement = text -> mapMovementToParent(text -> mapMovementFromScene(actual_movement));
+		text -> setPos(text -> pos() + applied_movement);
+	}
+}
+
+/**
+	Constructeur
+	@param diagram Schema sur lequel on deplace des champs de texte
+	@param texts Textes deplaces : chaque ConductorTextItem est associe a un
+	couple de position : avant et apres le deplacement
+	@param m translation subie par les elements
+	@param parent QUndoCommand parent
+*/
+MoveConductorsTextsCommand::MoveConductorsTextsCommand(
+	Diagram *diagram,
+	QUndoCommand *parent
+) :
+	QUndoCommand(parent),
+	diagram(diagram),
+	first_redo(true)
+{
+}
+
+/// Destructeur
+MoveConductorsTextsCommand::~MoveConductorsTextsCommand() {
+}
+
+/// annule le deplacement
+void MoveConductorsTextsCommand::undo() {
+	foreach(ConductorTextItem *cti, texts_to_move_.keys()) {
+		QPointF movement = texts_to_move_[cti].first;
+		bool was_already_moved = texts_to_move_[cti].second;
+		
+		cti -> forceMovedByUser(was_already_moved);
+		if (was_already_moved) {
+			cti -> setPos(cti -> pos() - movement);
+		}
+	}
+}
+
+/// refait le deplacement
+void MoveConductorsTextsCommand::redo() {
+	if (first_redo) {
+		first_redo = false;
+	} else {
+		foreach(ConductorTextItem *cti, texts_to_move_.keys()) {
+			QPointF movement = texts_to_move_[cti].first;
+			
+			cti -> forceMovedByUser(true);
+			cti -> setPos(cti -> pos() + movement);
+		}
+	}
+}
+
+/**
+	Ajout un mouvement de champ de texte a cet objet
+	@param text_item Champ de texte deplace ; si celui-ci est deja connu de l'objet d'annulation, il sera ignore
+	@param old_pos Position du champ de texte avant le mouvement
+	@param new_pos Position du champ de texte apres le mouvement
+	@param alread_moved true si le champ de texte etait deja a une position personnalisee par l'utilisateur, false sinon
+*/
+void MoveConductorsTextsCommand::addTextMovement(ConductorTextItem *text_item, const QPointF &old_pos, const QPointF &new_pos, bool already_moved) {
+	// si le champ de texte est deja connu de l'objet d'annulation, il sera ignore
+	if (texts_to_move_.contains(text_item)) return;
+	
+	// on memorise le champ de texte, en l'associant au mouvement effectue et a son etat avant le deplacement
+	texts_to_move_.insert(text_item, qMakePair(new_pos - old_pos, already_moved));
+	
+	// met a jour la description de l'objet d'annulation
+	regenerateTextLabel();
+}
+
+/**
+	Genere la description de l'objet d'annulation
+*/
+void MoveConductorsTextsCommand::regenerateTextLabel() {
+	QString moved_content_sentence = QET::ElementsAndConductorsSentence(0, 0, texts_to_move_.count());
+	
+	setText(
+		QString(
+			QObject::tr(
+				"d\351placer %1",
+				"undo caption - %1 is a sentence listing the moved content"
+			).arg(moved_content_sentence)
+		)
+	);
+}
+
+/**
+	Constructeur
+	@param dti Champ de texte modifie
+	@param before texte avant
+	@param after texte apres
+	@param parent QUndoCommand parent
+*/
+ChangeDiagramTextCommand::ChangeDiagramTextCommand(
+	DiagramTextItem *dti,
+	const QString &before,
+	const QString &after,
+	QUndoCommand *parent
+) :
+	QUndoCommand(QObject::tr("modifier le texte", "undo caption"), parent),
+	text_item(dti),
+	text_before(before),
+	text_after(after),
+	first_redo(true)
+{
+}
+
+/// destructeur
+ChangeDiagramTextCommand::~ChangeDiagramTextCommand() {
+}
+
+/// annule la modification de texte
+void ChangeDiagramTextCommand::undo() {
+	text_item -> setHtml(text_before);
+}
+
+/// refait la modification de texte
+void ChangeDiagramTextCommand::redo() {
+	if (first_redo) {
+		first_redo = false;
+	} else {
+		text_item -> setHtml(text_after);
+	}
+}
+
+/**
+	Constructeur
+	@param elements Elements a pivoter associes a leur orientation d'origine
+	@param texts Textes a pivoter
+	@param parent QUndoCommand parent
+*/
+RotateElementsCommand::RotateElementsCommand(const QList<Element *> &elements, const QList<DiagramTextItem *> &texts, const QList<DiagramImageItem *> &images, QUndoCommand *parent) :
+	QUndoCommand(parent),
+	elements_to_rotate(elements),
+	texts_to_rotate(texts),
+	images_to_rotate(images),
+	applied_rotation_angle_(90.0)
+{
+	setText(
+		QString(
+			QObject::tr(
+				"pivoter %1",
+				"undo caption - %1 is a sentence listing the rotated content"
+			)
+		).arg(QET::ElementsAndConductorsSentence(elements.count(), 0, texts.count(), images.count()))
+	);
+}
+
+/// Destructeur
+RotateElementsCommand::~RotateElementsCommand() {
+}
+
+/// defait le pivotement
+void RotateElementsCommand::undo() {
+	foreach(Element *e, elements_to_rotate) {
+		e -> rotateBy(-applied_rotation_angle_);
+	}
+	foreach(DiagramTextItem *dti, texts_to_rotate) {
+		//ConductorTextItem have a default rotation angle, we apply a specific treatment
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(dti)) {
+			cti -> forceRotateByUser(previous_rotate_by_user_[cti]);
+			(cti -> wasRotateByUser()) ? cti -> rotateBy(-applied_rotation_angle_) :
+										 cti -> parentConductor() -> adjustTextItemPosition();
+		}
+		else {dti -> rotateBy(-applied_rotation_angle_);}
+	}
+	foreach(DiagramImageItem *dii, images_to_rotate) dii -> rotateBy(-applied_rotation_angle_);
+}
+
+/// refait le pivotement
+void RotateElementsCommand::redo() {
+	foreach(Element *e, elements_to_rotate) {
+		e -> rotateBy(applied_rotation_angle_);
+	}
+	foreach(DiagramTextItem *dti, texts_to_rotate) {
+		//we grab the previous rotation by user of each ConductorTextItem
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(dti)) {
+			previous_rotate_by_user_.insert(cti, cti -> wasRotateByUser());
+			cti -> forceRotateByUser(true);
+		}
+		dti -> rotateBy(applied_rotation_angle_);
+	}
+	foreach(DiagramImageItem *dii, images_to_rotate) dii -> rotateBy(applied_rotation_angle_);
+}
+
+/**
+	Constructeur
+	@param previous_state Hash associant les textes impactes par l'action et leur angle de rotation avant l'action
+	@param applied_rotation Nouvel angle de rotation, a appliquer au textes concernes
+	@param parent QUndoCommand parent
+*/
+RotateTextsCommand::RotateTextsCommand(const QHash<DiagramTextItem *, double> &previous_state, double applied_rotation, QUndoCommand *parent) :
+	QUndoCommand(parent),
+	texts_to_rotate(previous_state),
+	applied_rotation_angle_(applied_rotation)
+{
+	defineCommandName();
+}
+
+/**
+	Constructeur
+	@param texts Liste des textes impactes par l'action. L'objet retiendra leur angle de rotation au moment de sa construction.
+	@param applied_rotation Nouvel angle de rotation, a appliquer au textes concernes
+	@param parent QUndoCommand parent
+*/
+RotateTextsCommand::RotateTextsCommand(const QList<DiagramTextItem *> &texts, double applied_rotation, QUndoCommand *parent) :
+	QUndoCommand(parent),
+	applied_rotation_angle_(applied_rotation)
+{
+	foreach(DiagramTextItem *text, texts) {
+		texts_to_rotate.insert(text, text -> rotationAngle());
+	}
+	defineCommandName();
+}
+
+/**
+	Destructeur
+*/
+RotateTextsCommand::~RotateTextsCommand() {
+}
+
+/**
+	Annule la rotation des textes
+*/
+void RotateTextsCommand::undo() {
+	foreach(DiagramTextItem *text, texts_to_rotate.keys()) {
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(text))
+			cti -> forceRotateByUser(previous_rotate_by_user_[cti]);
+		text -> setRotationAngle(texts_to_rotate[text]);
+	}
+}
+
+/**
+	Applique l'angle de rotation aux textes
+*/
+void RotateTextsCommand::redo() {
+	foreach(DiagramTextItem *text, texts_to_rotate.keys()) {
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(text)) {
+			//we grab the previous rotation by user of each ConductorTextItem
+			previous_rotate_by_user_.insert(cti, cti -> wasRotateByUser());
+			cti -> forceRotateByUser(true);
+		}
+		text -> setRotationAngle(applied_rotation_angle_);
+	}
+}
+
+/**
+	Definit le nom de la commande d'annulation
+*/
+void RotateTextsCommand::defineCommandName() {
+	setText(
+		QString(
+			QObject::tr(
+				"orienter %1 \340 %2\260",
+				"undo caption - %1 looks like '42 texts', %2 is a rotation angle"
+			)
+		).arg(QET::ElementsAndConductorsSentence(0, 0, texts_to_rotate.count()))
+		.arg(applied_rotation_angle_)
+	);
+}
+
+/**
+	Constructeur
+	@param c Conducteur modifie
+	@param old_p ancien profil du conducteur
+	@param new_p nouveau profil du conducteur
+	@param path_t Trajectoire du trajet modifie
+	@param parent QUndoCommand parent
+*/
+ChangeConductorCommand::ChangeConductorCommand(
+	Conductor *c,
+	const ConductorProfile &old_p,
+	const ConductorProfile &new_p,
+	Qt::Corner path_t,
+	QUndoCommand *parent
+) :
+	QUndoCommand(QObject::tr("modifier un conducteur", "undo caption"), parent),
+	conductor(c),
+	old_profile(old_p),
+	new_profile(new_p),
+	path_type(path_t),
+	first_redo(true)
+{
+}
+
+/// Destructeur
+ChangeConductorCommand::~ChangeConductorCommand() {
+}
+
+/// Annule la modification du conducteur
+void ChangeConductorCommand::undo() {
+	conductor -> setProfile(old_profile, path_type);
+	conductor -> textItem() -> setPos(text_pos_before_mov_);
+}
+
+/// Refait la modification du conducteur
+void ChangeConductorCommand::redo() {
+	if (first_redo) {
+		first_redo = false;
+	} else {
+		conductor -> setProfile(new_profile, path_type);
+		conductor -> textItem() -> setPos(text_pos_after_mov_);
+	}
+}
+
+/**
+	Integre dans cet objet d'annulation le repositionnement du champ de texte
+	du conducteur
+	@param pos_before Position du texte avant la modification du conducteur
+	@param pos_after  Position du texte apres la modification du conducteur
+*/
+void ChangeConductorCommand::setConductorTextItemMove(const QPointF &pos_before, const QPointF &pos_after) {
+	text_pos_before_mov_ = pos_before;
+	text_pos_after_mov_  = pos_after;
+}
+
+/**
+	Constructeur
+	@param cp Conducteurs reinitialises, associes a leur ancien profil
+	@param parent QUndoCommand parent
+*/
+ResetConductorCommand::ResetConductorCommand(
+	const QHash<Conductor *, ConductorProfilesGroup> &cp,
+	QUndoCommand *parent
+) :
+	QUndoCommand(parent),
+	conductors_profiles(cp)
+{
+	setText(
+		QObject::tr(
+			"R\351initialiser %1",
+			"undo caption - %1 is a sentence listing the reset content"
+		).arg(QET::ElementsAndConductorsSentence(0, cp.count()))
+	);
+}
+
+/// Destructeur
+ResetConductorCommand::~ResetConductorCommand() {
+}
+
+/// Annule la reinitialisation des conducteurs
+void ResetConductorCommand::undo() {
+	foreach(Conductor *c, conductors_profiles.keys()) {
+		c -> setProfiles(conductors_profiles[c]);
+	}
+}
+
+/// Refait la reinitialisation des conducteurs
+void ResetConductorCommand::redo() {
+	foreach(Conductor *c, conductors_profiles.keys()) {
+		c -> setProfiles(ConductorProfilesGroup());
+	}
+}
+
+/**
+	Constructeur
+	@param d Schema dont on modifie le cartouche
+	@param old_ip Anciennes proprietes du cartouche
+	@param new_ip Nouvelles proprietes du cartouche
+	@param parent QUndoCommand parent
+*/
+ChangeTitleBlockCommand::ChangeTitleBlockCommand(
+	Diagram *d,
+	const TitleBlockProperties &old_ip,
+	const TitleBlockProperties &new_ip,
+	QUndoCommand *parent
+) :
+	QUndoCommand(QObject::tr("modifier le cartouche", "undo caption"), parent),
+	diagram(d),
+	old_titleblock(old_ip),
+	new_titleblock(new_ip)
+{
+}
+
+/// Destructeur
+ChangeTitleBlockCommand::~ChangeTitleBlockCommand() {
+}
+
+/// Annule la modification de cartouche
+void ChangeTitleBlockCommand::undo() {
+	diagram -> border_and_titleblock.importTitleBlock(old_titleblock);
+	diagram -> invalidate(diagram -> border());
+}
+
+/// Refait la modification de cartouche
+void ChangeTitleBlockCommand::redo() {
+	diagram -> border_and_titleblock.importTitleBlock(new_titleblock);
+	diagram -> invalidate(diagram -> border());
+}
+
+/**
+	Constructeur
+	@param dia Schema modifie
+	@param old_bp Anciennes proprietes du cadre du schema
+	@param new_bp Nouvelles proprietes du cadre du schema
+	@param parent QUndoCommand parent
+*/
+ChangeBorderCommand::ChangeBorderCommand(Diagram *dia, const BorderProperties &old_bp, const BorderProperties &new_bp, QUndoCommand *parent) :
+	QUndoCommand(QObject::tr("modifier les dimensions du sch\351ma", "undo caption"), parent),
+	diagram(dia),
+	old_properties(old_bp),
+	new_properties(new_bp)
+{
+}
+
+/// Destructeur
+ChangeBorderCommand::~ChangeBorderCommand() {
+}
+
+/// Annule les changements apportes au schema
+void ChangeBorderCommand::undo() {
+	diagram -> border_and_titleblock.importBorder(old_properties);
+}
+
+/// Refait les changements apportes au schema
+void ChangeBorderCommand::redo() {
+	diagram -> border_and_titleblock.importBorder(new_properties);
+}
+
+/**
+	Constructeur
+	@param c Le conducteur dont on modifie les proprietes
+	@param parent QUndoCommand parent
+*/
+ChangeConductorPropertiesCommand::ChangeConductorPropertiesCommand(Conductor *c, QUndoCommand *parent) :
+	QUndoCommand(QObject::tr("modifier les propri\351t\351s d'un conducteur", "undo caption"), parent),
+	conductor(c),
+	old_settings_set(false),
+	new_settings_set(false)
+{
+}
+
+/// Destructeur
+ChangeConductorPropertiesCommand::~ChangeConductorPropertiesCommand() {
+}
+
+/// definit l'ancienne configuration
+void ChangeConductorPropertiesCommand::setOldSettings(const ConductorProperties &properties) {
+	old_properties = properties;
+	old_settings_set = true;
+}
+
+/// definit la nouvelle configuration
+void ChangeConductorPropertiesCommand::setNewSettings(const ConductorProperties &properties) {
+	new_properties = properties;
+	new_settings_set = true;
+}
+
+/**
+	Annule les changements - Attention : les anciens et nouveaux parametres
+	doivent avoir ete definis a l'aide de setNewSettings et setOldSettings
+*/
+void ChangeConductorPropertiesCommand::undo() {
+	if (old_settings_set && new_settings_set) {
+		conductor -> setProperties(old_properties);
+		conductor -> update();
+	}
+}
+
+/**
+	Refait les changements - Attention : les anciens et nouveaux parametres
+	doivent avoir ete definis a l'aide de setNewSettings et setOldSettings
+*/
+void ChangeConductorPropertiesCommand::redo() {
+	if (old_settings_set && new_settings_set) {
+		conductor -> setProperties(new_properties);
+		conductor -> update();
+	}
+}
+
+/**
+	Constructeur
+	@param c La liste des conducteurs dont on modifie les proprietes
+	@param parent QUndoCommand parent
+*/
+ChangeSeveralConductorsPropertiesCommand::ChangeSeveralConductorsPropertiesCommand(QSet<Conductor *>c, QUndoCommand *parent) :
+	QUndoCommand(QObject::tr("modifier les propri\351t\351s de plusieurs conducteurs", "undo caption"), parent),
+	conductors(c),
+	old_settings_set(false),
+	new_settings_set(false)
+{
+}
+
+/// Destructeur
+ChangeSeveralConductorsPropertiesCommand::~ChangeSeveralConductorsPropertiesCommand() {
+}
+
+/// definit l'ancienne configuration
+void ChangeSeveralConductorsPropertiesCommand::setOldSettings(const QList<ConductorProperties> &properties) {
+	old_properties = properties;
+	old_settings_set = true;
+}
+
+/// definit la nouvelle configuration
+void ChangeSeveralConductorsPropertiesCommand::setNewSettings(const QList<ConductorProperties> &properties) {
+	new_properties = properties;
+	new_settings_set = true;
+}
+
+/**
+	Annule les changements - Attention : les anciens et nouveaux parametres
+	doivent avoir ete definis a l'aide de setNewSettings et setOldSettings
+*/
+void ChangeSeveralConductorsPropertiesCommand::undo() {
+	if (old_settings_set && new_settings_set) {
+		int i=0;
+		foreach(Conductor *c, conductors) {
+			c -> setProperties(old_properties.at(i));
+			c -> update();
+			i++;
+		}
+	}
+}
+
+/**
+	Refait les changements - Attention : les anciens et nouveaux parametres
+	doivent avoir ete definis a l'aide de setNewSettings et setOldSettings
+*/
+void ChangeSeveralConductorsPropertiesCommand::redo() {
+	if (old_settings_set && new_settings_set) {
+		int i=0;
+		foreach(Conductor *c, conductors) {
+			c -> setProperties(new_properties.at(i));
+			c -> update();
+			i++;
+		}
+	}
+}
+
+/**
+ * @brief ImageResizerCommand::ImageResizerCommand Constructor
+ * @param image
+ * @param old_ old size of image
+ * @param new_ new size of image
+ * @param parent undocommand parent
+ */
+ImageResizerCommand::ImageResizerCommand (DiagramImageItem *image, qreal &old_, qreal &new_, QUndoCommand *parent):
+	QUndoCommand(parent),
+	image_(image),
+	old_size (old_),
+	new_size (new_)
+{}
+
+/**
+ * @brief ImageResizerCommand::~ImageResizerCommand destructor
+ */
+ImageResizerCommand::~ImageResizerCommand() {}
+
+/**
+ * @brief ImageResizerCommand::undo set the old size
+ */
+void ImageResizerCommand::undo() {
+	image_ -> setScale(old_size);
+}
+
+/**
+ * @brief ImageResizerCommand::redo set the new size
+ */
+void ImageResizerCommand::redo() {
+	if (old_size<new_size) setText(QObject::tr("Agrandire une image \340 %1 %").arg(new_size*100));
+	else setText(QObject::tr("R\351duire une image \340 %1 %").arg(new_size*100));
+	image_ -> setScale(new_size);
+}

Added: trunk/sources/diagramcommands.h
===================================================================
--- trunk/sources/diagramcommands.h	                        (rev 0)
+++ trunk/sources/diagramcommands.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,579 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAM_COMMANDS_H
+#define DIAGRAM_COMMANDS_H
+#include <QtGui>
+#include "borderproperties.h"
+#include "qetgraphicsitem/conductor.h"
+#include "conductorproperties.h"
+#include "diagramcontent.h"
+#include "titleblockproperties.h"
+#include "qet.h"
+class Diagram;
+class DiagramTextItem;
+class Element;
+class ElementTextItem;
+class IndependentTextItem;
+class DiagramImageItem;
+
+/**
+	This command adds an element to a particular diagram.
+*/
+class AddElementCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	AddElementCommand(Diagram *, Element *, const QPointF &, QUndoCommand * = 0);
+	virtual ~AddElementCommand();
+	private:
+	AddElementCommand(const AddElementCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// added element
+	Element *element;
+	/// diagram the element is added to
+	Diagram *diagram;
+	/// position of the element on the diagram
+	QPointF position;
+};
+
+/**
+	This command adds an independent (i.e. related to neither an element nor a
+	conductor) text item to a particular diagram.
+*/
+class AddTextCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	AddTextCommand(Diagram *, IndependentTextItem *, const QPointF &, QUndoCommand * = 0);
+	virtual ~AddTextCommand();
+	private:
+	AddTextCommand(const AddTextCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// added text item
+	IndependentTextItem *textitem;
+	/// diagram the text item is added to
+	Diagram *diagram;
+	/// position of the text item on the diagram
+	QPointF position;
+};
+
+/**
+  This command adds an image item to a particular diagram
+*/
+class AddImageCommand : public QUndoCommand {
+	//constructors, destructor
+	public:
+	AddImageCommand (Diagram *, DiagramImageItem *, const QPointF &, QUndoCommand * = 0);
+	virtual ~AddImageCommand();
+	private:
+	AddImageCommand(const AddImageCommand &);
+
+	//methods
+	public:
+	virtual void undo();
+	virtual void redo();
+
+	// attributes
+	private:
+	/// added image item
+	DiagramImageItem *imageitem;
+	/// diagram the image item is added to
+	Diagram *diagram;
+	/// position of the image item on the diagram
+	QPointF position;
+
+};
+
+/**
+	This command adds a conductor to a particular diagram.
+*/
+class AddConductorCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	AddConductorCommand(Diagram *, Conductor *, QUndoCommand * = 0);
+	virtual ~AddConductorCommand();
+	private:
+	AddConductorCommand(const AddConductorCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// added conductor
+	Conductor *conductor;
+	/// diagram the conductor is added to
+	Diagram *diagram;
+};
+
+/**
+	This command removes content from a particular diagram.
+*/
+class DeleteElementsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	DeleteElementsCommand(Diagram *, const DiagramContent &, QUndoCommand * = 0);
+	virtual ~DeleteElementsCommand();
+	private:
+	DeleteElementsCommand(const DeleteElementsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// removed content
+	DiagramContent removed_content;
+	/// diagram which the content is removed from
+	Diagram *diagram;
+};
+
+/**
+	This command pastes some content onto a particular diagram.
+*/
+class PasteDiagramCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	PasteDiagramCommand(Diagram *, const DiagramContent &, QUndoCommand * = 0);
+	virtual ~PasteDiagramCommand();
+	private:
+	PasteDiagramCommand(const PasteDiagramCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// pasted content
+	DiagramContent content;
+	/// diagram content is pasted onto
+	Diagram *diagram;
+	/// filter stating what kinds of items should be pasted
+	int filter;
+	/// prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command cuts content from a particular diagram.
+*/
+class CutDiagramCommand : public DeleteElementsCommand {
+	// constructors, destructor
+	public:
+	CutDiagramCommand(Diagram *, const DiagramContent &, QUndoCommand * = 0);
+	virtual ~CutDiagramCommand();
+	private:
+	CutDiagramCommand(const CutDiagramCommand &);
+};
+
+/**
+	This command moves some content on a particular diagram.
+*/
+class MoveElementsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	MoveElementsCommand(Diagram *, const DiagramContent &, const QPointF &m, QUndoCommand * = 0);
+	virtual ~MoveElementsCommand();
+	private:
+	MoveElementsCommand(const MoveElementsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void move(const QPointF &);
+	virtual void addConductorTextItemMovement(ConductorTextItem *, const QPointF &, const QPointF &);
+	
+	// attributes
+	private:
+	/// diagram the movement takes place on.
+	Diagram *diagram;
+	/// moved content
+	DiagramContent content_to_move;
+	/// applied movement
+	QPointF movement;
+	/**
+		Moving elements impacts their conductors: either they are moved, or their path
+		needs to be generated again, which in turn tends to move their child text
+		items. This attribute holds both new and previous positions for each moved
+		text item.
+	*/
+	QHash<ConductorTextItem *, QPair<QPointF, QPointF> > moved_conductor_texts_;
+	/// prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command moves text items related to elements on a particular diagram.
+*/
+class MoveElementsTextsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	MoveElementsTextsCommand(Diagram *, const QSet<ElementTextItem *> &, const QPointF &m, QUndoCommand * = 0);
+	virtual ~MoveElementsTextsCommand();
+	private:
+	MoveElementsTextsCommand(const MoveElementsTextsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void move(const QPointF &);
+	
+	// attributes
+	private:
+	/// diagram the movement takes place on.
+	Diagram *diagram;
+	/// text items to be moved
+	QSet<ElementTextItem *> texts_to_move;
+	/// applied movement
+	QPointF movement;
+	/// prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command moves text items related to conductors on a particular
+	diagram.
+*/
+class MoveConductorsTextsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	MoveConductorsTextsCommand(Diagram *, QUndoCommand * = 0);
+	virtual ~MoveConductorsTextsCommand();
+	private:
+	MoveConductorsTextsCommand(const MoveConductorsTextsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void addTextMovement(ConductorTextItem *, const QPointF &, const QPointF &, bool = false);
+	
+	private:
+	void regenerateTextLabel();
+	
+	// attributes
+	private:
+	/// diagram the movement takes place on.
+	Diagram *diagram;
+	/// text items to be moved
+	QHash<ConductorTextItem *, QPair<QPointF, bool> > texts_to_move_;
+	/// prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This commad modifies a text item.
+*/
+class ChangeDiagramTextCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeDiagramTextCommand(DiagramTextItem *, const QString &before, const QString &after, QUndoCommand * = 0);
+	virtual ~ChangeDiagramTextCommand();
+	private:
+	ChangeDiagramTextCommand(const ChangeDiagramTextCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// modified text item
+	DiagramTextItem *text_item;
+	/// former text
+	QString text_before;
+	/// new text
+	QString text_after;
+	/// prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command rotates several elements or text items by a particular angle.
+*/
+class RotateElementsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	RotateElementsCommand(const QList<Element *> &elements, const QList<DiagramTextItem *> &, const QList<DiagramImageItem *> &, QUndoCommand * = 0);
+	virtual ~RotateElementsCommand();
+	private:
+	RotateElementsCommand(const RotateElementsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// hold rotated elements along with their former orientation
+	QList<Element *> elements_to_rotate;
+	/// text items to be rotated
+	QList<DiagramTextItem *> texts_to_rotate;
+	/// images item to be rotated
+	QList<DiagramImageItem *> images_to_rotate;
+	/// angle of rotation to be applied to text items
+	qreal applied_rotation_angle_;
+	/// previous state of each conductor text item
+	QHash<ConductorTextItem *, bool> previous_rotate_by_user_;
+};
+
+/**
+	This command directs several text items to a same particular angle of
+	rotation.
+*/
+class RotateTextsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	RotateTextsCommand(const QHash<DiagramTextItem *, double> &, double, QUndoCommand * = 0);
+	RotateTextsCommand(const QList<DiagramTextItem *> &,         double, QUndoCommand * = 0);
+	virtual ~RotateTextsCommand();
+	private:
+	RotateTextsCommand(const RotateTextsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	private:
+	void defineCommandName();
+	
+	// attributes
+	private:
+	/// hold rotated text items along with their former angle of rotation
+	QHash<DiagramTextItem *, double> texts_to_rotate;
+	/// angle of rotation of all text items after the command
+	double applied_rotation_angle_;
+	/// previous state of each conductor text item
+	QHash<ConductorTextItem *, bool> previous_rotate_by_user_;
+};
+
+/**
+	This command changes a particular conductor.
+*/
+class ChangeConductorCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeConductorCommand(Conductor *, const ConductorProfile &, const ConductorProfile &, Qt::Corner, QUndoCommand * = 0);
+	virtual ~ChangeConductorCommand();
+	private:
+	ChangeConductorCommand(const ChangeConductorCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void setConductorTextItemMove(const QPointF &, const QPointF &);
+	
+	// attributes
+	private:
+	/// changed conductor
+	Conductor *conductor;
+	/// profile before the change
+	ConductorProfile old_profile;
+	/// profile after the change
+	ConductorProfile new_profile;
+	/// Path type of the modified conductor
+	Qt::Corner path_type;
+	/// position of the text item before the change
+	QPointF text_pos_before_mov_;
+	/// position of the text item after the change
+	QPointF text_pos_after_mov_;
+	/// prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command resets conductor paths.
+*/
+class ResetConductorCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ResetConductorCommand(const QHash<Conductor *, ConductorProfilesGroup> &, QUndoCommand * = 0);
+	virtual ~ResetConductorCommand();
+	private:
+	ResetConductorCommand(const ResetConductorCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// impacted conductors along with their former profiles
+	QHash<Conductor *, ConductorProfilesGroup> conductors_profiles;
+};
+
+/**
+	This command changes the title block properties for a particular diagram.
+*/
+class ChangeTitleBlockCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeTitleBlockCommand(Diagram *, const TitleBlockProperties &, const TitleBlockProperties &, QUndoCommand * = 0);
+	virtual ~ChangeTitleBlockCommand();
+	private:
+	ChangeTitleBlockCommand(const ChangeTitleBlockCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// modified diagram
+	Diagram *diagram;
+	/// properties before the change
+	TitleBlockProperties old_titleblock;
+	/// properties after the change
+	TitleBlockProperties new_titleblock;
+};
+
+/**
+	This command changes the border properties of a particular diagram.
+*/
+class ChangeBorderCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeBorderCommand(Diagram *, const BorderProperties &, const BorderProperties &, QUndoCommand * = 0);
+	virtual ~ChangeBorderCommand();
+	private:
+	ChangeBorderCommand(const ChangeBorderCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// modified diagram
+	Diagram *diagram;
+	public:
+	/// properties before the change
+	BorderProperties old_properties;
+	/// properties after the change
+	BorderProperties new_properties;
+};
+
+/**
+	This command changes the properties for a particular conductor.
+*/
+class ChangeConductorPropertiesCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeConductorPropertiesCommand(Conductor *, QUndoCommand * = 0);
+	virtual ~ChangeConductorPropertiesCommand();
+	private:
+	ChangeConductorPropertiesCommand(const ChangeConductorPropertiesCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void setOldSettings(const ConductorProperties &);
+	virtual void setNewSettings(const ConductorProperties &);
+	
+	// attributes
+	private:
+	/// modified conductor
+	Conductor *conductor;
+	/// properties before the change
+	ConductorProperties old_properties;
+	/// properties after the change
+	ConductorProperties new_properties;
+	/// track whether pre-change properties were set
+	bool old_settings_set;
+	/// track whether post-change properties were set
+	bool new_settings_set;
+};
+
+/**
+	This command changes the properties for several conductors.
+*/
+class ChangeSeveralConductorsPropertiesCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeSeveralConductorsPropertiesCommand(QSet<Conductor *>, QUndoCommand * = 0);
+	virtual ~ChangeSeveralConductorsPropertiesCommand();
+	private:
+	ChangeSeveralConductorsPropertiesCommand(const ChangeSeveralConductorsPropertiesCommand &);
+
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void setOldSettings(const QList<ConductorProperties> &);
+	virtual void setNewSettings(const QList<ConductorProperties> &);
+
+	// attributes
+	private:
+	/// modified conductor
+	QSet<Conductor *> conductors;
+	/// properties before the change
+	QList <ConductorProperties> old_properties;
+	/// properties after the change
+	QList <ConductorProperties> new_properties;
+	/// track whether pre-change properties were set
+	bool old_settings_set;
+	/// track whether post-change properties were set
+	bool new_settings_set;
+};
+
+class ImageResizerCommand : public QUndoCommand {
+	//constructor and destructor
+	public:
+	ImageResizerCommand (DiagramImageItem *image, qreal &old_, qreal &new_, QUndoCommand *parent = 0);
+	virtual ~ImageResizerCommand();
+
+	//methods
+	public:
+	virtual void undo();
+	virtual void redo();
+
+	//attributes
+	private:
+	DiagramImageItem *image_;
+	qreal old_size, new_size;
+};
+
+#endif

Added: trunk/sources/diagramcontent.cpp
===================================================================
--- trunk/sources/diagramcontent.cpp	                        (rev 0)
+++ trunk/sources/diagramcontent.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,161 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramcontent.h"
+#include <QGraphicsItem>
+#include "qetgraphicsitem/element.h"
+#include "qetgraphicsitem/independenttextitem.h"
+#include "qetgraphicsitem/conductor.h"
+#include "qetgraphicsitem/diagramimageitem.h"
+
+/**
+	Constructeur par defaut. Ne contient rien.
+*/
+DiagramContent::DiagramContent() {
+}
+
+/**
+	Constructeur de copie.
+*/
+DiagramContent::DiagramContent(const DiagramContent &other) :
+	elements(other.elements),
+	textFields(other.textFields),
+	images(other.images),
+	conductorsToUpdate(other.conductorsToUpdate),
+	conductorsToMove(other.conductorsToMove),
+	otherConductors(other.otherConductors)
+{
+}
+
+/**
+	Constructeur
+*/
+DiagramContent::~DiagramContent() {
+}
+
+/**
+	@param filter Types de conducteurs desires
+	@return tous les conducteurs
+*/
+QList<Conductor *> DiagramContent::conductors(int filter) const {
+	QSet<Conductor *> result;
+	if (filter & ConductorsToMove)   result += conductorsToMove;
+	if (filter & ConductorsToUpdate) result += conductorsToUpdate;
+	if (filter & OtherConductors)    result += otherConductors;
+	if (filter & SelectedOnly) {
+		foreach(Conductor *conductor, result) {
+			if (!conductor -> isSelected()) result.remove(conductor);
+		}
+	}
+	return(result.toList());
+}
+
+/**
+	Vide le conteneur
+*/
+void DiagramContent::clear() {
+	elements.clear();
+	textFields.clear();
+	images.clear();
+	conductorsToUpdate.clear();
+	conductorsToMove.clear();
+	otherConductors.clear();
+}
+
+/**
+	@param filter Types desires
+	@return la liste des items formant le contenu du schema
+*/
+QList<QGraphicsItem *> DiagramContent::items(int filter) const {
+	QList<QGraphicsItem *> items_list;
+	foreach(QGraphicsItem *qgi, conductors(filter)) items_list << qgi;
+	if (filter & Elements)   foreach(QGraphicsItem *qgi, elements)   items_list << qgi;
+	if (filter & TextFields) foreach(QGraphicsItem *qgi, textFields)  items_list << qgi;
+	if (filter & Images) foreach(QGraphicsItem *qgi, images) items_list << qgi;
+	if (filter & SelectedOnly) {
+		foreach(QGraphicsItem *qgi, items_list) {
+			if (!qgi -> isSelected()) items_list.removeOne(qgi);
+		}
+	}
+	return(items_list);
+}
+
+/**
+	@param filter Types desires
+	@return le nombre d'items formant le contenu du schema
+*/
+int DiagramContent::count(int filter) const {
+	int count = 0;
+	if (filter & SelectedOnly) {
+		if (filter & Elements)           foreach(Element *element,     elements)                  { if (element   -> isSelected()) ++ count; }
+		if (filter & TextFields)         foreach(DiagramTextItem *dti, textFields)                { if (dti       -> isSelected()) ++ count; }
+		if (filter & Images)             foreach(DiagramImageItem *dii, images)                   { if (dii       -> isSelected()) ++ count; }
+		if (filter & ConductorsToMove)   foreach(Conductor *conductor, conductorsToMove)          { if (conductor -> isSelected()) ++ count; }
+		if (filter & ConductorsToUpdate) foreach(Conductor *conductor, conductorsToUpdate)        { if (conductor -> isSelected()) ++ count; }
+		if (filter & OtherConductors)    foreach(Conductor *conductor, otherConductors)           { if (conductor -> isSelected()) ++ count; }
+	}
+	else {
+		if (filter & Elements)           count += elements.count();
+		if (filter & TextFields)         count += textFields.count();
+		if (filter & Images)             count += images.count();
+		if (filter & ConductorsToMove)   count += conductorsToMove.count();
+		if (filter & ConductorsToUpdate) count += conductorsToUpdate.count();
+		if (filter & OtherConductors)    count += otherConductors.count();
+	}
+	return(count);
+}
+
+/**
+	Permet de composer rapidement la proposition "x elements, y conducteurs et
+	z champs de texte".
+	@param filter Types desires
+	@return la proposition decrivant le contenu.
+*/
+QString DiagramContent::sentence(int filter) const {
+	int elements_count   = (filter & Elements) ? elements.count() : 0;
+	int conductors_count = conductors(filter).count();
+	int textfields_count = (filter & TextFields) ? textFields.count() : 0;
+	int images_count	 = (filter & Images) ? images.count() : 0;
+	
+	return(
+		QET::ElementsAndConductorsSentence(
+			elements_count,
+			conductors_count,
+			textfields_count,
+			images_count
+		)
+	);
+}
+
+/**
+	Permet de debugger un contenu de schema
+	@param d Object QDebug a utiliser pour l'affichage des informations de debug
+	@param content Contenu de schema a debugger
+*/
+QDebug &operator<<(QDebug d, DiagramContent &content) {
+	Q_UNUSED(content);
+	d << "DiagramContent {" << "\n";
+	/*
+	FIXME Le double-heritage QObject / QGraphicsItem a casse cet operateur
+	d << "  elements :" << c.elements << "\n";
+	d << "  conductorsToUpdate :" << c.conductorsToUpdate << "\n";
+	d << "  conductorsToMove :" << c.conductorsToMove << "\n";
+	d << "  otherConductors :" << c.otherConductors << "\n";
+	*/
+	d << "}";
+	return(d.space());
+}

Added: trunk/sources/diagramcontent.h
===================================================================
--- trunk/sources/diagramcontent.h	                        (rev 0)
+++ trunk/sources/diagramcontent.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,73 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAM_CONTENT_H
+#define DIAGRAM_CONTENT_H
+#include <QtGui>
+class Conductor;
+class Element;
+class IndependentTextItem;
+class DiagramImageItem;
+/**
+	This class provides a container that makes the transmission of diagram content
+	to other functions/methods easier. The different kind of items are made
+	available through a handful of filter-aware methods. Considering selected
+	elements are to be moved, the filter notably distinguishes conductors to be
+	moved from those to be updated.
+	Please note this container does not systematically contains a whole
+	diagram: it may describe only a part of it, e.g. selected items.
+*/
+class DiagramContent {
+	public:
+	DiagramContent();
+	DiagramContent(const DiagramContent &);
+	~DiagramContent();
+	
+	/// Used to filter the different items carried by this container.
+	enum Filter {
+		Elements = 1,
+		TextFields = 2,
+		Images = 4,
+		ConductorsToMove = 8,
+		ConductorsToUpdate = 16,
+		OtherConductors = 32,
+		AnyConductor = 56,
+		All = 63,
+		SelectedOnly = 64
+	};
+	
+	/// Hold electrical elements
+	QSet<Element *> elements;
+	/// Hold independent text items
+	QSet<IndependentTextItem *> textFields;
+	/// Hold image
+	QSet<DiagramImageItem *> images;
+	/// Hold conductors that would get updated considering electrical elements are moved
+	QSet<Conductor *> conductorsToUpdate;
+	/// Hold conductors that would be moved as is considering electrical elements are moved
+	QSet<Conductor *> conductorsToMove;
+	/// Hold conductors that would be left untouched considering electrical elements are moved
+	QSet<Conductor *> otherConductors;
+	
+	QList<Conductor *> conductors(int = AnyConductor) const;
+	QList<QGraphicsItem *> items(int = All) const;
+	QString sentence(int = All) const;
+	int count(int = All) const;
+	void clear();
+};
+QDebug &operator<<(QDebug, DiagramContent &);
+#endif

Added: trunk/sources/diagramcontext.cpp
===================================================================
--- trunk/sources/diagramcontext.cpp	                        (rev 0)
+++ trunk/sources/diagramcontext.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,169 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramcontext.h"
+#include <QRegExp>
+#include "qet.h"
+
+/**
+	@return a list containing all the keys in the context object.
+*/
+QList<QString> DiagramContext::keys(DiagramContext::KeyOrder order) const {
+	if (order == None) {
+		return content_.keys();
+	} else {
+		QList<QString> keys_list = content_.keys();
+		if (order == Alphabetical) {
+			qSort(keys_list);
+		} else {
+			qSort(keys_list.begin(), keys_list.end(), DiagramContext::stringLongerThan);
+		}
+		return(keys_list);
+	}
+}
+
+/**
+	@param key string key
+	@return true if that key is known to the diagram context, false otherwise
+*/
+bool DiagramContext::contains(const QString &key) const {
+	return(content_.contains(key));
+}
+
+/**
+	@param key
+*/
+const QVariant DiagramContext::operator[](const QString &key) const {
+	return(content_[key]);
+}
+
+/**
+	@param key key to insert in the context - the key may only contain lowercase
+	letters and dashes
+	@see DiagramContext::keyIsAcceptable()
+	@param value value to insert in the context
+	@return true if the insertion succeeds, false otherwise
+*/
+bool DiagramContext::addValue(const QString &key, const QVariant &value) {
+	if (keyIsAcceptable(key)) {
+		content_.insert(key, value);
+		return(true);
+	}
+	return(false);
+}
+
+/**
+	Clear the content of this diagram context.
+*/
+void DiagramContext::clear() {
+	content_.clear();
+}
+
+/**
+	@return the number of key/value pairs stored in this object.
+*/
+int DiagramContext::count() {
+	return(content_.count());
+}
+
+bool DiagramContext::operator==(const DiagramContext &dc) const {
+	return(content_ == dc.content_);
+}
+
+bool DiagramContext::operator!=(const DiagramContext &dc) const {
+	return(!(*this == dc));
+}
+
+/**
+	Export this context properties under the \a e XML element, using tags
+	named \a tag_name (defaults to "property").
+*/
+void DiagramContext::toXml(QDomElement &e, const QString &tag_name) const {
+	foreach (QString key, keys()) {
+		QDomElement property = e.ownerDocument().createElement(tag_name);
+		property.setAttribute("name", key);
+		QDomText value = e.ownerDocument().createTextNode(content_[key].toString());
+		property.appendChild(value);
+		e.appendChild(property);
+	}
+}
+
+/**
+	Read this context properties from the \a e XML element, looking for tags named
+	\a tag_name (defaults to "property").
+*/
+void DiagramContext::fromXml(const QDomElement &e, const QString &tag_name) {
+	foreach (QDomElement property, QET::findInDomElement(e, tag_name)) {
+		if (!property.hasAttribute("name")) continue;
+		addValue(property.attribute("name"), QVariant(property.text()));
+	}
+}
+
+/**
+	Export this context properties to \a settings by creating an array named \a
+	array_name.
+*/
+void DiagramContext::toSettings(QSettings &settings, const QString &array_name) const {
+	settings.beginWriteArray(array_name);
+	int i = 0;
+	foreach (QString key, content_.keys()) {
+		settings.setArrayIndex(i);
+		settings.setValue("name", key);
+		settings.setValue("value", content_[key].toString());
+		++ i;
+	}
+	settings.endArray();
+}
+
+/**
+	Read this context properties from \a settings by running through the array
+	named \a array_name.
+*/
+void DiagramContext::fromSettings(QSettings &settings, const QString &array_name) {
+	int size = settings.beginReadArray(array_name);
+	for (int i = 0 ; i < size; ++ i) {
+		settings.setArrayIndex(i);
+		QString key = settings.value("name").toString();
+		if (key.isEmpty()) continue;
+		addValue(key, settings.value("value").toString());
+	}
+	settings.endArray();
+}
+
+/**
+	@return the regular expression used to check whether a given key is acceptable.
+	@see keyIsAcceptable()
+*/
+QString DiagramContext::validKeyRegExp() {
+	return("^[a-z0-9-]+$");
+}
+
+/**
+	@return True if \a a is longer than \a b, false otherwise.
+*/
+bool DiagramContext::stringLongerThan(const QString &a, const QString &b) {
+	return (a.length() > b.length());
+}
+
+/**
+	@param key a key string
+	@return true if that key is acceptable, false otherwise
+*/
+bool DiagramContext::keyIsAcceptable(const QString &key) const {
+	static QRegExp re(DiagramContext::validKeyRegExp());
+	return(re.exactMatch(key));
+}

Added: trunk/sources/diagramcontext.h
===================================================================
--- trunk/sources/diagramcontext.h	                        (rev 0)
+++ trunk/sources/diagramcontext.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,60 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAM_CONTEXT_H
+#define DIAGRAM_CONTEXT_H
+#include <QDomElement>
+#include <QHash>
+#include <QSettings>
+#include <QString>
+#include <QVariant>
+/**
+	This class represents a diagram context, i.e. the data (a list of key/value
+	pairs) of a diagram at a given time. It is notably used by titleblock templates
+	to fetch the informations they need to do their rendering.
+*/
+class DiagramContext {
+	public:
+	enum KeyOrder {
+		None,
+		Alphabetical,
+		DecreasingLength
+	};
+	QList<QString> keys(KeyOrder = None) const;
+	bool contains(const QString &) const;
+	const QVariant operator[](const QString &) const;
+	bool addValue(const QString &, const QVariant &);
+	void clear();
+	int count();
+	
+	bool operator==(const DiagramContext &) const;
+	bool operator!=(const DiagramContext &) const;
+	
+	void toXml(QDomElement &, const QString & = "property") const;
+	void fromXml(const QDomElement &, const QString & = "property");
+	void toSettings(QSettings &, const QString &) const;
+	void fromSettings(QSettings &, const QString &);
+	
+	static QString validKeyRegExp();
+	
+	private:
+	static bool stringLongerThan(const QString &, const QString &);
+	bool keyIsAcceptable(const QString &) const;
+	/// Diagram context data (key/value pairs)
+	QHash<QString, QVariant> content_;
+};
+#endif

Added: trunk/sources/diagramcontextwidget.cpp
===================================================================
--- trunk/sources/diagramcontextwidget.cpp	                        (rev 0)
+++ trunk/sources/diagramcontextwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,183 @@
+#include "diagramcontextwidget.h"
+#include <QHeaderView>
+#include <QLabel>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+/**
+	Constructor
+	@param parent Parent QWidget
+*/
+DiagramContextWidget::DiagramContextWidget(QWidget *parent) :
+	QWidget(parent)
+{
+	initWidgets();
+	initLayout();
+}
+
+/**
+	Destructor
+*/
+DiagramContextWidget::~DiagramContextWidget() {
+}
+
+/**
+	@return Whether this widget is read-only.
+*/
+bool DiagramContextWidget::isReadOnly() {
+	return(table_ -> isEnabled());
+}
+
+/**
+	@return the DiagramContext object edited by this widget.
+*/
+DiagramContext DiagramContextWidget::context() const {
+	DiagramContext context;
+	for (int i = 0 ; i < table_ -> rowCount() ; ++ i) {
+		QTableWidgetItem *qtwi_name  = table_ -> item(i, 0);
+		QTableWidgetItem *qtwi_value = table_ -> item(i, 1);
+		if (!qtwi_name || !qtwi_value) continue;
+		
+		QString key = qtwi_name -> text();
+		if (key.isEmpty()) continue;
+		
+		QString value = qtwi_value -> text();
+		context.addValue(key, value);
+	}
+	return(context);
+}
+
+/**
+	Load the content from \a context into this widget.
+*/
+void DiagramContextWidget::setContext(const DiagramContext &context) {
+	clear();
+	int i = 0;
+	foreach (QString key, context.keys(DiagramContext::Alphabetical)) {
+		table_ -> setItem(i, 0, new QTableWidgetItem(key));
+		table_ -> setItem(i, 1, new QTableWidgetItem(context[key].toString()));
+		++ i;
+	}
+	checkTableRows();
+}
+
+/**
+	@return The count of name-less rows in the table.
+*/
+int DiagramContextWidget::nameLessRowsCount() const {
+	int name_less_rows_count = 0;
+	for (int i = 0 ; i < table_ -> rowCount() ; ++ i) {
+		QTableWidgetItem *qtwi_name  = table_ -> item(i, 0);
+		if (qtwi_name && qtwi_name -> text().isEmpty()) ++ name_less_rows_count;
+	}
+	return(name_less_rows_count);
+}
+
+/**
+	@param ro Whether this widget should be read-only.
+*/
+void DiagramContextWidget::setReadOnly(bool ro) {
+	table_ -> setEnabled(!ro);
+}
+
+/**
+	Clear any value entered within this widget.
+*/
+void DiagramContextWidget::clear() {
+	table_ -> clearContents();
+	for (int i = 1 ; i < table_ -> rowCount() ; ++ i) {
+		table_ -> removeRow(i);
+	}
+	refreshFormatLabel();
+}
+
+
+/**
+	Highlight keys that would not be accepted by a DiagramContext object.
+	@return the number of highlighted keys.
+*/
+int DiagramContextWidget::highlightNonAcceptableKeys() {
+	static QRegExp re(DiagramContext::validKeyRegExp());
+	
+	QBrush fg_brush = table_ -> palette().brush(QPalette::WindowText);
+	
+	int invalid_keys = 0;
+	for (int i = 0 ; i < table_ -> rowCount() ; ++ i) {
+		QTableWidgetItem *qtwi_name  = table_ -> item(i, 0);
+		if (!qtwi_name) continue;
+		bool highlight = false;
+		if (!qtwi_name -> text().isEmpty()) {
+			if (!re.exactMatch(qtwi_name -> text())) {
+				highlight = true;
+				++ invalid_keys;
+			}
+		}
+		qtwi_name -> setForeground(highlight ? Qt::red : fg_brush);
+	}
+	return(invalid_keys);
+}
+
+/**
+	Sets the text describing the acceptable format for keys when adding extra
+	key/value pairs.
+*/
+void DiagramContextWidget::refreshFormatLabel() {
+	QString format_text = tr(
+		"Les noms ne peuvent contenir que des lettres minuscules, des "
+		"chiffres et des tirets."
+	);
+	
+	if (highlightNonAcceptableKeys()) {
+		format_text = QString("<span style=\"color: red;\">%1</span>").arg(format_text);
+	}
+	format_label -> setText(format_text);
+}
+
+/**
+	Adds a row in the additional fields table if needed.
+*/
+void DiagramContextWidget::checkTableRows() {
+	refreshFormatLabel();
+	if (!nameLessRowsCount()) {
+		int new_idx = table_ -> rowCount();
+		table_ -> setRowCount(new_idx + 1);
+		table_ -> setItem(new_idx, 0, new QTableWidgetItem(""));
+		table_ -> setItem(new_idx, 1, new QTableWidgetItem(""));
+	}
+}
+
+/**
+	Initialize child widgets.
+*/
+void DiagramContextWidget::initWidgets() {
+	format_label = new QLabel();
+	format_label -> setWordWrap(true);
+	format_label -> setAlignment(Qt::AlignJustify);
+	
+	table_ = new QTableWidget(0, 2);
+	table_ -> setSelectionMode(QAbstractItemView::SingleSelection);
+	table_ -> setHorizontalHeaderLabels(QStringList() << tr("Nom", "table header") << tr("Valeur", "table header"));
+	table_ -> horizontalHeader() -> setStretchLastSection(true);
+	
+	connect(table_, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(checkTableRows()));
+}
+
+/**
+	Initialize the layout of this widget.
+*/
+void DiagramContextWidget::initLayout() {
+	QVBoxLayout *vlayout0 = new QVBoxLayout();
+	vlayout0 -> setContentsMargins(0, 0, 0, 0);
+	vlayout0 -> addWidget(format_label);
+	vlayout0 -> addWidget(table_);
+	setLayout(vlayout0);
+}
+
+/**
+	Set Sorting on the table.
+*/
+void DiagramContextWidget::setSorting(bool enable, int column, Qt::SortOrder order ) {
+	table_ -> setSortingEnabled(enable);
+	table_ -> sortByColumn(column, order);
+}
+

Added: trunk/sources/diagramcontextwidget.h
===================================================================
--- trunk/sources/diagramcontextwidget.h	                        (rev 0)
+++ trunk/sources/diagramcontextwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,49 @@
+#ifndef DIAGRAMCONTEXTWIDGET_H
+#define DIAGRAMCONTEXTWIDGET_H
+#include <QWidget>
+#include "diagramcontext.h"
+class QLabel;
+class QTableWidget;
+/**
+	This class provides a table which enables end users to edit the key/value
+	pairs of a DiagamContext.
+*/
+class DiagramContextWidget : public QWidget {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	DiagramContextWidget(QWidget *parent = 0);
+	virtual ~DiagramContextWidget();
+	private:
+	DiagramContextWidget(const DiagramContextWidget &);
+	
+	// methods
+	public:
+	bool isReadOnly();
+	DiagramContext context() const;
+	void setContext(const DiagramContext &);
+	int nameLessRowsCount() const;
+	void setSorting(bool enable, int column, Qt::SortOrder order );
+	
+	signals:
+	
+	public slots:
+	void setReadOnly(bool);
+	void clear();
+	int highlightNonAcceptableKeys();
+	void refreshFormatLabel();
+	
+	private slots:
+	void checkTableRows();
+	
+	private:
+	void initWidgets();
+	void initLayout();
+	
+	// attributes
+	private:
+	QLabel *format_label; ///< label used to detail keys format
+	QTableWidget *table_; ///< table used to enter key/value pairs
+};
+
+#endif

Added: trunk/sources/diagramposition.cpp
===================================================================
--- trunk/sources/diagramposition.cpp	                        (rev 0)
+++ trunk/sources/diagramposition.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,70 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramposition.h"
+
+/**
+	Constructeur
+	@param letter Lettre(s) composant la position
+	@param number Numero composant la position
+	Si une chaine entierement invalide ou vide, ou bien un 0 est passe en
+	parametre, il en resulte un objet DiagramPosition invalide, dont la methode
+	isOutOfBounds renverra true.
+*/
+DiagramPosition::DiagramPosition(const QString &letter, unsigned int number) {
+	// purifie les lettres
+	letter_ = letter.toUpper();
+	letter_.remove(QRegExp("[^A-Z]"));
+	number_ = number;
+}
+
+/**
+	Destructeur
+*/
+DiagramPosition::~DiagramPosition() {
+}
+
+/**
+	@return les coordonnees stockees dans cet objet, ou un QPointF nul sinon.
+*/
+QPointF DiagramPosition::position() const {
+	return(position_);
+}
+
+/**
+	@param position Position a stocker dans cet objet
+*/
+void DiagramPosition::setPosition(const QPointF &position) {
+	position_ = position;
+}
+
+/**
+	@return une representation textuelle de la position
+*/
+QString DiagramPosition::toString() {
+	if (isOutOfBounds()) {
+		return("-");
+	}
+	return(QString("%1%2").arg(letter_).arg(number_));
+}
+
+/**
+	@return true si l'element est en dehors des bords du schema
+*/
+bool DiagramPosition::isOutOfBounds() const {
+	return(letter_.isEmpty() || !number_);
+}

Added: trunk/sources/diagramposition.h
===================================================================
--- trunk/sources/diagramposition.h	                        (rev 0)
+++ trunk/sources/diagramposition.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,48 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAM_POSITION_H
+#define DIAGRAM_POSITION_H
+#include <QPointF>
+#include <QString>
+#include <QRegExp>
+/**
+	This class stores the position of an electrical element on its parent diagram.
+	While exact coordinates can be stored for convenience, the concept of diagram
+	position as implemented by this class simply designates the intersection
+	between a column and a row, e.g. B2 or C4.
+*/
+class DiagramPosition {
+	// constructors, destructor
+	public:
+	DiagramPosition(const QString & = "", unsigned int = 0);
+	virtual ~DiagramPosition();
+	
+	// methods
+	public:
+	QPointF position() const;
+	void setPosition(const QPointF &);
+	QString toString();
+	bool isOutOfBounds() const;
+	
+	// attributes
+	private:
+	QString letter_;
+	unsigned int number_;
+	QPointF position_;
+};
+#endif

Added: trunk/sources/diagramprintdialog.cpp
===================================================================
--- trunk/sources/diagramprintdialog.cpp	                        (rev 0)
+++ trunk/sources/diagramprintdialog.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,613 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramprintdialog.h"
+#include "qetprintpreviewdialog.h"
+#include <math.h>
+#include "diagramschooser.h"
+#include "exportproperties.h"
+#include "qetapp.h"
+#include "qeticons.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur
+	@param project Schema a imprimer
+	@param parent  Widget parent du dialogue
+*/
+DiagramPrintDialog::DiagramPrintDialog(QETProject *project, QWidget *parent) :
+	QWidget(parent),
+	project_(project),
+	dialog_(0)
+{
+	// initialise l'imprimante
+	printer_ = new QPrinter();
+	
+	// orientation paysage par defaut
+	printer_ -> setOrientation(QPrinter::Landscape);
+}
+
+/**
+	Destructeur
+*/
+DiagramPrintDialog::~DiagramPrintDialog() {
+	delete dialog_;
+	delete printer_;
+}
+
+/**
+	Definit le nom du PDF si l'utilisateur choisit une sortie vers un PDF
+*/
+void DiagramPrintDialog::setFileName(const QString &name) {
+	file_name_ = name;
+}
+
+/**
+	@return le nom du PDF
+*/
+QString DiagramPrintDialog::fileName() const {
+	return(file_name_);
+}
+
+/**
+	Definit le nom du document
+*/
+void DiagramPrintDialog::setDocName(const QString &name) {
+	doc_name_ = name;
+}
+
+/**
+	@return le nom du document
+*/
+QString DiagramPrintDialog::docName() const {
+	return(doc_name_);
+}
+
+/**
+	@param diagram Diagram to be printed
+	@param include_titleblock (Optional, defaults to true) Whether the diagram
+	titleblock should be printed.
+	@return the rectangle to be printed
+*/
+QRect DiagramPrintDialog::diagramRect(Diagram *diagram, const ExportProperties &options) const {
+	if (!diagram) return(QRect());
+	
+	QRectF diagram_rect = diagram -> border();
+	if (!options.draw_titleblock) {
+		qreal titleblock_height = diagram -> border_and_titleblock.titleBlockHeight();
+		diagram_rect.setHeight(diagram_rect.height() - titleblock_height);
+	}
+	
+	// ajuste la bordure du schema d'un pixel (epaisseur du trait)
+	diagram_rect = diagram_rect.adjusted(0.0, 0.0, 1.0, 1.0);
+	
+	return(diagram_rect.toAlignedRect());
+}
+
+/**
+	Execute le dialogue d'impression
+*/
+void DiagramPrintDialog::exec() {
+	
+	// prise en compte du nom du document
+	if (!doc_name_.isEmpty()) printer_ -> setDocName(doc_name_);
+	printer_ -> setCreator(QString("QElectroTech %1").arg(QET::displayedVersion));
+	
+	// affichage d'un premier dialogue demandant a l'utilisateur le type
+	// d'impression qu'il souhaite effectuer
+	buildPrintTypeDialog();
+	if (dialog_ -> exec() == QDialog::Rejected) return;
+	
+	// parametrage de l'imprimante en fonction du type d'impression choisi
+	if (printer_choice_ -> isChecked()) {
+		// affichage du dialogue d'impression standard pour parametrer l'imprimante
+		QPrintDialog print_dialog(printer_, parentWidget());
+#ifdef Q_WS_MAC
+		print_dialog.setWindowFlags(Qt::Sheet);
+#endif
+		print_dialog.setWindowTitle(tr("Options d'impression", "window title"));
+		print_dialog.setEnabledOptions(QAbstractPrintDialog::PrintShowPageSize);
+		if (print_dialog.exec() == QDialog::Rejected) return;
+	} else if (pdf_choice_ -> isChecked()) {
+		printer_ -> setOutputFormat(QPrinter::PdfFormat);
+		printer_ -> setOutputFileName(filepath_field_ -> text());
+	} else {
+		printer_ -> setOutputFormat(QPrinter::PostScriptFormat);
+		printer_ -> setOutputFileName(filepath_field_ -> text());
+	}
+	loadPageSetupForCurrentPrinter();
+	
+	// Apercu avant impression
+#if defined Q_WS_X11
+	// suite a quelques soucis avec xfwm, on se contente d'une fenetre sous X11
+	QETPrintPreviewDialog preview_dialog(project_, printer_, parentWidget(), Qt::Window);
+#else
+	QETPrintPreviewDialog preview_dialog(project_, printer_, parentWidget());
+#endif
+	connect(
+		&preview_dialog,
+		SIGNAL(paintRequested(const QList<Diagram *> &, bool, const ExportProperties, QPrinter *)),
+		this,
+		SLOT(print(const QList<Diagram *> &, bool, const ExportProperties))
+	);
+	DiagramsChooser *dc = preview_dialog.diagramsChooser();
+	dc -> setSelectedAllDiagrams();
+	if (preview_dialog.exec() == QDialog::Rejected) return;
+	
+	savePageSetupForCurrentPrinter();
+	
+	// effectue l'impression en elle-meme
+	print(
+		dc -> selectedDiagrams(),
+		preview_dialog.fitDiagramsToPages(),
+		preview_dialog.exportProperties()
+	);
+}
+
+/**
+	@param diagram Schema a imprimer
+	@param options Rendering options
+	@param fullpage true pour utiliser toute la feuille dans le calcul
+	@return Le nombre de pages necessaires pour imprimer le schema
+	avec l'orientation et le format papier utilise dans l'imprimante en cours.
+*/
+int DiagramPrintDialog::pagesCount(Diagram *diagram, const ExportProperties &options, bool fullpage) const {
+	return(horizontalPagesCount(diagram, options, fullpage) * verticalPagesCount(diagram, options, fullpage));
+}
+
+/**
+	@param diagram Schema a imprimer
+	@param options Rendering options
+	@param fullpage true pour utiliser toute la feuille dans le calcul
+	@return La largeur du "poster" en nombre de pages pour imprimer le schema
+	avec l'orientation et le format papier utilise dans l'imprimante en cours.
+*/
+int DiagramPrintDialog::horizontalPagesCount(Diagram *diagram, const ExportProperties &options, bool fullpage) const {
+	// note : pageRect et Paper Rect tiennent compte de l'orientation du papier
+	QRect printable_area = fullpage ? printer_ -> paperRect() : printer_ -> pageRect();
+	QRect diagram_rect = diagramRect(diagram, options);
+	
+	int h_pages_count = int(ceil(qreal(diagram_rect.width()) / qreal(printable_area.width())));
+	return(h_pages_count);
+}
+
+/**
+	@param diagram Schema a imprimer
+	@param options Rendering options
+	@param fullpage true pour utiliser toute la feuille dans le calcul
+	@return La largeur du "poster" en nombre de pages pour imprimer le schema
+	avec l'orientation et le format papier utilise dans l'imprimante en cours.
+*/
+int DiagramPrintDialog::verticalPagesCount(Diagram *diagram, const ExportProperties &options, bool fullpage) const {
+	// note : pageRect et Paper Rect tiennent compte de l'orientation du papier
+	QRect printable_area = fullpage ? printer_ -> paperRect() : printer_ -> pageRect();
+	QRect diagram_rect = diagramRect(diagram, options);
+	
+	int v_pages_count = int(ceil(qreal(diagram_rect.height()) / qreal(printable_area.height())));
+	return(v_pages_count);
+}
+
+/**
+	Construit un dialogue non standard pour demander a l'utilisateur quelle type
+	d'impression il souhaite effectuer : PDF, PS ou imprimante physique
+*/
+void DiagramPrintDialog::buildPrintTypeDialog() {
+	// initialisation des widgets
+	dialog_           = new QDialog(parentWidget());
+#ifdef Q_WS_MAC
+	dialog_ -> setWindowFlags(Qt::Sheet);
+#endif
+	
+	printtype_label_  = new QLabel(tr("Quel type d'impression d\351sirez-vous effectuer ?"));
+	printer_icon_     = new QLabel();
+	pdf_icon_         = new QLabel();
+	ps_icon_          = new QLabel();
+	printtype_choice_ = new QButtonGroup();
+	printer_choice_   = new QRadioButton(tr("Impression sur une imprimante physique",               "Print type choice"));
+	pdf_choice_       = new QRadioButton(tr("Impression vers un fichier au format PDF",             "Print type choice"));
+	ps_choice_        = new QRadioButton(tr("Impression vers un fichier au format PostScript (PS)", "Print type choice"));
+	filepath_field_   = new QLineEdit();
+	browse_button_    = new QPushButton("...");
+	buttons_          = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	
+	dialog_ -> setWindowTitle(tr("Choix du type d'impression"));
+	printer_icon_ -> setPixmap(QET::Icons::Printer.pixmap(32, 32));
+	pdf_icon_     -> setPixmap(QET::Icons::PDF.pixmap(32, 32));
+	ps_icon_      -> setPixmap(QET::Icons::EPS.pixmap(32, 32));
+	printtype_choice_ -> addButton(printer_choice_);
+	printtype_choice_ -> addButton(pdf_choice_);
+	printtype_choice_ -> addButton(ps_choice_);
+	printer_choice_ -> setChecked(true);
+	if (!file_name_.isEmpty()) filepath_field_ -> setText(file_name_ + ".pdf");
+	
+	// connexions signaux / slots
+	connect(printer_choice_, SIGNAL(toggled(bool)), this,    SLOT(updatePrintTypeDialog()));
+	connect(pdf_choice_,     SIGNAL(toggled(bool)), this,    SLOT(updatePrintTypeDialog()));
+	connect(ps_choice_,      SIGNAL(toggled(bool)), this,    SLOT(updatePrintTypeDialog()));
+	connect(browse_button_,  SIGNAL(clicked(bool)), this,    SLOT(browseFilePrintTypeDialog()));
+	connect(buttons_,        SIGNAL(accepted()),    this,    SLOT(acceptPrintTypeDialog()));
+	connect(buttons_,        SIGNAL(rejected()),    dialog_, SLOT(reject()));
+	
+	// organisation graphique
+	glayout0_ = new QGridLayout();
+	hlayout0_ = new QHBoxLayout();
+	vlayout0_ = new QVBoxLayout();
+	
+	hlayout0_ -> addWidget(filepath_field_);
+	hlayout0_ -> addWidget(browse_button_);
+	glayout0_ -> addWidget(printer_icon_,   0, 0);
+	glayout0_ -> addWidget(printer_choice_, 0, 1);
+	glayout0_ -> addWidget(pdf_icon_,       1, 0);
+	glayout0_ -> addWidget(pdf_choice_,     1, 1);
+	glayout0_ -> addWidget(ps_icon_,        2, 0);
+	glayout0_ -> addWidget(ps_choice_,      2, 1);
+	glayout0_ -> addLayout(hlayout0_,       3, 1);
+	
+	vlayout0_ -> addWidget(printtype_label_);
+	vlayout0_ -> addLayout(glayout0_);
+	vlayout0_ -> addWidget(buttons_);
+	
+	dialog_ -> setLayout(vlayout0_);
+	
+	updatePrintTypeDialog();
+}
+
+/**
+	Assure la coherence du dialogue permettant le choix du type d'impression
+*/
+void DiagramPrintDialog::updatePrintTypeDialog() {
+	// imprime-t-on vers un fichier ?
+	bool file_print = !(printer_choice_ -> isChecked());
+	
+	// on n'active le champ fichier que pour les impressions vers un fichier
+	filepath_field_ -> setEnabled(file_print);
+	browse_button_  -> setEnabled(file_print);
+	
+	// on corrige eventuellement l'extension du fichier deja selectionne
+	if (file_print) {
+		QString filepath = filepath_field_ -> text();
+		if (!filepath.isEmpty()) {
+			if (pdf_choice_ -> isChecked() && filepath.endsWith(".ps")) {
+				QRegExp re("\\.ps$", Qt::CaseInsensitive);
+				filepath.replace(re, ".pdf");
+				filepath_field_ -> setText(filepath);
+			} else if (ps_choice_ -> isChecked() && filepath.endsWith(".pdf")) {
+				QRegExp re("\\.pdf$", Qt::CaseInsensitive);
+				filepath.replace(re, ".ps");
+				filepath_field_ -> setText(filepath);
+			}
+		}
+	}
+}
+
+/**
+	Verifie l'etat du dialogue permettant le choix du type d'impression lorsque
+	l'utilisateur le valide.
+*/
+void DiagramPrintDialog::acceptPrintTypeDialog() {
+	bool file_print = !(printer_choice_ -> isChecked());
+	if (file_print) {
+		// un fichier doit avoir ete entre
+		if (filepath_field_ -> text().isEmpty()) {
+			QET::MessageBox::information(
+				parentWidget(),
+				tr("Fichier manquant", "message box title"),
+				tr("Vous devez indiquer le chemin du fichier PDF/PS \340 cr\351er.", "message box content")
+			);
+		} else dialog_ -> accept();
+	} else {
+		// une imprimante doit avoir ete selectionnee
+		/// @todo
+		dialog_ -> accept();
+	}
+}
+
+/**
+	Permet a l'utilisateur de choisir un fichier 
+*/
+void DiagramPrintDialog::browseFilePrintTypeDialog() {
+	QString extension;
+	QString filter;
+	if (printer_choice_ -> isChecked()) return;
+	else if (pdf_choice_ -> isChecked()) {
+		extension = ".pdf";
+		filter    = tr("Fichiers PDF (*.pdf)",       "file filter");
+	}
+	else if (ps_choice_ -> isChecked()) {
+		extension = ".ps";
+		filter    = tr("Fichiers PostScript (*.ps)", "file filter");
+	}
+	
+	QString filepath = QFileDialog::getSaveFileName(
+		parentWidget(),
+		QString(),
+		filepath_field_ -> text(),
+		filter
+	);
+	
+	if (!filepath.isEmpty()) {
+		if (!filepath.endsWith(extension)) filepath += extension;
+		filepath = QDir::toNativeSeparators(QDir::cleanPath(filepath));
+		filepath_field_ -> setText(filepath);
+	}
+}
+
+/**
+	Effectue l'impression elle-meme
+	@param diagrams Schemas a imprimer
+	@param fit_page Booleen indiquant s'il faut adapter les schemas aux pages
+	ou non
+	@param options Options de rendu
+*/
+void DiagramPrintDialog::print(const QList<Diagram *> &diagrams, bool fit_page, const ExportProperties options) {
+	//qDebug() << "Demande d'impression de " << diagrams.count() << "schemas.";
+	
+	// QPainter utiliser pour effectuer le rendu
+	QPainter qp(printer_);
+	
+	// cas special : il n'y a aucun schema a imprimer
+	if (!diagrams.count()) {
+		qp.end();
+		return;
+	}
+	
+	// imprime les schemas
+	for (int i = 0 ; i < diagrams.count() ; ++ i) {
+		printDiagram(diagrams[i], fit_page, options, &qp, printer_);
+		if (i != diagrams.count() - 1) {
+			printer_ -> newPage();
+		}
+	}
+}
+
+/**
+	Imprime un schema
+	@param diagram Schema a imprimer
+	@param fit_page True pour adapter les schemas aux pages, false sinon
+	@param options Options de rendu a appliquer pour l'impression
+	@param qp QPainter a utiliser (deja initialise sur printer)
+	@param printer Imprimante a utiliser
+*/
+void DiagramPrintDialog::printDiagram(Diagram *diagram, bool fit_page, const ExportProperties &options, QPainter *qp, QPrinter *printer) {
+	//qDebug() << printer -> paperSize() << printer -> paperRect() << diagram -> title();
+	// l'imprimante utilise-t-elle toute la feuille ?
+	bool full_page = printer -> fullPage();
+	
+	// impression physique (!= fichier PDF)
+	if (printer -> outputFileName().isEmpty()) {
+		// utiliser cette condition pour agir differemment en cas d'impression physique
+	}
+	
+	saveReloadDiagramParameters(diagram, options, true);
+	
+	// deselectionne tous les elements
+	QList<QGraphicsItem *> selected_elmts = diagram -> selectedItems();
+	foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(false);
+	
+	// enleve le flag focusable de tous les elements concernes pour eviter toute reprise de focus par un champ de texte editable
+	QList<QGraphicsItem *> focusable_items;
+	foreach (QGraphicsItem *qgi, diagram -> items()) {
+		if (qgi -> flags() & QGraphicsItem::ItemIsFocusable) {
+			focusable_items << qgi;
+			qgi -> setFlag(QGraphicsItem::ItemIsFocusable, false);
+		}
+	}
+	
+	// evite toute autre forme d'interaction
+	foreach (QGraphicsView *view, diagram -> views()) {
+		view -> setInteractive(false);
+	}
+	
+	QRect diagram_rect = diagramRect(diagram, options);
+	if (fit_page) {
+		// impression adaptee sur une seule page
+		diagram -> render(qp, QRectF(), diagram_rect, Qt::KeepAspectRatio);
+	} else {
+		// impression sur une ou plusieurs pages
+		QRect printed_area = full_page ? printer -> paperRect() : printer -> pageRect();
+		//qDebug() << "impression sur une ou plusieurs pages";
+		//qDebug() << "  schema :" << diagram_rect;
+		//qDebug() << "  page   :" << printed_area;
+		
+		int used_width  = printed_area.width();
+		int used_height = printed_area.height();
+		int h_pages_count = horizontalPagesCount(diagram, options, full_page);
+		int v_pages_count = verticalPagesCount(diagram, options, full_page);
+		
+		QVector< QVector< QRect > > pages_grid;
+		// le schema est imprime sur une matrice de feuilles
+		// parcourt les lignes de la matrice
+		int y_offset = 0;
+		for (int i = 0 ; i < v_pages_count ; ++ i) {
+			pages_grid << QVector< QRect >();
+			
+			// parcourt les feuilles de la ligne
+			int x_offset = 0;
+			for (int j = 0 ; j < h_pages_count ; ++ j) {
+				pages_grid.last() << QRect(
+					QPoint(x_offset, y_offset),
+					QSize(
+						qMin(used_width,  diagram_rect.width()  - x_offset),
+						qMin(used_height, diagram_rect.height() - y_offset)
+					)
+				);
+				x_offset += used_width;
+			}
+			
+			y_offset += used_height;
+		}
+		
+		// ne retient que les pages a imprimer
+		QVector<QRect> pages_to_print;
+		for (int i = 0 ; i < v_pages_count ; ++ i) {
+			for (int j = 0 ; j < h_pages_count ; ++ j) {
+				pages_to_print << pages_grid.at(i).at(j);
+			}
+		}
+		//qDebug() << "  " << pages_to_print.count() << " pages a imprimer :";
+		
+		// parcourt les pages pour impression
+		for (int i = 0 ; i < pages_to_print.count() ; ++ i) {
+			QRect current_rect(pages_to_print.at(i));
+			//qDebug() << "    " << current_rect;
+			diagram -> render(
+				qp,
+				QRect(QPoint(0,0), current_rect.size()),
+				current_rect.translated(diagram_rect.topLeft()),
+				Qt::KeepAspectRatio
+			);
+			if (i != pages_to_print.count() - 1) {
+				printer -> newPage();
+			}
+		}
+	}
+	
+	// remet en place les interactions
+	foreach (QGraphicsView *view, diagram -> views()) {
+		view -> setInteractive(true);
+	}
+	
+	// restaure les flags focusable
+	foreach (QGraphicsItem *qgi, focusable_items) {
+		qgi -> setFlag(QGraphicsItem::ItemIsFocusable, true);
+	}
+	
+	// restaure les elements selectionnes
+	foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(true);
+	
+	saveReloadDiagramParameters(diagram, options, false);
+}
+
+/**
+	Sauve ou restaure les parametres du schema
+	@param diagram Schema dont on sauve ou restaure les parametres
+	@param options Parametres a appliquer
+	@param save true pour memoriser les parametres du schema et appliquer ceux
+	definis dans options, false pour restaurer les parametres
+*/
+void DiagramPrintDialog::saveReloadDiagramParameters(Diagram *diagram, const ExportProperties options, bool save) {
+	static ExportProperties state_exportProperties;
+	
+	if (save) {
+		// memorise les parametres relatifs au schema tout en appliquant les nouveaux
+		state_exportProperties = diagram -> applyProperties(options);
+	} else {
+		// restaure les parametres relatifs au schema
+		diagram -> applyProperties(state_exportProperties);
+	}
+}
+
+/**
+	Save parameters set in the "page setup" dialog into the QElectroTech
+	configuration. Key/values pairs are associated to the printer for which
+	they have been set.
+*/
+void DiagramPrintDialog::savePageSetupForCurrentPrinter() {
+	QSettings &settings = QETApp::settings();
+	QString printer_section = settingsSectionName(printer_);
+	
+	while (!settings.group().isEmpty()) settings.endGroup();
+	settings.beginGroup("printers");
+	settings.beginGroup(printer_section);
+	
+	settings.setValue("orientation", printer_ -> orientation() == QPrinter::Portrait ? "portrait" : "landscape");
+	settings.setValue("papersize", int(printer_ -> paperSize()));
+	if (printer_ -> paperSize() == QPrinter::Custom) {
+		QSizeF size = printer_ -> paperSize(QPrinter::Millimeter);
+		settings.setValue("customwidthmm", size.width());
+		settings.setValue("customheightmm", size.height());
+	} else {
+		settings.remove("customwidthmm");
+		settings.remove("customheightmm");
+	}
+	qreal left, top, right, bottom;
+	printer_ -> getPageMargins(&left, &top, &right, &bottom, QPrinter::Millimeter);
+	settings.setValue("marginleft", left);
+	settings.setValue("margintop", top);
+	settings.setValue("marginright", right);
+	settings.setValue("marginbottom", bottom);
+	settings.setValue("fullpage", printer_ -> fullPage() ? "true" : "false");
+	settings.endGroup();
+	settings.endGroup();
+	settings.sync();
+}
+
+
+/**
+	Load parameters previously set in the "page setup" dialog for the current
+	printer, if any.
+*/
+void DiagramPrintDialog::loadPageSetupForCurrentPrinter() {
+	QSettings &settings = QETApp::settings();
+	QString printer_section = settingsSectionName(printer_);
+	
+	while (!settings.group().isEmpty()) settings.endGroup();
+	settings.beginGroup("printers");
+	if (!settings.childGroups().contains(printer_section)) {
+		settings.endGroup();
+		return;
+	}
+	
+	settings.beginGroup(printer_section);
+	if (settings.contains("orientation")) {
+		QString value = settings.value("orientation", "landscape").toString();
+		printer_ -> setOrientation(value == "landscape" ? QPrinter::Landscape : QPrinter::Portrait);
+	}
+	if (settings.contains("papersize")) {
+		int value = settings.value("papersize", QPrinter::A4).toInt();
+		if (value == QPrinter::Custom) {
+			bool w_ok, h_ok;
+			int w = settings.value("customwidthmm", -1).toInt(&w_ok);
+			int h = settings.value("customheightmm", -1).toInt(&h_ok);
+			if (w_ok && h_ok && w != -1 && h != -1) {
+				printer_ -> setPaperSize(QSizeF(w, h), QPrinter::Millimeter);
+			}
+		} else if (value < QPrinter::Custom) {
+			printer_ -> setPaperSize(static_cast<QPrinter::PaperSize>(value));
+		}
+	}
+	
+	qreal margins[4];
+	printer_ -> getPageMargins(&margins[0], &margins[1], &margins[2], &margins[3], QPrinter::Millimeter);
+	QStringList margins_names(QStringList() << "left" << "top" << "right" << "bottom");
+	for (int i = 0 ; i < 4 ; ++ i) {
+		bool conv_ok;
+		qreal value = settings.value("margin" + margins_names.at(i), -1.0).toReal(&conv_ok);
+		if (conv_ok && value != -1.0) margins[i] = value;
+	}
+	printer_ -> setPageMargins(margins[0], margins[1], margins[2], margins[3], QPrinter::Millimeter);
+	printer_ -> setFullPage(settings.value("fullpage", "false").toString() == "true");
+	
+	settings.endGroup();
+	settings.endGroup();
+}
+
+/**
+	@return a section name for use with QSettings in order to store parameters
+	related to \a printer.
+*/
+QString DiagramPrintDialog::settingsSectionName(const QPrinter *printer) {
+	QPrinter::OutputFormat printer_format = printer -> outputFormat();
+	if (printer_format == QPrinter::NativeFormat) {
+		return(printer -> printerName().replace(" ", "_"));
+	} else if (printer_format == QPrinter::PdfFormat) {
+		return("QET_PDF_Printing");
+	} else if (printer_format == QPrinter::PostScriptFormat) {
+		return("QET_PS_Printing");
+	}
+	return(QString());
+}

Added: trunk/sources/diagramprintdialog.h
===================================================================
--- trunk/sources/diagramprintdialog.h	                        (rev 0)
+++ trunk/sources/diagramprintdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,87 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAM_PRINT_DIALOG_H
+#define DIAGRAM_PRINT_DIALOG_H
+#include <QtGui>
+#include "qetproject.h"
+#include "diagram.h"
+#include "exportproperties.h"
+/**
+	This class implements both the dialog allowing users to configure the printing
+	of a project file and the printing itself.
+*/
+class DiagramPrintDialog : public QWidget {
+	Q_OBJECT
+	// Constructors, destructor
+	public:
+	DiagramPrintDialog(QETProject *, QWidget * = 0);
+	virtual ~DiagramPrintDialog();
+	private:
+	DiagramPrintDialog(const DiagramPrintDialog &);
+	
+	// methods
+	public:
+	void setFileName(const QString &);
+	QString fileName() const;
+	void setDocName(const QString &);
+	QString docName() const;
+	QRect diagramRect(Diagram *, const ExportProperties &) const;
+	int pagesCount(Diagram *, const ExportProperties &, bool = false) const;
+	int horizontalPagesCount(Diagram *, const ExportProperties &, bool = false) const;
+	int verticalPagesCount(Diagram *, const ExportProperties &, bool = false) const;
+	void exec();
+	
+	private:
+	void buildPrintTypeDialog();
+	void buildDialog();
+	void saveReloadDiagramParameters(Diagram *, const ExportProperties, bool);
+	void savePageSetupForCurrentPrinter();
+	void loadPageSetupForCurrentPrinter();
+	QString settingsSectionName(const QPrinter *);
+	
+	private slots:
+	void print(const QList<Diagram *> &, bool, const ExportProperties);
+	void printDiagram(Diagram *, bool, const ExportProperties &, QPainter *, QPrinter * = 0);
+	void updatePrintTypeDialog();
+	void acceptPrintTypeDialog();
+	void browseFilePrintTypeDialog();
+	
+	// attributes
+	private:
+	QETProject *project_;
+	QPrinter *printer_;
+	QString doc_name_;
+	QString file_name_;
+	
+	QDialog *dialog_;
+	QLabel *printtype_label_;
+	QGridLayout *glayout0_;
+	QVBoxLayout *vlayout0_;
+	QHBoxLayout *hlayout0_;
+	QLabel *printer_icon_;
+	QLabel *pdf_icon_;
+	QLabel *ps_icon_;
+	QButtonGroup *printtype_choice_;
+	QRadioButton *printer_choice_;
+	QRadioButton *pdf_choice_;
+	QRadioButton *ps_choice_;
+	QLineEdit *filepath_field_;
+	QPushButton *browse_button_;
+	QDialogButtonBox *buttons_;
+};
+#endif

Added: trunk/sources/diagramschooser.cpp
===================================================================
--- trunk/sources/diagramschooser.cpp	                        (rev 0)
+++ trunk/sources/diagramschooser.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,187 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramschooser.h"
+#include "qetproject.h"
+#include "diagram.h"
+
+/**
+	Constructeur
+	@param project Projet dont il faut afficher les schemas
+	@param parent QWidget parent de ce widget
+*/
+DiagramsChooser::DiagramsChooser(QETProject *project, QWidget *parent) :
+	QScrollArea(parent),
+	project_(project),
+	widget0_(0),
+	vlayout0_(0)
+{
+	setFrameShadow(QFrame::Sunken);
+	setFrameShape(QFrame::StyledPanel);
+	setLineWidth(3);
+	setMidLineWidth(3);
+	
+	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+	setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+	setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
+	
+	updateList();
+	
+	setWidget(widget0_);
+}
+
+/**
+	Destructeur
+*/
+DiagramsChooser::~DiagramsChooser() {
+}
+
+/**
+	@return le projet dont ce widget affiche les schemas
+*/
+QETProject *DiagramsChooser::project() const {
+	return(project_);
+}
+
+/**
+	@return la liste des schemas selectionnes
+*/
+QList<Diagram *> DiagramsChooser::selectedDiagrams() const {
+	QList<Diagram *> selected_diagrams;
+	foreach(Diagram *diagram, project_ -> diagrams()) {
+		QCheckBox *check_box = diagrams_[diagram];
+		if (check_box && check_box -> isChecked()) {
+			selected_diagrams << diagram;
+		}
+	}
+	return(selected_diagrams);
+}
+
+/**
+	@return la liste des schemas qui ne sont pas selectionnes
+*/
+QList<Diagram *> DiagramsChooser::nonSelectedDiagrams() const {
+	QList<Diagram *> selected_diagrams;
+	foreach(Diagram *diagram, diagrams_.keys()) {
+		if (!(diagrams_[diagram] -> isChecked())) {
+			selected_diagrams << diagram;
+		}
+	}
+	return(selected_diagrams);
+}
+
+/**
+	@param diagram Un schema cense etre present dans ce widget
+*/
+bool DiagramsChooser::diagramIsSelected(Diagram *const diagram) const {
+	QCheckBox *checkbox = diagrams_.value(diagram);
+	if (!checkbox) return(false);
+	return(checkbox -> isChecked());
+}
+
+/**
+	Selectionne les schemas contenus dans la liste diagrams_list
+	@param diagrams_list Liste de schemas a selectionner
+	@param select true pour selectionne les schemas de la liste, false pour les
+	deselectionner
+	@param reset true pour deselectionner tous les schemas avant de
+	selectionner ceux de la liste
+*/
+void DiagramsChooser::setSelectedDiagrams(const QList<Diagram *> &diagrams_list, bool select, bool reset) {
+	// evite d'emettre une rafale de signaux pour cette operation
+	blockSignals(true);
+	
+	// deselectionne tous les schemas si demande
+	if (reset) {
+		foreach(QCheckBox *check_box, diagrams_.values()) {
+			check_box -> setChecked(false);
+		}
+	}
+	
+	int changes = 0;
+	QCheckBox *check_box;
+	foreach(Diagram *diagram, diagrams_list) {
+		if ((check_box = diagrams_[diagram])) {
+			if (check_box -> isChecked() != select) {
+				check_box -> setChecked(select);
+				++ changes;
+			}
+		}
+	}
+	
+	blockSignals(false);
+	if (reset || changes) {
+		emit(selectionChanged());
+	}
+}
+
+/**
+	Selectionne ou deselectionne tous les schemas
+	@param select true pour selectionne les schemas de la liste, false pour les
+	deselectionner
+*/
+void DiagramsChooser::setSelectedAllDiagrams(bool select) {
+	blockSignals(true);
+	foreach(QCheckBox *check_box, diagrams_.values()) {
+		check_box -> setChecked(select);
+	}
+	blockSignals(false);
+	emit(selectionChanged());
+}
+
+/**
+	Met a jour la liste des schemas du projet
+*/
+void DiagramsChooser::updateList() {
+	if (!project_) return;
+	
+	// retient la liste des schemas deja selectionnes
+	QList<Diagram *> selected_diagrams = selectedDiagrams();
+	
+	// detruit les checkbox existantes
+	QList<QCheckBox *> checkboxes = diagrams_.values();
+	qDeleteAll(checkboxes.begin(), checkboxes.end());
+	
+	buildLayout();
+	
+	// recree les checkbox necessaires
+	foreach(Diagram *diagram, project_ -> diagrams()) {
+		// titre du schema
+		QString diagram_title = diagram -> title();
+		if (diagram_title.isEmpty()) diagram_title = tr("Sch\351ma sans titre");
+		
+		QCheckBox *checkbox = new QCheckBox(diagram_title);
+		checkbox -> setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum));
+		checkbox -> setChecked(selected_diagrams.contains(diagram));
+		connect(checkbox, SIGNAL(toggled(bool)), this, SIGNAL(selectionChanged()));
+		diagrams_.insert(diagram, checkbox);
+		vlayout0_ -> addWidget(checkbox, 0, Qt::AlignLeft | Qt::AlignTop);
+	}
+}
+
+/**
+	Met en place la disposition du widget
+*/
+void DiagramsChooser::buildLayout() {
+	if (vlayout0_) return;
+	vlayout0_ = new QVBoxLayout();
+	widget0_  = new QWidget();
+	widget0_ -> setLayout(vlayout0_);
+	widget0_ -> setMinimumSize(170, 0);
+	widget0_ -> setMaximumSize(470, 10000);
+	widget0_ -> setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
+}

Added: trunk/sources/diagramschooser.h
===================================================================
--- trunk/sources/diagramschooser.h	                        (rev 0)
+++ trunk/sources/diagramschooser.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,62 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAMS_CHOOSER_H
+#define DIAGRAMS_CHOOSER_H
+#include <QtGui>
+class QETProject;
+class Diagram;
+/**
+	This class provides a widget which allows users to select 0 to n diagrams
+	among those of a particular project.
+*/
+class DiagramsChooser : public QScrollArea {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	DiagramsChooser(QETProject *, QWidget * = 0);
+	virtual ~DiagramsChooser();
+	private:
+	DiagramsChooser(const DiagramsChooser &);
+	
+	// methods
+	public:
+	QETProject *project() const;
+	QList<Diagram *> selectedDiagrams() const;
+	QList<Diagram *> nonSelectedDiagrams() const;
+	bool diagramIsSelected(Diagram * const) const;
+	void setSelectedDiagrams(const QList<Diagram *> &, bool = true, bool = true);
+	void setSelectedAllDiagrams(bool = true);
+	
+	public slots:
+	void updateList();
+	
+	signals:
+	void selectionChanged();
+	
+	private:
+	void buildLayout();
+	
+	// attributes
+	private:
+	QETProject *project_;
+	QWidget *widget0_;
+	QVBoxLayout *vlayout0_;
+	QHash<Diagram *, QCheckBox *> diagrams_;
+};
+#endif

Added: trunk/sources/diagramview.cpp
===================================================================
--- trunk/sources/diagramview.cpp	                        (rev 0)
+++ trunk/sources/diagramview.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1510 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramview.h"
+#include "diagram.h"
+#include "qetgraphicsitem/customelement.h"
+#include "qetgraphicsitem/ghostelement.h"
+#include "qetgraphicsitem/conductor.h"
+#include "diagramcommands.h"
+#include "diagramposition.h"
+#include "conductorpropertieswidget.h"
+#include "qetgraphicsitem/conductortextitem.h"
+#include "qetgraphicsitem/elementtextitem.h"
+#include "qetgraphicsitem/independenttextitem.h"
+#include "qetgraphicsitem/diagramimageitem.h"
+#include "titleblockpropertieswidget.h"
+#include "templatelocation.h"
+#include "qetapp.h"
+#include "qetproject.h"
+#include "borderpropertieswidget.h"
+#include "integrationmoveelementshandler.h"
+#include "integrationmovetemplateshandler.h"
+#include "qetdiagrameditor.h"
+#include "qeticons.h"
+#include "qetmessagebox.h"
+#include "qtextorientationspinboxwidget.h"
+#include <QGraphicsObject>
+
+#include <QGraphicsPixmapItem>
+#include <QGraphicsSceneMouseEvent>
+
+
+/**
+	Constructeur
+	@param diagram Schema a afficher ; si diagram vaut 0, un nouveau Diagram est utilise
+	@param parent Le QWidget parent de cette vue de schema
+*/
+DiagramView::DiagramView(Diagram *diagram, QWidget *parent) : QGraphicsView(parent) {
+	setAttribute(Qt::WA_DeleteOnClose, true);
+	setInteractive(true);
+	current_behavior = noAction;
+
+	QString whatsthis = tr(
+		"Ceci est la zone dans laquelle vous concevez vos sch\351mas en y ajoutant"
+		" des \351l\351ments et en posant des conducteurs entre leurs bornes. Il est"
+		" \351galement possible d'ajouter des textes ind\351pendants.",
+		"\"What's this?\" tip"
+	);
+	setWhatsThis(whatsthis);
+	
+	// active l'antialiasing
+	setRenderHint(QPainter::Antialiasing, true);
+	setRenderHint(QPainter::TextAntialiasing, true);
+	setRenderHint(QPainter::SmoothPixmapTransform, true);
+	
+	scene = diagram ? diagram : new Diagram(this);
+	setScene(scene);
+	scene -> undoStack().setClean();
+	setWindowIcon(QET::Icons::QETLogo);
+	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
+	setResizeAnchor(QGraphicsView::AnchorUnderMouse);
+	setAlignment(Qt::AlignLeft | Qt::AlignTop);
+	setSelectionMode();
+	adjustSceneRect();
+	updateWindowTitle();
+	
+	context_menu = new QMenu(this);
+	paste_here = new QAction(QET::Icons::EditPaste, tr("Coller ici", "context menu action"), this);
+	connect(paste_here, SIGNAL(triggered()), this, SLOT(pasteHere()));
+	
+	connect(scene, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
+	connect(scene, SIGNAL(readOnlyChanged(bool)), this, SLOT(applyReadOnly()));
+	connect(&(scene -> border_and_titleblock), SIGNAL(borderChanged(QRectF, QRectF)), this, SLOT(adjustSceneRect()));
+	connect(&(scene -> border_and_titleblock), SIGNAL(displayChanged()),              this, SLOT(adjustSceneRect()));
+	connect(&(scene -> border_and_titleblock), SIGNAL(diagramTitleChanged(const QString &)), this, SLOT(updateWindowTitle()));
+	connect(&(scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(updateWindowTitle()));
+	
+	connect(this, SIGNAL(aboutToAddElement()), this, SLOT(addDroppedElement()), Qt::QueuedConnection);
+	connect(
+		this, SIGNAL(aboutToSetDroppedTitleBlockTemplate(const TitleBlockTemplateLocation &)),
+		this, SLOT(setDroppedTitleBlockTemplate(const TitleBlockTemplateLocation &)),
+		Qt::QueuedConnection
+	);
+	QShortcut *edit_conductor_color_shortcut = new QShortcut(QKeySequence(Qt::Key_F2), this);
+	connect(edit_conductor_color_shortcut, SIGNAL(activated()), this, SLOT(editSelectedConductorColor()));
+}
+
+/**
+	Destructeur
+*/
+DiagramView::~DiagramView() {
+}
+
+/**
+	Selectionne tous les objets du schema
+*/
+void DiagramView::selectAll() {
+	scene -> selectAll();
+}
+
+/**
+	Deslectionne tous les objets selectionnes
+*/
+void DiagramView::selectNothing() {
+	scene -> deselectAll();
+}
+
+/**
+	Inverse l'etat de selection de tous les objets du schema
+*/
+void DiagramView::selectInvert() {
+	scene -> invertSelection();
+}
+
+/**
+	Supprime les composants selectionnes
+*/
+void DiagramView::deleteSelection() {
+	if (scene -> isReadOnly()) return;
+	DiagramContent removed_content = scene -> selectedContent();
+	scene -> clearSelection();
+	scene -> undoStack().push(new DeleteElementsCommand(scene, removed_content));
+	adjustSceneRect();
+}
+
+/**
+	Pivote les composants selectionnes
+*/
+void DiagramView::rotateSelection() {
+	if (scene -> isReadOnly()) return;
+	
+	// recupere les elements et les champs de texte a pivoter
+	QList<Element *> elements_to_rotate;
+	QList<DiagramTextItem *> texts_to_rotate;
+	QList<DiagramImageItem *> images_to_rotate;
+	foreach (QGraphicsItem *item, scene -> selectedItems()) {
+		if (Element *e = qgraphicsitem_cast<Element *>(item)) {
+			elements_to_rotate << e;
+		} else if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(item)) {
+			texts_to_rotate << cti;
+		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
+			texts_to_rotate << iti;
+		} else if (ElementTextItem *eti = qgraphicsitem_cast<ElementTextItem *>(item)) {
+			// on ne pivote un texte d'element que si son parent n'est pas selectionne
+			if (eti -> parentItem() && !eti -> parentItem() -> isSelected()) {
+				texts_to_rotate << eti;
+			}
+		} else if (DiagramImageItem *dii = qgraphicsitem_cast<DiagramImageItem *>(item)) {
+			images_to_rotate << dii;
+		}
+	}
+	
+	// effectue les rotations s'il y a quelque chose a pivoter
+	if (elements_to_rotate.isEmpty() && texts_to_rotate.isEmpty() && images_to_rotate.isEmpty()) return;
+	scene -> undoStack().push(new RotateElementsCommand(elements_to_rotate, texts_to_rotate, images_to_rotate));
+}
+
+void DiagramView::rotateTexts() {
+	if (scene -> isReadOnly()) return;
+	
+	// recupere les champs de texte a orienter
+	QList<DiagramTextItem *> texts_to_rotate;
+	foreach (QGraphicsItem *item, scene -> selectedItems()) {
+		if (ConductorTextItem *cti = qgraphicsitem_cast<ConductorTextItem *>(item)) {
+			texts_to_rotate << cti;
+		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
+			texts_to_rotate << iti;
+		} else if (ElementTextItem *eti = qgraphicsitem_cast<ElementTextItem *>(item)) {
+			// ici, on pivote un texte d'element meme si son parent est selectionne
+			texts_to_rotate << eti;
+		}
+	}
+	
+	// effectue les rotations s'il y a quelque chose a pivoter
+	if (texts_to_rotate.isEmpty()) return;
+	
+	// demande un angle a l'utilisateur
+	QDialog ori_text_dialog(diagramEditor());
+	ori_text_dialog.setSizeGripEnabled(false);
+#ifdef Q_WS_MAC
+	ori_text_dialog.setWindowFlags(Qt::Sheet);
+#endif
+	ori_text_dialog.setWindowTitle(tr("Orienter les textes s\351lectionn\351s", "window title"));
+// 	ori_text_dialog.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+	
+	
+	QTextOrientationSpinBoxWidget *ori_widget = QETApp::createTextOrientationSpinBoxWidget();
+	ori_widget -> setParent(&ori_text_dialog);
+	if (texts_to_rotate.count() == 1) {
+		ori_widget -> setOrientation(texts_to_rotate.at(0) -> rotationAngle());
+	}
+	ori_widget -> spinBox() -> selectAll();
+	
+	// boutons
+	QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	connect(&buttons, SIGNAL(accepted()), &ori_text_dialog, SLOT(accept()));
+	connect(&buttons, SIGNAL(rejected()), &ori_text_dialog, SLOT(reject()));
+	
+	// ajout dans une disposition verticale
+	QVBoxLayout layout_v(&ori_text_dialog);
+	layout_v.setSizeConstraint(QLayout::SetFixedSize);
+	layout_v.addWidget(ori_widget);
+	layout_v.addStretch();
+	layout_v.addWidget(&buttons);
+	
+	// si le dialogue est accepte
+	if (ori_text_dialog.exec() == QDialog::Accepted) {
+		scene -> undoStack().push(new RotateTextsCommand(texts_to_rotate, ori_widget -> orientation()));
+	}
+}
+
+/**
+	Accepte ou refuse le drag'n drop en fonction du type de donnees entrant
+	@param e le QDragEnterEvent correspondant au drag'n drop tente
+*/
+void DiagramView::dragEnterEvent(QDragEnterEvent *e) {
+	if (e -> mimeData() -> hasFormat("application/x-qet-element-uri")) {
+		e -> acceptProposedAction();
+	} else if (e -> mimeData() -> hasFormat("application/x-qet-titleblock-uri")) {
+		e -> acceptProposedAction();
+	} else if (e -> mimeData() -> hasText()) {
+		e -> acceptProposedAction();
+	} else {
+		e -> ignore();
+	}
+}
+
+/**
+	Gere les dragleaveevent
+	@param e le QDragEnterEvent correspondant au drag'n drop sortant
+*/
+void DiagramView::dragLeaveEvent(QDragLeaveEvent *e) {
+	Q_UNUSED(e);
+}
+
+/**
+	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 DiagramView::dragMoveEvent(QDragMoveEvent *e) {
+	if (e -> mimeData() -> hasFormat("text/plain")) e -> acceptProposedAction();
+	else e-> ignore();
+}
+
+/**
+	Handle the drops accepted on diagram (elements and title block templates). 
+	@param e the QDropEvent describing the current drag'n drop
+*/
+void DiagramView::dropEvent(QDropEvent *e) {
+
+	if (e -> mimeData() -> hasFormat("application/x-qet-element-uri")) {
+		handleElementDrop(e);
+	} else if (e -> mimeData() -> hasFormat("application/x-qet-titleblock-uri")) {
+		handleTitleBlockDrop(e);
+	} else if (e -> mimeData() -> hasText()) {
+		handleTextDrop(e);
+	}
+}
+
+/**
+	Handle the drop of an element.
+	@param e the QDropEvent describing the current drag'n drop
+*/
+void DiagramView::handleElementDrop(QDropEvent *e) {
+	// fetch the element location from the drop event
+	QString elmt_path = e -> mimeData() -> text();
+	
+	ElementsLocation location(ElementsLocation::locationFromString(elmt_path));
+	
+	// verifie qu'il existe un element correspondant a cet emplacement
+	ElementsCollectionItem *dropped_item = QETApp::collectionItem(location);
+	if (!dropped_item) return;
+	
+	next_location_ = location;
+	next_position_ = e-> pos();
+	
+	emit(aboutToAddElement());
+}
+
+/**
+	Handle the drop of an element.
+	@param e the QDropEvent describing the current drag'n drop
+*/
+void DiagramView::handleTitleBlockDrop(QDropEvent *e) {
+	// fetch the title block template location from the drop event
+	TitleBlockTemplateLocation tbt_loc;
+	tbt_loc.fromString(e -> mimeData() -> text());
+	if (tbt_loc.isValid()) {
+		emit(aboutToSetDroppedTitleBlockTemplate(tbt_loc));
+	}
+}
+
+/**
+ * @brief DiagramView::handleTextDrop
+ *handle the drop of text, html markup are automatically detected.
+ * @param e the QDropEvent describing the current drag'n drop
+ */
+void DiagramView::handleTextDrop(QDropEvent *e) {
+	if (e -> mimeData() -> hasText()) {
+		if (e -> mimeData() -> hasHtml()) addDiagramTextAtPos(e->pos()) -> setHtml(e -> mimeData() -> text());
+		else addDiagramTextAtPos(e -> pos(), e -> mimeData() -> text());
+	}
+}
+
+/**
+	Set the Diagram in visualisation mode
+*/
+void DiagramView::setVisualisationMode() {
+	setDragMode(ScrollHandDrag);
+	applyReadOnly();
+	setInteractive(false);
+	emit(modeChanged());
+}
+
+/**
+	Set the Diagram in Selection mode
+*/
+void DiagramView::setSelectionMode() {
+	setDragMode(RubberBandDrag);
+	setInteractive(true);
+	applyReadOnly();
+	emit(modeChanged());
+}
+
+/**
+	Agrandit le schema (+33% = inverse des -25 % de zoomMoins())
+*/
+void DiagramView::zoomIn() {
+	scale(4.0/3.0, 4.0/3.0);
+	adjustGridToZoom();
+}
+
+/**
+	Retrecit le schema (-25% = inverse des +33 % de zoomPlus())
+*/
+void DiagramView::zoomOut() {
+	scale(0.75, 0.75);
+	adjustGridToZoom();
+}
+
+/**
+	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 DiagramView::zoomFit() {
+	adjustSceneRect();
+	fitInView(sceneRect(), Qt::KeepAspectRatio);
+	adjustGridToZoom();
+}
+
+/**
+	Adjust zoom to fit all elements in the view, regardless of diagram borders.
+*/
+void DiagramView::zoomContent() {
+	fitInView(scene -> itemsBoundingRect(), Qt::KeepAspectRatio);
+	adjustGridToZoom();
+}
+
+/**
+	Reinitialise le zoom
+*/
+void DiagramView::zoomReset() {
+	resetMatrix();
+	adjustGridToZoom();
+}
+
+/**
+	Copie les elements selectionnes du schema dans le presse-papier puis les supprime
+*/
+void DiagramView::cut() {
+	copy();
+	DiagramContent cut_content = scene -> selectedContent();
+	scene -> clearSelection();
+	scene -> undoStack().push(new CutDiagramCommand(scene, cut_content));
+}
+
+/**
+	Copie les elements selectionnes du schema dans le presse-papier
+*/
+void DiagramView::copy() {
+	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
+	@param pos coin superieur gauche (en coordonnees de la scene) du rectangle
+	englobant le contenu colle
+	@param clipboard_mode Type de presse-papier a prendre en compte
+*/
+void DiagramView::paste(const QPointF &pos, QClipboard::Mode clipboard_mode) {
+	if (!isInteractive() || scene -> isReadOnly()) return;
+	
+	QString texte_presse_papier = QApplication::clipboard() -> text(clipboard_mode);
+	if ((texte_presse_papier).isEmpty()) return;
+	
+	QDomDocument document_xml;
+	if (!document_xml.setContent(texte_presse_papier)) return;
+	
+	// objet pour recuperer le contenu ajoute au schema par le coller
+	DiagramContent content_pasted;
+	scene -> fromXml(document_xml, pos, false, &content_pasted);
+	
+	// si quelque chose a effectivement ete ajoute au schema, on cree un objet d'annulation
+	if (content_pasted.count()) {
+		scene -> clearSelection();
+		scene -> undoStack().push(new PasteDiagramCommand(scene, content_pasted));
+		adjustSceneRect();
+	}
+}
+
+/**
+	Colle le contenu du presse-papier sur le schema a la position de la souris
+*/
+void DiagramView::pasteHere() {
+	paste(mapToScene(paste_here_pos));
+}
+
+/**
+	Manage the events press click mouse :
+	 *  click to add an independent text field
+*/
+void DiagramView::mousePressEvent(QMouseEvent *e) {
+	if (fresh_focus_in_) {
+		switchToVisualisationModeIfNeeded(e);
+		fresh_focus_in_ = false;
+	}
+	if (isInteractive() && !scene -> isReadOnly() && e -> buttons() == Qt::LeftButton) {
+		switch (current_behavior) {
+			case noAction:
+				break;
+			case addingText:
+				addDiagramTextAtPos(mapToScene(e -> pos()));
+				break;
+			case addingImage:
+				addDiagramImageAtPos(mapToScene(e -> pos()));
+				break;
+			case dragView:
+				break;
+			default:
+				break;
+		}
+		current_behavior = noAction;
+	}
+	// workaround for drag view with hold wheel click and drag mouse
+	// see also mouseMoveEvent() and mouseReleaseEvent()
+	if (e -> buttons() == Qt::MidButton) {
+		setCursor(Qt::ClosedHandCursor);
+		reference_view_ = mapToScene(e -> pos());
+		center_view_ = mapToScene(this -> viewport() -> rect()).boundingRect().center();
+		return;
+	}
+	QGraphicsView::mousePressEvent(e);
+}
+
+/**
+ * @brief DiagramView::mouseMoveEvent
+ * Manage the event move mouse
+ */
+void DiagramView::mouseMoveEvent(QMouseEvent *e) {
+	if ((e -> buttons() & Qt::MidButton) == Qt::MidButton) {
+		QPointF move = reference_view_ - mapToScene(e -> pos());
+		this -> centerOn(center_view_ + move);
+		center_view_ = mapToScene(this -> viewport() -> rect()).boundingRect().center();
+		return;
+	}
+	QGraphicsView::mouseMoveEvent(e);
+}
+
+/**
+ * @brief DiagramView::mouseReleaseEvent
+ * Manage event release click mouse
+ */
+void DiagramView::mouseReleaseEvent(QMouseEvent *e) {
+	if (e -> button() == Qt::MidButton) {
+		setCursor(Qt::ArrowCursor);
+		return;
+	}
+	QGraphicsView::mouseReleaseEvent(e);
+}
+
+/**
+	Manage wheel event of mouse
+	@param e QWheelEvent
+*/
+void DiagramView::wheelEvent(QWheelEvent *e) {
+	//Zoom and scrolling
+	if (e->buttons() != Qt::MidButton) {
+		if (!(e -> modifiers() & Qt::ControlModifier)) {
+			if (e -> delta() > 0){
+				zoomIn();
+			}
+			else{
+				zoomOut();
+			}
+		}
+		else {
+			QAbstractScrollArea::wheelEvent(e);
+		}
+	}
+}
+
+/**
+	Handles "Focus in" events. Reimplemented here to store the fact the focus
+	was freshly acquired again using the mouse. This information is later used
+	in DiagramView::mousePressEvent().
+*/
+void DiagramView::focusInEvent(QFocusEvent *e) {
+	if (e -> reason() == Qt::MouseFocusReason) {
+		fresh_focus_in_ = true;
+	}
+}
+
+/**
+	Handles "key press" events. Reimplemented here to switch to visualisation
+	mode if needed.
+*/
+void DiagramView::keyPressEvent(QKeyEvent *e) {
+	switchToVisualisationModeIfNeeded(e);
+	QGraphicsView::keyPressEvent(e);
+}
+
+/**
+	Handles "key release" events. Reimplemented here to switch to selection
+	mode if needed.
+*/
+void DiagramView::keyReleaseEvent(QKeyEvent *e) {
+	switchToSelectionModeIfNeeded(e);
+	QGraphicsView::keyReleaseEvent(e);
+}
+
+/**
+	@return le titre de cette vue ; cela correspond au titre du schema
+	visualise precede de la mention "Schema". Si le titre du schema est vide,
+	la mention "Schema sans titre" est utilisee
+	@see Diagram::title()
+*/
+QString DiagramView::title() const {
+	QString view_title;
+	QString diagram_title(scene -> title());
+	if (diagram_title.isEmpty()) {
+		view_title = tr("Sans titre", "what to display for untitled diagrams");
+	} else {
+		view_title = diagram_title;
+	}
+	return(view_title);
+}
+
+/**
+	Edite les informations du schema.
+*/
+void DiagramView::editDiagramProperties() {
+	bool diagram_is_read_only = scene -> isReadOnly();
+	
+	// recupere le cartouche et les dimensions du schema
+	TitleBlockProperties  titleblock  = scene -> border_and_titleblock.exportTitleBlock();
+	BorderProperties border = scene -> border_and_titleblock.exportBorder();
+	ConductorProperties conductors = scene -> defaultConductorProperties;
+	
+	// construit le dialogue
+	QDialog popup(diagramEditor());
+	popup.setWindowModality(Qt::WindowModal);
+#ifdef Q_WS_MAC
+	popup.setWindowFlags(Qt::Sheet);
+#endif
+	
+	popup.setWindowTitle(tr("Propri\351t\351s du sch\351ma", "window title"));
+	
+	BorderPropertiesWidget *border_infos = new BorderPropertiesWidget(border, &popup);
+	border_infos -> setReadOnly(diagram_is_read_only);
+	
+	TitleBlockPropertiesWidget  *titleblock_infos  = new TitleBlockPropertiesWidget(titleblock, false, &popup);
+	if (QETProject *parent_project = scene -> project()) {
+		titleblock_infos -> setTitleBlockTemplatesCollection(parent_project -> embeddedTitleBlockTemplatesCollection());
+		titleblock_infos -> setTitleBlockTemplatesVisible(true);
+		// we have to parse again the TitleBlockProperties object, since the
+		// first parsing did not know of our templates
+		titleblock_infos -> setTitleBlockProperties(titleblock);
+		// relay the signal that requires a title block template edition
+		connect(titleblock_infos, SIGNAL(editTitleBlockTemplate(QString, bool)), this, SIGNAL(editTitleBlockTemplate(QString, bool)));
+	}
+	titleblock_infos -> setReadOnly(diagram_is_read_only);
+	
+	ConductorPropertiesWidget *cpw = new ConductorPropertiesWidget(conductors);
+	cpw -> setReadOnly(diagram_is_read_only);
+	
+	// boutons
+	QDialogButtonBox boutons(diagram_is_read_only ? QDialogButtonBox::Ok : QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	connect(&boutons, SIGNAL(accepted()), &popup, SLOT(accept()));
+	connect(&boutons, SIGNAL(rejected()), &popup, SLOT(reject()));
+	
+	// usual layout for these three widgets
+	QHBoxLayout *hlayout1 = new QHBoxLayout();
+	QVBoxLayout *vlayout2 = new QVBoxLayout();
+	
+	vlayout2 -> addWidget(border_infos);
+	vlayout2 -> addWidget(titleblock_infos);
+	vlayout2 -> setSpacing(5);
+	hlayout1 -> addLayout(vlayout2);
+	hlayout1 -> addWidget(cpw);
+	hlayout1 -> setAlignment(cpw, Qt::AlignTop);
+	
+	// ajout dans une disposition verticale
+	QVBoxLayout layout_v(&popup);
+	layout_v.addLayout(hlayout1);
+	layout_v.addStretch();
+	layout_v.addWidget(&boutons);
+
+
+	// si le dialogue est accepte
+	if (popup.exec() == QDialog::Accepted && !diagram_is_read_only) {
+		TitleBlockProperties new_titleblock   = titleblock_infos  -> titleBlockProperties();
+		BorderProperties new_border = border_infos -> borderProperties();
+		ConductorProperties new_conductors = cpw -> conductorProperties();
+		
+		bool adjust_scene = false;
+		
+		// s'il y a des modifications au cartouche
+		if (new_titleblock != titleblock) {
+			scene -> undoStack().push(new ChangeTitleBlockCommand(scene, titleblock, new_titleblock));
+			adjust_scene = true;
+		}
+		
+		// s'il y a des modifications aux dimensions du schema
+		if (new_border != border) {
+			scene -> undoStack().push(new ChangeBorderCommand(scene, border, new_border));
+			adjust_scene = true;
+		}
+		
+		// if modifcations have been made to the conductors properties
+		if (new_conductors != conductors) {
+			/// TODO implement an undo command to allow the user to undo/redo this action
+			scene -> defaultConductorProperties = new_conductors;
+		}
+		if (adjust_scene) adjustSceneRect();
+	}
+}
+
+/**
+	@return true s'il y a des items selectionnes sur le schema, false sinon
+*/
+bool DiagramView::hasSelectedItems() {
+	return(scene -> selectedItems().size() > 0);
+}
+
+/**
+	@return true s'il y a des items selectionnes sur le schema et que ceux-ci
+	peuvent etre copies dans le presse-papier, false sinon
+*/
+bool DiagramView::hasCopiableItems() {
+	foreach(QGraphicsItem *qgi, scene -> selectedItems()) {
+		if (
+			qgraphicsitem_cast<Element *>(qgi) ||
+			qgraphicsitem_cast<IndependentTextItem *>(qgi) ||
+			qgraphicsitem_cast<DiagramImageItem *>(qgi)
+		) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	@return true s'il y a des items selectionnes sur le schema et que ceux-ci
+	peuvent etre supprimes, false sinon
+*/
+bool DiagramView::hasDeletableItems() {
+	foreach(QGraphicsItem *qgi, scene -> selectedItems()) {
+		if (
+			qgraphicsitem_cast<Element *>(qgi) ||
+			qgraphicsitem_cast<Conductor *>(qgi) ||
+			qgraphicsitem_cast<IndependentTextItem *>(qgi) ||
+			qgraphicsitem_cast<DiagramImageItem *>(qgi)
+		) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	Ajoute une colonne au schema.
+*/
+void DiagramView::addColumn() {
+	if (scene -> isReadOnly()) return;
+	BorderProperties old_bp = scene -> border_and_titleblock.exportBorder();
+	BorderProperties new_bp = scene -> border_and_titleblock.exportBorder();
+	new_bp.columns_count += 1;
+	scene -> undoStack().push(new ChangeBorderCommand(scene, old_bp, new_bp));
+}
+
+/**
+	Enleve une colonne au schema.
+*/
+void DiagramView::removeColumn() {
+	if (scene -> isReadOnly()) return;
+	BorderProperties old_bp = scene -> border_and_titleblock.exportBorder();
+	BorderProperties new_bp = scene -> border_and_titleblock.exportBorder();
+	new_bp.columns_count -= 1;
+	scene -> undoStack().push(new ChangeBorderCommand(scene, old_bp, new_bp));
+}
+
+/**
+	Agrandit le schema en hauteur
+*/
+void DiagramView::addRow() {
+	if (scene -> isReadOnly()) return;
+	BorderProperties old_bp = scene -> border_and_titleblock.exportBorder();
+	BorderProperties new_bp = scene -> border_and_titleblock.exportBorder();
+	new_bp.rows_count += 1;
+	scene -> undoStack().push(new ChangeBorderCommand(scene, old_bp, new_bp));
+}
+
+/**
+	Retrecit le schema en hauteur
+*/
+void DiagramView::removeRow() {
+	if (scene -> isReadOnly()) return;
+	BorderProperties old_bp = scene -> border_and_titleblock.exportBorder();
+	BorderProperties new_bp = scene -> border_and_titleblock.exportBorder();
+	new_bp.rows_count -= 1;
+	scene -> undoStack().push(new ChangeBorderCommand(scene, old_bp, new_bp));
+}
+
+/**
+	Ajuste le sceneRect (zone du schema visualisee par le DiagramView) afin que
+	celui inclut a la fois les elements dans et en dehors du cadre et le cadre
+	lui-meme.
+*/
+void DiagramView::adjustSceneRect() {
+	QRectF old_scene_rect = sceneRect();
+	
+	// rectangle delimitant l'ensemble des elements
+	QRectF elements_bounding_rect = scene -> itemsBoundingRect();
+	
+	// rectangle contenant le cadre = colonnes + cartouche
+	QRectF border_bounding_rect = scene -> border().adjusted(-Diagram::margin, -Diagram::margin, Diagram::margin, Diagram::margin);
+	
+	// ajuste la sceneRect
+	QRectF new_scene_rect = elements_bounding_rect.united(border_bounding_rect);
+	setSceneRect(new_scene_rect);
+	
+	// met a jour la scene
+	scene -> update(old_scene_rect.united(new_scene_rect));
+}
+
+/**
+	Met a jour le titre du widget
+*/
+void DiagramView::updateWindowTitle() {
+	QString view_title(title());
+	
+	// verifie si le document a ete modifie
+	bool modified_diagram = !(scene -> undoStack().isClean());
+	
+	// specifie le titre du widget
+	setWindowTitle(view_title + " [*]");
+	setWindowModified(modified_diagram);
+	
+	// emet le signal titleChanged en ajoutant manuellement [*] si le schema a ete modifie
+	QString emitted_title = view_title;
+	if (modified_diagram) emitted_title += " [*]";
+	emit(titleChanged(this, emitted_title));
+}
+
+/**
+	Enables or disables the drawing grid according to the amount of pixels display
+*/
+void DiagramView::adjustGridToZoom() {
+	QRectF viewed_scene = viewedSceneRect();
+	scene -> setDisplayGrid(viewed_scene.width() < 2000 || viewed_scene.height() < 2000);
+}
+
+/**
+	@return le rectangle du schema (classe Diagram) visualise par ce DiagramView
+*/
+QRectF DiagramView::viewedSceneRect() const {
+	// recupere la taille du widget viewport
+	QSize viewport_size = viewport() -> size();
+	
+	// recupere la transformation viewport -> scene
+	QTransform view_to_scene   = viewportTransform().inverted();
+	
+	// mappe le coin superieur gauche et le coin inferieur droit de la viewport sur la scene
+	QPointF scene_left_top     = view_to_scene.map(QPointF(0.0, 0.0));
+	QPointF scene_right_bottom = view_to_scene.map(QPointF(viewport_size.width(), viewport_size.height()));
+	
+	// en deduit le rectangle visualise par la scene
+	return(QRectF(scene_left_top, scene_right_bottom));
+}
+
+/**
+	Cette methode permet de determiner s'il faut ou non integrer au projet un
+	element dont on connait l'emplacement.
+	L'element droppe est integre a la collection du projet :
+	  * s'il appartient a un autre projet, quelque soit la specification de
+	  l'utilisateur a ce propos ;
+	  * s'il appartient a la collection commune ou a la collection
+	  personnelle ET que l'utilisateur a autorise l'integration automatique
+	  des elements dans les projets.
+	@param location Emplacement de l'element
+	@return true si l'element doit etre integre, false sinon
+	
+*/
+bool DiagramView::mustIntegrateElement(const ElementsLocation &location) const {
+	// l'utilisateur a-t-il autorise l'integration automatique des elements dans les projets ?
+	bool auto_integration_enabled = QETApp::settings().value("diagrameditor/integrate-elements", true).toBool();
+	
+	// l'element appartient-il a un projet et si oui, est-ce un autre projet ?
+	bool elmt_from_project = location.project();
+	bool elmt_from_another_project = elmt_from_project && location.project() != scene -> project();
+	
+	// faut-il integrer l'element ?
+	bool must_integrate_element = (elmt_from_another_project || (auto_integration_enabled && !elmt_from_project));
+	
+	return(must_integrate_element);
+}
+
+/**
+	@param tbt_loc A title block template location
+	@return true if the title block template needs to be integrated in the
+	parent project before being applied to the current diagram, or false if it
+	can be directly applied
+*/
+bool DiagramView::mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation &tbt_loc) const {
+	// unlike elements, the integration of title block templates is mandatory, so we simply check whether the parent project of the template is also the parent project of the diagram
+	QETProject *tbt_parent_project = tbt_loc.parentProject();
+	if (!tbt_parent_project) return(true);
+	
+	return(tbt_parent_project != scene -> project());
+}
+
+/**
+	@param location Emplacement de l'element a ajouter sur le schema
+	@param pos Position (dans les coordonnees de la vue) a laquelle l'element sera ajoute
+*/
+bool DiagramView::addElementAtPos(const ElementsLocation &location, const QPoint &pos) {
+	// construit une instance de l'element correspondant a l'emplacement
+	int state;
+	Element *el = new CustomElement(location, 0, 0, &state);
+	if (state) {
+		delete el;
+		return(false);
+	}
+	
+	// pose de l'element sur le schema
+	diagram() -> undoStack().push(new AddElementCommand(diagram(), el, mapToScene(pos)));
+	return(true);
+}
+
+/**
+	Fait en sorte que le schema ne soit editable que s'il n'est pas en lecture
+	seule
+*/
+void DiagramView::applyReadOnly() {
+	if (!scene) return;
+	
+	bool is_writable = !scene -> isReadOnly();
+	setInteractive(is_writable);
+	setAcceptDrops(is_writable);
+}
+
+/**
+	Edite les proprietes des objets selectionnes
+*/
+void DiagramView::editSelectionProperties() {
+	// recupere la selection
+	DiagramContent selection = scene -> selectedContent();
+	
+	// s'il n'y a rien de selectionne, cette methode ne fait rien
+	int selected_items_count = selection.count(DiagramContent::All | DiagramContent::SelectedOnly);
+	if (!selected_items_count) return;
+	
+	// si la selection ne comprend qu'un seul objet, on l'edite via un dialogue approprie
+	if (selected_items_count == 1) {
+		// cas d'un conducteur selectionne
+		QList<Conductor *> selected_conductors = selection.conductors(DiagramContent::AnyConductor | DiagramContent::SelectedOnly);
+		if (selected_conductors.count() == 1) {
+			editConductor(selected_conductors.at(0));
+			return;
+		}
+		
+		// cas d'un element selectionne
+		if (selection.elements.count() == 1) {
+			editElement(selection.elements.toList().at(0));
+			return;
+		}
+		
+		// cas d'un champ de texte selectionne : pour le moment, on traite comme une selection multiple
+	}
+	
+	// sinon on affiche un simple listing des elements selectionnes
+	QET::MessageBox::information(
+		this,
+		tr("Propri\351t\351s de la s\351lection"),
+		QString(
+			tr(
+				"La s\351lection contient %1.",
+				"%1 is a sentence listing the selected objects"
+			)
+		).arg(selection.sentence(DiagramContent::All | DiagramContent::SelectedOnly))
+	);
+}
+
+/**
+	Edit the color of the selected conductor; does nothing if multiple conductors are selected
+*/
+void DiagramView::editSelectedConductorColor() {
+	// retrieve selected content
+	DiagramContent selection = scene -> selectedContent();
+	
+	// we'll focus on the selected conductor (we do not handle multiple conductors edition)
+	QList<Conductor *> selected_conductors = selection.conductors(DiagramContent::AnyConductor | DiagramContent::SelectedOnly);
+	if (selected_conductors.count() == 1) {
+		editConductorColor(selected_conductors.at(0));
+	}
+}
+
+/**
+	Affiche des informations sur un element
+	@param element Element a afficher
+*/
+void DiagramView::editElement(Element *element) {
+	if (!element) return;
+	
+	CustomElement *custom_element = qobject_cast<CustomElement *>(element);
+	GhostElement  *ghost_element  = qobject_cast<GhostElement  *>(element);
+	
+	// type de l'element
+	QString description_string;
+	if (ghost_element) {
+		description_string += tr("\311l\351ment manquant");
+	} else {
+		description_string += tr("\311l\351ment");
+	}
+	description_string += "\n";
+	
+	// nom, nombre de bornes, dimensions
+	description_string += QString(tr("Nom\240: %1\n")).arg(element -> name());
+	int folio_index = scene -> folioIndex();
+	if (folio_index != -1) {
+		description_string += QString(tr("Folio\240: %1\n")).arg(folio_index + 1);
+	}
+	description_string += QString(tr("Position\240: %1\n")).arg(scene -> convertPosition(element -> scenePos()).toString());
+	description_string += QString(tr("Dimensions\240: %1\327%2\n")).arg(element -> size().width()).arg(element -> size().height());
+	description_string += QString(tr("Bornes\240: %1\n")).arg(element -> terminals().count());
+	description_string += QString(tr("Connexions internes\240: %1\n")).arg(element -> internalConnections() ? tr("Autoris\351es") : tr("Interdites"));
+	description_string += QString(tr("Champs de texte\240: %1\n")).arg(element -> texts().count());
+	
+	if (custom_element) {
+		description_string += QString(tr("Emplacement\240: %1\n")).arg(custom_element -> location().toString());
+	}
+	
+	// titre et boutons du dialogue
+	QString description_title = tr("Propri\351t\351s de l'\351l\351ment s\351lectionn\351");
+	QPushButton *find_in_panel = new QPushButton(tr("Retrouver dans le panel"));
+	QPushButton *edit_element = new QPushButton(tr("\311diter l'\351l\351ment"));
+	edit_element->setIcon(QET::Icons::ElementEdit);
+	
+	// dialogue en lui-meme
+	QMessageBox edit_element_dialog(diagramEditor());
+#ifdef Q_WS_MAC
+	edit_element_dialog.setWindowFlags(Qt::Sheet);
+#endif
+	edit_element_dialog.setIcon(QMessageBox::Information);
+	edit_element_dialog.setWindowTitle(description_title);
+	edit_element_dialog.setText(description_title);
+	edit_element_dialog.setInformativeText(description_string);
+	edit_element_dialog.addButton(find_in_panel, QMessageBox::ApplyRole);
+	edit_element_dialog.addButton(edit_element, QMessageBox::ApplyRole);
+	edit_element_dialog.addButton(QMessageBox::Ok);
+	edit_element_dialog.setDefaultButton(QMessageBox::Ok);
+	edit_element_dialog.setEscapeButton(QMessageBox::Ok);
+	edit_element_dialog.exec();
+	
+	// Permet de trouver l'element dans la collection
+	if (edit_element_dialog.clickedButton() == find_in_panel) {
+		emit(findElementRequired(custom_element -> location()));
+	}
+	
+	// Trouve l'element dans la collection et l'edite
+	if (edit_element_dialog.clickedButton() == edit_element) {
+		emit(findElementRequired(custom_element -> location()));
+		emit(editElementRequired(custom_element -> location()));
+	}
+}
+
+/**
+	Affiche un dialogue permettant d'editer le conducteur selectionne.
+	Ne fait rien s'il y a 0 ou plusieurs conducteurs selectionnes.
+*/
+void DiagramView::editConductor() {
+	QList<Conductor *> selected_conductors(scene -> selectedConductors().toList());
+	
+	// on ne peut editer qu'un conducteur a la fois
+	if (selected_conductors.count() != 1) return;
+	Conductor *edited_conductor = selected_conductors.first();
+	
+	editConductor(edited_conductor);
+}
+
+/**
+	Edite le conducteur passe en parametre
+	@param edited_conductor Conducteur a editer
+*/
+void DiagramView::editConductor(Conductor *edited_conductor) {
+	if (scene -> isReadOnly()) return;
+	if (!edited_conductor) return;
+	
+	// initialise l'editeur de proprietes pour le conducteur
+	ConductorProperties old_properties = edited_conductor -> properties();
+	ConductorPropertiesWidget *cpw = new ConductorPropertiesWidget(old_properties);
+	
+	// l'insere dans un dialogue
+	QDialog conductor_dialog(diagramEditor());
+#ifdef Q_WS_MAC
+	conductor_dialog.setWindowFlags(Qt::Sheet);
+#endif
+	conductor_dialog.setWindowTitle(tr("\311diter les propri\351t\351s d'un conducteur", "window title"));
+	QVBoxLayout *dialog_layout = new QVBoxLayout(&conductor_dialog);
+	dialog_layout -> addWidget(cpw);
+	dialog_layout -> addStretch();
+	QDialogButtonBox *dbb = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	dialog_layout -> addWidget(dbb);
+	connect(dbb, SIGNAL(accepted()), &conductor_dialog, SLOT(accept()));
+	connect(dbb, SIGNAL(rejected()), &conductor_dialog, SLOT(reject()));
+	cpw -> setFocus(Qt::ActiveWindowFocusReason);
+	
+	// execute le dialogue et met a jour le conducteur
+	if (conductor_dialog.exec() == QDialog::Accepted) {
+		// recupere les nouvelles proprietes
+		ConductorProperties new_properties = cpw -> conductorProperties();
+		if (new_properties != old_properties) {
+			int qmbreturn=0;
+			//if conductor isn't alone at this potential and text is changed
+			//ask user to apply text on every conductors of this potential
+			if ((edited_conductor -> relatedPotentialConductors().size() >= 1) && (old_properties.text != new_properties.text)){
+				qmbreturn = QMessageBox::question(diagramEditor(), tr("Textes de conducteurs"),
+												  tr("Voulez-vous appliquer le nouveau texte \n"
+													 "\340 l'ensemble des conducteurs de ce potentiel ?"),
+												  QMessageBox::No| QMessageBox::Yes, QMessageBox::Yes);
+
+				if (qmbreturn == QMessageBox::Yes){
+					QSet <Conductor *> conductorslist = edited_conductor -> relatedPotentialConductors();
+					conductorslist << edited_conductor;
+					QList <ConductorProperties> old_properties_list, new_properties_list;
+					ConductorProperties cp;
+
+					foreach (Conductor *c, conductorslist) {
+						if (c == edited_conductor) {
+							old_properties_list << old_properties;
+							new_properties_list << new_properties;
+						}
+						else {
+							old_properties_list << c -> properties();
+							cp = c -> properties();
+							cp.text = new_properties.text;
+							c -> setProperties(cp);
+							new_properties_list << c -> properties();
+							c -> setText(new_properties.text);
+						}
+					}
+					//initialize the corresponding UndoCommand object
+					ChangeSeveralConductorsPropertiesCommand *cscpc = new ChangeSeveralConductorsPropertiesCommand(conductorslist);
+					cscpc -> setOldSettings(old_properties_list);
+					cscpc -> setNewSettings(new_properties_list);
+					diagram() -> undoStack().push(cscpc);
+				}
+			}
+			if (qmbreturn == 0 || qmbreturn == QMessageBox::No) {
+				// initialise l'objet UndoCommand correspondant
+				ChangeConductorPropertiesCommand *ccpc = new ChangeConductorPropertiesCommand(edited_conductor);
+				ccpc -> setOldSettings(old_properties);
+				ccpc -> setNewSettings(new_properties);
+				diagram() -> undoStack().push(ccpc);
+			}
+		}
+	}
+}
+
+/**
+	Edit the color of the given conductor
+	@param edited_conductor Conductor we want to change the color
+*/
+void DiagramView::editConductorColor(Conductor *edited_conductor) {
+	if (scene -> isReadOnly()) return;
+	if (!edited_conductor) return;
+	
+	// store the initial properties of the provided conductor
+	ConductorProperties initial_properties = edited_conductor -> properties();
+	
+	// prepare a color dialog showing the initial conductor color
+	QColorDialog *color_dialog = new QColorDialog(this);
+	color_dialog -> setWindowTitle(tr("Choisir la nouvelle couleur de ce conducteur"));
+#ifdef Q_WS_MAC
+	color_dialog -> setWindowFlags(Qt::Sheet);
+#endif
+	color_dialog -> setCurrentColor(initial_properties.color);
+	
+	// asks the user what color he wishes to apply
+	if (color_dialog -> exec() == QDialog::Accepted) {
+		QColor new_color = color_dialog -> selectedColor();
+		if (new_color != initial_properties.color) {
+			// the user chose a different color
+			ConductorProperties new_properties = initial_properties;
+			new_properties.color = new_color;
+			
+			ChangeConductorPropertiesCommand *ccpc = new ChangeConductorPropertiesCommand(edited_conductor);
+			ccpc -> setOldSettings(initial_properties);
+			ccpc -> setNewSettings(new_properties);
+			diagram() -> undoStack().push(ccpc);
+		}
+	}
+}
+
+/**
+	Reinitialise le profil des conducteurs selectionnes
+*/
+void DiagramView::resetConductors() {
+	if (scene -> isReadOnly()) return;
+	// recupere les conducteurs selectionnes
+	QSet<Conductor *> selected_conductors = scene -> selectedConductors();
+	
+	// repere les conducteurs modifies (= profil non nul)
+	QHash<Conductor *, ConductorProfilesGroup> conductors_and_profiles;
+	foreach(Conductor *conductor, selected_conductors) {
+		ConductorProfilesGroup profile = conductor -> profiles();
+		if (
+			!profile[Qt::TopLeftCorner].isNull() ||\
+			!profile[Qt::TopRightCorner].isNull() ||\
+			!profile[Qt::BottomLeftCorner].isNull() ||\
+			!profile[Qt::BottomRightCorner].isNull()
+		) {
+			conductors_and_profiles.insert(conductor, profile);
+		}
+	}
+	
+	if (conductors_and_profiles.isEmpty()) return;
+	scene -> undoStack().push(new ResetConductorCommand(conductors_and_profiles));
+}
+
+/**
+	Gere les evenements de la DiagramView
+	@param e Evenement
+*/
+bool DiagramView::event(QEvent *e) {
+	// fait en sorte que les raccourcis clavier arrivent prioritairement sur la
+	// vue plutot que de remonter vers les QMenu / QAction
+	if (
+		e -> type() == QEvent::ShortcutOverride &&
+		selectedItemHasFocus()
+	) {
+		e -> accept();
+		return(true);
+	}
+	return(QGraphicsView::event(e));
+}
+
+/**
+	Switch to visualisation mode if the user is pressing Ctrl and Shift.
+	@return true if the view was switched to visualisation mode, false
+	otherwise.
+*/
+bool DiagramView::switchToVisualisationModeIfNeeded(QInputEvent *e) {
+	if (isCtrlShifting(e) && !selectedItemHasFocus()) {
+		if (dragMode() != QGraphicsView::ScrollHandDrag) {
+			current_behavior = dragView;
+			setVisualisationMode();
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	Switch back to selection mode if the user is not pressing Ctrl and Shift.
+	@return true if the view was switched to selection mode, false
+	otherwise.
+*/
+bool DiagramView::switchToSelectionModeIfNeeded(QInputEvent *e) {
+	if (current_behavior == dragView && !selectedItemHasFocus() && !isCtrlShifting(e)) {
+		setSelectionMode();
+		current_behavior = noAction;
+		return(true);
+	}
+	return(false);
+}
+
+/**
+	@return true if the user is pressing Ctrl and Shift simultaneously.
+*/
+bool DiagramView::isCtrlShifting(QInputEvent *e) {
+	bool result = false;
+	// note: QInputEvent::modifiers and QKeyEvent::modifiers() do not return the
+	// same values, hence the casts
+	if (e -> type() == QEvent::KeyPress || e -> type() == QEvent::KeyRelease) {
+		if (QKeyEvent *ke = static_cast<QKeyEvent *>(e)) {
+			result = (ke -> modifiers() == (Qt::ControlModifier | Qt::ShiftModifier));
+		}
+	} else if (e -> type() >= QEvent::MouseButtonPress && e -> type() <= QEvent::MouseMove) {
+		if (QMouseEvent *me = static_cast<QMouseEvent *>(e)) {
+			result = (me -> modifiers() == (Qt::ControlModifier | Qt::ShiftModifier));
+		}
+	}
+	return(result);
+}
+
+/**
+	@return true if there is a selected item and that item has the focus.
+*/
+bool DiagramView::selectedItemHasFocus() {
+	return(
+		scene -> hasFocus() &&
+		scene -> focusItem() &&
+		scene -> focusItem() -> isSelected()
+	);
+}
+
+/**
+	Passe le DiagramView en mode "ajout de texte". Un clic cree alors un
+	nouveau champ de texte.
+*/
+void DiagramView::addText() {
+	if (scene -> isReadOnly()) return;
+	current_behavior = addingText;
+}
+
+
+/**	To edit the text through the htmlEditor
+*/
+void DiagramView::editText() {
+	if (scene -> isReadOnly()) return;
+	// Get text to edit
+	QList<DiagramTextItem *> texts_to_edit;
+	foreach (QGraphicsItem *item, scene -> selectedItems()) {
+		if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item)) {
+			texts_to_edit << iti;
+		} else if (ElementTextItem *eti = qgraphicsitem_cast<ElementTextItem *>(item)) {
+			// here...
+			texts_to_edit << eti;
+		}
+	}
+	// Test if any text existe..
+	if (texts_to_edit.isEmpty()) return;	
+	else texts_to_edit.at(0)->edit();	
+}
+
+
+/**
+* @brief DiagramView::addImage
+*/
+void DiagramView::addImage() {
+	if (scene -> isReadOnly()) return;
+
+	QString pathPictures = QDesktopServices::storageLocation ( QDesktopServices::PicturesLocation );
+	QString fileName = QFileDialog::getOpenFileName(this, tr("Selectionner une image..."), pathPictures.toStdString().c_str(), tr("Image Files (*.png *.jpg *.bmp *.svg)"));
+	if(fileName.isEmpty()) {
+		emit ImageAddedCanceled(false);
+		return;
+	}
+
+	int ret = image_to_add_.load(fileName);
+	if(!ret){
+		QMessageBox::critical(this, tr("Erreur"), tr("Impossible de charger l'image...D\351soler :("));
+		return;
+	}
+	current_behavior = addingImage;
+}
+
+/**
+ * @brief DiagramView::editImage
+ * open edit image dialog if only one image is selected
+ */
+void DiagramView::editImage() {
+	if (scene -> isReadOnly()) return;
+	QList <QGraphicsItem *> images = diagram() -> selectedContent().items(DiagramContent::Images);
+	if (images.count() != 1) return;
+	DiagramImageItem *image;
+	if ((image = qgraphicsitem_cast<DiagramImageItem *> (images.first()))) {
+		image -> editProperty();
+	}
+}
+
+/**
+* @brief DiagramView::addDiagramImageAtPos
+* @param pos
+* @return
+*/
+DiagramImageItem *DiagramView::addDiagramImageAtPos(const QPointF &pos) {
+
+	if (!isInteractive() || scene -> isReadOnly()) return(0);
+
+	// cree un nouveau champ image
+	DiagramImageItem *Imageitem = new DiagramImageItem( QPixmap::fromImage(image_to_add_) );
+
+	// le place a la position pos en gerant l'annulation
+	scene -> undoStack().push(new AddImageCommand(scene, Imageitem, pos));
+	adjustSceneRect();
+	
+	// emet le signal ImageAdded
+	emit(ImageAdded(false));
+
+	return(Imageitem);
+
+}
+
+/**
+	Cree un nouveau champ de texte et le place a la position pos
+	en gerant l'annulation ; enfin, le signal textAdded est emis.
+	@param pos Position du champ de texte ajoute
+	@return le champ de texte ajoute
+*/
+IndependentTextItem *DiagramView::addDiagramTextAtPos(const QPointF &pos, const QString &text) {
+	if (!isInteractive() || scene -> isReadOnly()) return(0);
+	
+	// cree un nouveau champ de texte
+	IndependentTextItem *iti;
+	if (text.isEmpty()) {
+		iti = new IndependentTextItem("_");
+	} else iti = new IndependentTextItem(text);
+	
+	// le place a la position pos en gerant l'annulation
+	scene -> undoStack().push(new AddTextCommand(scene, iti, pos));
+	adjustSceneRect();
+	
+	// emet le signal textAdded
+	emit(textAdded(false));
+
+	return(iti);
+}
+
+/**
+	Gere le menu contextuel
+	@param e Evenement decrivant la demande de menu contextuel
+*/
+void DiagramView::contextMenuEvent(QContextMenuEvent *e) {
+	if (QGraphicsItem *qgi = scene -> itemAt(mapToScene(e -> pos()))) {
+		if (!qgi -> isSelected()) scene -> clearSelection();
+		qgi -> setSelected(true);
+	}
+	
+	if (QETDiagramEditor *qde = diagramEditor()) {
+		context_menu -> clear();
+		if (scene -> selectedItems().isEmpty()) {
+			paste_here_pos = e -> pos();
+			paste_here -> setEnabled(Diagram::clipboardMayContainDiagram());
+			context_menu -> addAction(paste_here);
+			context_menu -> addSeparator();
+			context_menu -> addAction(qde -> infos_diagram);
+			context_menu -> addAction(qde -> prj_diagramNum);
+			context_menu -> addAction(qde -> add_column);
+			context_menu -> addAction(qde -> remove_column);
+			context_menu -> addAction(qde -> add_row);
+			context_menu -> addAction(qde -> remove_row);
+		} else {
+			context_menu -> addAction(qde -> cut);
+			context_menu -> addAction(qde -> copy);
+			context_menu -> addSeparator();
+			context_menu -> addAction(qde -> conductor_reset);
+			context_menu -> addSeparator();
+			context_menu -> addAction(qde -> delete_selection);
+			context_menu -> addAction(qde -> rotate_selection);
+			context_menu -> addAction(qde -> rotate_texts);
+			context_menu -> addAction(qde -> edit_selection);
+			context_menu -> addAction(qde -> find_element);
+			context_menu -> addAction(qde -> selection_prop);
+		}
+		
+		// affiche le menu contextuel
+		context_menu -> popup(e -> globalPos());
+	}
+	e -> accept();
+}
+
+/**
+	@return l'editeur de schemas parent ou 0
+*/
+QETDiagramEditor *DiagramView::diagramEditor() const {
+	// remonte la hierarchie des widgets
+	QWidget *w = const_cast<DiagramView *>(this);
+	while (w -> parentWidget() && !w -> isWindow()) {
+		w = w -> parentWidget();
+	}
+	// la fenetre est supposee etre un QETDiagramEditor
+	return(qobject_cast<QETDiagramEditor *>(w));
+}
+
+/**
+	Gere les double-clics sur le schema
+*/
+void DiagramView::mouseDoubleClickEvent(QMouseEvent *e) {
+	BorderTitleBlock &bi = scene -> border_and_titleblock;
+	
+	// recupere le rectangle corespondant au cartouche
+	QRectF titleblock_rect(
+		Diagram::margin,
+		Diagram::margin + bi.diagramHeight(),
+		bi.titleBlockWidth(),
+		bi.titleBlockHeight()
+	);
+	
+	// recupere le rectangle correspondant aux en-tetes des colonnes
+	QRectF columns_rect(
+		Diagram::margin,
+		Diagram::margin,
+		bi.borderWidth(),
+		bi.columnsHeaderHeight()
+	);
+	
+	// recupere le rectangle correspondant aux en-tetes des lignes
+	QRectF rows_rect(
+		Diagram::margin,
+		Diagram::margin,
+		bi.rowsHeaderWidth(),
+		bi.diagramHeight()
+	);
+	
+	// coordonnees du clic par rapport au schema
+	QPointF click_pos = viewportTransform().inverted().map(e -> pos());
+	
+	// detecte le double-clic sur le cartouche ou les colonnes
+	if (QGraphicsItem *qgi = itemAt(e -> pos())) {
+		if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
+			editConductor(c);
+		} else if (Element *element = qgraphicsitem_cast<Element *>(qgi)) {
+			editElement(element);
+		} else {
+			QGraphicsView::mouseDoubleClickEvent(e);
+		}
+	} else if (titleblock_rect.contains(click_pos) || columns_rect.contains(click_pos) || rows_rect.contains(click_pos)) {
+		// edite les proprietes du schema
+		editDiagramProperties();
+	} else {
+		QGraphicsView::mouseDoubleClickEvent(e);
+	}
+}
+
+/**
+	Cette methode ajoute l'element designe par l'emplacement location a la
+	position pos. Si necessaire, elle demande l'integration de l'element au
+	projet.
+	@see mustIntegrateElement
+*/
+void DiagramView::addDroppedElement() {
+	ElementsLocation location = next_location_;
+	QPoint pos = next_position_;
+	
+	if (!mustIntegrateElement(location)) {
+		addElementAtPos(location, pos);
+	} else {
+		QString error_msg;
+		IntegrationMoveElementsHandler *integ_handler = new IntegrationMoveElementsHandler(this);
+		QString integ_path = scene -> project() -> integrateElement(location.toString(), integ_handler, error_msg);
+		delete integ_handler;
+		if (integ_path.isEmpty()) {
+			qDebug() << "DiagramView::addDroppedElement : Impossible d'ajouter l'element. Motif : " << qPrintable(error_msg);
+			return;
+		}
+		addElementAtPos(ElementsLocation::locationFromString(integ_path), pos);
+	}
+	adjustSceneRect();
+}
+
+/**
+	@param tbt TitleBlockTemplateLocation
+*/
+void DiagramView::setDroppedTitleBlockTemplate(const TitleBlockTemplateLocation &tbt) {
+	// fetch the current title block properties
+	TitleBlockProperties titleblock_properties_before = scene -> border_and_titleblock.exportTitleBlock();
+	
+	// check the provided template is not already applied
+	QETProject *tbt_parent_project = tbt.parentProject();
+	if (tbt_parent_project && tbt_parent_project == scene -> project()) {
+		// same parent project and same name = same title block template
+		if (tbt.name() == titleblock_properties_before.template_name) return;
+	}
+	
+	// integrate the provided template into the project if needed
+	QString integrated_template_name = tbt.name();
+	if (mustIntegrateTitleBlockTemplate(tbt)) {
+		IntegrationMoveTitleBlockTemplatesHandler *handler = new IntegrationMoveTitleBlockTemplatesHandler(this);
+		//QString error_message;
+		integrated_template_name = scene -> project() -> integrateTitleBlockTemplate(tbt, handler);
+		if (integrated_template_name.isEmpty()) return;
+	}
+	
+	// apply the provided title block template
+	if (titleblock_properties_before.template_name == integrated_template_name) return;
+	TitleBlockProperties titleblock_properties_after = titleblock_properties_before;
+	titleblock_properties_after.template_name = integrated_template_name;
+	scene -> undoStack().push(new ChangeTitleBlockCommand(scene, titleblock_properties_before, titleblock_properties_after));
+	adjustSceneRect();
+}

Added: trunk/sources/diagramview.h
===================================================================
--- trunk/sources/diagramview.h	                        (rev 0)
+++ trunk/sources/diagramview.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,169 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAMVIEW_H
+#define DIAGRAMVIEW_H
+#include <QtGui>
+#include "elementslocation.h"
+#include "templatelocation.h"
+class Conductor;
+class Diagram;
+class Element;
+class IndependentTextItem;
+class DiagramImageItem;
+class QETDiagramEditor;
+/**
+	This class provides a widget to render an electric diagram in an editable,
+	interactive way.
+*/
+class DiagramView : public QGraphicsView {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	DiagramView(Diagram * = 0, QWidget * = 0);
+	virtual ~DiagramView();
+	
+	enum behavior {noAction, addingText, addingImage, dragView};
+
+	private:
+	DiagramView(const DiagramView &);
+	
+	// attributes
+	private:
+	Diagram *scene;
+	QMenu *context_menu;
+	QAction *paste_here;
+	QAction *find_element_;
+	QPoint paste_here_pos;
+	behavior current_behavior;
+	bool fresh_focus_in_;               ///< Indicate the focus was freshly gained
+	ElementsLocation next_location_;
+	QPoint next_position_;
+	QPointF reference_view_;
+	QPointF center_view_;
+	QImage image_to_add_;
+	
+	// methods
+	public:
+	QString title() const;
+	void editDiagramProperties();
+	void addColumn();
+	void removeColumn();
+	void addRow();
+	void removeRow();
+	/// @return the diagram rendered by this view
+	Diagram *diagram() { return(scene); }
+	QETDiagramEditor *diagramEditor() const;
+	bool hasSelectedItems();
+	bool hasCopiableItems();
+	bool hasDeletableItems();
+	void addText();
+	void editText();
+	void addImage();
+	void editImage();
+	IndependentTextItem *addDiagramTextAtPos(const QPointF &, const QString &text = 0);
+	DiagramImageItem *addDiagramImageAtPos(const QPointF &);
+	
+	protected:
+	virtual void mouseDoubleClickEvent(QMouseEvent *);
+	virtual void contextMenuEvent(QContextMenuEvent *);
+	virtual void wheelEvent(QWheelEvent *);
+	virtual void focusInEvent(QFocusEvent *);
+	virtual void keyPressEvent(QKeyEvent *);
+	virtual void keyReleaseEvent(QKeyEvent *);
+	virtual bool event(QEvent *);
+	virtual bool switchToVisualisationModeIfNeeded(QInputEvent *e);
+	virtual bool switchToSelectionModeIfNeeded(QInputEvent *e);
+	virtual bool isCtrlShifting(QInputEvent *);
+	virtual bool selectedItemHasFocus();
+	
+	private:
+	void mousePressEvent(QMouseEvent *);
+	void mouseMoveEvent(QMouseEvent *);
+	void mouseReleaseEvent(QMouseEvent *);
+	void dragEnterEvent(QDragEnterEvent *);
+	void dragLeaveEvent(QDragLeaveEvent *);
+	void dragMoveEvent(QDragMoveEvent *);
+	void dropEvent(QDropEvent *);
+	void handleElementDrop(QDropEvent *);
+	void handleTitleBlockDrop(QDropEvent *);
+	void handleTextDrop(QDropEvent *);
+	QRectF viewedSceneRect() const;
+	bool mustIntegrateElement(const ElementsLocation &) const;
+	bool mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation &) const;
+	bool addElementAtPos(const ElementsLocation &, const QPoint &);
+	
+	signals:
+	/// Signal emitted after the selection changed
+	void selectionChanged();
+	/// Signal emitted after the selection mode changed
+	void modeChanged();
+	/// Signal emitted after a text was added
+	void textAdded(bool);
+	/// Signal emitted after the diagram title changed
+	void titleChanged(DiagramView *, const QString &);
+	/// Signal emitted before integrating an element
+	void aboutToAddElement();
+	/// Signal emitted before integrating a title block template
+	void aboutToSetDroppedTitleBlockTemplate(const TitleBlockTemplateLocation &);
+	/// Signal emitted when users wish to locate an element from the diagram within elements collection
+	void findElementRequired(const ElementsLocation &);
+	/// Signal emitted when users wish to edit an element from the diagram
+	void editElementRequired(const ElementsLocation &);
+	/// Signal emitted when users want to edit and/or duplicate an existing title block template
+	void editTitleBlockTemplate(const QString &, bool);
+	/// Signal emitted after a image was added
+	void ImageAdded(bool);
+	/// Signal emmitted fater windows selection image have been canceled
+	void ImageAddedCanceled(bool);
+	
+	public slots:
+	void selectNothing();
+	void selectAll();
+	void selectInvert();
+	void deleteSelection();
+	void rotateSelection();
+	void rotateTexts();
+	void setVisualisationMode();
+	void setSelectionMode();
+	void zoomIn();
+	void zoomOut();
+	void zoomFit();
+	void zoomContent();
+	void zoomReset();
+	void cut();
+	void copy();
+	void paste(const QPointF & = QPointF(), QClipboard::Mode = QClipboard::Clipboard);
+	void pasteHere();
+	void adjustSceneRect();
+	void updateWindowTitle();
+	void editSelectionProperties();
+	void editSelectedConductorColor();
+	void editElement(Element *);
+	void editConductor();
+	void editConductor(Conductor *);
+	void editConductorColor(Conductor *);
+	void resetConductors();
+	
+	private slots:
+	void addDroppedElement();
+	void setDroppedTitleBlockTemplate(const TitleBlockTemplateLocation &);
+	void adjustGridToZoom();
+	void applyReadOnly();
+};
+#endif

Added: trunk/sources/editor/arceditor.cpp
===================================================================
--- trunk/sources/editor/arceditor.cpp	                        (rev 0)
+++ trunk/sources/editor/arceditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,169 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "arceditor.h"
+#include "styleeditor.h"
+#include "partarc.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param arc L'arc a editer
+	@param parent le Widget parent
+*/
+ArcEditor::ArcEditor(QETElementEditor *editor, PartArc *arc, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(arc)
+{
+	style_ = new StyleEditor(editor);
+	x = new QLineEdit();
+	y = new QLineEdit();
+	h = new QLineEdit();
+	v = new QLineEdit();
+	start_angle = new QSpinBox();
+	angle       = new QSpinBox();
+	start_angle -> setRange(-360, 360);
+	angle       -> setRange(-360, 360);
+	
+	x -> setValidator(new QDoubleValidator(x));
+	y -> setValidator(new QDoubleValidator(y));
+	h -> setValidator(new QDoubleValidator(h));
+	v -> setValidator(new QDoubleValidator(v));
+	
+	QVBoxLayout *v_layout = new QVBoxLayout(this);
+	
+	QGridLayout *grid = new QGridLayout();
+	grid -> addWidget(new QLabel(tr("Centre : ")),            0, 0);
+	grid -> addWidget(new QLabel("x"),                        1, 0, Qt::AlignRight);
+	grid -> addWidget(x,                                      1, 1);
+	grid -> addWidget(new QLabel("y"),                        1, 2);
+	grid -> addWidget(y,                                      1, 3);
+	grid -> addWidget(new QLabel(tr("Diam\350tres : ")),      2, 0);
+	grid -> addWidget(new QLabel(tr("horizontal :")),         3, 0);
+	grid -> addWidget(h,                                      3, 1);
+	grid -> addWidget(new QLabel(tr("vertical :")),           4, 0);
+	grid -> addWidget(v,                                      4, 1);
+	grid -> addWidget(new QLabel(tr("Angle de d\351part :")), 5, 0);
+	grid -> addWidget(start_angle,                            5, 1);
+	grid -> addWidget(new QLabel(tr("Angle :")),              6, 0);
+	grid -> addWidget(angle,                                  6, 1);
+	
+	v_layout -> addWidget(style_);
+	v_layout -> addLayout(grid);
+	
+	updateForm();
+	
+	activeConnections(true);
+}
+
+/// Destructeur
+ArcEditor::~ArcEditor() {
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur d'arc acceptera d'editer la primitive new_part s'il s'agit d'un
+	objet de la classe PartArc.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool ArcEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		style_ -> setPart(0);
+		return(true);
+	}
+	if (PartArc *part_arc = dynamic_cast<PartArc *>(new_part)) {
+		part = part_arc;
+		style_ -> setPart(part);
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *ArcEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour l'arc a partir a partir des donnees du formulaire
+*/
+void ArcEditor::updateArc() {
+	if (!part) return;
+	part -> setProperty("x",           x -> text().toDouble());
+	part -> setProperty("y",           y -> text().toDouble());
+	part -> setProperty("diameter_h",  h -> text().toDouble());
+	part -> setProperty("diameter_v",  v -> text().toDouble());
+	part -> setProperty("start_angle", -start_angle -> value() + 90);
+	part -> setProperty("angle",       -angle -> value());
+}
+
+/// Met a jour l'abscisse du centre de l'arc et cree un objet d'annulation
+void ArcEditor::updateArcX() { addChangePartCommand(tr("abscisse"),               part, "x",           x -> text().toDouble());       }
+/// Met a jour l'ordonnee du centre de l'arc et cree un objet d'annulation
+void ArcEditor::updateArcY() { addChangePartCommand(tr("ordonn\351e"),            part, "y",           y -> text().toDouble());       }
+/// Met a jour le diametre horizontal de l'arc et cree un objet d'annulation
+void ArcEditor::updateArcH() { addChangePartCommand(tr("diam\350tre horizontal"), part, "diameter_h",  h -> text().toDouble());       }
+/// Met a jour le diametre vertical de l'arc et cree un objet d'annulation
+void ArcEditor::updateArcV() { addChangePartCommand(tr("diam\350tre vertical"),   part, "diameter_v",  v -> text().toDouble());       }
+/// Met a jour l'angle de depart de l'arc et cree un objet d'annulation
+void ArcEditor::updateArcS() { addChangePartCommand(tr("angle de d\351part"),     part, "start_angle", -start_angle -> value() + 90); }
+/// Met a jour l'etendue de l'arc et cree un objet d'annulation
+void ArcEditor::updateArcA() { addChangePartCommand(tr("angle"),                  part, "angle",       -angle -> value());            }
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void ArcEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	x -> setText(part -> property("x").toString());
+	y -> setText(part -> property("y").toString());
+	h -> setText(part -> property("diameter_h").toString());
+	v -> setText(part -> property("diameter_v").toString());
+	start_angle -> setValue(-part -> startAngle() + 90);
+	angle -> setValue(-part -> angle());
+	activeConnections(true);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void ArcEditor::activeConnections(bool active) {
+	if (active) {
+		connect(x,           SIGNAL(editingFinished()), this, SLOT(updateArcX()));
+		connect(y,           SIGNAL(editingFinished()), this, SLOT(updateArcY()));
+		connect(h,           SIGNAL(editingFinished()), this, SLOT(updateArcH()));
+		connect(v,           SIGNAL(editingFinished()), this, SLOT(updateArcV()));
+		connect(start_angle, SIGNAL(editingFinished()), this, SLOT(updateArcS()));
+		connect(angle,       SIGNAL(editingFinished()), this, SLOT(updateArcA()));
+	} else {
+		disconnect(x,           SIGNAL(editingFinished()), this, SLOT(updateArcX()));
+		disconnect(y,           SIGNAL(editingFinished()), this, SLOT(updateArcY()));
+		disconnect(h,           SIGNAL(editingFinished()), this, SLOT(updateArcH()));
+		disconnect(v,           SIGNAL(editingFinished()), this, SLOT(updateArcV()));
+		disconnect(start_angle, SIGNAL(editingFinished()), this, SLOT(updateArcS()));
+		disconnect(angle,       SIGNAL(editingFinished()), this, SLOT(updateArcA()));
+	}
+}

Added: trunk/sources/editor/arceditor.h
===================================================================
--- trunk/sources/editor/arceditor.h	                        (rev 0)
+++ trunk/sources/editor/arceditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,61 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ARC_EDITOR_H
+#define ARC_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartArc;
+class StyleEditor;
+/**
+	This class provides a widget to edit elliptical arcs within the element editor.
+*/
+class ArcEditor : public ElementItemEditor {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ArcEditor(QETElementEditor *, PartArc * = 0, QWidget * = 0);
+	virtual ~ArcEditor();
+	private:
+	ArcEditor(const ArcEditor &);
+	
+	// attributes
+	private:
+	PartArc *part;
+	StyleEditor *style_;
+	QLineEdit *x, *y, *h, *v;
+	QSpinBox *angle, *start_angle;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateArc();
+	void updateArcX();
+	void updateArcY();
+	void updateArcH();
+	void updateArcV();
+	void updateArcS();
+	void updateArcA();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/customelementgraphicpart.cpp
===================================================================
--- trunk/sources/editor/customelementgraphicpart.cpp	                        (rev 0)
+++ trunk/sources/editor/customelementgraphicpart.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,220 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "customelementgraphicpart.h"
+
+/**
+	Ecrit les attributs de style dans un element XML
+	@param qde L'element XML a modifier
+	
+*/
+void CustomElementGraphicPart::stylesToXml(QDomElement &qde) const {
+	QString css_like_styles;
+	
+	css_like_styles += "line-style:";
+	if      (_linestyle == DashedStyle) css_like_styles += "dashed";
+	if      (_linestyle == DottedStyle) css_like_styles += "dotted";
+	if      (_linestyle == DashdottedStyle)css_like_styles += "dashdotted";
+	else if (_linestyle == NormalStyle) css_like_styles += "normal";
+	
+	css_like_styles += ";line-weight:";
+	if      (_lineweight == NoneWeight)   css_like_styles += "none";
+	else if (_lineweight == ThinWeight)   css_like_styles += "thin";
+	else if (_lineweight == NormalWeight) css_like_styles += "normal";
+	
+	css_like_styles += ";filling:";
+	if      (_filling == NoneFilling)  css_like_styles += "none";
+	else if (_filling == BlackFilling) css_like_styles += "black";
+	else if (_filling == WhiteFilling) css_like_styles += "white";
+	else if (_filling == GreenFilling) css_like_styles += "green";
+	else if (_filling == BlueFilling) css_like_styles += "blue";
+	else if (_filling == RedFilling) css_like_styles += "red";
+
+	
+	css_like_styles += ";color:";
+	if      (_color == WhiteColor) css_like_styles += "white";
+	else if (_color == BlackColor) css_like_styles += "black";
+	else if (_color == GreenColor) css_like_styles += "green";
+	else if (_color == RedColor) css_like_styles += "red";
+	else if (_color == BlueColor) css_like_styles += "blue";
+	
+	qde.setAttribute("style", css_like_styles);
+	qde.setAttribute("antialias", _antialiased ? "true" : "false");
+}
+
+/**
+	Lit les attributs de style depuis un element XML
+	@param qde L'element XML a analyser
+*/
+void CustomElementGraphicPart::stylesFromXml(const QDomElement &qde) {
+	resetStyles();
+	
+	// recupere la liste des couples style / valeur
+	QStringList styles = qde.attribute("style").split(";", QString::SkipEmptyParts);
+	
+	// analyse chaque couple
+	QRegExp rx("^\\s*([a-z-]+)\\s*:\\s*([a-z-]+)\\s*$");
+	foreach (QString style, styles) {
+		if (!rx.exactMatch(style)) continue;
+		QString style_name = rx.cap(1);
+		QString style_value = rx.cap(2);
+		if (style_name == "line-style") {
+			if      (style_value == "dashed") _linestyle = DashedStyle;
+			if      (style_value == "dotted") _linestyle = DottedStyle;
+			if      (style_value == "dashdotted") _linestyle = DashdottedStyle;
+			else if (style_value == "normal") _linestyle = NormalStyle;
+			// il n'y a pas de else car les valeurs non conformes sont ignorees (idem par la suite)
+		} else if (style_name == "line-weight") {
+			if      (style_value == "thin")   _lineweight = ThinWeight;
+			else if (style_value == "normal") _lineweight = NormalWeight;
+			else if (style_value == "none")   _lineweight = NoneWeight;
+		} else if (style_name == "filling") {
+			if      (style_value == "white") _filling = WhiteFilling;
+			else if (style_value == "black") _filling = BlackFilling;
+			else if (style_value == "red") _filling   = RedFilling;
+			else if (style_value == "green") _filling = GreenFilling;
+			else if (style_value == "blue") _filling  = BlueFilling;
+			else if (style_value == "none")  _filling = NoneFilling;
+		} else if (style_name == "color") {
+			if      (style_value == "black") _color = BlackColor;
+			else if (style_value == "white") _color = WhiteColor;
+			else if (style_value == "green") _color = GreenColor;
+			else if (style_value == "red") _color   = RedColor;
+			else if (style_value == "blue") _color  = BlueColor;
+		}
+	}
+	
+	// recupere l'antialiasing
+	_antialiased = qde.attribute("antialias") == "true";
+}
+
+/**
+	Remet les styles par defaut
+*/
+void CustomElementGraphicPart::resetStyles() {
+	_linestyle = NormalStyle;
+	_lineweight = NormalWeight;
+	_filling = NoneFilling;
+	_color = BlackColor;
+	_antialiased = false;
+}
+
+/**
+	Applique les styles a un Qpainter
+	@param painter QPainter a modifier
+*/
+void CustomElementGraphicPart::applyStylesToQPainter(QPainter &painter) const {
+	// recupere le QPen et la QBrush du QPainter
+	QPen pen = painter.pen();
+	QBrush brush = painter.brush();
+	
+	// applique le style de trait
+	if      (_linestyle == DashedStyle) pen.setStyle(Qt::DashLine);
+	if      (_linestyle == DashdottedStyle) pen.setStyle(Qt::DashDotLine);
+	if      (_linestyle == DottedStyle) pen.setStyle(Qt::DotLine);
+	else if (_linestyle == NormalStyle) pen.setStyle(Qt::SolidLine);
+	
+	// applique l'epaisseur de trait
+	if      (_lineweight == NoneWeight)   pen.setColor(QColor(0, 0, 0, 0));
+	else if (_lineweight == ThinWeight)   pen.setWidth(0);
+	else if (_lineweight == NormalWeight) pen.setWidthF(1.0);
+	
+	// applique le remplissage
+	if (_filling == NoneFilling) {
+		brush.setStyle(Qt::NoBrush);
+	} else if (_filling == BlackFilling) {
+		brush.setStyle(Qt::SolidPattern);
+		brush.setColor(Qt::black);
+	} else if (_filling == WhiteFilling) {
+		brush.setStyle(Qt::SolidPattern);
+		brush.setColor(Qt::white);
+	} else if (_filling == GreenFilling) {
+		brush.setStyle(Qt::SolidPattern);
+		brush.setColor(Qt::green);
+	} else if (_filling == RedFilling) {
+		brush.setStyle(Qt::SolidPattern);
+		brush.setColor(Qt::red);
+	} else if (_filling == BlueFilling) {
+		brush.setStyle(Qt::SolidPattern);
+		brush.setColor(Qt::blue);
+	}
+	
+	// applique la couleur de trait
+	if      (_color == WhiteColor) pen.setColor(QColor(255, 255, 255, pen.color().alpha()));
+	else if (_color == BlackColor) pen.setColor(QColor(  0,   0,   0, pen.color().alpha()));
+	else if (_color == GreenColor) pen.setColor(QColor(Qt::green));
+	else if (_color == RedColor) pen.setColor(QColor(Qt::red));
+	else if (_color == BlueColor) pen.setColor(QColor(Qt::blue));
+	
+	
+	// applique l'antialiasing
+	painter.setRenderHint(QPainter::Antialiasing,          _antialiased);
+	painter.setRenderHint(QPainter::TextAntialiasing,      _antialiased);
+	painter.setRenderHint(QPainter::SmoothPixmapTransform, _antialiased);
+	
+	painter.setPen(pen);
+	painter.setBrush(brush);
+}
+
+/**
+	Specifie la valeur d'une propriete de style donnee.
+	@param property propriete a modifier. Valeurs acceptees :
+		* line-style : type de trait (@see LineStyle)
+		* line-weight : epaisseur du traut (@see LineWeight)
+		* filling : couleur de remplissage (@see Color)
+		* color : couleur du trait (@see Color)
+		* antialias : utiliser l'antialiasing ou non (booleen)
+	@param value Valeur a attribuer a la propriete
+*/
+void CustomElementGraphicPart::setProperty(const QString &property, const QVariant &value) {
+	if (property == "line-style") {
+		setLineStyle(static_cast<LineStyle>(value.toInt()));
+	} else if (property == "line-weight") {
+		setLineWeight(static_cast<LineWeight>(value.toInt()));
+	} else if (property == "filling") {
+		setFilling(static_cast<Filling>(value.toInt()));
+	} else if (property == "color") {
+		setColor(static_cast<Color>(value.toInt()));
+	} else if (property == "antialias") {
+		setAntialiased(value.toBool());
+	}
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete de style donnee.
+	@param property propriete lue. Valeurs acceptees :
+		* line-style : type de trait (@see LineStyle)
+		* line-weight : epaisseur du traut (@see LineWeight)
+		* filling : couleur de remplissage (@see Color)
+		* color : couleur du trait (@see Color)
+		* antialias : utiliser l'antialiasing ou non (booleen)
+	@return La valeur de la propriete property
+*/
+QVariant CustomElementGraphicPart::property(const QString &property) {
+	if (property == "line-style") {
+		return(lineStyle());
+	} else if (property == "line-weight") {
+		return(lineWeight());
+	} else if (property == "filling") {
+		return(filling());
+	} else if (property == "color") {
+		return(color());
+	} else if (property == "antialias") {
+		return(antialiased());
+	}
+	return(QVariant());
+}

Added: trunk/sources/editor/customelementgraphicpart.h
===================================================================
--- trunk/sources/editor/customelementgraphicpart.h	                        (rev 0)
+++ trunk/sources/editor/customelementgraphicpart.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,192 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CUSTOM_ELEMENT_GRAPHIC_PART_H
+#define CUSTOM_ELEMENT_GRAPHIC_PART_H
+#include <QPainter>
+#include "customelementpart.h"
+#include "styleeditor.h"
+class QETElementEditor;
+typedef CustomElementGraphicPart CEGP;
+/**
+	This class represents an element visual/geometric primitive. It provides
+	methods to manage style attributes common to most primitives.
+*/
+class CustomElementGraphicPart : public CustomElementPart {
+	public:
+	/// This enum lists the various line styles available to draw primitives.
+	enum LineStyle {
+		NormalStyle, ///< Normal line
+		DashedStyle, ///< Dashed line
+		DottedStyle,  ///< Dotted line
+		DashdottedStyle  ///< Dashdotted  line
+	};
+	
+	/// This enum lists the various line weights available to draw primitives.
+	enum LineWeight {
+		NormalWeight, ///< Normal line
+		ThinWeight,   ///< Thin line
+		NoneWeight    ///< Invisible line
+	};
+	
+	/// This enum lists the various filling colors available to draw primitives.
+	enum Filling {
+		NoneFilling,  ///< No filling (i.e. transparent)
+		BlackFilling, ///< Black filling
+		WhiteFilling,  ///< White filling
+		GreenFilling, ///< Green filling
+		RedFilling,  ///< Red filling
+		BlueFilling  ///< Green filling
+	};
+	
+	/// This enum lists the various line colors available to draw primitives.
+	enum Color {
+		BlackColor, ///< Black line
+		WhiteColor, ///< White line
+		GreenColor, ///< Green line
+		RedColor,  ///<  Red line
+		BlueColor  ///<  Blue line
+	};
+	
+	// constructors, destructor
+	public:
+	/**
+		Constructor
+		@param editor Element editor this primitive lives in.
+	*/
+	CustomElementGraphicPart(QETElementEditor *editor) :
+		CustomElementPart(editor),
+		_linestyle(NormalStyle),
+		_lineweight(NormalWeight),
+		_filling(NoneFilling),
+		_color(BlackColor),
+		_antialiased(false)
+	{
+	};
+	
+	/// Destructor
+	virtual ~CustomElementGraphicPart() {
+	};
+	
+	// attributes
+	private:
+	LineStyle _linestyle;
+	LineWeight _lineweight;
+	Filling _filling ;
+	Color _color;
+	bool _antialiased;
+	
+	// methods
+	public:
+	void setLineStyle(LineStyle);
+	void setLineWeight(LineWeight);
+	void setFilling(Filling);
+	void setColor(Color);
+	void setAntialiased(bool);
+	
+	LineStyle lineStyle() const;
+	LineWeight lineWeight() const;
+	Filling filling() const;
+	Color color() const;
+	bool antialiased() const;
+	
+	void setProperty(const QString &, const QVariant &);
+	QVariant property(const QString &);
+	
+	protected:
+	void stylesToXml(QDomElement &) const;
+	void stylesFromXml(const QDomElement &);
+	void resetStyles();
+	void applyStylesToQPainter(QPainter &) const;
+};
+
+/**
+	Set the primitive line style.
+	@param ls the new line style
+*/
+inline void CustomElementGraphicPart::setLineStyle(LineStyle ls) {
+	_linestyle = ls;
+}
+
+/**
+	Set the primitive line weight.
+	@param lw the new line weight
+*/
+inline void CustomElementGraphicPart::setLineWeight(LineWeight lw) {
+	_lineweight = lw;
+}
+
+/**
+	Set the filling color.
+	@param f the new filling color
+*/
+inline void CustomElementGraphicPart::setFilling(Filling f) {
+	_filling = f;
+}
+
+/**
+	Set the line color.
+	@param c the new line color
+*/
+inline void CustomElementGraphicPart::setColor(Color c) {
+	_color = c;
+}
+
+/**
+	@return the current line style
+*/
+inline CustomElementGraphicPart::LineStyle CustomElementGraphicPart::lineStyle() const {
+	return(_linestyle);
+}
+
+/**
+	@return the current line weight
+*/
+inline CustomElementGraphicPart::LineWeight CustomElementGraphicPart::lineWeight() const {
+	return(_lineweight);
+}
+
+/**
+	@return the current filling color
+*/
+inline CustomElementGraphicPart::Filling CustomElementGraphicPart::filling() const {
+	return(_filling);
+}
+
+/**
+	@return the current line color
+*/
+inline CustomElementGraphicPart::Color CustomElementGraphicPart::color() const {
+	return(_color);
+}
+
+/**
+	Set whether the primitive should be drawn antialiased.
+	@param aa True to enable antialiasing, false to disable it.
+*/
+inline void CustomElementGraphicPart::setAntialiased(bool aa) {
+	_antialiased = aa;
+}
+
+/**
+	@return whether the primitive is drawn antialiased.
+*/
+inline bool CustomElementGraphicPart::antialiased() const {
+	return(_antialiased);
+}
+
+#endif

Added: trunk/sources/editor/customelementpart.cpp
===================================================================
--- trunk/sources/editor/customelementpart.cpp	                        (rev 0)
+++ trunk/sources/editor/customelementpart.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,141 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "customelementpart.h"
+#include "qetgraphicsitem/customelement.h"
+#include "qetelementeditor.h"
+
+/// @return le QETElementEditor auquel cet editeur appartient
+QETElementEditor *CustomElementPart::elementEditor() const {
+	return(element_editor);
+}
+
+/**
+	Appelle le slot updateCurrentPartEditor de l'editeur
+	@see QETElementEditor::updateCurrentPartEditor()
+*/
+void CustomElementPart::updateCurrentPartEditor() const {
+	if (element_editor) {
+		element_editor -> updateCurrentPartEditor();
+	}
+}
+
+/// @return l'ElementScene contenant les parties editees par cet editeur
+ElementScene *CustomElementPart::elementScene() const {
+	return(element_editor -> elementScene());
+}
+
+/// @return la QUndoStack a utiliser pour les annulations
+QUndoStack &CustomElementPart::undoStack() const {
+	return(elementScene() -> undoStack());
+}
+
+/// @return this primitive as a QGraphicsItem
+QGraphicsItem *CustomElementPart::toItem() {
+	return(dynamic_cast<QGraphicsItem *>(this));
+}
+
+/**
+	This method is called by the decorator when it manages only a single
+	primitive. This brings the possibility to implement custom behaviour, such
+	as text edition, points edition or specific resizing.
+	The default implementation does nothing.
+*/
+void CustomElementPart::setDecorator(ElementPrimitiveDecorator *decorator) {
+	Q_UNUSED(decorator)
+}
+
+/**
+	This method is called by the decorator when it needs to determine the best
+	way to interactively scale a primitive. It is typically called when only a
+	single primitive is being scaled.
+	The default implementation systematically returns
+	QET::SnapScalingPointToGrid
+*/
+QET::ScalingMethod CustomElementPart::preferredScalingMethod() const {
+	return(QET::SnapScalingPointToGrid);
+}
+
+/**
+	This method is called by the decorator when it manages only a single
+	primitive and it received a mouse press event.
+	The implementation should return true if the primitive accepts the event, false otherwise.
+	The default implementation returns false.
+*/
+bool CustomElementPart::singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
+	return(false);
+}
+
+/**
+	This method is called by the decorator when it manages only a single
+	primitive and it received a mouse move event.
+	The implementation should return true if the primitive accepts the event, false otherwise.
+	The default implementation returns false.
+*/
+bool CustomElementPart::singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
+	return(false);
+}
+
+/**
+	This method is called by the decorator when it manages only a single
+	primitive and it received a mouse release event.
+	The implementation should return true if the primitive accepts the event, false otherwise.
+	The default implementation returns false.
+*/
+bool CustomElementPart::singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
+	return(false);
+}
+
+/**
+	This method is called by the decorator when it manages only a single
+	primitive and it received a mouse double click event.
+	The implementation should return true if the primitive accepts the event, false otherwise.
+	The default implementation returns false.
+*/
+bool CustomElementPart::singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
+	return(false);
+}
+
+/**
+	Helper method to map points in CustomElementPart::handleUserTransformation()
+	@param initial_selection_rect Selection rectangle when the movement started, in scene coordinates
+	@param new_selection_rect New selection rectangle, in scene coordinates
+	@param points List of points when the movement started, in scene coordinates.
+	@return The list of points mapped from initial_selection_rect to new_selection_rect
+*/
+QList<QPointF> CustomElementPart::mapPoints(const QRectF &initial_selection_rect, const QRectF &new_selection_rect, const QList<QPointF> &points) {
+	QList<QPointF> new_points;
+	if (!points.count()) return(new_points);
+	
+	// compare the new selection rectangle with the stored one to get the scaling ratio
+	qreal sx = new_selection_rect.width() / initial_selection_rect.width();
+	qreal sy = new_selection_rect.height() / initial_selection_rect.height();
+	
+	QPointF initial_top_left = initial_selection_rect.topLeft();
+	qreal new_top_left_x = new_selection_rect.x();
+	qreal new_top_left_y = new_selection_rect.y();
+	
+	foreach (QPointF point, points) {
+		QPointF point_offset = point - initial_top_left;
+		new_points << QPointF(
+			new_top_left_x + (point_offset.rx() * sx),
+			new_top_left_y + (point_offset.y() * sy)
+		);
+	}
+	
+	return(new_points);
+}

Added: trunk/sources/editor/customelementpart.h
===================================================================
--- trunk/sources/editor/customelementpart.h	                        (rev 0)
+++ trunk/sources/editor/customelementpart.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,114 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CUSTOM_ELEMENT_PART_H
+#define CUSTOM_ELEMENT_PART_H
+#include <QtGui>
+#include <QtXml>
+#include <QImage>
+#include "qet.h"
+class CustomElement;
+class ElementPrimitiveDecorator;
+class ElementScene;
+class QETElementEditor;
+
+/**
+	This abstract class represents a primitive of the visual representation of an
+	electrical element. The Element, FixedElement and CustomElement classes do not
+	embed its attributes and methods in order to remain lightweight; indeed, there
+	is no point for those classes to store their visual representation with
+	anything more complex than a QImage.
+*/
+class CustomElementPart {
+	// constructors, destructor
+	public:
+	/**
+		Constructor
+		@param editor Element editor this primitive is attached to
+	*/
+	CustomElementPart(QETElementEditor *editor) : element_editor(editor) {}
+	/// Destructor
+	virtual ~CustomElementPart() {}
+	
+	private:
+	CustomElementPart(const CustomElementPart &);
+	
+	// attributes
+	private:
+	QETElementEditor *element_editor;
+	
+	// methods
+	public:
+	/**
+		Load the primitive from an XML element that describes it
+	*/
+	virtual void fromXml(const QDomElement &) = 0;
+	/**
+		Export the primitive as an XML element
+	*/
+	virtual const QDomElement toXml(QDomDocument &) const = 0;
+	/**
+		Set a specific property of the primitive
+	*/
+	virtual void setProperty(const QString &, const QVariant &) = 0;
+	/**
+		Get the current value of a specific primitive property
+	*/
+	virtual QVariant property(const QString &) = 0;
+	/**
+		@return whether the primitive appears to be useless (e.g. 0-length line)
+		Typically, useless primitives are discarded when saving the element.
+	*/
+	virtual bool isUseless() const = 0;
+	virtual QRectF sceneGeometricRect() const = 0;
+	/**
+		Inform this part a user-induced transformation is about to begin. This method can be used to save data required by handleUserTransformation().
+	*/
+	virtual void startUserTransformation(const QRectF &) = 0;
+	/**
+		Make this part fit into the provided rectangle.
+	*/
+	virtual void handleUserTransformation(const QRectF &, const QRectF &) = 0;
+	/// @return a pointer to the parent element editor
+	virtual QETElementEditor *elementEditor() const;
+	/**
+		Call the updateCurrentPartEditor() slot of the editor
+		@see QETElementEditor::updateCurrentPartEditor()
+	*/
+	virtual void updateCurrentPartEditor() const;
+	/// @return a pointer to the parent editing scene
+	virtual ElementScene *elementScene() const;
+	/// @return the element editor undo stack
+	virtual QUndoStack &undoStack() const;
+	/// @return the name of the primitive
+	virtual QString name() const = 0;
+	/// @return the name that will be used as XML tag when exporting the primitive
+	virtual QString xmlName() const = 0;
+	
+	virtual QGraphicsItem *toItem();
+	
+	virtual void setDecorator(ElementPrimitiveDecorator *);
+	virtual QET::ScalingMethod preferredScalingMethod() const;
+	virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	
+	protected:
+	QList<QPointF> mapPoints(const QRectF &, const QRectF &, const QList<QPointF> &);
+};
+#endif

Added: trunk/sources/editor/editorcommands.cpp
===================================================================
--- trunk/sources/editor/editorcommands.cpp	                        (rev 0)
+++ trunk/sources/editor/editorcommands.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,662 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "editorcommands.h"
+
+/**
+	Constructs an ElementEditionCommand, thus embedding the provided \a scene and \a view.
+	@param parent Parent command
+*/
+ElementEditionCommand::ElementEditionCommand(ElementScene *scene, ElementView *view, QUndoCommand *parent):
+	QUndoCommand(parent),
+	editor_scene_(scene),
+	editor_view_(view)
+{
+}
+
+/**
+	Constructs an ElementEditionCommand, thus embedding the provided \a scene and \a view.
+	@param text Text describing the effect of the command
+	@param parent Parent command
+*/
+ElementEditionCommand::ElementEditionCommand(const QString &text, ElementScene *scene, ElementView *view, QUndoCommand *parent):
+	QUndoCommand(text, parent),
+	editor_scene_(scene),
+	editor_view_(view)
+{
+}
+
+/**
+	Destructor
+*/
+ElementEditionCommand::~ElementEditionCommand() {
+}
+
+/**
+	@return the element editor/scene the command should take place on
+*/
+ElementScene *ElementEditionCommand::elementScene() const {
+	return(editor_scene_);
+}
+
+/**
+	Define \a scene as the element editor/scene the command should take place
+*/
+void ElementEditionCommand::setElementScene(ElementScene *scene) {
+	editor_scene_ = scene;
+}
+
+/**
+	@return the view the effect of the command should be rendered on
+*/
+ElementView *ElementEditionCommand::elementView() const {
+	return(editor_view_);
+}
+
+/**
+	Define \a view as the view the effect of the command should be rendered on
+*/
+void ElementEditionCommand::setElementView(ElementView *view) {
+	editor_view_ = view;
+}
+
+/*** DeletePartsCommand ***/
+/**
+	Constructeur
+	@param scene ElementScene concernee
+	@param parts Liste des parties supprimees
+	@param parent QUndoCommand parent
+*/
+DeletePartsCommand::DeletePartsCommand(
+	ElementScene *scene,
+	const QList<QGraphicsItem *> parts,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(QObject::tr("suppression", "undo caption"), scene, 0, parent),
+	deleted_parts(parts)
+{
+	foreach(QGraphicsItem *qgi, deleted_parts) {
+		editor_scene_ -> qgiManager().manage(qgi);
+	}
+}
+
+/// Destructeur : detruit egalement les parties supprimees
+DeletePartsCommand::~DeletePartsCommand() {
+	foreach(QGraphicsItem *qgi, deleted_parts) {
+		editor_scene_ -> qgiManager().release(qgi);
+	}
+}
+
+/// Restaure les parties supprimees
+void DeletePartsCommand::undo() {
+	editor_scene_ -> blockSignals(true);
+	foreach(QGraphicsItem *qgi, deleted_parts) {
+		editor_scene_ -> addItem(qgi);
+	}
+	editor_scene_ -> blockSignals(false);
+}
+
+/// Supprime les parties
+void DeletePartsCommand::redo() {
+	editor_scene_ -> blockSignals(true);
+	foreach(QGraphicsItem *qgi, deleted_parts) {
+		editor_scene_ -> removeItem(qgi);
+	}
+	editor_scene_ -> blockSignals(false);
+}
+
+/*** CutPartsCommand ***/
+/**
+	Constructeur
+	@param view ElementView concernee
+	@param c Liste des parties collees
+	@param parent QUndoCommand parent
+*/
+PastePartsCommand::PastePartsCommand(
+	ElementView *view,
+	const ElementContent &c,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(view ? view -> scene() : 0, view, parent),
+	content_(c),
+	uses_offset(false),
+	first_redo(true)
+{
+	setText(QObject::tr("coller"));
+	editor_scene_ -> qgiManager().manage(content_);
+}
+
+/// Destructeur
+PastePartsCommand::~PastePartsCommand() {
+	editor_scene_ -> qgiManager().release(content_);
+}
+
+/// annule le coller
+void PastePartsCommand::undo() {
+	// enleve les parties
+	editor_scene_ -> blockSignals(true);
+	foreach(QGraphicsItem *part, content_) {
+		editor_scene_ -> removeItem(part);
+	}
+	editor_scene_ -> blockSignals(false);
+	if (uses_offset) {
+		editor_view_ -> offset_paste_count_    = old_offset_paste_count_;
+		editor_view_ -> start_top_left_corner_ = old_start_top_left_corner_;
+	}
+	editor_view_ -> adjustSceneRect();
+}
+
+/// refait le coller
+void PastePartsCommand::redo() {
+	if (first_redo) first_redo = false;
+	else {
+		// pose les parties
+		editor_scene_ -> blockSignals(true);
+		foreach(QGraphicsItem *part, content_) {
+			editor_scene_ -> addItem(part);
+		}
+		editor_scene_ -> blockSignals(false);
+		if (uses_offset) {
+			editor_view_ -> offset_paste_count_    = new_offset_paste_count_;
+			editor_view_ -> start_top_left_corner_ = new_start_top_left_corner_;
+		}
+	}
+	editor_scene_ -> slot_select(content_);
+	editor_view_ -> adjustSceneRect();
+}
+
+/**
+	Indique a cet objet d'annulation que le c/c a annuler ou refaire etait un
+	c/c avec decalage ; il faut plus d'informations pour annuler ce type de
+	collage.
+*/
+void PastePartsCommand::setOffset(int old_offset_pc, const QPointF &old_start_tlc, int new_offset_pc, const QPointF &new_start_tlc) {
+	old_offset_paste_count_    = old_offset_pc;
+	old_start_top_left_corner_ = old_start_tlc;
+	new_offset_paste_count_    = new_offset_pc;
+	new_start_top_left_corner_ = new_start_tlc;
+	uses_offset = true;
+}
+
+/*** CutPartsCommand ***/
+/**
+	Constructeur
+	@param scene ElementScene concernee
+	@param parts Liste des parties coupees
+	@param parent QUndoCommand parent
+*/
+CutPartsCommand::CutPartsCommand(
+	ElementScene *scene,
+	const QList<QGraphicsItem *> parts,
+	QUndoCommand *parent
+) :
+	DeletePartsCommand(scene, parts, parent)
+{
+	setText(QString(QObject::tr("couper des parties", "undo caption")));
+}
+
+/// Destructeur
+CutPartsCommand::~CutPartsCommand() {
+}
+
+/*** MovePartsCommand ***/
+/**
+	Constructeur
+	@param m Mouvement sous forme de QPointF
+	@param scene ElementScene concernee
+	@param parts Liste des parties deplacees
+	@param parent QUndoCommand parent
+*/
+MovePartsCommand::MovePartsCommand(
+	const QPointF &m,
+	ElementScene *scene,
+	const QList<QGraphicsItem *> parts,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(QObject::tr("d\351placement", "undo caption"), scene, 0, parent),
+	movement(m),
+	first_redo(true)
+{
+	moved_parts = parts;
+}
+
+/// Destructeur
+MovePartsCommand::~MovePartsCommand() {
+}
+
+/// Annule le deplacement
+void MovePartsCommand::undo() {
+	foreach(QGraphicsItem *qgi, moved_parts) qgi -> moveBy(-movement.x(), -movement.y());
+}
+
+/// Refait le deplacement
+void MovePartsCommand::redo() {
+	// le premier appel a redo, lors de la construction de l'objet, ne doit pas se faire
+	if (first_redo) {
+		first_redo = false;
+		return;
+	}
+	foreach(QGraphicsItem *qgi, moved_parts) qgi -> moveBy(movement.x(), movement.y());
+}
+
+/*** AddPartCommand ***/
+/**
+	Constructeur
+	@param name Nom de la partie ajoutee
+	@param scene ElementScene concernee
+	@param p partie ajoutee
+	@param parent QUndoCommand parent
+*/
+AddPartCommand::AddPartCommand(
+	const QString &name,
+	ElementScene *scene,
+	QGraphicsItem *p,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(QString(QObject::tr("ajout %1", "undo caption")).arg(name), scene, 0, parent),
+	part(p),
+	first_redo(true)
+{
+	editor_scene_ -> qgiManager().manage(part);
+}
+
+/// Destructeur
+AddPartCommand::~AddPartCommand() {
+	editor_scene_ -> qgiManager().release(part);
+}
+
+/// Annule l'ajout
+void AddPartCommand::undo() {
+	editor_scene_ -> removeItem(part);
+}
+
+/// Refait l'ajout
+void AddPartCommand::redo() {
+	// le premier appel a redo, lors de la construction de l'objet, ne doit pas se faire
+	if (first_redo) {
+		if (!part -> zValue()) {
+			// the added part has no specific zValue already defined, we put it
+			// above existing items (but still under terminals)
+			QList<QGraphicsItem *> existing_items = editor_scene_ -> zItems(ElementScene::SortByZValue | ElementScene::SelectedOrNot);
+			qreal z = existing_items.count() ? existing_items.last() -> zValue() + 1 : 1;
+			part -> setZValue(z);
+		}
+		editor_scene_ -> clearSelection();
+		part -> setSelected(true);
+		first_redo = false;
+		return;
+	}
+	editor_scene_ -> addItem(part);
+}
+
+/**
+	Constructeur
+	@param name   nom de la propriete modifiee
+	@param part   partie modifiee
+	@param prop   propriete modifiee
+	@param old_v  ancienne valeur
+	@param new_v  nouvelle valeur
+	@param parent qUndoCommand parent
+*/
+ChangePartCommand::ChangePartCommand(
+	const QString &name,
+	CustomElementPart *part,
+	const QString &prop,
+	const QVariant &old_v,
+	const QVariant &new_v,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(QString(QObject::tr("modification %1", "undo caption")).arg(name), 0, 0, parent),
+	cep(part),
+	property(prop),
+	old_value(old_v),
+	new_value(new_v)
+{
+}
+
+/// Destructeur
+ChangePartCommand::~ChangePartCommand() {
+}
+
+/// Annule le changement
+void ChangePartCommand::undo() {
+	cep -> setProperty(property, old_value);
+}
+
+/// Refait le changement
+void ChangePartCommand::redo() {
+	cep -> setProperty(property, new_value);
+}
+
+/**
+	Constructeur
+	@param p Polygone edite
+	@param o_points points avant le changement
+	@param n_points points apres le changement
+	@param parent QUndoCommand parent
+*/
+ChangePolygonPointsCommand::ChangePolygonPointsCommand(
+	PartPolygon *p,
+	const QVector<QPointF> &o_points,
+	const QVector<QPointF> &n_points,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(QObject::tr("modification points polygone", "undo caption"), 0, 0, parent),
+	polygon(p),
+	old_points(o_points),
+	new_points(n_points)
+{
+}
+
+/// Destructeur
+ChangePolygonPointsCommand::~ChangePolygonPointsCommand() {
+}
+
+/// Annule le changement
+void ChangePolygonPointsCommand::undo() {
+	polygon -> setPolygon(old_points);
+}
+
+/// Refait le changement
+void ChangePolygonPointsCommand::redo() {
+	polygon -> setPolygon(new_points);
+}
+
+/**
+	Constructeur
+	@param element_scene Element edite
+	@param before Listes des noms avant changement
+	@param after Listes des noms apres changement
+	@param parent QUndoCommand parent
+*/
+ChangeNamesCommand::ChangeNamesCommand(
+	ElementScene *element_scene,
+	const NamesList &before,
+	const NamesList &after,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(QObject::tr("modification noms", "undo caption"), element_scene, 0, parent),
+	names_before(before),
+	names_after(after)
+{
+}
+
+/// Destructeur
+ChangeNamesCommand::~ChangeNamesCommand() {
+}
+
+/// Annule le changement
+void ChangeNamesCommand::undo() {
+	editor_scene_ -> setNames(names_before);
+}
+
+/// Refait le changement
+void ChangeNamesCommand::redo() {
+	editor_scene_ -> setNames(names_after);
+}
+
+/**
+	Constructeur
+	@param elmt ElementScene concernee
+	@param o Option decrivant le type de traitement applique aux zValues des parties de l'element
+	@param parent QUndoCommand parent
+*/
+ChangeZValueCommand::ChangeZValueCommand(
+	ElementScene *elmt,
+	ChangeZValueCommand::Option o,
+	QUndoCommand *parent
+) :
+	ElementEditionCommand(elmt, 0, parent),
+	option(o)
+{
+	// retrieve all primitives but terminals
+	QList<QGraphicsItem *> items_list = editor_scene_ -> zItems(ElementScene::SortByZValue | ElementScene::SelectedOrNot);
+	
+	// prend un snapshot des zValues
+	foreach(QGraphicsItem *qgi, items_list) undo_hash.insert(qgi, qgi -> zValue());
+	
+	// choisit le nom en fonction du traitement
+	if (option == BringForward) {
+		setText(QObject::tr("amener au premier plan", "undo caption"));
+		applyBringForward(items_list);
+	} else if (option == Raise) {
+		setText(QObject::tr("rapprocher", "undo caption"));
+		applyRaise(items_list);
+	} else if (option == Lower) {
+		setText(QObject::tr("\351loigner", "undo caption"));
+		applyLower(items_list);
+	} else if (option == SendBackward) {
+		setText(QObject::tr("envoyer au fond", "undo caption"));
+		applySendBackward(items_list);
+	}
+}
+
+/// Destructeur
+ChangeZValueCommand::~ChangeZValueCommand() {
+}
+
+/// Annule les changements de zValue
+void ChangeZValueCommand::undo() {
+	foreach(QGraphicsItem *qgi, undo_hash.keys()) qgi -> setZValue(undo_hash[qgi]);
+}
+
+/// Refait les changements de zValue
+void ChangeZValueCommand::redo() {
+	foreach(QGraphicsItem *qgi, redo_hash.keys()) qgi -> setZValue(redo_hash[qgi]);
+}
+
+/**
+	Amene les elements selectionnes au premier plan
+	@param items_list Liste des elements (selectionnes et non selectionnes)
+*/
+void ChangeZValueCommand::applyBringForward(const QList<QGraphicsItem *> &items_list) {
+	QList<QGraphicsItem *> non_selected_items = items_list;
+	QList<QGraphicsItem *> selected_items;
+	foreach(QGraphicsItem *qgi, non_selected_items) {
+		if (qgi -> isSelected()) {
+			selected_items << qgi;
+			non_selected_items.removeAt(non_selected_items.indexOf(qgi));
+		}
+	}
+	int z = 1;
+	foreach(QGraphicsItem *qgi, non_selected_items) redo_hash.insert(qgi, z ++);
+	foreach(QGraphicsItem *qgi,     selected_items) redo_hash.insert(qgi, z ++);
+}
+
+/**
+	Remonte les elements selectionnes d'un plan
+	@param items_list Liste des elements (selectionnes et non selectionnes)
+*/
+void ChangeZValueCommand::applyRaise(const QList<QGraphicsItem *> &items_list) {
+	QList<QGraphicsItem *> my_items_list = items_list;
+	
+	for (int i = my_items_list.count() - 2 ; i >= 0 ; -- i) {
+		if (my_items_list[i] -> isSelected()) {
+			if (!my_items_list[i +1] -> isSelected()) {
+				my_items_list.swap(i, i + 1);
+			}
+		}
+	}
+	int z = 1;
+	foreach(QGraphicsItem *qgi, my_items_list) redo_hash.insert(qgi, z ++);
+}
+
+/**
+	Descend les elements selectionnes d'un plan
+	@param items_list Liste des elements (selectionnes et non selectionnes)
+*/
+void ChangeZValueCommand::applyLower(const QList<QGraphicsItem *> &items_list) {
+	QList<QGraphicsItem *> my_items_list = items_list;
+	
+	for (int i = 1 ; i < my_items_list.count() ; ++ i) {
+		if (my_items_list[i] -> isSelected()) {
+			if (!my_items_list[i - 1] -> isSelected()) {
+				my_items_list.swap(i, i - 1);
+			}
+		}
+	}
+	
+	int z = 1;
+	foreach(QGraphicsItem *qgi, my_items_list) redo_hash.insert(qgi, z ++);
+}
+
+/**
+	Envoie les elements selectionnes au fond
+	@param items_list Liste des elements (selectionnes et non selectionnes)
+*/
+void ChangeZValueCommand::applySendBackward(const QList<QGraphicsItem *> &items_list) {
+	QList<QGraphicsItem *> non_selected_items = items_list;
+	QList<QGraphicsItem *> selected_items;
+	foreach(QGraphicsItem *qgi, non_selected_items) {
+		if (qgi -> isSelected()) {
+			selected_items << qgi;
+			non_selected_items.removeAt(non_selected_items.indexOf(qgi));
+		}
+	}
+	int z = 1;
+	foreach(QGraphicsItem *qgi,     selected_items) redo_hash.insert(qgi, z ++);
+	foreach(QGraphicsItem *qgi, non_selected_items) redo_hash.insert(qgi, z ++);
+}
+
+/**
+	Constructeur
+	@param elmt ElementScene concernee
+	@param old_infos Informations complementaires precedentes
+	@param new_infos Nouvelles informations complementaires
+	@param parent QUndoCommand parent
+*/
+ChangeInformationsCommand::ChangeInformationsCommand(ElementScene *elmt, const QString &old_infos, const QString &new_infos, QUndoCommand *parent) :
+	ElementEditionCommand(QObject::tr("modification informations complementaires", "undo caption"), elmt, 0, parent),
+	old_informations_(old_infos),
+	new_informations_(new_infos)
+{
+}
+
+/// Destructeur
+ChangeInformationsCommand::~ChangeInformationsCommand() {
+}
+
+/// Annule le changement d'autorisation pour les connexions internes
+void ChangeInformationsCommand::undo() {
+	editor_scene_ -> setInformations(old_informations_);
+}
+
+/// Refait le changement d'autorisation pour les connexions internes
+void ChangeInformationsCommand::redo() {
+	editor_scene_ -> setInformations(new_informations_);
+}
+
+/**
+	Constructor
+	@param scene Modified ElementScene
+	@param parent Parent QUndoCommand
+*/
+ScalePartsCommand::ScalePartsCommand(ElementScene *scene, QUndoCommand * parent) :
+	ElementEditionCommand(scene, 0, parent),
+	first_redo(true)
+{
+}
+
+/**
+	Destructor
+*/
+ScalePartsCommand::~ScalePartsCommand() {
+}
+
+/**
+	Undo the scaling operation
+*/
+void ScalePartsCommand::undo() {
+	scale(new_rect_, original_rect_);
+}
+
+/**
+	Redo the scaling operation
+*/
+void ScalePartsCommand::redo() {
+	if (first_redo) {
+		first_redo = false;
+		return;
+	}
+	scale(original_rect_, new_rect_);
+}
+
+/**
+	@return the element editor/scene the command should take place on
+*/
+ElementScene *ScalePartsCommand::elementScene() const {
+	return(editor_scene_);
+}
+
+/**
+	Set \a primitives as the list of primitives to be scaled by this command
+*/
+void ScalePartsCommand::setScaledPrimitives(const QList<CustomElementPart *> &primitives) {
+	scaled_primitives_ = primitives;
+	adjustText();
+}
+
+/**
+	@return the list of primitives to be scaled by this command
+*/
+QList<CustomElementPart *> ScalePartsCommand::scaledPrimitives() const {
+	return(scaled_primitives_);
+}
+
+/**
+	Define the transformation applied by this command
+	@param original_rect Bounding rectangle for all scaled primitives before the operation
+	@param original_rect Bounding rectangle for all scaled primitives after the operation
+*/
+void ScalePartsCommand::setTransformation(const QRectF &original_rect, const QRectF &new_rect) {
+	original_rect_ = original_rect;
+	new_rect_ = new_rect;
+}
+
+/**
+	@return the transformation applied by this command. The returned rectangles
+	are the bounding rectangles for all scaled primitives respectively before
+	and after the operation.
+*/
+QPair<QRectF, QRectF> ScalePartsCommand::transformation() {
+	return(QPair<QRectF, QRectF>(original_rect_, new_rect_));
+}
+
+/**
+	Apply the scaling operation from \a before to \a after.
+*/
+void ScalePartsCommand::scale(const QRectF &before, const QRectF &after) {
+	if (!scaled_primitives_.count()) return;
+	if (before == after) return;
+	if (!before.width() || !before.height()) return; // cowardly flee division by zero FIXME?
+	
+	foreach (CustomElementPart *part_item, scaled_primitives_) {
+		part_item -> startUserTransformation(before);
+		part_item -> handleUserTransformation(before, after);
+	}
+}
+
+/**
+	Generate the text describing what this command does exactly.
+*/
+void ScalePartsCommand::adjustText() {
+	if (scaled_primitives_.count() == 1) {
+		setText(QObject::tr("redimensionnement %1", "undo caption -- %1 is the resized primitive type name").arg(scaled_primitives_.first() -> name()));
+	} else {
+		setText(QObject::tr("redimensionnement de %1 primitives", "undo caption -- %1 always > 1").arg(scaled_primitives_.count()));
+	}
+}

Added: trunk/sources/editor/editorcommands.h
===================================================================
--- trunk/sources/editor/editorcommands.h	                        (rev 0)
+++ trunk/sources/editor/editorcommands.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,352 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef EDITOR_COMMANDS_H
+#define EDITOR_COMMANDS_H
+#include "customelementpart.h"
+#include "partpolygon.h"
+#include "elementview.h"
+#include "elementscene.h"
+#include "elementcontent.h"
+#include "qgimanager.h"
+#include <QtGui>
+
+/**
+	ElementEditionCommand is the base class for all commands classes involved in
+	the edition of an electrical element. It provides commonly required methods
+	and attributes, such as accessors to the modified scene and view.
+*/
+class ElementEditionCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ElementEditionCommand(ElementScene * = 0, ElementView * = 0, QUndoCommand * = 0);
+	ElementEditionCommand(const QString &, ElementScene * = 0, ElementView * = 0, QUndoCommand * = 0);
+	virtual ~ElementEditionCommand();
+	private:
+	ElementEditionCommand(const ElementEditionCommand &);
+	
+	// methods
+	public:
+	ElementScene *elementScene() const;
+	void setElementScene(ElementScene *);
+	ElementView *elementView() const;
+	void setElementView(ElementView *);
+	
+	// attributes
+	protected:
+	/// Element editor/view/scene the command should take place on
+	ElementScene *editor_scene_;
+	ElementView *editor_view_;
+};
+
+/**
+	This command deletes one or several primitives/parts when editing an
+	electrical element.
+*/
+class DeletePartsCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	DeletePartsCommand(ElementScene *, const QList<QGraphicsItem *>, QUndoCommand * = 0);
+	virtual ~DeletePartsCommand();
+	private:
+	DeletePartsCommand(const DeletePartsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// Deleted primitives
+	QList<QGraphicsItem *> deleted_parts;
+};
+
+/**
+	This command pastes primitives when editing an electrical element.
+*/
+class PastePartsCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	PastePartsCommand(ElementView *, const ElementContent &, QUndoCommand * = 0);
+	virtual ~PastePartsCommand();
+	private:
+	PastePartsCommand(const PastePartsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void setOffset(int, const QPointF &, int, const QPointF &);
+	
+	// attributes
+	private:
+	/// Pasted content
+	ElementContent content_;
+	/// Data required to undo a copy/paste with offset
+	int old_offset_paste_count_;
+	QPointF old_start_top_left_corner_;
+	int new_offset_paste_count_;
+	QPointF new_start_top_left_corner_;
+	bool uses_offset;
+	/// Prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command cut primitives when editing an electrical element.
+*/
+class CutPartsCommand : public DeletePartsCommand {
+	// constructors, destructor
+	public:
+	CutPartsCommand(ElementScene *, const QList<QGraphicsItem *>, QUndoCommand * = 0);
+	virtual ~CutPartsCommand();
+	private:
+	CutPartsCommand(const CutPartsCommand &);
+};
+
+/**
+	This command moves primitives when editing an electrical element.
+*/
+class MovePartsCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	MovePartsCommand(const QPointF &, ElementScene *, const QList<QGraphicsItem *>, QUndoCommand * = 0);
+	virtual ~MovePartsCommand();
+	private:
+	MovePartsCommand(const MovePartsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// List of moved primitives
+	QList<QGraphicsItem *> moved_parts;
+	/// applied movement
+	QPointF movement;
+	/// Prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command adds a primitive when editing an electrical element.
+*/
+class AddPartCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	AddPartCommand(const QString &, ElementScene *, QGraphicsItem *, QUndoCommand * = 0);
+	virtual ~AddPartCommand();
+	private:
+	AddPartCommand(const AddPartCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// Added primitive
+	QGraphicsItem *part;
+	/// Prevent the first call to redo()
+	bool first_redo;
+};
+
+/**
+	This command changes a property of a primitive when editing an electrical
+	element.
+*/
+class ChangePartCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	ChangePartCommand(const QString &, CustomElementPart *, const QString &, const QVariant &, const QVariant &, QUndoCommand * = 0);
+	virtual ~ChangePartCommand();
+	private:
+	ChangePartCommand(const ChangePartCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// Changed primitive
+	CustomElementPart *cep;
+	/// Changed property
+	QString property;
+	/// Former value
+	QVariant old_value;
+	/// New value
+	QVariant new_value;
+};
+
+/**
+	This command changes the points of a polygon when editing an electrical
+	element.
+*/
+class ChangePolygonPointsCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	ChangePolygonPointsCommand(PartPolygon *, const QVector<QPointF> &, const QVector<QPointF> &, QUndoCommand * = 0);
+	virtual ~ChangePolygonPointsCommand();
+	private:
+	ChangePolygonPointsCommand(const ChangePolygonPointsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	/// Changed polygon
+	PartPolygon *polygon;
+	/// Former points
+	QVector<QPointF> old_points;
+	/// New points
+	QVector<QPointF> new_points;
+};
+
+/**
+	This command changes the translated names of an electrical element.
+*/
+class ChangeNamesCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	ChangeNamesCommand(ElementScene *, const NamesList &, const NamesList &, QUndoCommand * = 0);
+	virtual ~ChangeNamesCommand();
+	private:
+	ChangeNamesCommand(const ChangeNamesCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// List of former names
+	NamesList names_before;
+	/// List of new names
+	NamesList names_after;
+};
+
+/**
+	This command changes the zValue of a set of primitives when editing an
+	electrical element.
+*/
+class ChangeZValueCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	/// List the various kind of changes for the zValue
+	enum Option {
+		BringForward, ///< Bring primitives to the foreground so they have the highest zValue
+		Raise,        ///< Raise primitives one layer above their current one; zValues are incremented
+		Lower,        ///< Send primitives one layer below their current one; zValues are decremented
+		SendBackward  ///< Send primitives to the background so they have the lowest zValue
+	};
+	ChangeZValueCommand(ElementScene *, Option, QUndoCommand * = 0);
+	virtual ~ChangeZValueCommand();
+	private:
+	ChangeZValueCommand(const ChangeZValueCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	private:
+	void applyBringForward(const QList<QGraphicsItem *> &);
+	void applyRaise(const QList<QGraphicsItem *> &);
+	void applyLower(const QList<QGraphicsItem *> &);
+	void applySendBackward(const QList<QGraphicsItem *> &);
+	
+	// attributes
+	private:
+	/// associates impacted primitives with their former zValues
+	QHash<QGraphicsItem *, qreal> undo_hash;
+	/// associates impacted primitives with their new zValues
+	QHash<QGraphicsItem *, qreal> redo_hash;
+	/// kind of treatment to apply
+	Option option;
+};
+
+/**
+	This command changes extra information carried by an electrical element.
+*/
+class ChangeInformationsCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	ChangeInformationsCommand(ElementScene *, const QString &, const QString &, QUndoCommand * = 0);
+	virtual ~ChangeInformationsCommand();
+	private:
+	ChangeInformationsCommand(const ChangeInformationsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// Former information
+	QString old_informations_;
+	/// New information
+	QString new_informations_;
+};
+
+/**
+	This command scales primitives when editing an electrical element.
+*/
+class ScalePartsCommand : public ElementEditionCommand {
+	// constructors, destructor
+	public:
+	ScalePartsCommand(ElementScene * = 0, QUndoCommand * = 0);
+	virtual ~ScalePartsCommand();
+	private:
+	ScalePartsCommand(const ScalePartsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	ElementScene *elementScene() const;
+	void setScaledPrimitives(const QList<CustomElementPart *> &);
+	QList<CustomElementPart *> scaledPrimitives() const;
+	void setTransformation(const QRectF &, const QRectF &);
+	QPair<QRectF, QRectF> transformation();
+	
+	protected:
+	void scale(const QRectF &before, const QRectF &after);
+	void adjustText();
+	
+	// attributes
+	private:
+	/// List of moved primitives
+	QList<CustomElementPart *> scaled_primitives_;
+	/// original rect items fit in
+	QRectF original_rect_;
+	/// new rect items should fit in
+	QRectF new_rect_;
+	/// Prevent the first call to redo()
+	bool first_redo;
+};
+
+#endif

Added: trunk/sources/editor/elementcontent.h
===================================================================
--- trunk/sources/editor/elementcontent.h	                        (rev 0)
+++ trunk/sources/editor/elementcontent.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,31 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_CONTENT_H
+#define ELEMENT_CONTENT_H
+#include <QList>
+class QGraphicsItem;
+/**
+	When edited using the element editor, electrical elements are decomposed into
+	visual primitives. The ElementContent class represents a set of visual
+	primitives composing all or a part of an electrical element.
+	
+	Note: currently, ElementContent is a simple typedef for
+	QList\<QGraphicsItem *\>
+*/
+typedef QList<QGraphicsItem *> ElementContent;
+#endif

Added: trunk/sources/editor/elementitemeditor.cpp
===================================================================
--- trunk/sources/editor/elementitemeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/elementitemeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,95 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementitemeditor.h"
+#include "qetelementeditor.h"
+#include "editorcommands.h"
+
+/**
+	Constructeur
+	@param editor QETElementEditor auquel cet editeur appartient
+	@param parent QWidget parent de cet editeur
+*/
+ElementItemEditor::ElementItemEditor(QETElementEditor *editor, QWidget *parent) :
+	QWidget(parent),
+	element_editor(editor)
+{
+}
+
+/// @return le QETElementEditor auquel cet editeur appartient
+QETElementEditor *ElementItemEditor::elementEditor() const {
+	return(element_editor);
+}
+
+/// @return l'ElementScene contenant les parties editees par cet editeur
+ElementScene *ElementItemEditor::elementScene() const {
+	return(element_editor -> elementScene());
+}
+
+/// @return la QUndoStack a utiliser pour les annulations
+QUndoStack &ElementItemEditor::undoStack() const {
+	return(elementScene() -> undoStack());
+}
+
+/**
+	Ajoute une ChangePartCommand a l'UndoStack. L'ancienne valeur sera
+	automatiquement recuperee. A noter que cette methode ne fait rien si
+	l'ancienne valeur et la nouvelle sont egales ou encore si part vaut 0
+	@param desc   nom de la propriete modifiee
+	@param part   partie modifiee
+	@param prop   propriete modifiee
+	@param new_v  nouvelle valeur
+*/
+void ElementItemEditor::addChangePartCommand(const QString &desc, CustomElementPart *part, const QString &prop, const QVariant &new_v) {
+	// ne fait rien si part vaut 0
+	if (!part) return;
+	
+	// recupere l'ancienne valeur
+	QVariant old_v = part -> property(prop);
+	
+	// ne fait rien si l'ancienne valeur et la nouvelle sont egales 
+	if (old_v == new_v) return;
+	
+	undoStack().push(
+		new ChangePartCommand(
+			desc + " " + element_type_name,
+			part,
+			prop,
+			old_v,
+			new_v
+		)
+	);
+}
+
+/// @return Le nom du type d'element edite
+QString ElementItemEditor::elementTypeName() const {
+	return(element_type_name);
+}
+
+/// @param name Nom du type d'element edite
+void ElementItemEditor::setElementTypeName(const QString &name) {
+	element_type_name = name;
+}
+
+/**
+	Detache l'editeur de la primitive qu'il edite.
+	Equivaut a setPart(0)
+	@see setPart
+*/
+void ElementItemEditor::detach() {
+	setPart(0);
+}

Added: trunk/sources/editor/elementitemeditor.h
===================================================================
--- trunk/sources/editor/elementitemeditor.h	                        (rev 0)
+++ trunk/sources/editor/elementitemeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,56 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_ITEM_EDITOR_H
+#define ELEMENT_ITEM_EDITOR_H
+#include <QtGui>
+class QETElementEditor;
+class ElementScene;
+class CustomElementPart;
+/**
+	This is the base class for primitives editors within the element editor. It
+	provides methods to access the editor itself, the undo stack, the edition
+	scene and even a method to easily take a ChangePartCommand into account.
+*/
+class ElementItemEditor : public QWidget {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ElementItemEditor(QETElementEditor *, QWidget * = 0);
+	virtual ~ElementItemEditor() {};
+	private:
+	ElementItemEditor(const ElementItemEditor &);
+	
+	// methods
+	public:
+	virtual QETElementEditor *elementEditor() const;
+	virtual ElementScene *elementScene() const;
+	virtual QUndoStack &undoStack() const;
+	virtual void addChangePartCommand(const QString &, CustomElementPart *, const QString &, const QVariant &);
+	virtual QString elementTypeName() const;
+	virtual void setElementTypeName(const QString &);
+	virtual void detach();
+	virtual bool setPart(CustomElementPart *) = 0;
+	virtual CustomElementPart *currentPart() const = 0;
+	virtual void updateForm() = 0;
+	
+	// attributes
+	private:
+	QETElementEditor *element_editor;
+	QString element_type_name;
+};
+#endif

Added: trunk/sources/editor/elementprimitivedecorator.cpp
===================================================================
--- trunk/sources/editor/elementprimitivedecorator.cpp	                        (rev 0)
+++ trunk/sources/editor/elementprimitivedecorator.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,723 @@
+#include "elementprimitivedecorator.h"
+#include "elementscene.h"
+#include "customelementpart.h"
+#include "editorcommands.h"
+#include "qet.h"
+#include <QPainter>
+#include <QDebug>
+#include <QGraphicsSceneHoverEvent>
+#include <QStyleOptionGraphicsItem>
+#include <QGraphicsScene>
+#include <QTransform>
+
+/**
+	Constructor
+	@param parent Parent QGraphicsItem
+*/
+ElementPrimitiveDecorator::ElementPrimitiveDecorator(QGraphicsItem *parent):
+	QGraphicsObject(parent)
+{
+	init();
+}
+
+/**
+	Destructor
+*/
+ElementPrimitiveDecorator::~ElementPrimitiveDecorator() {
+}
+
+/**
+	@return the internal bouding rect, i.e. the smallest rectangle containing
+	the bounding rectangle of every selected item.
+*/
+QRectF ElementPrimitiveDecorator::internalBoundingRect() const {
+	if (!decorated_items_.count() || !scene()) return(QRectF());
+
+	//if @decorated_items_ contain one item and if this item is a vertical or horizontal partline, apply a specific methode
+	if ((decorated_items_.count() == 1) && (decorated_items_.first() -> xmlName() == "line")) {
+		QRectF horto = decorated_items_.first() -> sceneGeometricRect();
+		if (!horto.width() || !horto.height()) {
+			return (getSceneBoundingRect(decorated_items_.first() -> toItem()));
+		}
+	}
+	QRectF rect = decorated_items_.first() -> sceneGeometricRect();
+	foreach (CustomElementPart *item, decorated_items_) {
+		rect = rect.united(item -> sceneGeometricRect());
+	}
+	return(rect);
+}
+/**
+	@return the outer bounds of the decorator as a rectangle.
+*/
+QRectF ElementPrimitiveDecorator::boundingRect() const {
+	const qreal additional_margin = 2.5;
+	
+	QRectF rect = effective_bounding_rect_;
+	rect.adjust(-additional_margin, -additional_margin, additional_margin, additional_margin);
+	return(rect);
+}
+
+/**
+	Paint the contents of an item in local coordinates, using \a painter, with
+	respect to \a option and
+	@param option The option parameter provides style options for the item, such
+	as its state, exposed area and its level-of-detail hints.
+	@param The widget argument is optional. If provided, it points to the
+	widget that is being painted on; otherwise, it is 0. For cached painting,
+	widget is always 0.
+*/
+void ElementPrimitiveDecorator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
+	Q_UNUSED(option)
+	Q_UNUSED(widget)
+	painter -> save();
+	
+	// paint the original bounding rect
+	painter -> setPen(Qt::DashLine);
+	//QGraphicsItemGroup::paint(painter, option, widget);
+	painter -> drawRect(modified_bounding_rect_);
+	drawSquares(painter, option, widget);
+	
+	// uncomment to draw the real bouding rect (=adjusted internal bounding rect)
+	// painter -> setBrush(QBrush(QColor(240, 0, 0, 127)));
+	// painter -> drawRect(boundingRect());
+	painter -> restore();
+}
+
+/**
+	@param items the new list of items this decorator is suposed to manipulate.
+*/
+void ElementPrimitiveDecorator::setItems(const QList<CustomElementPart *> &items) {
+	if (CustomElementPart *single_item = singleItem()) {
+		if (items.count() == 1 && items.first() == single_item) {
+			// no actual change
+			goto end_setItems;
+		}
+		
+		// break any connection between the former single selected item (if any) and
+		// the decorator
+		single_item -> setDecorator(0);
+		if (QGraphicsObject *single_object = dynamic_cast<QGraphicsObject *>(single_item)) {
+			disconnect(single_object, 0, this, 0);
+		}
+	}
+	
+	decorated_items_ = items;
+	
+	// when only a single primitive is selected, the decorator behaves specially
+	// to enable extra features, such as text edition, internal points movements,
+	// etc.
+	if (CustomElementPart *single_item = singleItem()) {
+		single_item -> setDecorator(this);
+	}
+	
+	end_setItems:
+	adjust();
+	show();
+	if (focusItem() != this) {
+		setFocus();
+	}
+}
+
+/**
+	@param items the new list of items this decorator is suposed to manipulate.
+*/
+void ElementPrimitiveDecorator::setItems(const QList<QGraphicsItem *> &items) {
+	QList<CustomElementPart *> primitives;
+	foreach (QGraphicsItem *item, items) {
+		if (CustomElementPart *part_item = dynamic_cast<CustomElementPart *>(item)) {
+			primitives << part_item;
+		}
+	}
+	setItems(primitives);
+}
+
+/**
+	@return the list of items this decorator is supposed to manipulate
+*/
+QList<CustomElementPart *> ElementPrimitiveDecorator::items() const {
+	return(decorated_items_);
+}
+
+/**
+	@return the list of items this decorator is supposed to manipulate
+*/
+QList<QGraphicsItem *> ElementPrimitiveDecorator::graphicsItems() const {
+	QList<QGraphicsItem *> list;
+	foreach (CustomElementPart *part_item, decorated_items_) {
+		if (QGraphicsItem *item = dynamic_cast<QGraphicsItem *>(part_item)) {
+			list << item;
+		}
+	}
+	return(list);
+}
+
+/**
+	Adjust the visual decorator according to the currently assigned items.
+	It is notably called by setItems().
+*/
+void ElementPrimitiveDecorator::adjust() {
+	saveOriginalBoundingRect();
+	modified_bounding_rect_ = original_bounding_rect_;
+	adjustEffectiveBoundingRect();
+}
+
+/**
+	Handle events generated when the mouse hovers over the decorator.
+	@param event Object describing the hover event.
+*/
+void ElementPrimitiveDecorator::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+	QList<QRectF> rects = getResizingSquares();
+	QPointF pos = event -> pos();
+	
+	if (rects.at(QET::ResizeFromTopLeftCorner).contains(pos) || rects.at(QET::ResizeFromBottomRightCorner).contains(pos)) {
+		setCursor(Qt::SizeFDiagCursor);
+	} else if (rects.at(QET::ResizeFromTopRightCorner).contains(pos) || rects.at(QET::ResizeFromBottomLeftCorner).contains(pos)) {
+		setCursor(Qt::SizeBDiagCursor);
+	} else if (rects.at(QET::ResizeFromTopCenterCorner).contains(pos) || rects.at(QET::ResizeFromBottomCenterCorner).contains(pos)) {
+		setCursor(Qt::SizeVerCursor);
+	} else if (rects.at(QET::ResizeFromMiddleLeftCorner).contains(pos) || rects.at(QET::ResizeFromMiddleRightCorner).contains(pos)) {
+		setCursor(Qt::SizeHorCursor);
+	} else if (internalBoundingRect().contains(pos)) {
+		setCursor(Qt::SizeAllCursor);
+	} else {
+		setCursor(Qt::ArrowCursor);
+	}
+}
+
+/**
+	Handle event generated when mouse buttons are pressed.
+	@param event Object describing the mouse event
+*/
+void ElementPrimitiveDecorator::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+	QList<QRectF> rects = getResizingSquares();
+	QPointF pos = event -> pos();
+	
+	current_operation_square_ = resizingSquareAtPos(pos);
+	bool accept = false;
+	if (current_operation_square_ != QET::NoOperation) {
+		accept = true;
+	} else {
+		if (internalBoundingRect().contains(pos)) {
+			if (CustomElementPart *single_item = singleItem()) {
+				bool event_accepted = single_item -> singleItemPressEvent(this, event);
+				if (event_accepted) {
+					event -> ignore();
+					return;
+				}
+			}
+			current_operation_square_ = QET::MoveArea;
+			accept = true;
+		}
+	}
+	
+	if (accept) {
+		if (current_operation_square_ > QET::NoOperation) {
+			first_pos_ = latest_pos_ = mapToScene(rects.at(current_operation_square_).center());
+		} else {
+			first_pos_ = decorated_items_.at(0) -> toItem() -> scenePos();
+			latest_pos_ = event -> scenePos();
+			mouse_offset_ = event -> scenePos() - first_pos_;
+		}
+		startMovement();
+		event -> accept();
+	} else {
+		event -> ignore();
+	}
+}
+
+/**
+	Handle events generated when mouse buttons are double clicked.
+	@param event Object describing the mouse event
+*/
+void ElementPrimitiveDecorator::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
+	//QGraphicsObject::mouseDoubleClickEvent(event);
+	if (CustomElementPart *single_item = singleItem()) {
+		bool event_accepted = single_item -> singleItemDoubleClickEvent(this, event);
+		if (event_accepted) {
+			event -> ignore();
+			return;
+		}
+	}
+}
+
+/**
+	Handle event generated when the mouse is moved and the decorator is the mouse grabber item.
+	@param event Object describing the mouse event
+	@see QGraphicsScene::mouseGrabberItem()
+*/
+void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+	QList<QRectF> rects = getResizingSquares();
+	
+	QPointF scene_pos = event -> scenePos();
+	QPointF movement = scene_pos - latest_pos_;
+	
+	if (current_operation_square_ > QET::NoOperation) {
+		// This is a scaling operation.
+		
+		// For convenience purposes, we may need to adjust mouse movements.
+		QET::ScalingMethod scaling_method = scalingMethod(event);
+		if (scaling_method > QET::FreeScaling) {
+			// real, non-rounded movement from the mouse press event
+			QPointF global_movement = scene_pos - first_pos_;
+			
+			QPointF rounded_global_movement;
+			if (scaling_method == QET::SnapScalingPointToGrid) {
+				// real, rounded movement from the mouse press event
+				rounded_global_movement = snapConstPointToGrid(global_movement);
+			}
+			else {
+				QRectF new_bounding_rect = original_bounding_rect_;
+				applyMovementToRect(current_operation_square_, global_movement, new_bounding_rect);
+				
+				const qreal scale_epsilon = 20.0; // rounds to 0.05
+				QPointF delta = deltaForRoundScaling(original_bounding_rect_, new_bounding_rect, scale_epsilon);
+				
+				// real, rounded movement from the mouse press event
+				rounded_global_movement = global_movement + delta;
+			}
+			
+			// rounded position of the current mouse move event
+			QPointF rounded_scene_pos = first_pos_ + rounded_global_movement;
+			
+			// when scaling the selection, consider the center of the currently dragged resizing rectangle
+			QPointF current_position = mapToScene(rects.at(current_operation_square_).center());
+			// determine the final, effective movement
+			movement = rounded_scene_pos - current_position;
+		}
+	}
+	else if (current_operation_square_ == QET::MoveArea) {
+		// When moving the selection, consider the position of the first selected item
+		QPointF current_position = scene_pos - mouse_offset_;
+		QPointF rounded_current_position = snapConstPointToGrid(current_position);
+		movement = rounded_current_position - decorated_items_.at(0) -> toItem() -> scenePos();
+	}
+	else {
+		// Neither a movement nor a scaling operation -- perhaps the underlying item
+		// is interested in the mouse event for custom operations?
+		if (CustomElementPart *single_item = singleItem()) {
+			bool event_accepted = single_item -> singleItemMoveEvent(this, event);
+			if (event_accepted) {
+				event -> ignore();
+				return;
+			}
+		}
+	}
+	
+	QRectF bounding_rect = modified_bounding_rect_;
+	applyMovementToRect(current_operation_square_, movement, modified_bounding_rect_);
+	if (modified_bounding_rect_ != bounding_rect) {
+		adjustEffectiveBoundingRect();
+	}
+	latest_pos_ = event -> scenePos();
+	
+	if (current_operation_square_ == QET::MoveArea) {
+		translateItems(movement);
+	} else {
+		scaleItems(original_bounding_rect_, modified_bounding_rect_);
+	}
+}
+
+/**
+	Handle event generated when a mouse buttons are releaseis moved and the
+	decorator is the mouse grabber item.
+	@param event Object describing the mouse event
+	@see QGraphicsScene::mouseGrabberItem()
+*/
+void ElementPrimitiveDecorator::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(event)
+	
+	ElementEditionCommand *command = 0;
+	if (current_operation_square_ > QET::NoOperation) {
+		ScalePartsCommand *scale_command = new ScalePartsCommand();
+		scale_command -> setScaledPrimitives(items());
+		scale_command -> setTransformation(
+			mapToScene(original_bounding_rect_).boundingRect(),
+			mapToScene(modified_bounding_rect_).boundingRect()
+		);
+		command = scale_command;
+	} else if (current_operation_square_ == QET::MoveArea) {
+		QPointF movement = mapToScene(modified_bounding_rect_.topLeft()) - mapToScene(original_bounding_rect_.topLeft());
+		if (!movement.isNull()) {
+			MovePartsCommand *move_command = new MovePartsCommand(movement, 0, graphicsItems());
+			command = move_command;
+		}
+	} else {
+		if (CustomElementPart *single_item = singleItem()) {
+			bool event_accepted = single_item -> singleItemReleaseEvent(this, event);
+			if (event_accepted) {
+				event -> ignore();
+				return;
+			}
+		}
+	}
+	
+	if (command) {
+		emit(actionFinished(command));
+	}
+	
+	if (current_operation_square_ != QET::NoOperation) {
+		adjust();
+	}
+	
+	current_operation_square_ = QET::NoOperation;
+}
+
+/**
+	@reimp QGraphicsItem::keyPressEvent
+*/
+void ElementPrimitiveDecorator::keyPressEvent(QKeyEvent *e) {
+	const qreal movement_length = 1.0;
+	QPointF movement;
+	switch(e -> key()) {
+		case Qt::Key_Left:  movement = QPointF(-movement_length, 0.0); break;
+		case Qt::Key_Right: movement = QPointF(+movement_length, 0.0); break;
+		case Qt::Key_Up:    movement = QPointF(0.0, -movement_length); break;
+		case Qt::Key_Down:  movement = QPointF(0.0, +movement_length); break;
+	}
+	if (!movement.isNull() && focusItem() == this) {
+		if (!moving_by_keys_) {
+			moving_by_keys_ = true;
+			keys_movement_ = movement;
+		} else {
+			keys_movement_ += movement;
+		}
+		foreach(QGraphicsItem *qgi, graphicsItems()) {
+			qgi -> setPos(qgi -> pos() + movement);
+			adjust();
+		}
+	}
+	
+	QGraphicsObject::keyPressEvent(e);
+}
+
+/**
+	@reimp QGraphicsItem::keyReleaseEvent
+*/
+void ElementPrimitiveDecorator::keyReleaseEvent(QKeyEvent *e) {
+	// detecte le relachement d'une touche de direction ( = deplacement de parties)
+	if (
+		(e -> key() == Qt::Key_Left  || e -> key() == Qt::Key_Right  ||\
+		 e -> key() == Qt::Key_Up    || e -> key() == Qt::Key_Down) &&\
+		moving_by_keys_  && !e -> isAutoRepeat()
+	) {
+		// cree un objet d'annulation pour le mouvement qui vient de se finir
+		emit(actionFinished(new MovePartsCommand(keys_movement_, 0, graphicsItems())));
+		keys_movement_ = QPointF();
+		moving_by_keys_ = false;
+	}
+	QGraphicsObject::keyPressEvent(e);
+}
+
+/**
+	Initialize an ElementPrimitiveDecorator
+*/
+void ElementPrimitiveDecorator::init() {
+	setFlag(QGraphicsItem::ItemIsFocusable, true);
+	grid_step_x_ = grid_step_y_ = 1;
+	setAcceptHoverEvents(true);
+}
+
+/**
+	Save the original bounding rectangle.
+*/
+void ElementPrimitiveDecorator::saveOriginalBoundingRect() {
+	original_bounding_rect_ = internalBoundingRect();
+}
+
+/**
+	Adjust the effective bounding rect. This method should be called after the
+	modified_bouding_rect_ attribute was modified.
+*/
+void ElementPrimitiveDecorator::adjustEffectiveBoundingRect() {
+	prepareGeometryChange();
+	effective_bounding_rect_ = modified_bounding_rect_ | effective_bounding_rect_;
+	update();
+}
+
+/**
+	Start a movement (i.e. either a move or scaling operation)
+*/
+void ElementPrimitiveDecorator::startMovement() {
+	adjust();
+	
+	foreach(CustomElementPart *item, decorated_items_) {
+		item -> startUserTransformation(mapToScene(original_bounding_rect_).boundingRect());
+	}
+}
+
+/**
+	Apply the movement described by \a movement_type and \a movement to \a rect.
+*/
+void ElementPrimitiveDecorator::applyMovementToRect(int movement_type, const QPointF &movement, QRectF &rect) {
+	qreal new_value;
+	QPointF new_point;
+	
+	switch (movement_type) {
+		case QET::MoveArea:
+			rect.translate(movement.x(), movement.y());
+			break;
+		case QET::ResizeFromTopLeftCorner:
+			new_point = rect.topLeft() + movement;
+			rect.setTopLeft(new_point);
+			break;
+		case QET::ResizeFromTopCenterCorner:
+			new_value = rect.top() + movement.y();
+			rect.setTop(new_value);
+			break;
+		case QET::ResizeFromTopRightCorner:
+			new_point = rect.topRight() + movement;
+			rect.setTopRight(new_point);
+			break;
+		case QET::ResizeFromMiddleLeftCorner:
+			new_value = rect.left() + movement.x();
+			rect.setLeft(new_value);
+			break;
+		case QET::ResizeFromMiddleRightCorner:
+			new_value = rect.right() + movement.x();
+			rect.setRight(new_value);
+			break;
+		case QET::ResizeFromBottomLeftCorner:
+			new_point = rect.bottomLeft() + movement;
+			rect.setBottomLeft(new_point);
+			break;
+		case QET::ResizeFromBottomCenterCorner:
+			new_value = rect.bottom() + movement.y();
+			rect.setBottom(new_value);
+			break;
+		case QET::ResizeFromBottomRightCorner:
+			new_point = rect.bottomRight() + movement;
+			rect.setBottomRight(new_point);
+			break;
+	}
+}
+
+CustomElementPart *ElementPrimitiveDecorator::singleItem() const {
+	if (decorated_items_.count() == 1) {
+		return(decorated_items_.first());
+	}
+	return(0);
+}
+
+/**
+	Translated the managed items by the \a movement
+*/
+void ElementPrimitiveDecorator::translateItems(const QPointF &movement) {
+	if (!decorated_items_.count()) return;
+	
+	foreach(QGraphicsItem *qgi, graphicsItems()) {
+		// this is a naive, proof-of-concept implementation; we actually need to take
+		// the grid into account and create a command object in mouseReleaseEvent()
+		qgi -> moveBy(movement.x(), movement.y());
+	}
+}
+
+
+/**
+	Scale the managed items, provided they originally fit within \a
+	original_rect and they should now fit \a new_rect
+*/
+void ElementPrimitiveDecorator::scaleItems(const QRectF &original_rect, const QRectF &new_rect) {
+	if (!decorated_items_.count()) return;
+	if (original_rect == new_rect) return;
+	if (!original_rect.width() || !original_rect.height()) return; // cowardly flee division by zero FIXME?
+	
+	QRectF scene_original_rect = mapToScene(original_rect).boundingRect();
+	QRectF scene_new_rect = mapToScene(new_rect).boundingRect();
+	
+	foreach(CustomElementPart *item, decorated_items_) {
+		item -> handleUserTransformation(scene_original_rect, scene_new_rect);
+	}
+}
+
+/**
+	@return the bounding rectangle of \a item, in scene coordinates
+*/
+QRectF ElementPrimitiveDecorator::getSceneBoundingRect(QGraphicsItem *item) const {
+	if (!item) return(QRectF());
+	return(item -> mapRectToScene(item -> boundingRect()));
+}
+
+/**
+	Draw all known resizing squares using \a painter.
+	@param option The option parameter provides style options for the item, such
+	as its state, exposed area and its level-of-detail hints.
+	@param The widget argument is optional. If provided, it points to the
+	widget that is being painted on; otherwise, it is 0. For cached painting,
+	widget is always 0.
+*/
+void ElementPrimitiveDecorator::drawSquares(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
+	foreach (QRectF rect, getResizingSquares()) {
+		drawResizeSquare(painter, option, widget, rect);
+	}
+}
+
+/**
+	Draw the provided resizing square \a rect using \a painter.
+	@param option The option parameter provides style options for the item, such
+	as its state, exposed area and its level-of-detail hints.
+	@param The widget argument is optional. If provided, it points to the
+	widget that is being painted on; otherwise, it is 0. For cached painting,
+	widget is always 0.
+*/
+void ElementPrimitiveDecorator::drawResizeSquare(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, const QRectF &rect) {
+	QColor inner(0xFF, 0xFF, 0xFF);
+	QColor outer(0x00, 0x61, 0xFF);
+	if (decorated_items_.count() > 1) {
+		outer = QColor(0x1A, 0x5C, 0x14);
+	}
+	drawGenericSquare(painter, option, widget, rect, inner, outer);
+}
+
+/**
+	Draw a generic square \a rect using \a painter.
+	@param inner Color used to fill the square
+	@param outer Color usd to draw the outline
+	@param option The option parameter provides style options for the item, such
+	as its state, exposed area and its level-of-detail hints.
+	@param The widget argument is optional. If provided, it points to the
+	widget that is being painted on; otherwise, it is 0. For cached painting,
+	widget is always 0.
+*/
+void ElementPrimitiveDecorator::drawGenericSquare(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, const QRectF &rect, const QColor &inner, const QColor &outer) {
+	Q_UNUSED(option)
+	Q_UNUSED(widget)
+	// 1.0px will end up to level_of_details px once rendered
+	// qreal level_of_details = option->levelOfDetailFromTransform(painter -> transform());
+	
+	painter -> save();
+	painter -> setBrush(QBrush(inner));
+	QPen square_pen(QBrush(outer), 2.0, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
+	square_pen.setCosmetic(true);
+	painter -> setPen(square_pen);
+	painter -> drawRect(rect);
+	painter -> restore();
+}
+
+/**
+	@return A list containing all known resizing squares, based on the
+	modified_bounding_rect_ attribute. They are ordered following
+	QET::OperationAreas, so it is possible to use positive values from this enum
+	to fetch the corresponding square in the list.
+	@see QET::OperationAreas
+*/
+QList<QRectF> ElementPrimitiveDecorator::getResizingSquares() {
+	QRectF primitive_rect = modified_bounding_rect_;
+	QRectF half_primitive_rect1 = primitive_rect;
+	half_primitive_rect1.setHeight(half_primitive_rect1.height() / 2.0);
+	QRectF half_primitive_rect2 = primitive_rect;
+	half_primitive_rect2.setWidth(half_primitive_rect2.width() / 2.0);
+	
+	QList<QRectF> rects;
+	
+	rects << getGenericSquare(primitive_rect.topLeft());
+	rects << getGenericSquare(half_primitive_rect2.topRight());
+	rects << getGenericSquare(primitive_rect.topRight());
+	rects << getGenericSquare(half_primitive_rect1.bottomLeft());
+	rects << getGenericSquare(half_primitive_rect1.bottomRight());
+	rects << getGenericSquare(primitive_rect.bottomLeft());
+	rects << getGenericSquare(half_primitive_rect2.bottomRight());
+	rects << getGenericSquare(primitive_rect.bottomRight());
+	
+	/// TODO cache the rects instead of calculating them again and again?
+	return(rects);
+}
+
+/**
+	@return the square to be drawn to represent \a position
+*/
+QRectF ElementPrimitiveDecorator::getGenericSquare(const QPointF &position) {
+	const qreal square_half_size = 0.5;
+	return(
+		QRectF(
+			position.x() - square_half_size,
+			position.y() - square_half_size,
+			square_half_size * 2.0,
+			square_half_size * 2.0
+		)
+	);
+}
+
+/**
+	@return the index of the square containing the \a position point or -1 if
+	none matches.
+*/
+int ElementPrimitiveDecorator::resizingSquareAtPos(const QPointF &position) {
+	QList<QRectF> rects = getResizingSquares();
+	int current_square = QET::NoOperation;
+	for (int i = 0 ; i < rects.count() ; ++ i) {
+		if (rects.at(i).contains(position)) {
+			current_square = i;
+		}
+	}
+	return(current_square);
+}
+
+/**
+	Receive two rects, assuming they share a common corner and current is a \a
+	scaled version of \a original.
+	Calculate the scale ratios implied by this assumption, round them to the
+	nearest multiple of \a epsilon, then return the horizontal and vertical
+	offsets to be applied in order to pass from \a current to \a original scaled
+	by the rounded factors.
+	This method can be used to adjust a mouse movement so that it inputs a
+	round scaling operation.
+*/
+QPointF ElementPrimitiveDecorator::deltaForRoundScaling(const QRectF &original, const QRectF &current, qreal epsilon) {
+	qreal sx = current.width()  / original.width();
+	qreal sy = current.height() / original.height();
+	
+	qreal sx_rounded = QET::round(sx, epsilon);
+	qreal sy_rounded = QET::round(sy, epsilon);
+	
+	QPointF delta(
+		original.width()  * (sx_rounded - sx),
+		original.height() * (sy_rounded - sy)
+	);
+	return(delta);
+}
+
+/**
+	Round the coordinates of \a point so it is snapped to the grid defined by the
+	grid_step_x_ and grid_step_y_ attributes.
+*/
+QPointF ElementPrimitiveDecorator::snapConstPointToGrid(const QPointF &point) const {
+	return(
+		QPointF(
+			qRound(point.x() / grid_step_x_) * grid_step_x_,
+			qRound(point.y() / grid_step_y_) * grid_step_y_
+		)
+	);
+}
+
+/**
+	Round the coordinates of \a point so it is snapped to the grid defined by the
+	grid_step_x_ and grid_step_y_ attributes.
+*/
+void ElementPrimitiveDecorator::snapPointToGrid(QPointF &point) const {
+	point.rx() = qRound(point.x() / grid_step_x_) * grid_step_x_;
+	point.ry() = qRound(point.y() / grid_step_y_) * grid_step_y_;
+}
+
+/**
+	@return whether the current operation should take the grid into account
+	according to the state of the provided \a event
+*/
+bool ElementPrimitiveDecorator::mustSnapToGrid(QGraphicsSceneMouseEvent *event) {
+	return(!(event -> modifiers() & Qt::ControlModifier));
+}
+
+/**
+	@param event Mouse event during the scale operations -- simply passed to mustSnapToGrid()
+	@return the scaling method to be used for the currently decorated items.
+	@see QET::ScalingMethod
+	@see mustSnapToGrid()
+*/
+QET::ScalingMethod ElementPrimitiveDecorator::scalingMethod(QGraphicsSceneMouseEvent *event) {
+	if (event && !mustSnapToGrid(event)) {
+		return(QET::FreeScaling);
+	}
+	if (CustomElementPart *single_item = singleItem()) {
+		return single_item -> preferredScalingMethod();
+	}
+	return QET::RoundScaleRatios;
+}

Added: trunk/sources/editor/elementprimitivedecorator.h
===================================================================
--- trunk/sources/editor/elementprimitivedecorator.h	                        (rev 0)
+++ trunk/sources/editor/elementprimitivedecorator.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,97 @@
+#ifndef ELEMENTPRIMITIVEDECORATOR_H
+#define ELEMENTPRIMITIVEDECORATOR_H
+#include <QGraphicsObject>
+#include "qet.h"
+class ElementEditionCommand;
+class ElementScene;
+class CustomElementPart;
+
+/**
+	This class represents a decorator rendered above selected items so users
+	can manipulate (move, resize, ...) them.
+	
+	The implementation considers four kinds of bounding rects:
+	  - the actual, effective bounding rect as returned by the boundingRect() method
+	  - the original bounding rect, i.e. the rect containing all selected items at
+	      the beginning of operations (or after a command object was generated)
+	  - the new bounding rect, after the user moved or resized items
+	  - the former bounding rect, due to implementation details
+*/
+class ElementPrimitiveDecorator : public QGraphicsObject {
+	Q_OBJECT
+	
+	public:
+	ElementPrimitiveDecorator(QGraphicsItem * = 0);
+	virtual ~ElementPrimitiveDecorator();
+	
+	enum { Type = UserType + 2200 };
+	
+	// methods
+	QRectF internalBoundingRect() const;
+	virtual QRectF boundingRect () const;
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	virtual int type() const { return Type; }
+	void setItems(const QList<QGraphicsItem *> &);
+	void setItems(const QList<CustomElementPart *> &);
+	QList<CustomElementPart *> items() const;
+	QList<QGraphicsItem *> graphicsItems() const;
+	
+	public slots:
+	void adjust();
+	
+	signals:
+	void actionFinished(ElementEditionCommand *);
+	
+	protected:
+	void hoverMoveEvent(QGraphicsSceneHoverEvent *);
+	void mousePressEvent(QGraphicsSceneMouseEvent *);
+	void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
+	void mouseMoveEvent(QGraphicsSceneMouseEvent *);
+	void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
+	void keyPressEvent(QKeyEvent *);
+	void keyReleaseEvent(QKeyEvent *);
+	QPointF deltaForRoundScaling(const QRectF &, const QRectF &, qreal);
+	QPointF snapConstPointToGrid(const QPointF &) const;
+	void snapPointToGrid(QPointF &) const;
+	bool mustSnapToGrid(QGraphicsSceneMouseEvent *);
+	QET::ScalingMethod scalingMethod(QGraphicsSceneMouseEvent *);
+	
+	private:
+	void init();
+	void saveOriginalBoundingRect();
+	void adjustEffectiveBoundingRect();
+	void startMovement();
+	void applyMovementToRect(int, const QPointF &, QRectF &);
+	CustomElementPart *singleItem() const;
+	void translateItems(const QPointF &);
+	void scaleItems(const QRectF &, const QRectF &);
+	QRectF getSceneBoundingRect(QGraphicsItem *) const;
+	void drawSquares(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+	void drawResizeSquare(QPainter *, const QStyleOptionGraphicsItem *, QWidget *, const QRectF &);
+	void drawGenericSquare(QPainter *, const QStyleOptionGraphicsItem *, QWidget *, const QRectF &, const QColor &, const QColor &);
+	QList<QRectF> getResizingSquares();
+	QRectF getGenericSquare(const QPointF &);
+	int resizingSquareAtPos(const QPointF &);
+	
+	// attributes
+	private:
+	QList<CustomElementPart *> decorated_items_;
+	QRectF effective_bounding_rect_; ///< actual, effective bounding rect -- never shrinks
+	QRectF original_bounding_rect_; ///< original bounding rect
+	QRectF modified_bounding_rect_; ///< new bounding rect, after the user moved or resized items
+	
+	/**
+		Index of the square leading the current operation (resizing, etc.) or -1 if no
+		operation is occurring, -2 for a move operation.
+	*/
+	int current_operation_square_;
+	int grid_step_x_;              ///< Grid horizontal step
+	int grid_step_y_;              ///< Grid horizontal step
+	QPointF first_pos_;            ///< First point involved within the current resizing operation
+	QPointF latest_pos_;           ///< Latest point involved within the current resizing operation
+	QPointF mouse_offset_;         ///< Offset between the mouse position and the point to be snapped to grid when moving selection
+	bool moving_by_keys_;          ///< Whether we are currently moving our decorated items using the arrow keys
+	QPointF keys_movement_;           ///< Movement applied to our decorated items using the arrow keys
+};
+
+#endif

Added: trunk/sources/editor/elementscene.cpp
===================================================================
--- trunk/sources/editor/elementscene.cpp	                        (rev 0)
+++ trunk/sources/editor/elementscene.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1169 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscene.h"
+#include "qetelementeditor.h"
+#include "elementprimitivedecorator.h"
+#include <cmath>
+#include "partline.h"
+#include "partrectangle.h"
+#include "partellipse.h"
+#include "partpolygon.h"
+#include "partterminal.h"
+#include "parttext.h"
+#include "parttextfield.h"
+#include "partarc.h"
+#include "editorcommands.h"
+#include "elementcontent.h"
+#include "nameslist.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent le Widget parent
+*/
+ElementScene::ElementScene(QETElementEditor *editor, QObject *parent) :
+	QGraphicsScene(parent),
+	qgi_manager(this),
+	element_editor(editor),
+	decorator_(0)
+{
+	setItemIndexMethod(NoIndex);
+	current_polygon = NULL;
+	setGrid(1, 1);
+	initPasteArea();
+	undo_stack.setClean();
+	decorator_lock_ = new QMutex(QMutex::NonRecursive);
+	connect(&undo_stack, SIGNAL(indexChanged(int)), this, SLOT(managePrimitivesGroups()));
+	connect(this, SIGNAL(selectionChanged()), this, SLOT(managePrimitivesGroups()));
+}
+
+/// Destructeur
+ElementScene::~ElementScene() {
+	delete decorator_lock_;
+}
+
+/**
+	Passe la scene en mode "selection et deplacement de parties"
+*/
+void ElementScene::slot_move() {
+	behavior = Normal;
+}
+
+/**
+	Passe la scene en mode "ajout de ligne"
+*/
+void ElementScene::slot_addLine() {
+	behavior = Line;
+}
+
+/**
+	Passe la scene en mode "ajout de rectangle"
+*/
+void ElementScene::slot_addRectangle() {
+	behavior = Rectangle;
+}
+
+/**
+	Passe la scene en mode "ajout de cercle"
+*/
+void ElementScene::slot_addCircle() {
+	behavior = Circle;
+}
+
+/**
+	Passe la scene en mode "ajout d'ellipse"
+*/
+void ElementScene::slot_addEllipse() {
+	behavior = Ellipse;
+}
+
+/**
+	Passe la scene en mode "ajout de polygone"
+*/
+void ElementScene::slot_addPolygon() {
+	behavior = Polygon;
+}
+
+
+/**
+	Passe la scene en mode "ajout de texte statique"
+*/
+void ElementScene::slot_addText() {
+	behavior = Text;
+}
+
+/**
+	Passe la scene en mode "ajout de borne"
+*/
+void ElementScene::slot_addTerminal() {
+	behavior = Terminal;
+}
+
+/**
+	Passe la scene en mode "ajout d'arc de cercle"
+*/
+void ElementScene::slot_addArc() {
+	behavior = Arc;
+}
+
+/**
+	Passe la scene en mode "ajout de champ de texte"
+*/
+void ElementScene::slot_addTextField() {
+	behavior = TextField;
+}
+
+/**
+	Gere les mouvements de la souris
+	@param e objet decrivant l'evenement
+*/
+void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
+	QPointF event_pos = e -> scenePos();
+	if (mustSnapToGrid(e)) snapToGrid(event_pos);
+	
+	if (behavior != Polygon && current_polygon != NULL) current_polygon = NULL;
+	if (behavior == PasteArea) {
+		QRectF current_rect(paste_area_ -> rect());
+		current_rect.moveCenter(event_pos);
+		paste_area_ -> setRect(current_rect);
+		return;
+	}
+	
+	QRectF temp_rect;
+	QPointF temp_point;
+	QPolygonF temp_polygon;
+	if (e -> buttons() & Qt::LeftButton) {
+		switch(behavior) {
+			case Line:
+				current_line -> setLine(QLineF(current_line -> line().p1(), event_pos));
+				break;
+			case Rectangle:
+				temp_rect = current_rectangle -> rect();
+				temp_rect.setBottomRight(event_pos);
+				current_rectangle -> setRect(temp_rect);
+				break;
+			case Ellipse:
+				temp_rect = current_ellipse -> rect();
+				temp_rect.setBottomRight(event_pos);
+				current_ellipse -> setRect(temp_rect);
+				break;
+			case Arc:
+				temp_rect = current_arc -> rect();
+				temp_rect.setBottomRight(event_pos);
+				current_arc -> setRect(temp_rect);
+				break;
+			case Polygon:
+				if (current_polygon == NULL) break;
+				temp_polygon = current_polygon -> polygon();
+				temp_polygon.pop_back();
+				temp_polygon << event_pos;
+				current_polygon -> setPolygon(temp_polygon);
+				break;
+			case Normal:
+			default:
+				QGraphicsScene::mouseMoveEvent(e);
+		}
+	} else if (behavior == Polygon && current_polygon != NULL) {
+		temp_polygon = current_polygon -> polygon();
+		temp_polygon.pop_back();
+		temp_polygon << event_pos;
+		current_polygon -> setPolygon(temp_polygon);
+	} else QGraphicsScene::mouseMoveEvent(e);
+}
+
+/**
+	Gere les appuis sur les boutons de la souris
+	@param e objet decrivant l'evenement
+*/
+void ElementScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
+	QPointF event_pos = e -> scenePos();
+	if (mustSnapToGrid(e)) snapToGrid(event_pos);
+	
+	if (behavior != Polygon && current_polygon != NULL) current_polygon = NULL;
+	QPolygonF temp_polygon;
+	if (e -> button() & Qt::LeftButton) {
+		switch(behavior) {
+			case Line:
+				current_line = new PartLine(element_editor, 0, this);
+				current_line -> setLine(QLineF(event_pos, event_pos));
+				break;
+			case Rectangle:
+				current_rectangle = new PartRectangle(element_editor, 0, this);
+				current_rectangle -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
+				break;
+			case Ellipse:
+				current_ellipse = new PartEllipse(element_editor, 0, this);
+				current_ellipse -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
+				current_ellipse -> setProperty("antialias", true);
+				break;
+			case Arc:
+				current_arc = new PartArc(element_editor, 0, this);
+				current_arc -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
+				current_arc -> setProperty("antialias", true);
+				break;
+			case Polygon:
+				if (current_polygon == NULL) {
+					current_polygon = new PartPolygon(element_editor, 0, this);
+					temp_polygon = QPolygonF(0);
+				} else temp_polygon = current_polygon -> polygon();
+				// au debut, on insere deux points
+				if (!temp_polygon.count()) temp_polygon << event_pos;
+				temp_polygon << event_pos;
+				current_polygon -> setPolygon(temp_polygon);
+				break;
+			case Normal:
+			default:
+				QGraphicsScene::mousePressEvent(e);
+		}
+	} else QGraphicsScene::mousePressEvent(e);
+}
+
+/**
+	Gere les relachements de boutons de la souris
+	@param e objet decrivant l'evenement
+*/
+void ElementScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
+	QPointF event_pos = e -> scenePos();
+	if (mustSnapToGrid(e)) snapToGrid(event_pos);
+	
+	PartTerminal *terminal;
+	PartText *text;
+	PartTextField *textfield;
+	if (behavior != Polygon && current_polygon != NULL) current_polygon = NULL;
+	
+	if (behavior == PasteArea) {
+		defined_paste_area_ = paste_area_ -> rect();
+		removeItem(paste_area_);
+		emit(pasteAreaDefined(defined_paste_area_));
+		behavior = Normal;
+		return;
+	}
+	
+	if (e -> button() & Qt::LeftButton) {
+		switch(behavior) {
+			case Line:
+				if (qgiManager().manages(current_line)) break;
+				undo_stack.push(new AddPartCommand(tr("ligne"), this, current_line));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case Rectangle:
+				if (qgiManager().manages(current_rectangle)) break;
+				current_rectangle -> setRect(current_rectangle -> rect().normalized());
+				undo_stack.push(new AddPartCommand(tr("rectangle"), this, current_rectangle));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case Ellipse:
+				if (qgiManager().manages(current_ellipse)) break;
+				current_ellipse -> setRect(current_ellipse -> rect().normalized());
+				undo_stack.push(new AddPartCommand(tr("ellipse"), this, current_ellipse));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case Arc:
+				if (qgiManager().manages(current_arc)) break;
+				current_arc-> setRect(current_arc -> rect().normalized());
+				undo_stack.push(new AddPartCommand(tr("arc"), this, current_arc));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case Terminal:
+				terminal = new PartTerminal(element_editor, 0, this);
+				terminal -> setPos(event_pos);
+				undo_stack.push(new AddPartCommand(tr("borne"), this, terminal));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case Text:
+				text = new PartText(element_editor, 0, this);
+				text -> setPos(event_pos);
+				undo_stack.push(new AddPartCommand(tr("texte"), this, text));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case TextField:
+				textfield = new PartTextField(element_editor, 0, this);
+				textfield -> setPos(event_pos);
+				undo_stack.push(new AddPartCommand(tr("champ de texte"), this, textfield));
+				emit(partsAdded());
+				endCurrentBehavior(e);
+				break;
+			case Normal:
+			default:
+				// detecte les deplacements de parties
+				QGraphicsScene::mouseReleaseEvent(e);
+				moving_parts_ = false;
+		}
+	} else if (e -> button() & Qt::RightButton) {
+		if (behavior == Polygon && current_polygon != NULL) {
+			undo_stack.push(new AddPartCommand(tr("polygone"), this, current_polygon));
+			current_polygon = NULL;
+			emit(partsAdded());
+			endCurrentBehavior(e);
+		} else QGraphicsScene::mouseReleaseEvent(e);
+	} else QGraphicsScene::mouseReleaseEvent(e);
+}
+
+/**
+	Dessine l'arriere-plan de l'editeur, cad l'indicateur de hotspot.
+	@param p Le QPainter a utiliser pour dessiner
+	@param rect Le rectangle de la zone a dessiner
+*/
+void ElementScene::drawForeground(QPainter *p, const QRectF &rect) {
+	Q_UNUSED(rect);
+	p -> save();
+	
+	// desactive tout antialiasing, sauf pour le texte
+	p -> setRenderHint(QPainter::Antialiasing, false);
+	p -> setRenderHint(QPainter::TextAntialiasing, true);
+	p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
+	
+	p -> setPen(Qt::red);
+	p -> setBrush(Qt::NoBrush);
+	p -> drawLine(-20, 0, 20, 0);
+	p -> drawLine(0, -20, 0, 20);
+	p -> restore();
+}
+
+/**
+	A partir d'un evenement souris, cette methode regarde si la touche shift est
+	enfoncee ou non. Si oui, elle laisse le comportement en cours (cercle,
+	texte, polygone, ...). Si non, elle repasse en mode normal / selection.
+	@param event objet decrivant l'evenement souris
+*/
+void ElementScene::endCurrentBehavior(const QGraphicsSceneMouseEvent *event) {
+	if (!(event -> modifiers() & Qt::ShiftModifier)) {
+		// la touche Shift n'est pas enfoncee : on demande le mode normal
+		behavior = Normal;
+		emit(needNormalMode());
+	}
+}
+
+/**
+	@return la taille horizontale de la grille
+*/
+int ElementScene::xGrid() const {
+	return(x_grid);
+}
+
+/**
+	@return la taille verticale de la grille
+*/
+int ElementScene::yGrid() const {
+	return(y_grid);
+}
+
+/**
+	@param x_g Taille horizontale de la grille
+	@param y_g Taille verticale de la grille
+*/
+void ElementScene::setGrid(int x_g, int y_g) {
+	x_grid = x_g ? x_g : 1;
+	y_grid = y_g ? y_g : 1;
+}
+
+/**
+	Exporte l'element en XML
+	@param all_parts Booleen (a vrai par defaut) indiquant si le XML genere doit
+	representer tout l'element ou seulement les elements selectionnes
+	@return un document XML decrivant l'element
+*/
+const QDomDocument ElementScene::toXml(bool all_parts) {
+	QRectF size= elementSceneGeometricRect();
+	//if the element doesn't contains the origin point of the scene
+	//we move the element to the origin for solve this default before saving
+	if (!size.contains(0,0) && all_parts) {
+		centerElementToOrigine();
+		//recalcul the size after movement
+		size= elementSceneGeometricRect();
+	}
+
+	//define the size of the element by the upper multiple of 10
+	int upwidth = ((qRound(size.width())/10)*10)+10;
+	if ((qRound(size.width())%10) > 6) upwidth+=10;
+
+	int upheight = ((qRound(size.height())/10)*10)+10;
+	if ((qRound(size.height())%10) > 6) upheight+=10;
+
+	//the margin between the real size of the element and the rectangle that delimits
+	int xmargin = qRound(upwidth - size.width());
+	int ymargin = qRound(upheight - size.height());
+
+	// document XML
+	QDomDocument xml_document;
+	// racine du document XML
+	QDomElement root = xml_document.createElement("definition");
+	root.setAttribute("type",        "element");
+	root.setAttribute("width",       QString("%1").arg(upwidth));
+	root.setAttribute("height",      QString("%1").arg(upheight));
+	root.setAttribute("hotspot_x",   QString("%1").arg(-(qRound(size.x() - (xmargin/2)))));
+	root.setAttribute("hotspot_y",   QString("%1").arg(-(qRound(size.y() - (ymargin/2)))));
+	root.setAttribute("orientation", "dyyy"); //we keep the orientation for compatibility with previous version of qet
+	root.setAttribute("version", QET::version);
+	root.setAttribute("ic", "true"); //we keep the internal connection for compatibility with previous version of qet
+	
+	// noms de l'element
+	root.appendChild(_names.toXml(xml_document));
+	
+	// informations complementaires de l'element
+	QDomElement informations_element = xml_document.createElement("informations");
+	root.appendChild(informations_element);
+	informations_element.appendChild(xml_document.createTextNode(informations()));
+	
+	QDomElement description = xml_document.createElement("description");
+	// description de l'element
+	foreach(QGraphicsItem *qgi, zItems()) {
+		// si l'export ne concerne que la selection, on ignore les parties non selectionnees
+		if (!all_parts && !qgi -> isSelected()) continue;
+		if (CustomElementPart *ce = dynamic_cast<CustomElementPart *>(qgi)) {
+			if (ce -> isUseless()) continue;
+			description.appendChild(ce -> toXml(xml_document));
+		}
+	}
+	root.appendChild(description);
+	
+	xml_document.appendChild(root);
+	return(xml_document);
+}
+
+/**
+	@param xml_document un document XML decrivant un element
+	@return le boundingRect du contenu de l'element
+*/
+QRectF ElementScene::boundingRectFromXml(const QDomDocument &xml_document) {
+	// charge les parties depuis le document XML
+	ElementContent loaded_content = loadContent(xml_document);
+	if (loaded_content.isEmpty()) return(QRectF());
+	
+	// calcule le boundingRect
+	QRectF bounding_rect = elementContentBoundingRect(loaded_content);
+	
+	// detruit les parties chargees
+	qDeleteAll(loaded_content);
+	
+	return(bounding_rect);
+}
+
+/**
+	Importe l'element 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 xml_document un document XML decrivant l'element
+	@param position La position des parties importees
+	@param consider_informations Si vrai, les informations complementaires
+	(dimensions, hotspot, etc.) seront prises en compte
+	@param content_ptr si ce pointeur vers un ElementContent est different de 0,
+	il sera rempli avec le contenu ajoute a l'element par le fromXml
+	@return true si l'import a reussi, false sinon
+*/
+void ElementScene::fromXml(
+	const QDomDocument &xml_document,
+	const QPointF &position,
+	bool consider_informations,
+	ElementContent *content_ptr
+) {
+	QString error_message;
+	bool state = true;
+	
+	// prend en compte les informations de l'element
+	if (consider_informations) {
+		state = applyInformations(xml_document, &error_message);
+	}
+	
+	// parcours des enfants de la definition : parties de l'element
+	if (state) {
+		ElementContent loaded_content = loadContent(xml_document, &error_message);
+		if (position != QPointF()) {
+			addContentAtPos(loaded_content, position, &error_message);
+		} else {
+			addContent(loaded_content, &error_message);
+		}
+		
+		// renvoie le contenu ajoute a l'element
+		if (content_ptr) {
+			*content_ptr = loaded_content;
+		}
+	}
+}
+
+/**
+	@return the minimum, margin-less rectangle the element can fit into, in scene
+	coordinates. It is different from itemsBoundingRect() because it is not supposed
+	to imply any margin.
+*/
+QRectF ElementScene::elementSceneGeometricRect() const{
+	QRectF esgr;
+	foreach (QGraphicsItem *qgi, items()) {
+		if (qgi -> type() == ElementPrimitiveDecorator::Type) continue;
+		if (qgi -> type() == QGraphicsRectItem::Type) continue;
+		if (CustomElementPart *cep = dynamic_cast <CustomElementPart*> (qgi)) {
+			esgr |= cep -> sceneGeometricRect();
+		}
+	}
+	return (esgr);
+}
+
+/**
+	@return true si l'element comporte au moins une borne, false s'il n'en a
+	aucune.
+*/
+bool ElementScene::containsTerminals() const {
+	foreach(QGraphicsItem *qgi,items()) {
+		if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	@return la pile d'annulations de cet editeur d'element
+*/
+QUndoStack &ElementScene::undoStack() {
+	return(undo_stack);
+}
+
+/**
+	@return le gestionnaire de QGraphicsItem de cet editeur d'element
+*/
+QGIManager &ElementScene::qgiManager() {
+	return(qgi_manager);
+}
+
+/**
+	@return true si le presse-papier semble contenir un element
+*/
+bool ElementScene::clipboardMayContainElement() {
+	QString clipboard_text = QApplication::clipboard() -> text().trimmed();
+	bool may_be_element = clipboard_text.startsWith("<definition") && clipboard_text.endsWith("</definition>");
+	return(may_be_element);
+}
+
+/**
+	@param clipboard_content chaine de caractere, provenant vraisemblablement du
+	presse-papier.
+	@return true si clipboard_content a ete copie depuis cet element.
+*/
+bool ElementScene::wasCopiedFromThisElement(const QString &clipboard_content) {
+	return(clipboard_content == last_copied_);
+}
+
+/**
+	Gere le fait de couper la selection = l'exporter en XML dans le
+	presse-papier puis la supprimer.
+*/
+void ElementScene::cut() {
+	copy();
+	QList<QGraphicsItem *> cut_content = selectedItems();
+	clearSelection();
+	undoStack().push(new CutPartsCommand(this, cut_content));
+}
+
+/**
+	Gere le fait de copier la selection = l'exporter en XML dans le
+	presse-papier.
+*/
+void ElementScene::copy() {
+	// accede au presse-papier
+	QClipboard *clipboard = QApplication::clipboard();
+	
+	// genere la description XML de la selection
+	QString clipboard_content = toXml(false).toString(4);
+	
+	// met la description XML dans le presse-papier
+	if (clipboard -> supportsSelection()) {
+		clipboard -> setText(clipboard_content, QClipboard::Selection);
+	}
+	clipboard -> setText(clipboard_content);
+	
+	// retient le dernier contenu copie
+	last_copied_ = clipboard_content;
+}
+
+/**
+	Gere le fait de coller le contenu du presse-papier = l'importer dans le
+	presse-papier a une position donnee.
+*/
+void ElementScene::paste() {
+	
+}
+
+void ElementScene::contextMenu(QContextMenuEvent *event) {
+	if (behavior == ElementScene::Normal)
+		element_editor -> contextMenu(event);
+}
+
+/**
+	Selectionne une liste de parties
+	@param content liste des parties a selectionner
+*/
+void ElementScene::slot_select(const ElementContent &content) {
+	blockSignals(true);
+	clearSelection();
+	foreach(QGraphicsItem *qgi, content) qgi -> setSelected(true);
+	blockSignals(false);
+	emit(selectionChanged());
+}
+
+/**
+	Selectionne tout
+*/
+void ElementScene::slot_selectAll() {
+	slot_select(items());
+}
+
+/**
+	Deselectionne tout
+*/
+void ElementScene::slot_deselectAll() {
+	slot_select(ElementContent());
+}
+
+/**
+	Inverse la selection
+*/
+void ElementScene::slot_invertSelection() {
+	blockSignals(true);
+	foreach(QGraphicsItem *qgi, items()) qgi -> setSelected(!qgi -> isSelected());
+	blockSignals(false);
+	emit(selectionChanged());
+}
+
+/**
+	Supprime les elements selectionnes
+*/
+void ElementScene::slot_delete() {
+	// verifie qu'il y a qqc de selectionne
+	QList<QGraphicsItem *> selected_items = selectedItems();
+	if (selected_items.isEmpty()) return;
+	
+	// efface tout ce qui est selectionne
+	undo_stack.push(new DeletePartsCommand(this, selected_items));
+	
+	// removing items does not trigger QGraphicsScene::selectionChanged()
+	emit(partsRemoved());
+	emit(selectionChanged());
+}
+
+/**
+	Lance un dialogue pour editer les informations complementaires de cet
+	element. Concretement, ce champ libre est destine a accueillir des informations
+	sur l'auteur de l'element, sa licence, etc.
+*/
+void ElementScene::slot_editAuthorInformations() {
+	bool is_read_only = element_editor && element_editor -> isReadOnly();
+	
+	// cree un dialogue
+	QDialog dialog_author(element_editor);
+	dialog_author.setModal(true);
+#ifdef Q_WS_MAC
+	dialog_author.setWindowFlags(Qt::Sheet);
+#endif
+	dialog_author.setMinimumSize(400, 260);
+	dialog_author.setWindowTitle(tr("\311diter les informations sur l'auteur", "window title"));
+	QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog_author);
+	
+	// ajoute un champ explicatif au dialogue
+	QLabel *information_label = new QLabel(tr("Vous pouvez utiliser ce champ libre pour mentionner les auteurs de l'\351l\351ment, sa licence, ou tout autre renseignement que vous jugerez utile."));
+	information_label -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
+	information_label -> setWordWrap(true);
+	dialog_layout -> addWidget(information_label);
+	
+	// ajoute un QTextEdit au dialogue
+	QTextEdit *text_field = new QTextEdit();
+	text_field -> setAcceptRichText(false);
+	text_field -> setPlainText(informations());
+	text_field -> setReadOnly(is_read_only);
+	dialog_layout -> addWidget(text_field);
+	
+	// ajoute deux boutons au dialogue
+	QDialogButtonBox *dialog_buttons = new QDialogButtonBox(is_read_only ? QDialogButtonBox::Ok : QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	dialog_layout -> addWidget(dialog_buttons);
+	connect(dialog_buttons, SIGNAL(accepted()),    &dialog_author, SLOT(accept()));
+	connect(dialog_buttons, SIGNAL(rejected()),    &dialog_author, SLOT(reject()));
+	
+	// lance le dialogue
+	if (dialog_author.exec() == QDialog::Accepted && !is_read_only) {
+		QString new_infos = text_field -> toPlainText().remove(QChar(13)); // CR-less text
+		if (new_infos != informations()) {
+			undoStack().push(new ChangeInformationsCommand(this, informations(), new_infos));
+		}
+	}
+}
+
+/**
+	Lance un dialogue pour editer les noms de cet element
+*/
+void ElementScene::slot_editNames() {
+	bool is_read_only = element_editor && element_editor -> isReadOnly();
+	
+	// cree un dialogue
+	QDialog dialog(element_editor);
+#ifdef Q_WS_MAC
+	dialog.setWindowFlags(Qt::Sheet);
+#endif
+	dialog.setModal(true);
+	dialog.setMinimumSize(400, 330);
+	dialog.setWindowTitle(tr("\311diter les noms", "window title"));
+	QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog);
+	
+	// ajoute un champ explicatif au dialogue
+	QLabel *information_label = new QLabel(tr("Vous pouvez sp\351cifier le nom de l'\351l\351ment dans plusieurs langues."));
+	information_label -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
+	information_label -> setWordWrap(true);
+	dialog_layout -> addWidget(information_label);
+	
+	// ajoute un NamesListWidget au dialogue
+	NamesListWidget *names_widget = new NamesListWidget();
+	names_widget -> setNames(_names);
+	names_widget -> setReadOnly(is_read_only);
+	dialog_layout -> addWidget(names_widget);
+	
+	// ajoute deux boutons au dialogue
+	QDialogButtonBox *dialog_buttons = new QDialogButtonBox(is_read_only ? QDialogButtonBox::Ok : QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	dialog_layout -> addWidget(dialog_buttons);
+	connect(dialog_buttons, SIGNAL(accepted()),     names_widget, SLOT(check()));
+	connect(names_widget,   SIGNAL(inputChecked()), &dialog,      SLOT(accept()));
+	connect(dialog_buttons, SIGNAL(rejected()),     &dialog,      SLOT(reject()));
+	
+	// lance le dialogue
+	if (dialog.exec() == QDialog::Accepted && !is_read_only) {
+		NamesList new_names(names_widget -> names());
+		if (new_names != _names) undoStack().push(new ChangeNamesCommand(this, _names, new_names));
+	}
+}
+
+/**
+	Amene les elements selectionnes au premier plan
+*/
+void ElementScene::slot_bringForward() {
+	undoStack().push(new ChangeZValueCommand(this, ChangeZValueCommand::BringForward));
+	emit(partsZValueChanged());
+}
+
+/**
+	Remonte les elements selectionnes d'un plan
+*/
+void ElementScene::slot_raise() {
+	undoStack().push(new ChangeZValueCommand(this, ChangeZValueCommand::Raise));
+	emit(partsZValueChanged());
+}
+
+/**
+	Descend les elements selectionnes d'un plan
+*/
+void ElementScene::slot_lower() {
+	undoStack().push(new ChangeZValueCommand(this, ChangeZValueCommand::Lower));
+	emit(partsZValueChanged());
+}
+
+/**
+	Envoie les elements selectionnes au fond
+*/
+void ElementScene::slot_sendBackward() {
+	undoStack().push(new ChangeZValueCommand(this, ChangeZValueCommand::SendBackward));
+	emit(partsZValueChanged());
+}
+
+/**
+	@return the list of primitives currently present on the scene.
+*/
+QList<CustomElementPart *> ElementScene::primitives() const {
+	QList<CustomElementPart *> primitives_list;
+	foreach (QGraphicsItem *item, items()) {
+		if (CustomElementPart *primitive = dynamic_cast<CustomElementPart *>(item)) {
+			primitives_list << primitive;
+		}
+	}
+	return(primitives_list);
+}
+
+/**
+	@param include_terminals true pour inclure les bornes, false sinon
+	@return les parties de l'element ordonnes par zValue croissante
+*/
+QList<QGraphicsItem *> ElementScene::zItems(ItemOptions options) const {
+	// handle dummy request, i.e. when neither Selected nor NonSelected are set
+	if (!(options & ElementScene::Selected) && !(options & ElementScene::NonSelected)) {
+		return(QList<QGraphicsItem *>());
+	}
+	
+	// retrieve all items
+	QList<QGraphicsItem *> all_items_list(items());
+	QMutableListIterator<QGraphicsItem *> i(all_items_list);
+	
+	// remove unrequired items
+	if ((options & ElementScene::SelectedOrNot) != ElementScene::SelectedOrNot) {
+		bool keep_selected = options & ElementScene::Selected;
+		while (i.hasNext()) {
+			if (i.next() -> isSelected() != keep_selected) {
+				i.remove();
+			}
+		}
+	}
+	
+	QList<QGraphicsItem *> terminals;
+	QList<QGraphicsItem *> helpers;
+	for (i.toFront(); i.hasNext(); ) {
+		i.next();
+		QGraphicsItem *qgi = i.value();
+		if (
+			qgi -> type() == ElementPrimitiveDecorator::Type ||
+			qgi -> type() == QGraphicsRectItem::Type
+		) {
+			i.remove();
+			helpers << qgi;
+		}
+		else if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
+			i.remove();
+			terminals << qgi;
+		}
+	}
+	
+	// ordonne les parties par leur zValue
+	if (options & SortByZValue) {
+		qSort(all_items_list.begin(), all_items_list.end(), ElementScene::zValueLessThan);
+	}
+	
+	// rajoute eventuellement les bornes
+	if (options & ElementScene::IncludeTerminals) {
+		all_items_list += terminals;
+	}
+	if (options & ElementScene::IncludeHelperItems) {
+		all_items_list += helpers;
+	}
+	return(all_items_list);
+}
+
+/**
+	@return les parties graphiques selectionnees
+*/
+ElementContent ElementScene::selectedContent() const {
+	ElementContent content;
+	foreach(QGraphicsItem *qgi, zItems()) {
+		if (qgi -> isSelected()) content << qgi;
+	}
+	return(content);
+}
+
+/**
+	@param to_paste Rectangle englobant les parties a coller
+	@return le rectangle ou il faudra coller ces parties
+*/
+void ElementScene::getPasteArea(const QRectF &to_paste) {
+	// on le dessine sur la scene
+	paste_area_ -> setRect(to_paste);
+	addItem(paste_area_);
+	
+	// on passe la scene en mode "recherche de zone pour copier/coller"
+	behavior = PasteArea;
+}
+
+/**
+	Supprime les parties de l'element et les objets d'annulations.
+	Les autres caracteristiques sont conservees.
+*/
+void ElementScene::reset() {
+	// supprime les objets d'annulation
+	undoStack().clear();
+
+	// enleve les elements de la scene
+	foreach (QGraphicsItem *qgi, items()) {	
+		removeItem(qgi);
+		qgiManager().release(qgi);
+	}
+	decorator_ = 0;
+}
+
+/**
+	@param content Contenu ( = parties) d'un element
+	@return le boundingRect de ces parties, exprime dans les coordonnes de la
+	scene
+*/
+QRectF ElementScene::elementContentBoundingRect(const ElementContent &content) const {
+	QRectF bounding_rect;
+	foreach(QGraphicsItem *qgi, content) {
+		// skip non-primitives QGraphicsItems (paste area, selection decorator)
+		if (qgi -> type() == ElementPrimitiveDecorator::Type) continue;
+		if (qgi -> type() == QGraphicsRectItem::Type) continue;
+		bounding_rect |= qgi -> sceneBoundingRect();
+	}
+	return(bounding_rect);
+}
+
+/**
+	Applique les informations (dimensions, hostpot, orientations, connexions
+	internes, noms et informations complementaires) contenu dans un document XML.
+	@param xml_document Document XML a analyser
+	@param error_message pointeur vers une QString ; si error_message est
+	different de 0, un message d'erreur sera stocke dedans si necessaire
+	@return true si la lecture et l'application des informations s'est bien
+	passee, false sinon.
+*/
+bool ElementScene::applyInformations(const QDomDocument &xml_document, QString *error_message) {
+	// la racine est supposee etre une definition d'element
+	QDomElement root = xml_document.documentElement();
+	if (root.tagName() != "definition" || root.attribute("type") != "element") {
+		if (error_message) {
+			*error_message = tr("Ce document XML n'est pas une d\351finition d'\351l\351ment.", "error message");
+		}
+		return(false);
+	}
+
+	// extrait les noms de la definition XML
+	_names.fromXml(root);
+	
+	// extrait les informations complementaires
+	setInformations(QString());
+	for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		QDomElement elmt = node.toElement();
+		if (elmt.isNull()) continue;
+		if (elmt.tagName() == "informations") {
+			setInformations(elmt.text());
+			break;
+		}
+	}
+	
+	return(true);
+}
+
+/**
+	Par le document XML xml_document et retourne le contenu ( = liste de
+	parties) correspondant.
+	@param xml_document Document XML a analyser
+	@param error_message pointeur vers une QString ; si error_message est
+	different de 0, un message d'erreur sera stocke dedans si necessaire
+*/
+ElementContent ElementScene::loadContent(const QDomDocument &xml_document, QString *error_message) {
+	ElementContent loaded_parts;
+	
+	// la racine est supposee etre une definition d'element
+	QDomElement root = xml_document.documentElement();
+	if (root.tagName() != "definition" || root.attribute("type") != "element") {
+		if (error_message) {
+			*error_message = tr("Ce document XML n'est pas une d\351finition d'\351l\351ment.", "error message");
+		}
+		return(loaded_parts);
+	}
+	
+	// chargement de la description graphique de l'element
+	for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		QDomElement elmts = node.toElement();
+		if (elmts.isNull()) continue;
+		if (elmts.tagName() == "description") {
+			
+			//  = parcours des differentes parties du dessin
+			int z = 1;
+			for (QDomNode n = node.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
+				QDomElement qde = n.toElement();
+				if (qde.isNull()) continue;
+				CustomElementPart *cep;
+				if      (qde.tagName() == "line")     cep = new PartLine     (element_editor, 0, 0);
+				else if (qde.tagName() == "rect")     cep = new PartRectangle(element_editor, 0, 0);
+				else if (qde.tagName() == "ellipse")  cep = new PartEllipse  (element_editor, 0, 0);
+				else if (qde.tagName() == "circle")   cep = new PartEllipse  (element_editor, 0, 0);
+				else if (qde.tagName() == "polygon")  cep = new PartPolygon  (element_editor, 0, 0);
+				else if (qde.tagName() == "terminal") cep = new PartTerminal (element_editor, 0, 0);
+				else if (qde.tagName() == "text")     cep = new PartText     (element_editor, 0, 0);
+				else if (qde.tagName() == "input")    cep = new PartTextField(element_editor, 0, 0);
+				else if (qde.tagName() == "arc")      cep = new PartArc      (element_editor, 0, 0);
+				else continue;
+				if (QGraphicsItem *qgi = dynamic_cast<QGraphicsItem *>(cep)) {
+					if (!qgi -> zValue()) qgi -> setZValue(z++);
+					loaded_parts << qgi;
+				}
+				cep -> fromXml(qde);
+			}
+		}
+	}
+	
+	return(loaded_parts);
+}
+
+/**
+	Ajoute le contenu content a cet element
+	@param content contenu ( = liste de parties) a charger
+	@param error_message pointeur vers une QString ; si error_message est
+	different de 0, un message d'erreur sera stocke dedans si necessaire
+	@return Le contenu ajoute
+*/
+ElementContent ElementScene::addContent(const ElementContent &content, QString *error_message) {
+	Q_UNUSED(error_message);
+	foreach(QGraphicsItem *part, content) {
+		addPrimitive(part);
+	}
+	return(content);
+}
+
+/**
+	Ajoute le contenu content a cet element
+	@param content contenu ( = liste de parties) a charger
+	@param pos Position du coin superieur gauche du contenu apres avoir ete ajoute
+	@param error_message pointeur vers une QString ; si error_message est
+	different de 0, un message d'erreur sera stocke dedans si necessaire
+	@return Le contenu ajoute
+*/
+ElementContent ElementScene::addContentAtPos(const ElementContent &content, const QPointF &pos, QString *error_message) {
+	Q_UNUSED(error_message);
+	// calcule le boundingRect du contenu a ajouter
+	QRectF bounding_rect = elementContentBoundingRect(content);
+	
+	// en deduit le decalage a appliquer aux parties pour les poser au point demander
+	QPointF offset = pos - bounding_rect.topLeft();
+	
+	// ajoute les parties avec le decalage adequat
+	foreach(QGraphicsItem *part, content) {
+		part -> setPos(part -> pos() + offset);
+		addPrimitive(part);
+	}
+	return(content);
+}
+
+/**
+	Add a primitive to the scene by wrapping it within an
+	ElementPrimitiveDecorator group.
+*/
+void ElementScene::addPrimitive(QGraphicsItem *primitive) {
+	if (!primitive) return;
+	addItem(primitive);
+}
+
+/**
+	Initialise la zone de collage
+*/
+void ElementScene::initPasteArea() {
+	paste_area_ = new QGraphicsRectItem();
+	paste_area_ -> setZValue(1000000);
+	
+	QPen paste_area_pen;
+	paste_area_pen.setStyle(Qt::DashDotLine);
+	paste_area_pen.setColor(QColor(30, 56, 86, 255));
+	
+	QBrush paste_area_brush;
+	paste_area_brush.setStyle(Qt::SolidPattern);
+	paste_area_brush.setColor(QColor(90, 167, 255, 64));
+	
+	paste_area_ -> setPen(paste_area_pen);
+	paste_area_ -> setBrush(paste_area_brush);
+}
+
+/**
+	Arrondit les coordonnees du point passees en parametre de facon a ce que ce
+	point soit aligne sur la grille.
+	@param point une reference vers un QPointF. Cet objet sera modifie.
+	
+*/
+void ElementScene::snapToGrid(QPointF &point) {
+	point.rx() = qRound(point.x() / x_grid) * x_grid;
+	point.ry() = qRound(point.y() / y_grid) * y_grid;
+}
+
+/**
+	@param e Evenement souris
+	@return true s'il faut utiliser le snap-to-grid
+	Typiquement, cette methode retourne true si l'evenement souris se produit
+	sans la touche Ctrl enfoncee.
+*/
+bool ElementScene::mustSnapToGrid(QGraphicsSceneMouseEvent *e) {
+	return(!(e -> modifiers() & Qt::ControlModifier));
+}
+
+/**
+	@return true if \a item1's zValue() is less than \a item2's.
+*/
+bool ElementScene::zValueLessThan(QGraphicsItem *item1, QGraphicsItem *item2) {
+	return(item1-> zValue() < item2 -> zValue());
+}
+
+/**
+ * @brief ElementScene::centerElementToOrigine
+ * try to center better is possible the element to the scene
+ * (the calcul isn't optimal but work good)
+ */
+void ElementScene::centerElementToOrigine() {
+	QRectF size= elementSceneGeometricRect();
+	int center_x = qRound(size.center().x());
+	int center_y = qRound(size.center().y());
+
+	//define the movement of translation
+	int move_x = center_x - (qRound(center_x) %10);
+	if (center_x < 0) move_x -= 10;
+	int move_y = center_y - (qRound(center_y) %10);
+	if (center_y < 0) move_y -= 10;
+
+		//move each primitive by @move
+		foreach (QGraphicsItem *qgi, items()) {
+			if (qgi -> type() == ElementPrimitiveDecorator::Type) continue;
+			if (qgi -> type() == QGraphicsRectItem::Type) continue;
+			//deselect item for disable decorator
+			qgi -> setSelected(false);
+			qgi -> moveBy(-(move_x), -(move_y));
+		}
+		emit (needZoomFit());
+}
+
+/**
+	Ensure the decorator is adequately shown, hidden or updated so it always
+	represents the current selection.
+*/
+void ElementScene::managePrimitivesGroups() {
+	// this function is not supposed to be reentrant
+	if (!decorator_lock_ -> tryLock()) return;
+	
+	if (!decorator_) {
+		decorator_ = new ElementPrimitiveDecorator();
+		connect(decorator_, SIGNAL(actionFinished(ElementEditionCommand*)), this, SLOT(stackAction(ElementEditionCommand *)));
+		addItem(decorator_);
+		decorator_ -> hide();
+	}
+	
+	// should we hide the decorator?
+	QList<QGraphicsItem *> selected_items = zItems(ElementScene::Selected | ElementScene::IncludeTerminals);
+	if (!selected_items.count()) {
+		decorator_ -> hide();
+	} else {
+		decorator_ -> setZValue(1000000);
+		decorator_ -> setPos(0, 0);
+		decorator_ -> setItems(selected_items);
+	}
+	decorator_lock_ -> unlock();
+}
+
+/**
+	Push the provided \a command on the undo stack.
+*/
+void ElementScene::stackAction(ElementEditionCommand *command) {
+	if (command -> elementScene()) {
+		if (command -> elementScene() != this) return;
+	} else {
+		command -> setElementScene(this);
+	}
+	
+	if (!command -> elementView()) {
+		foreach (QGraphicsView *view, views()) {
+			if (ElementView *element_view = dynamic_cast<ElementView *>(view)) {
+				command -> setElementView(element_view);
+				break;
+			}
+		}
+	}
+	
+	undoStack().push(command);
+}

Added: trunk/sources/editor/elementscene.h
===================================================================
--- trunk/sources/editor/elementscene.h	                        (rev 0)
+++ trunk/sources/editor/elementscene.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,231 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_SCENE_H
+#define ELEMENT_SCENE_H
+#include <QtGui>
+#include <QtXml>
+#include "nameslistwidget.h"
+#include "qgimanager.h"
+#include "elementcontent.h"
+class CustomElementPart;
+class ElementEditionCommand;
+class ElementPrimitiveDecorator;
+class QETElementEditor;
+class PartLine;
+class PartRectangle;
+class PartEllipse;
+class PartPolygon;
+class PartArc;
+/**
+	This class is the canvas allowing the visual edition of an electrial element.
+	It displays the various primitives composing the drawing of the element, the
+	border due to its fixed size and its hotspot.
+*/
+class ElementScene : public QGraphicsScene {
+	Q_OBJECT
+	
+	// enum
+	public:
+	enum Behavior { Normal, Line, Rectangle, Circle, Ellipse, Polygon, Text, Terminal, Arc, TextField, PasteArea };
+	enum ItemOption {
+		SortByZValue = 1,
+		IncludeTerminals = 2,
+		IncludeHelperItems = 4,
+		Selected = 8,
+		NonSelected = 16,
+		SelectedOrNot = 24
+	};
+	Q_DECLARE_FLAGS(ItemOptions, ItemOption);
+	
+	// constructors, destructor
+	public:
+	ElementScene(QETElementEditor *, QObject * = 0);
+	virtual ~ElementScene();
+	
+	private:
+	ElementScene(const ElementScene &);
+	
+	// attributes
+	private:
+	/// List of localized names
+	NamesList _names;
+	/// Extra informations
+	QString informations_;
+	/// QGraphicsItem manager
+	QGIManager qgi_manager;
+	/// Undo stack
+	QUndoStack undo_stack;
+	/**
+		fsi_pos (first selected item pos) : Position of the forst selected item: used
+		to cancel mouse movements; also used to handle movements using keybard
+		arrwows.
+	*/
+	QPointF fsi_pos;
+	QPointF moving_press_pos;
+	bool moving_parts_;
+	
+	/// Variables related to drawing
+	Behavior behavior;
+	PartLine *current_line;
+	PartRectangle *current_rectangle;
+	PartEllipse *current_ellipse;
+	PartPolygon *current_polygon;
+	PartArc *current_arc;
+	QETElementEditor *element_editor;
+	
+	/// Variables to manage the paste area on the scene
+	QGraphicsRectItem *paste_area_;
+	QRectF defined_paste_area_;
+	
+	/// Variables to handle copy/paste with offset
+	QString last_copied_;
+	
+	/// Decorator item displayed when at least one item is selected
+	ElementPrimitiveDecorator *decorator_;
+	
+	///< Size of the horizontal grid step
+	int x_grid;
+	///< Size of the vertical grid step
+	int y_grid;
+	
+	// methods
+	public:
+	void setNames(const NamesList &);
+	NamesList names() const;
+	bool internalConnections();
+	void setInternalConnections(bool);
+	QString informations() const;
+	void setInformations(const QString &);
+	virtual int xGrid() const;
+	virtual int yGrid() const;
+	virtual void setGrid(int, int);
+	virtual const QDomDocument toXml(bool = true);
+	virtual QRectF boundingRectFromXml(const QDomDocument &);
+	virtual void fromXml(const QDomDocument &, const QPointF & = QPointF(), bool = true, ElementContent * = 0);
+	virtual void reset();
+	virtual QList<CustomElementPart *> primitives() const;
+	virtual QList<QGraphicsItem *> zItems(ItemOptions options = ItemOptions(SortByZValue | IncludeTerminals | SelectedOrNot)) const;
+	virtual ElementContent selectedContent() const;
+	virtual void getPasteArea(const QRectF &);
+	QRectF elementSceneGeometricRect () const;
+	bool containsTerminals() const;
+	QUndoStack &undoStack();
+	QGIManager &qgiManager();
+	static bool clipboardMayContainElement();
+	bool wasCopiedFromThisElement(const QString &);
+	void cut();
+	void copy();
+	void paste();
+	void contextMenu (QContextMenuEvent *event);
+	
+	protected:
+	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
+	virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
+	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
+	virtual void drawForeground(QPainter *, const QRectF &);
+	virtual void endCurrentBehavior(const QGraphicsSceneMouseEvent *);
+	
+	private:
+	QRectF elementContentBoundingRect(const ElementContent &) const;
+	bool applyInformations(const QDomDocument &, QString * = 0);
+	ElementContent loadContent(const QDomDocument &, QString * = 0);
+	ElementContent addContent(const ElementContent &, QString * = 0);
+	ElementContent addContentAtPos(const ElementContent &, const QPointF &, QString * = 0);
+	void addPrimitive(QGraphicsItem *);
+	void initPasteArea();
+	void snapToGrid(QPointF &);
+	bool mustSnapToGrid(QGraphicsSceneMouseEvent *);
+	static bool zValueLessThan(QGraphicsItem *, QGraphicsItem *);
+	QMutex *decorator_lock_;
+	void centerElementToOrigine();
+	
+	public slots:
+	void slot_move();
+	void slot_addLine();
+	void slot_addRectangle();
+	void slot_addCircle();
+	void slot_addEllipse();
+	void slot_addPolygon();
+	void slot_addText();
+	void slot_addArc();
+	void slot_addTerminal();
+	void slot_addTextField();
+	void slot_select(const ElementContent &);
+	void slot_selectAll();
+	void slot_deselectAll();
+	void slot_invertSelection();
+	void slot_delete();
+	void slot_editNames();
+	void slot_editAuthorInformations();
+	void slot_bringForward();
+	void slot_raise();
+	void slot_lower();
+	void slot_sendBackward();
+	void managePrimitivesGroups();
+	void stackAction(ElementEditionCommand *);
+	
+	signals:
+	/**
+		Signal emitted when the scene requires the element editor to switch back to
+		normal mode.
+	*/
+	void needNormalMode();
+	/// Signal emitted after one or several parts were added
+	void partsAdded();
+	/// Signal emitted after one or several parts were removed
+	void partsRemoved();
+	/// Signal emitted when the zValue of one or several parts change
+	void partsZValueChanged();
+	/// Signal emitted when users have defined the copy/paste area
+	void pasteAreaDefined(const QRectF &);
+	/// Signal emitted when need zoomFit
+	void needZoomFit();
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ElementScene::ItemOptions)
+
+
+/**
+	@param nameslist New set of naes for the currently edited element
+*/
+inline void ElementScene::setNames(const NamesList &nameslist) {
+	_names = nameslist;
+}
+
+/**
+	@return the list of names of the currently edited element
+*/
+inline NamesList ElementScene::names() const {
+	return(_names);
+}
+
+/**
+	@return extra informations of the currently edited element
+*/
+inline QString ElementScene::informations() const {
+	return(informations_);
+}
+
+/**
+	@param infos new extra information for the currently edited element
+*/
+inline void ElementScene::setInformations(const QString &infos) {
+	informations_ = infos;
+}
+
+#endif

Added: trunk/sources/editor/elementview.cpp
===================================================================
--- trunk/sources/editor/elementview.cpp	                        (rev 0)
+++ trunk/sources/editor/elementview.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,492 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementview.h"
+#include "qetelementeditor.h"
+#include "editorcommands.h"
+/**
+	Constructeur
+	@param scene ElementScene visualisee par cette ElementView
+	@param parent QWidget parent de cette ElementView
+*/
+ElementView::ElementView(ElementScene *scene, QWidget *parent) :
+	QGraphicsView(scene, parent),
+	scene_(scene),
+	offset_paste_count_(0)
+{
+	setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
+	setInteractive(true);
+	setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+	setResizeAnchor(QGraphicsView::AnchorUnderMouse);
+	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
+	zoomReset();
+	connect(scene_, SIGNAL(pasteAreaDefined(const QRectF &)), this, SLOT(pasteAreaDefined(const QRectF &)));
+	connect(scene_, SIGNAL(partsAdded()), this, SLOT(adjustSceneRect()));
+	connect(scene_, SIGNAL(needZoomFit()), this, SLOT(zoomFit()));
+}
+
+/// Destructeur
+ElementView::~ElementView() {
+}
+
+/// @return l'ElementScene visualisee par cette ElementView
+ElementScene *ElementView::scene() const {
+	return(scene_);
+}
+
+/**
+	@return le rectangle de l'element visualise par cet ElementView
+*/
+QRectF ElementView::viewedSceneRect() const {
+	// recupere la taille du widget viewport
+	QSize viewport_size = viewport() -> size();
+	
+	// recupere la transformation viewport -> scene
+	QTransform view_to_scene   = viewportTransform().inverted();
+	
+	// mappe le coin superieur gauche et le coin inferieur droit de la viewport sur la scene
+	QPointF scene_left_top     = view_to_scene.map(QPointF(0.0, 0.0));
+	QPointF scene_right_bottom = view_to_scene.map(QPointF(viewport_size.width(), viewport_size.height()));
+	
+	// en deduit le rectangle visualise par la scene
+	return(QRectF(scene_left_top, scene_right_bottom));
+}
+
+void ElementView::contextMenuEvent(QContextMenuEvent *event) {
+	scene_ -> contextMenu(event);
+}
+
+/**
+	Definit l'ElementScene visualisee par cette ElementView
+	@param s l'ElementScene visualisee par cette ElementView
+*/
+void ElementView::setScene(ElementScene *s) {
+	QGraphicsView::setScene(s);
+	scene_ = s;
+}
+
+/**
+	Set the Diagram in visualisation mode
+*/
+void ElementView::setVisualisationMode() {
+	setDragMode(ScrollHandDrag);
+	setInteractive(false);
+	emit(modeChanged());
+}
+
+/**
+	Set the Diagram in Selection mode
+*/
+void ElementView::setSelectionMode() {
+	setDragMode(RubberBandDrag);
+	setInteractive(true);
+	emit(modeChanged());
+}
+
+/**
+	Agrandit le schema (+33% = inverse des -25 % de zoomMoins())
+*/
+void ElementView::zoomIn() {
+	adjustSceneRect();
+	scale(4.0/3.0, 4.0/3.0);
+}
+
+/**
+	Retrecit le schema (-25% = inverse des +33 % de zoomPlus())
+*/
+void ElementView::zoomOut() {
+	adjustSceneRect();
+	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 ElementView::zoomFit() {
+	resetSceneRect();
+	fitInView(sceneRect(), Qt::KeepAspectRatio);
+}
+
+/**
+	Reinitialise le zoom
+*/
+void ElementView::zoomReset() {
+	resetSceneRect();
+	resetMatrix();
+	scale(4.0, 4.0);
+}
+
+/**
+	Ajuste le sceneRect (zone du schéma visualisée par l'ElementView) afin que
+	celui-ci inclut à la fois les primitives de l'élément ainsi que le viewport
+	de la scène avec une marge de 1/4 d'elle-même.
+*/
+void ElementView::adjustSceneRect() {
+	QRectF esgr = scene_ -> elementSceneGeometricRect();
+	QRectF vpbr = mapToScene(this -> viewport() -> rect()).boundingRect();
+	QRectF new_scene_rect = vpbr.adjusted(-vpbr.height()/4, -vpbr.width()/4, vpbr.height()/4, vpbr.width()/4);
+	setSceneRect(new_scene_rect.united(esgr));
+}
+
+/**
+ * @brief ElementView::resetSceneRect
+ * reset le sceneRect (zone du schéma visualisée par l'ElementView) afin que
+ * celui-ci inclut uniquement les primitives de l'élément dessiné.
+ */
+void ElementView::resetSceneRect() {
+	setSceneRect(scene_ -> elementSceneGeometricRect());
+}
+
+/**
+	Gere le fait de couper la selection = l'exporter en XML dans le
+	presse-papier puis la supprimer.
+*/
+void ElementView::cut() {
+	// delegue cette action a la scene
+	scene_ -> cut();
+	offset_paste_count_ = -1;
+}
+
+/**
+	Gere le fait de copier la selection = l'exporter en XML dans le
+	presse-papier.
+*/
+void ElementView::copy() {
+	// delegue cette action a la scene
+	scene_ -> copy();
+	offset_paste_count_ = 0;
+}
+
+/**
+	Gere le fait de coller le contenu du presse-papier = l'importer dans
+	l'element. Cette methode examine le contenu du presse-papier. Si celui-ci
+	semble avoir ete copie depuis cet element, il est colle a cote de sa zone
+	d'origine ; s'il est recolle, il sera colle un cran a cote de la zone deja
+	recollee, etc.
+	Sinon, cette methode demande a l'utilisateur de definir la zone ou le
+	collage devra s'effectuer.
+	@see pasteAreaDefined(const QRectF &)
+*/
+void ElementView::paste() {
+	QString clipboard_text = QApplication::clipboard() -> text();
+	if (clipboard_text.isEmpty()) return;
+	
+	QDomDocument document_xml;
+	if (!document_xml.setContent(clipboard_text)) return;
+	
+	if (scene_ -> wasCopiedFromThisElement(clipboard_text)) {
+		// copier/coller avec decalage
+		pasteWithOffset(document_xml);
+	} else {
+		// copier/coller par choix de la zone de collage
+		QRectF pasted_content_bounding_rect = scene_ -> boundingRectFromXml(document_xml);
+		if (pasted_content_bounding_rect.isEmpty()) return;
+		
+		to_paste_in_area_ = clipboard_text;
+		getPasteArea(pasted_content_bounding_rect);
+	}
+}
+
+/**
+	Colle le contenu du presse-papier en demandant systematiquement a
+	l'utilisateur de choisir une zone de collage
+*/
+void ElementView::pasteInArea() {
+	QString clipboard_text = QApplication::clipboard() -> text();
+	if (clipboard_text.isEmpty()) return;
+	
+	QDomDocument document_xml;
+	if (!document_xml.setContent(clipboard_text)) return;
+	
+	QRectF pasted_content_bounding_rect = scene_ -> boundingRectFromXml(document_xml);
+	if (pasted_content_bounding_rect.isEmpty()) return;
+	
+	// copier/coller par choix de la zone de collage
+	to_paste_in_area_ = clipboard_text;
+	getPasteArea(pasted_content_bounding_rect);
+}
+
+/**
+	Gere le fait de coller le contenu du presse-papier = l'importer dans
+	l'element. Cette methode examine le contenu du presse-papier. Si celui-ci
+	est exploitable, elle le colle a la position passee en parametre.
+	@see pasteAreaDefined(const QRectF &)
+	@param position Point de collage
+*/
+ElementContent ElementView::paste(const QPointF &position) {
+	QString clipboard_text = QApplication::clipboard() -> text();
+	if (clipboard_text.isEmpty()) return(ElementContent());
+	
+	QDomDocument document_xml;
+	if (!document_xml.setContent(clipboard_text)) return(ElementContent());
+	
+	// objet pour recuperer le contenu ajoute au schema par le coller
+	return(paste(document_xml, position));
+}
+
+/**
+	@param to_paste Rectangle englobant les parties a coller
+*/
+void ElementView::getPasteArea(const QRectF &to_paste) {
+	// on copie le rectangle fourni - on s'interesse a ses dimensions, pas a sa position
+	QRectF used_rect(to_paste);
+	
+	// on lui attribue pour centre l'origine du repere
+	if (underMouse()) {
+		used_rect.moveCenter(mapToScene(mapFromGlobal(QCursor::pos())));
+	} else {
+		used_rect.moveCenter(QPointF(0.0, 0.0));
+	}
+	scene_ -> getPasteArea(used_rect);
+}
+
+/**
+	Slot appele lorsque la scene annonce avoir defini une zone de collage
+	@param target_rect Rectangle cible pour le collage
+*/
+ElementContent ElementView::pasteAreaDefined(const QRectF &target_rect) {
+	if (to_paste_in_area_.isEmpty()) return(ElementContent());
+	
+	QDomDocument xml_document;
+	if (!xml_document.setContent(to_paste_in_area_)) {
+		to_paste_in_area_.clear();
+		return(ElementContent());
+	} else {
+		return(paste(xml_document, target_rect.topLeft()));
+	}
+}
+
+/**
+	Colle le document XML xml_document a la position pos
+	@param xml_document Document XML a coller
+	@param pos Coin superieur gauche du rectangle cible
+*/
+ElementContent ElementView::paste(const QDomDocument &xml_document, const QPointF &pos) {
+	// objet pour recuperer le contenu ajoute au schema par le coller
+	ElementContent content_pasted;
+	scene_ -> fromXml(xml_document, pos, false, &content_pasted);
+	
+	// si quelque chose a effectivement ete ajoute au schema, on cree un objet d'annulation
+	if (content_pasted.count()) {
+		scene_ -> clearSelection();
+		PastePartsCommand *undo_object = new PastePartsCommand(this, content_pasted);
+		scene_ -> undoStack().push(undo_object);
+	}
+	return(content_pasted);
+}
+
+/**
+	Colle le document XML xml_document a la position pos
+	@param xml_document Document XML a coller
+*/
+ElementContent ElementView::pasteWithOffset(const QDomDocument &xml_document) {
+	// objet pour recuperer le contenu ajoute au schema par le coller
+	ElementContent content_pasted;
+	
+	// rectangle source
+	QRectF pasted_content_bounding_rect = scene_ -> boundingRectFromXml(xml_document);
+	if (pasted_content_bounding_rect.isEmpty()) return(content_pasted);
+	
+	// copier/coller avec decalage
+	QRectF final_pasted_content_bounding_rect;
+	++ offset_paste_count_;
+	if (!offset_paste_count_) {
+		// the pasted content was cut
+		start_top_left_corner_ = pasted_content_bounding_rect.topLeft();
+		final_pasted_content_bounding_rect = pasted_content_bounding_rect;
+	}
+	else {
+		// the pasted content was copied
+		if (offset_paste_count_ == 1) {
+			start_top_left_corner_ = pasted_content_bounding_rect.topLeft();
+		} else {
+			pasted_content_bounding_rect.moveTopLeft(start_top_left_corner_);
+		}
+		
+		// on applique le decalage qui convient
+		final_pasted_content_bounding_rect = applyMovement(
+			pasted_content_bounding_rect,
+			QETElementEditor::pasteOffset()
+		);
+	}
+	QPointF old_start_top_left_corner = start_top_left_corner_;
+	start_top_left_corner_ = final_pasted_content_bounding_rect.topLeft();
+	scene_ -> fromXml(xml_document, start_top_left_corner_, false, &content_pasted);
+	
+	// si quelque chose a effectivement ete ajoute au schema, on cree un objet d'annulation
+	if (content_pasted.count()) {
+		scene_ -> clearSelection();
+		PastePartsCommand *undo_object = new PastePartsCommand(this, content_pasted);
+		undo_object -> setOffset(offset_paste_count_ - 1, old_start_top_left_corner, offset_paste_count_, start_top_left_corner_);
+		scene_ -> undoStack().push(undo_object);
+	}
+	return(content_pasted);
+}
+
+/**
+	Gere les clics sur la vue - permet de coller lorsaue l'on enfonce le bouton
+	du milieu de la souris.
+	@param e QMouseEvent decrivant l'evenement souris
+*/
+void ElementView::mousePressEvent(QMouseEvent *e) {
+	// Select visualisation or selection mode
+	if (e -> buttons() == Qt::MidButton) {
+		setCursor(Qt::ClosedHandCursor);
+		reference_view_ = mapToScene(e -> pos());
+		center_view_ = mapToScene(this -> viewport() -> rect()).boundingRect().center();
+		return;
+	}
+	QGraphicsView::mousePressEvent(e);
+}
+
+/**
+ * @brief ElementView::mouseMoveEvent
+ * Manage the event move mouse
+ */
+void ElementView::mouseMoveEvent(QMouseEvent *e) {
+	if ((e -> buttons() & Qt::MidButton) == Qt::MidButton) {
+		QPointF move = reference_view_ - mapToScene(e -> pos());
+		this -> centerOn(center_view_ + move);
+		center_view_ = mapToScene(this -> viewport() -> rect()).boundingRect().center();
+		adjustSceneRect();
+		return;
+	}
+	QGraphicsView::mouseMoveEvent(e);
+}
+
+/**
+ * @brief ElementView::mouseReleaseEvent
+ * Manage event release click mouse
+ */
+void ElementView::mouseReleaseEvent(QMouseEvent *e) {
+	if (e -> button() == Qt::MidButton) {
+		setCursor(Qt::ArrowCursor);
+		return;
+	}
+	QGraphicsView::mouseReleaseEvent(e);
+}
+
+/**
+	Gere les actions liees a la rollette de la souris
+	@param e QWheelEvent decrivant l'evenement rollette
+*/
+void ElementView::wheelEvent(QWheelEvent *e) {
+	//Zoom and scrolling
+	if (e->buttons() != Qt::MidButton) {
+		if (!(e -> modifiers() & Qt::ControlModifier)) {
+			if (e -> delta() > 0){
+				zoomIn();
+			}
+			else{
+				zoomOut();
+			}
+		}
+		else {
+			QAbstractScrollArea::wheelEvent(e);
+		}
+	}
+}
+
+/**
+	Dessine l'arriere-plan de l'editeur, cad la grille.
+	@param p Le QPainter a utiliser pour dessiner
+	@param r Le rectangle de la zone a dessiner
+*/
+void ElementView::drawBackground(QPainter *p, const QRectF &r) {
+	p -> save();
+	
+	// desactive tout antialiasing, sauf pour le texte
+	p -> setRenderHint(QPainter::Antialiasing, false);
+	p -> setRenderHint(QPainter::TextAntialiasing, true);
+	p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
+	
+	// dessine un fond blanc
+	p -> setPen(Qt::NoPen);
+	p -> setBrush(Qt::white);
+	p -> drawRect(r);
+		
+	// determine le zoom en cours
+	qreal zoom_factor = matrix().m11();
+	
+	// choisit la granularite de la grille en fonction du zoom en cours
+	int drawn_x_grid = scene_ -> xGrid();
+	int drawn_y_grid = scene_ -> yGrid();
+	bool draw_grid = true;
+	bool draw_cross = false;
+	if (zoom_factor < (4.0/3.0)) {
+		// pas de grille du tout
+		draw_grid = false;
+	} else if (zoom_factor < 4.0) {
+		// grille a 10 px
+		drawn_x_grid *= 10;
+		drawn_y_grid *= 10;
+	} else if (zoom_factor < 6.0) {
+		// grille a 2 px (avec croix)
+		drawn_x_grid *= 2;
+		drawn_y_grid *= 2;
+		draw_cross = true;
+	} else {
+		// grille a 1 px (avec croix)
+		draw_cross = true;
+	}
+	
+	if (draw_grid) {
+		// 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 % drawn_x_grid) ++ g_x;
+		int g_y = (int)ceil(r.y());
+		while (g_y % drawn_y_grid) ++ g_y;
+		
+		for (int gx = g_x ; gx < limite_x ; gx += drawn_x_grid) {
+			for (int gy = g_y ; gy < limite_y ; gy += drawn_y_grid) {
+				if (draw_cross) {
+					if (!(gx % 10) && !(gy % 10)) {
+						p -> drawLine(QLineF(gx - 0.25, gy, gx + 0.25, gy));
+						p -> drawLine(QLineF(gx, gy - 0.25, gx, gy + 0.25));
+					} else {
+						p -> drawPoint(gx, gy);
+					}
+				} else {
+					p -> drawPoint(gx, gy);
+				}
+			}
+		}
+	}
+	p -> restore();
+}
+
+/**
+	Applique le decalage offset dans le sens movement au rectangle start
+	@param start rectangle a decaler
+	@param movement Orientation du decalage a appliquer
+	@param offset Decalage a appliquer
+*/
+QRectF ElementView::applyMovement(const QRectF &start, const QPointF &offset) {
+	// calcule le decalage a appliquer a partir de l'offset
+	QPointF final_offset;
+	final_offset.rx() =  start.width() + offset.x();
+
+	// applique le decalage ainsi calcule
+	return(start.translated(final_offset));
+}

Added: trunk/sources/editor/elementview.h
===================================================================
--- trunk/sources/editor/elementview.h	                        (rev 0)
+++ trunk/sources/editor/elementview.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,91 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_VIEW_H
+#define ELEMENT_VIEW_H
+#include <QGraphicsView>
+#include "elementscene.h"
+
+/**
+	This class provides a widget to render an ElementScene instance, i.e. the
+	edition class for electrical elements.
+*/
+class ElementView : public QGraphicsView {
+	Q_OBJECT
+	friend class PastePartsCommand;
+	
+	// constructors, destructor
+	public:
+	ElementView(ElementScene *, QWidget * = 0);
+	virtual ~ElementView();
+	
+	private:
+	ElementView(const ElementView &);
+	
+	// methods
+	public:
+	ElementScene *scene() const;
+	void setScene(ElementScene *);
+	QRectF viewedSceneRect() const;
+	void contextMenuEvent(QContextMenuEvent *event);
+	
+	protected:
+	void mousePressEvent(QMouseEvent *);
+	void mouseMoveEvent(QMouseEvent *);
+	void mouseReleaseEvent(QMouseEvent *);
+	void wheelEvent(QWheelEvent *);
+	virtual void drawBackground(QPainter *, const QRectF &);
+	
+	private:
+	QRectF applyMovement(const QRectF &, const QPointF &);
+	
+	public slots:
+	void setVisualisationMode();
+	void setSelectionMode();
+	void zoomIn();
+	void zoomOut();
+	void zoomFit();
+	void zoomReset();
+	void adjustSceneRect();
+	void resetSceneRect ();
+	void cut();
+	void copy();
+	void paste();
+	void pasteInArea();
+	
+	signals:
+	/// Signal emitted after the mode changed
+	void modeChanged();
+	
+	private slots:
+	void getPasteArea(const QRectF &);
+	ElementContent pasteAreaDefined(const QRectF &);
+	ElementContent paste(const QPointF &);
+	ElementContent paste(const QDomDocument &, const QPointF &);
+	ElementContent pasteWithOffset(const QDomDocument &);
+	
+	// attributes
+	private:
+	ElementScene *scene_;
+	QString to_paste_in_area_;
+	int offset_paste_count_;
+	QPointF start_top_left_corner_;
+	QPointF reference_view_;
+	QPointF center_view_;
+	bool is_moving_view_;               ///< Indicate whether the visualisation mode has been enabled due to mouse/keyboard interactions
+};
+#endif

Added: trunk/sources/editor/ellipseeditor.cpp
===================================================================
--- trunk/sources/editor/ellipseeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/ellipseeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,149 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "ellipseeditor.h"
+#include "styleeditor.h"
+#include "partellipse.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param ellipse L'ellipse a editer
+	@param parent le Widget parent
+*/
+EllipseEditor::EllipseEditor(QETElementEditor *editor, PartEllipse *ellipse, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(ellipse)
+{
+	style_ = new StyleEditor(editor);
+	
+	x = new QLineEdit();
+	y = new QLineEdit();
+	h = new QLineEdit();
+	v = new QLineEdit();
+	
+	x -> setValidator(new QDoubleValidator(x));
+	y -> setValidator(new QDoubleValidator(y));
+	h -> setValidator(new QDoubleValidator(h));
+	v -> setValidator(new QDoubleValidator(v));
+	
+	QVBoxLayout *v_layout = new QVBoxLayout(this);
+	
+	QGridLayout *grid = new QGridLayout();
+	grid -> addWidget(new QLabel(tr("Centre : ")),       0, 0);
+	grid -> addWidget(new QLabel("x"),                   1, 0, Qt::AlignRight);
+	grid -> addWidget(x,                                 1, 1);
+	grid -> addWidget(new QLabel("y"),                   1, 2);
+	grid -> addWidget(y,                                 1, 3);
+	grid -> addWidget(new QLabel(tr("Diam\350tres : ")), 2, 0);
+	grid -> addWidget(new QLabel(tr("horizontal :")),    3, 0);
+	grid -> addWidget(h,                                 3, 1);
+	grid -> addWidget(new QLabel(tr("vertical :")),      4, 0);
+	grid -> addWidget(v,                                 4, 1);
+	
+	v_layout -> addWidget(style_);
+	v_layout -> addLayout(grid);
+	
+	activeConnections(true);
+	updateForm();
+}
+
+/// Destructeur
+EllipseEditor::~EllipseEditor() {
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur d'ellipse acceptera d'editer la primitive new_part s'il s'agit
+	d'un objet de la classe PartEllipse.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool EllipseEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		style_ -> setPart(0);
+		return(true);
+	}
+	if (PartEllipse *part_ellipse = dynamic_cast<PartEllipse *>(new_part)) {
+		part = part_ellipse;
+		style_ -> setPart(part);
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *EllipseEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour l'ellipse a partir des donnees du formulaire
+*/
+void EllipseEditor::updateEllipse() {
+	if (!part) return;
+	part -> setProperty("x", x -> text().toDouble());
+	part -> setProperty("y", y -> text().toDouble());
+	part -> setProperty("diameter_h", h -> text().toDouble());
+	part -> setProperty("diameter_v", v -> text().toDouble());
+}
+
+/// Met a jour l'abscisse du centre de l'ellipse et cree un objet d'annulation
+void EllipseEditor::updateEllipseX() { addChangePartCommand(tr("abscisse"),               part, "x",           x -> text().toDouble());       }
+/// Met a jour l'ordonnee du centre de l'ellipse et cree un objet d'annulation
+void EllipseEditor::updateEllipseY() { addChangePartCommand(tr("ordonn\351e"),            part, "y",           y -> text().toDouble());       }
+/// Met a jour le diametre horizontal de l'ellipse et cree un objet d'annulation
+void EllipseEditor::updateEllipseH() { addChangePartCommand(tr("diam\350tre horizontal"), part, "diameter_h",  h -> text().toDouble());       }
+/// Met a jour le diametre vertical de l'ellipse et cree un objet d'annulation
+void EllipseEditor::updateEllipseV() { addChangePartCommand(tr("diam\350tre vertical"),   part, "diameter_v",  v -> text().toDouble());       }
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void EllipseEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	x -> setText(part -> property("x").toString());
+	y -> setText(part -> property("y").toString());
+	h -> setText(part -> property("diameter_h").toString());
+	v -> setText(part -> property("diameter_v").toString());
+	activeConnections(true);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void EllipseEditor::activeConnections(bool active) {
+	if (active) {
+		connect(x, SIGNAL(editingFinished()), this, SLOT(updateEllipseX()));
+		connect(y, SIGNAL(editingFinished()), this, SLOT(updateEllipseY()));
+		connect(h, SIGNAL(editingFinished()), this, SLOT(updateEllipseH()));
+		connect(v, SIGNAL(editingFinished()), this, SLOT(updateEllipseV()));
+	} else {
+		disconnect(x, SIGNAL(editingFinished()), this, SLOT(updateEllipseX()));
+		disconnect(y, SIGNAL(editingFinished()), this, SLOT(updateEllipseY()));
+		disconnect(h, SIGNAL(editingFinished()), this, SLOT(updateEllipseH()));
+		disconnect(v, SIGNAL(editingFinished()), this, SLOT(updateEllipseV()));
+	}
+}

Added: trunk/sources/editor/ellipseeditor.h
===================================================================
--- trunk/sources/editor/ellipseeditor.h	                        (rev 0)
+++ trunk/sources/editor/ellipseeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,58 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELLIPSE_EDITOR_H
+#define ELLIPSE_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartEllipse;
+class StyleEditor;
+/**
+	This class provides a widget to edit ellipses within the element editor.
+*/
+class EllipseEditor : public ElementItemEditor {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	EllipseEditor(QETElementEditor *, PartEllipse * = 0, QWidget * = 0);
+	virtual ~EllipseEditor();
+	private:
+	EllipseEditor(const EllipseEditor &);
+	
+	// attributes
+	private:
+	PartEllipse *part;
+	StyleEditor *style_;
+	QLineEdit *x, *y, *h, *v;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateEllipse();
+	void updateEllipseX();
+	void updateEllipseY();
+	void updateEllipseH();
+	void updateEllipseV();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/lineeditor.cpp
===================================================================
--- trunk/sources/editor/lineeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/lineeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,208 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "lineeditor.h"
+#include "styleeditor.h"
+#include "partline.h"
+#include "qet.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param line La ligne a editer
+	@param parent le Widget parent
+*/
+LineEditor::LineEditor(QETElementEditor *editor, PartLine *line, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(line)
+{
+	style_ = new StyleEditor(editor);
+	
+	x1 = new QLineEdit();
+	y1 = new QLineEdit();
+	x2 = new QLineEdit();
+	y2 = new QLineEdit();
+	
+	x1 -> setValidator(new QDoubleValidator(x1));
+	y1 -> setValidator(new QDoubleValidator(y1));
+	x2 -> setValidator(new QDoubleValidator(x2));
+	y2 -> setValidator(new QDoubleValidator(y2));
+	
+	end1_type = new QComboBox();
+	end1_type -> addItem(QET::Icons::EndLineNone,     tr("Normale",                "type of the 1st end of a line"), QET::None    );
+	end1_type -> addItem(QET::Icons::EndLineSimple,   tr("Fl\350che simple",       "type of the 1st end of a line"), QET::Simple  );
+	end1_type -> addItem(QET::Icons::EndLineTriangle, tr("Fl\350che triangulaire", "type of the 1st end of a line"), QET::Triangle);
+	end1_type -> addItem(QET::Icons::EndLineCircle,   tr("Cercle",                 "type of the 1st end of a line"), QET::Circle  );
+	end1_type -> addItem(QET::Icons::EndLineDiamond,  tr("Carr\351",               "type of the 1st end of a line"), QET::Diamond );
+	end2_type = new QComboBox();
+	end2_type -> addItem(QET::Icons::EndLineNone,     tr("Normale",                "type of the 2nd end of a line"), QET::None    );
+	end2_type -> addItem(QET::Icons::EndLineSimple,   tr("Fl\350che simple",       "type of the 2nd end of a line"), QET::Simple  );
+	end2_type -> addItem(QET::Icons::EndLineTriangle, tr("Fl\350che triangulaire", "type of the 2nd end of a line"), QET::Triangle);
+	end2_type -> addItem(QET::Icons::EndLineCircle,   tr("Cercle",                 "type of the 2nd end of a line"), QET::Circle  );
+	end2_type -> addItem(QET::Icons::EndLineDiamond,  tr("Carr\351",               "type of the 2nd end of a line"), QET::Diamond );
+	
+	end1_length = new QLineEdit();
+	end2_length = new QLineEdit();
+	
+	end1_length -> setValidator(new QDoubleValidator(end1_length));
+	end2_length -> setValidator(new QDoubleValidator(end2_length));
+	
+	QGridLayout *grid = new QGridLayout();
+	grid -> addWidget(new QLabel("x1"),        0, 0);
+	grid -> addWidget(x1,                      0, 1);
+	grid -> addWidget(new QLabel("y1"),        0, 2);
+	grid -> addWidget(y1,                      0, 3);
+	grid -> addWidget(new QLabel("x2"),        1, 0);
+	grid -> addWidget(x2,                      1, 1);
+	grid -> addWidget(new QLabel("y2"),        1, 2);
+	grid -> addWidget(y2,                      1, 3);
+	
+	QGridLayout *grid2 = new QGridLayout();
+	grid2 -> addWidget(new QLabel(tr("Fin 1")), 0, 0);
+	grid2 -> addWidget(end1_type,               0, 1);
+	grid2 -> addWidget(end1_length,             0, 2);
+	grid2 -> addWidget(new QLabel(tr("Fin 2")), 1, 0);
+	grid2 -> addWidget(end2_type,               1, 1);
+	grid2 -> addWidget(end2_length,             1, 2);
+	
+	QVBoxLayout *v_layout = new QVBoxLayout(this);
+	v_layout -> addWidget(style_);
+	v_layout -> addLayout(grid);
+	v_layout -> addLayout(grid2);
+	updateForm();
+}
+
+/// Destructeur
+LineEditor::~LineEditor() {
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de ligne acceptera d'editer la primitive new_part s'il s'agit d'un
+	objet de la classe PartLine.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool LineEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		style_ -> setPart(0);
+		return(true);
+	}
+	if (PartLine *part_line = dynamic_cast<PartLine *>(new_part)) {
+		part = part_line;
+		style_ -> setPart(part);
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *LineEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour la ligne a partir des donnees du formulaire
+*/
+void LineEditor::updateLine() {
+	if (!part) return;
+	part -> setFirstEndType(static_cast<QET::EndType>(end1_type -> currentIndex()));
+	part -> setFirstEndLength(end1_length -> text().toDouble());
+	part -> setSecondEndType(static_cast<QET::EndType>(end2_type -> currentIndex()));
+	part -> setSecondEndLength(end2_length -> text().toDouble());
+	part -> setLine(
+		QLineF(
+			part -> mapFromScene(
+				x1 -> text().toDouble(),
+				y1 -> text().toDouble()
+			),
+			part -> mapFromScene(
+				x2 -> text().toDouble(),
+				y2 -> text().toDouble()
+			)
+		)
+	);
+}
+
+/// Met a jour l'abscisse du premier point de la ligne et cree un objet d'annulation
+void LineEditor::updateLineX1() { addChangePartCommand(tr("abscisse point 1"),    part, "x1", x1 -> text().toDouble()); }
+/// Met a jour l'ordonnee du premier point de la ligne et cree un objet d'annulation
+void LineEditor::updateLineY1() { addChangePartCommand(tr("ordonn\351e point 1"), part, "y1", y1 -> text().toDouble()); }
+/// Met a jour l'abscisse du second point de la ligne et cree un objet d'annulation
+void LineEditor::updateLineX2() { addChangePartCommand(tr("abscisse point 2"),    part, "x2", x2 -> text().toDouble()); }
+/// Met a jour l'ordonnee du second point de la ligne et cree un objet d'annulation
+void LineEditor::updateLineY2() { addChangePartCommand(tr("ordonn\351e point 2"), part, "y2", y2 -> text().toDouble()); }
+/// Met a jour le type de la premiere extremite
+void LineEditor::updateLineEndType1() {   addChangePartCommand(tr("type fin 1"),     part, "end1",    end1_type -> currentIndex());   }
+/// Met a jour la longueur de la premiere extremite
+void LineEditor::updateLineEndLength1() { addChangePartCommand(tr("longueur fin 1"), part, "length1", end1_length -> text()); }
+/// Met a jour le type de la seconde extremite
+void LineEditor::updateLineEndType2() {   addChangePartCommand(tr("type fin 2"),     part, "end2",    end2_type -> currentIndex());   }
+/// Met a jour la longueur de la seconde extremite
+void LineEditor::updateLineEndLength2() { addChangePartCommand(tr("longueur fin 2"), part, "length2", end2_length -> text()); }
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void LineEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	QPointF p1(part -> sceneP1());
+	QPointF p2(part -> sceneP2());
+	x1 -> setText(QString("%1").arg(p1.x()));
+	y1 -> setText(QString("%1").arg(p1.y()));
+	x2 -> setText(QString("%1").arg(p2.x()));
+	y2 -> setText(QString("%1").arg(p2.y()));
+	end1_type -> setCurrentIndex(part -> firstEndType());
+	end1_length -> setText(QString("%1").arg(part -> firstEndLength()));
+	end2_type -> setCurrentIndex(part -> secondEndType());
+	end2_length -> setText(QString("%1").arg(part -> secondEndLength()));
+	activeConnections(true);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void LineEditor::activeConnections(bool active) {
+	if (active) {
+		connect(x1, SIGNAL(editingFinished()), this, SLOT(updateLineX1()));
+		connect(y1, SIGNAL(editingFinished()), this, SLOT(updateLineY1()));
+		connect(x2, SIGNAL(editingFinished()), this, SLOT(updateLineX2()));
+		connect(y2, SIGNAL(editingFinished()), this, SLOT(updateLineY2()));
+		connect(end1_type,   SIGNAL(currentIndexChanged(int)), this, SLOT(updateLineEndType1()));
+		connect(end1_length, SIGNAL(editingFinished()),        this, SLOT(updateLineEndLength1()));
+		connect(end2_type,   SIGNAL(currentIndexChanged(int)), this, SLOT(updateLineEndType2()));
+		connect(end2_length, SIGNAL(editingFinished()),        this, SLOT(updateLineEndLength2()));
+	} else {
+		disconnect(x1, SIGNAL(editingFinished()), this, SLOT(updateLineX1()));
+		disconnect(y1, SIGNAL(editingFinished()), this, SLOT(updateLineY1()));
+		disconnect(x2, SIGNAL(editingFinished()), this, SLOT(updateLineX2()));
+		disconnect(y2, SIGNAL(editingFinished()), this, SLOT(updateLineY2()));
+		disconnect(end1_type,   SIGNAL(currentIndexChanged(int)), this, SLOT(updateLineEndType1()));
+		disconnect(end1_length, SIGNAL(editingFinished()),        this, SLOT(updateLineEndLength1()));
+		disconnect(end2_type,   SIGNAL(currentIndexChanged(int)), this, SLOT(updateLineEndType2()));
+		disconnect(end2_length, SIGNAL(editingFinished()),        this, SLOT(updateLineEndLength2()));
+	}
+}

Added: trunk/sources/editor/lineeditor.h
===================================================================
--- trunk/sources/editor/lineeditor.h	                        (rev 0)
+++ trunk/sources/editor/lineeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,64 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef LINE_EDITOR_H
+#define LINE_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartLine;
+class StyleEditor;
+/**
+	This class provides a widget to edit lines within the element editor.
+*/
+class LineEditor : public ElementItemEditor {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	LineEditor(QETElementEditor *, PartLine * = 0, QWidget * = 0);
+	virtual ~LineEditor();
+	private:
+	LineEditor(const LineEditor &);
+	
+	// attributes
+	private:
+	PartLine *part;
+	StyleEditor *style_;
+	QLineEdit *x1, *y1, *x2, *y2;
+	QComboBox *end1_type, *end2_type;
+	QLineEdit *end1_length, *end2_length;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateLine();
+	void updateLineX1();
+	void updateLineY1();
+	void updateLineX2();
+	void updateLineY2();
+	void updateLineEndType1();
+	void updateLineEndLength1();
+	void updateLineEndType2();
+	void updateLineEndLength2();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/partarc.cpp
===================================================================
--- trunk/sources/editor/partarc.cpp	                        (rev 0)
+++ trunk/sources/editor/partarc.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,295 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "partarc.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de cet arc
+	@param scene La scene sur laquelle figure cet arc
+*/
+PartArc::PartArc(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) :
+	QGraphicsEllipseItem(parent, scene),
+	CustomElementGraphicPart(editor),
+	_angle(-90),
+	start_angle(0)
+{
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+/// Destructeur
+PartArc::~PartArc() {
+}
+
+/**
+	Dessine l'arc de cercle
+	@param painter QPainter a utiliser pour rendre le dessin
+	@param options Options pour affiner le rendu
+	@param widget Widget sur lequel le rendu est effectue
+*/
+void PartArc::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	Q_UNUSED(widget);
+	applyStylesToQPainter(*painter);
+	// enleve systematiquement la couleur de fond
+	painter -> setBrush(Qt::NoBrush);
+	QPen t = painter -> pen();
+	t.setCosmetic(options && options -> levelOfDetail < 1.0);
+	painter -> setPen(t);
+	
+	if (isSelected()) {
+		// dessine l'ellipse en noir
+		painter -> drawEllipse(rect());
+		
+		// dessine l'arc en rouge
+		t.setColor(Qt::red);
+		painter -> setPen(t);
+	}
+	
+	painter -> drawArc(rect(), start_angle * 16, _angle * 16);
+	if (isSelected()) {
+		// dessine la croix au centre de l'ellipse
+		painter -> setRenderHint(QPainter::Antialiasing, false);
+		painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue);
+		QPointF center = rect().center();
+		painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y()));
+		painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0));
+	}
+}
+
+/**
+	Exporte l'arc de cercle en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant l'arc de cercle
+*/
+const QDomElement PartArc::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element = xml_document.createElement("arc");
+	QPointF top_left(sceneTopLeft());
+	xml_element.setAttribute("x", QString("%1").arg(top_left.x()));
+	xml_element.setAttribute("y", QString("%1").arg(top_left.y()));
+	xml_element.setAttribute("width",  QString("%1").arg(rect().width()));
+	xml_element.setAttribute("height", QString("%1").arg(rect().height()));
+	xml_element.setAttribute("start", QString("%1").arg(start_angle));
+	xml_element.setAttribute("angle", QString("%1").arg(_angle));
+	stylesToXml(xml_element);
+	return(xml_element);
+}
+
+/**
+	Importe les proprietes d'un arc de cercle depuis un element XML
+	@param qde Element XML a lire
+*/
+void PartArc::fromXml(const QDomElement &qde) {
+	stylesFromXml(qde);
+	setRect(
+		QRectF(
+			mapFromScene(
+				qde.attribute("x", "0").toDouble(),
+				qde.attribute("y", "0").toDouble()
+			),
+			QSizeF(
+				qde.attribute("width",  "0").toDouble(),
+				qde.attribute("height", "0").toDouble()
+			)
+		)
+	);
+	setStartAngle(qde.attribute("start", "0").toInt());
+	setAngle(qde.attribute("angle", "-90").toInt());
+}
+
+/**
+	@return le coin superieur gauche du rectangle dans lequel s'inscrit
+	l'ellipse dont fait partie cet arc, dans les coordonnees de la scene.
+*/
+QPointF PartArc::sceneTopLeft() const {
+	return(mapToScene(rect().topLeft()));
+}
+
+/**
+	Specifie la valeur d'une propriete donnee de l'arc
+	@param property propriete a modifier. Valeurs acceptees :
+		* x : abscisse du centre de l'ellipse dont fait partie l'arc
+		* y : ordonnee du centre de l'ellipse dont fait partie l'arc
+		* diameter_h : diametre horizontal de l'ellipse dont fait partie l'arc
+		* diameter_v : diametre vertical de l'ellipse dont fait partie l'arc
+		* start_angle : angle de depart
+		* angle : taille de l'arc de cercle
+	@param value Valeur a attribuer a la propriete
+*/
+void PartArc::setProperty(const QString &property, const QVariant &value) {
+	CustomElementGraphicPart::setProperty(property, value);
+	if (!value.canConvert(QVariant::Double)) return;
+	if (property == "x") {
+		QRectF current_rect = rect();
+		QPointF current_pos = mapToScene(current_rect.center());
+		setRect(current_rect.translated(value.toDouble() - current_pos.x(), 0.0));
+	} else if (property == "y") {
+		QRectF current_rect = rect();
+		QPointF current_pos = mapToScene(current_rect.center());
+		setRect(current_rect.translated(0.0, value.toDouble() - current_pos.y()));
+	} else if (property == "diameter_h") {
+		qreal new_width = qAbs(value.toDouble());
+		QRectF current_rect = rect();
+		current_rect.translate((new_width - current_rect.width()) / -2.0, 0.0);
+		current_rect.setWidth(new_width);
+		setRect(current_rect);
+	} else if (property == "diameter_v") {
+		qreal new_height = qAbs(value.toDouble());
+		QRectF current_rect = rect();
+		current_rect.translate(0.0, (new_height - current_rect.height()) / -2.0);
+		current_rect.setHeight(new_height);
+		setRect(current_rect);
+	} else if (property == "start_angle") {
+		setStartAngle(value.toInt() );
+	} else if (property == "angle") {
+		setAngle(value.toInt());
+	}
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee de l'arc de cercle
+	@param property propriete lue. Valeurs acceptees :
+		* x : abscisse du centre de l'ellipse dont fait partie l'arc
+		* y : ordonnee du centre de l'ellipse dont fait partie l'arc
+		* diameter_h : diametre horizontal de l'ellipse dont fait partie l'arc
+		* diameter_v : diametre vertical de l'ellipse dont fait partie l'arc
+		* start_angle : angle de depart
+		* angle : taille de l'arc de cercle
+	@return La valeur de la propriete property
+*/
+QVariant PartArc::property(const QString &property) {
+	// appelle la methode property de CustomElementGraphicpart pour les styles
+	QVariant style_property = CustomElementGraphicPart::property(property);
+	if (style_property != QVariant()) return(style_property);
+	
+	if (property == "x") {
+		return(mapToScene(rect().center()).x());
+	} else if (property == "y") {
+		return(mapToScene(rect().center()).y());
+	} else if (property == "diameter_h") {
+		return(rect().width());
+	} else if (property == "diameter_v") {
+		return(rect().height());
+	} else if (property == "start_angle") {
+		return(start_angle);
+	} else if (property == "angle") {
+		return(_angle);
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartArc::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (scene()) {
+		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsEllipseItem::itemChange(change, value));
+}
+
+/**
+	Permet de modifier l'etendue de l'arc de cercle.
+	Il s'agit d'un angle, exprime en degres.
+	Si l'angle est positif, l'arc s'etendra dans le sens des aiguilles d'une
+	montre.
+	@param a la nouvelle taille de l'arc de cercle
+*/
+void PartArc::setAngle(int a) {
+	_angle = a;
+}
+
+/**
+	Permet de modifier la position de depart de l'arc de cercle.
+	Il s'agit d'un angle, exprime en degres.
+	l'angle "0 degre" est situe a "3 heures".
+	@param a la nouvelle taille de l'arc de cercle
+*/
+void PartArc::setStartAngle(int a) {
+	start_angle = a;
+}
+
+/**
+	@return l'etendue de l'arc de cercle
+*/
+int PartArc::angle() const {
+	return(_angle);
+}
+
+/**
+	@return la position de depart de l'arc de cercle
+*/
+int PartArc::startAngle() const {
+	return(start_angle);
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Un arc est pertinent des lors que ses dimensions et son etendue ne sont
+	pas nulles.
+*/
+bool PartArc::isUseless() const {
+	return(rect().isNull() || !angle());
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartArc::sceneGeometricRect() const {
+	return(mapToScene(rect()).boundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartArc::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	saved_points_.clear();
+	saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight());
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartArc::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	QList<QPointF> mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_);
+	setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1))));
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartArc::boundingRect() const {
+	qreal adjust = 1.5;
+	QRectF r(QGraphicsEllipseItem::boundingRect().normalized());
+	r.adjust(-adjust, -adjust, adjust, adjust);
+	return(r);
+}

Added: trunk/sources/editor/partarc.h
===================================================================
--- trunk/sources/editor/partarc.h	                        (rev 0)
+++ trunk/sources/editor/partarc.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,73 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_ARC_H
+#define PART_ARC_H
+#include <QtGui>
+#include "customelementgraphicpart.h"
+/**
+	This class represents an elliptical arc primitive which may be used to
+	compose the drawing of an electrical element within the element editor.
+*/
+class PartArc : public QGraphicsEllipseItem, public CustomElementGraphicPart {
+	// constructors, destructor
+	public:
+	PartArc(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartArc();
+	
+	private:
+	PartArc(const PartArc &);
+	
+	// attributes
+	private:
+	int _angle;
+	int start_angle;
+	
+	// methods
+	public:
+	enum { Type = UserType + 1101 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartArc.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	virtual QString name() const { return(QObject::tr("arc", "element part name")); }
+	virtual QString xmlName() const { return(QString("arc")); }
+	virtual const QDomElement toXml(QDomDocument &) const;
+	virtual void fromXml(const QDomElement &);
+	virtual QPointF sceneTopLeft() const;
+	virtual QRectF boundingRect() const;
+	virtual void setAngle(int);
+	virtual void setStartAngle(int);
+	virtual int angle() const;
+	virtual int startAngle() const;
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	
+	protected:
+	QVariant itemChange(GraphicsItemChange, const QVariant &);
+	
+	private:
+	QList<QPointF> saved_points_;
+};
+#endif

Added: trunk/sources/editor/partellipse.cpp
===================================================================
--- trunk/sources/editor/partellipse.cpp	                        (rev 0)
+++ trunk/sources/editor/partellipse.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,239 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "partellipse.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de cette ellipse
+	@param scene La scene sur laquelle figure cette ellipse
+*/
+PartEllipse::PartEllipse(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : QGraphicsEllipseItem(parent, scene), CustomElementGraphicPart(editor) {
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+/// Destructeur
+PartEllipse::~PartEllipse() {
+}
+
+/**
+	Dessine l'ellipse
+	@param painter QPainter a utiliser pour rendre le dessin
+	@param options Options pour affiner le rendu
+	@param widget Widget sur lequel le rendu est effectue
+*/
+void PartEllipse::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	Q_UNUSED(widget);
+	applyStylesToQPainter(*painter);
+	QPen t = painter -> pen();
+	t.setCosmetic(options && options -> levelOfDetail < 1.0);
+	if (isSelected()) {
+		t.setColor(Qt::red);
+	}
+	painter -> setPen(t);
+	painter -> drawEllipse(rect());
+	if (isSelected()) {
+		painter -> setRenderHint(QPainter::Antialiasing, false);
+		painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue);
+		QPointF center = rect().center();
+		painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y()));
+		painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0));
+	}
+}
+
+/**
+	Exporte l'ellipse en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant l'ellipse
+*/
+const QDomElement PartEllipse::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element;
+	if (qFuzzyCompare(rect().width(), rect().height())) {
+		xml_element = xml_document.createElement("circle");
+		xml_element.setAttribute("diameter", QString("%1").arg(rect().width()));
+	} else {
+		xml_element = xml_document.createElement("ellipse");
+		xml_element.setAttribute("width",  QString("%1").arg(rect().width()));
+		xml_element.setAttribute("height", QString("%1").arg(rect().height()));
+	}
+	QPointF top_left(sceneTopLeft());
+	xml_element.setAttribute("x", QString("%1").arg(top_left.x()));
+	xml_element.setAttribute("y", QString("%1").arg(top_left.y()));
+	stylesToXml(xml_element);
+	return(xml_element);
+}
+
+/**
+	Importe les proprietes d'une ellipse depuis un element XML
+	@param qde Element XML a lire
+*/
+void PartEllipse::fromXml(const QDomElement &qde) {
+	stylesFromXml(qde);
+	qreal width, height;
+	if (qde.tagName() == "ellipse") {
+		width = qde.attribute("width",  "0").toDouble();
+		height = qde.attribute("height", "0").toDouble();
+	} else {
+		width = height = qde.attribute("diameter", "0").toDouble();
+	}
+	setRect(
+		QRectF(
+			mapFromScene(
+				qde.attribute("x", "0").toDouble(),
+				qde.attribute("y", "0").toDouble()
+			),
+			QSizeF(width, height)
+		)
+	);
+}
+
+/**
+	Specifie la valeur d'une propriete donnee de l'ellipse
+	@param property propriete a modifier. Valeurs acceptees :
+		* x : abscisse du centre de l'ellipse
+		* y : ordonnee du centre de l'ellipse
+		* diameter_h : diametre horizontal de l'ellipse
+		* diameter_v : diametre vertical de l'ellipse
+	@param value Valeur a attribuer a la propriete
+*/
+void PartEllipse::setProperty(const QString &property, const QVariant &value) {
+	CustomElementGraphicPart::setProperty(property, value);
+	if (!value.canConvert(QVariant::Double)) return;
+	if (property == "x") {
+		QRectF current_rect = rect();
+		QPointF current_pos = mapToScene(current_rect.center());
+		setRect(current_rect.translated(value.toDouble() - current_pos.x(), 0.0));
+	} else if (property == "y") {
+		QRectF current_rect = rect();
+		QPointF current_pos = mapToScene(current_rect.center());
+		setRect(current_rect.translated(0.0, value.toDouble() - current_pos.y()));
+	} else if (property == "diameter_h") {
+		qreal new_width = qAbs(value.toDouble());
+		QRectF current_rect = rect();
+		current_rect.translate((new_width - current_rect.width()) / -2.0, 0.0);
+		current_rect.setWidth(new_width);
+		setRect(current_rect);
+	} else if (property == "diameter_v") {
+		qreal new_height = qAbs(value.toDouble());
+		QRectF current_rect = rect();
+		current_rect.translate(0.0, (new_height - current_rect.height()) / -2.0);
+		current_rect.setHeight(new_height);
+		setRect(current_rect);
+	}
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee de l'ellipse
+	@param property propriete lue. Valeurs acceptees :
+		* x : abscisse du centre de l'ellipse
+		* y : ordonnee du centre de l'ellipse
+		* diameter_h : diametre horizontal de l'ellipse
+		* diameter_v : diametre vertical de l'ellipse
+	@return La valeur de la propriete property
+*/
+QVariant PartEllipse::property(const QString &property) {
+	// appelle la methode property de CustomElementGraphicpart pour les styles
+	QVariant style_property = CustomElementGraphicPart::property(property);
+	if (style_property != QVariant()) return(style_property);
+	
+	if (property == "x") {
+		return(mapToScene(rect().center()).x());
+	} else if (property == "y") {
+		return(mapToScene(rect().center()).y());
+	} else if (property == "diameter_h") {
+		return(rect().width());
+	} else if (property == "diameter_v") {
+		return(rect().height());
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartEllipse::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (scene()) {
+		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsEllipseItem::itemChange(change, value));
+}
+
+/**
+	@return le coin superieur gauche du rectangle dans lequel s'inscrit
+	l'ellipse, dans les coordonnees de la scene.
+*/
+QPointF PartEllipse::sceneTopLeft() const {
+	return(mapToScene(rect().topLeft()));
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Une ellipse est pertinente des lors que ses dimensions ne sont pas nulles
+*/
+bool PartEllipse::isUseless() const {
+	return(rect().isNull());
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartEllipse::sceneGeometricRect() const {
+	return(mapToScene(rect()).boundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartEllipse::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	// we keep track of our own rectangle at the moment in scene coordinates too
+	saved_points_.clear();
+	saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight());
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartEllipse::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	QList<QPointF> mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_);
+	setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1))));
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartEllipse::boundingRect() const {
+	qreal adjust = 1.5;
+	QRectF r(QGraphicsEllipseItem::boundingRect().normalized());
+	r.adjust(-adjust, -adjust, adjust, adjust);
+	return(r);
+}

Added: trunk/sources/editor/partellipse.h
===================================================================
--- trunk/sources/editor/partellipse.h	                        (rev 0)
+++ trunk/sources/editor/partellipse.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,64 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_ELLIPSE_H
+#define PART_ELLIPSE_H
+#include <QtGui>
+#include "customelementgraphicpart.h"
+/**
+	This class represents an ellipse primitive which may be used to compose the
+	drawing of an electrical element within the element editor.
+*/
+class PartEllipse : public QGraphicsEllipseItem, public CustomElementGraphicPart {
+	// constructors, destructor
+	public:
+	PartEllipse(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartEllipse();
+	
+	private:
+	PartEllipse(const PartEllipse &);
+	
+	// methods
+	public:
+	enum { Type = UserType + 1103 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartEllipse.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	virtual QString name() const { return(QObject::tr("ellipse", "element part name")); }
+	virtual QString xmlName() const { return(QString("ellipse")); }
+	virtual const QDomElement toXml(QDomDocument &) const;
+	virtual void fromXml(const QDomElement &);
+	virtual QPointF sceneTopLeft() const;
+	virtual QRectF boundingRect() const;
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	
+	protected:
+	QVariant itemChange(GraphicsItemChange, const QVariant &);
+	
+	private:
+	QList<QPointF> saved_points_;
+};
+#endif

Added: trunk/sources/editor/partline.cpp
===================================================================
--- trunk/sources/editor/partline.cpp	                        (rev 0)
+++ trunk/sources/editor/partline.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,593 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "partline.h"
+#include <cmath>
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de cette ligne
+	@param scene La scene sur laquelle figure cette ligne
+*/
+PartLine::PartLine(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) :
+	QGraphicsLineItem(parent, scene),
+	CustomElementGraphicPart(editor),
+	first_end(QET::None),
+	first_length(1.5),
+	second_end(QET::None),
+	second_length(1.5)
+{
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+/// Destructeur
+PartLine::~PartLine() {
+}
+
+/**
+	@param end_type Type d'extremite
+	@return Le nombre de "longueurs" requises pour dessiner une extremite de type end_type
+*/
+uint PartLine::requiredLengthForEndType(const QET::EndType &end_type) {
+	uint length_count_required = 0;
+	if (end_type == QET::Circle || end_type == QET::Diamond) {
+		length_count_required = 2;
+	} else if (end_type == QET::Simple || end_type == QET::Triangle) {
+		length_count_required = 1;
+	}
+	return(length_count_required);
+}
+
+/**
+	Dessine la ligne
+	@param painter QPainter a utiliser pour rendre le dessin
+	@param options Options pour affiner le rendu
+	@param widget Widget sur lequel le rendu est effectue
+*/
+void PartLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	Q_UNUSED(widget);
+	// inutile de dessiner une ligne nulle
+	if (line().p1() == line().p2()) return;
+	applyStylesToQPainter(*painter);
+	QPen t = painter -> pen();
+	t.setJoinStyle(Qt::MiterJoin);
+	t.setCosmetic(options && options -> levelOfDetail < 1.0);
+	if (isSelected()) {
+		t.setColor(Qt::red);
+	}
+	painter -> setPen(t);
+	
+	QPointF point1(line().p1());
+	QPointF point2(line().p2());
+	
+	qreal line_length(line().length());
+	qreal pen_width = painter -> pen().widthF();
+	
+	qreal length1 = first_length;
+	qreal length2 = second_length;
+	
+	//debugPaint(painter);
+	
+	// determine s'il faut dessiner les extremites
+	bool draw_1st_end, draw_2nd_end;
+	qreal reduced_line_length = line_length - (length1 * requiredLengthForEndType(first_end));
+	draw_1st_end = first_end && reduced_line_length >= 0;
+	if (draw_1st_end) {
+		reduced_line_length -= (length2 * requiredLengthForEndType(second_end));
+	} else {
+		reduced_line_length = line_length - (length2 * requiredLengthForEndType(second_end));
+	}
+	draw_2nd_end = second_end && reduced_line_length >= 0;
+	
+	// dessine la premiere extremite
+	QPointF start_point, stop_point;
+	if (draw_1st_end) {
+		QList<QPointF> four_points1(fourEndPoints(point1, point2, length1));
+		if (first_end == QET::Circle) {
+			painter -> drawEllipse(QRectF(four_points1[0] - QPointF(length1, length1), QSizeF(length1 * 2.0, length1 * 2.0)));
+			start_point = four_points1[1];
+		} else if (first_end == QET::Diamond) {
+			painter -> drawPolygon(QPolygonF() << four_points1[1] << four_points1[2] << point1 << four_points1[3]);
+			start_point = four_points1[1];
+		} else if (first_end == QET::Simple) {
+			painter -> drawPolyline(QPolygonF() << four_points1[3] << point1 << four_points1[2]);
+			start_point = point1;
+			
+		} else if (first_end == QET::Triangle) {
+			painter -> drawPolygon(QPolygonF() << four_points1[0] << four_points1[2] << point1 << four_points1[3]);
+			start_point = four_points1[0];
+		}
+		
+		// ajuste le depart selon l'epaisseur du trait
+		if (pen_width && (first_end == QET::Simple || first_end == QET::Circle)) {
+			start_point = QLineF(start_point, point2).pointAt(pen_width / 2.0 / line_length);
+		}
+	} else {
+		start_point = point1;
+	}
+	
+	// dessine la seconde extremite
+	if (draw_2nd_end) {
+		QList<QPointF> four_points2(fourEndPoints(point2, point1, length2));
+		if (second_end == QET::Circle) {
+			painter -> drawEllipse(QRectF(four_points2[0] - QPointF(length2, length2), QSizeF(length2 * 2.0, length2 * 2.0)));
+			stop_point = four_points2[1];
+		} else if (second_end == QET::Diamond) {
+			painter -> drawPolygon(QPolygonF() << four_points2[2] << point2 << four_points2[3] << four_points2[1]);
+			stop_point = four_points2[1];
+		} else if (second_end == QET::Simple) {
+			painter -> drawPolyline(QPolygonF() << four_points2[3] << point2 << four_points2[2]);
+			stop_point = point2;
+		} else if (second_end == QET::Triangle) {
+			painter -> drawPolygon(QPolygonF() << four_points2[0] << four_points2[2] << point2 << four_points2[3] << four_points2[0]);
+			stop_point = four_points2[0];
+		}
+		
+		// ajuste l'arrivee selon l'epaisseur du trait
+		if (pen_width && (second_end == QET::Simple || second_end == QET::Circle)) {
+			stop_point = QLineF(point1, stop_point).pointAt((line_length - (pen_width / 2.0)) / line_length);
+		}
+	} else {
+		stop_point = point2;
+	}
+	
+	painter -> drawLine(start_point, stop_point);
+}
+
+/**
+	Exporte la ligne en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant la ligne
+*/
+const QDomElement PartLine::toXml(QDomDocument &xml_document) const {
+	
+	QPointF p1(sceneP1());
+	QPointF p2(sceneP2());
+	
+	QDomElement xml_element = xml_document.createElement("line");
+	xml_element.setAttribute("x1", QString("%1").arg(p1.x()));
+	xml_element.setAttribute("y1", QString("%1").arg(p1.y()));
+	xml_element.setAttribute("x2", QString("%1").arg(p2.x()));
+	xml_element.setAttribute("y2", QString("%1").arg(p2.y()));
+	xml_element.setAttribute("end1", QET::endTypeToString(first_end));
+	xml_element.setAttribute("length1", QString("%1").arg(first_length));
+	xml_element.setAttribute("end2", QET::endTypeToString(second_end));
+	xml_element.setAttribute("length2", QString("%1").arg(second_length));
+	
+	stylesToXml(xml_element);
+	return(xml_element);
+}
+
+/**
+	Importe les proprietes d'une ligne depuis un element XML
+	@param qde Element XML a lire
+*/
+void PartLine::fromXml(const QDomElement &qde) {
+	stylesFromXml(qde);
+	setLine(
+		QLineF(
+			mapFromScene(
+				qde.attribute("x1", "0").toDouble(),
+				qde.attribute("y1", "0").toDouble()
+			),
+			mapFromScene(
+				qde.attribute("x2", "0").toDouble(),
+				qde.attribute("y2", "0").toDouble()
+			)
+		)
+	);
+	first_end    = QET::endTypeFromString(qde.attribute("end1"));
+	first_length = qde.attribute("length1", "1.5").toDouble();
+	second_end   = QET::endTypeFromString(qde.attribute("end2"));
+	second_length = qde.attribute("length2", "1.5").toDouble();
+}
+
+/**
+	Specifie la valeur d'une propriete donnee de la ligne
+	@param property propriete a modifier. Valeurs acceptees :
+		* x1 : abscisse du premier point
+		* y1 : ordonnee du second point
+		* x2 : abscisse du premier point
+		* y2 : ordonnee du second point
+		*end1 : type d'embout du premier point
+		*end2 : type d'embout du second point
+	@param value Valeur a attribuer a la propriete
+*/
+void PartLine::setProperty(const QString &property, const QVariant &value) {
+	CustomElementGraphicPart::setProperty(property, value);
+	if (!value.canConvert(QVariant::Double)) return;
+	QPointF new_p1(sceneP1()), new_p2(sceneP2());
+	bool setline = true;
+	if (property == "x1") {
+		new_p1.setX(value.toDouble());
+	} else if (property == "y1") {
+		new_p1.setY(value.toDouble());
+	} else if (property == "x2") {
+		new_p2.setX(value.toDouble());
+	} else if (property == "y2") {
+		new_p2.setY(value.toDouble());
+	} else {
+		setline = false;
+		if (property == "end1") {
+			setFirstEndType(static_cast<QET::EndType>(value.toUInt()));
+		} else if (property == "end2") {
+			setSecondEndType(static_cast<QET::EndType>(value.toUInt()));
+		} else if (property == "length1") {
+			setFirstEndLength(value.toDouble());
+		} else if (property == "length2") {
+			setSecondEndLength(value.toDouble());
+		}
+	}
+	if (setline) setLine(QLineF(mapFromScene(new_p1), mapFromScene(new_p2)));
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee de la ligne
+	@param property propriete lue. Valeurs acceptees :
+		* x1 : abscisse du premier point
+		* y1 : ordonnee du second point
+		* x2 : abscisse du premier point
+		* y2 : ordonnee du second point
+	@return La valeur de la propriete property
+*/
+QVariant PartLine::property(const QString &property) {
+	// appelle la methode property de CustomElementGraphicpart pour les styles
+	QVariant style_property = CustomElementGraphicPart::property(property);
+	if (style_property != QVariant()) return(style_property);
+	
+	if (property == "x1") {
+		return(sceneP1().x());
+	} else if (property == "y1") {
+		return(sceneP1().y());
+	} else if (property == "x2") {
+		return(sceneP2().x());
+	} else if (property == "y2") {
+		return(sceneP2().y());
+	} else if (property == "end1") {
+		return(firstEndType());
+	} else if (property == "end2") {
+		return(secondEndType());
+	} else if (property == "length1") {
+		return(firstEndLength());
+	} else if (property == "length2") {
+		return(secondEndLength());
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartLine::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (scene()) {
+		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsLineItem::itemChange(change, value));
+}
+
+/**
+	@return le premier point, dans les coordonnees de la scene.
+*/
+QPointF PartLine::sceneP1() const {
+	return(mapToScene(line().p1()));
+}
+
+/**
+	@return le second point, dans les coordonnees de la scene.
+*/
+QPointF PartLine::sceneP2() const {
+	return(mapToScene(line().p2()));
+}
+
+/**
+	@return la forme selectionnable de la ligne
+*/
+QPainterPath PartLine::shape() const {
+	QList<QPointF> points = fourShapePoints();
+	QPainterPath t;
+	t.setFillRule(Qt::WindingFill);
+	t.moveTo(points.at(0));
+	t.lineTo(points.at(1));
+	t.lineTo(points.at(2));
+	t.lineTo(points.at(3));
+	t.lineTo(points.at(0));
+	
+	// n'en fait pas plus si la ligne se ramene a un point
+	if (line().p1() == line().p2()) return(t);
+	
+	// ajoute un cercle pour l'extremite 1 si besoin
+	if (first_end) {
+		QPainterPath t2;
+		t2.addEllipse(firstEndCircleRect());
+		t.addPath(t2.subtracted(t));
+	}
+	
+	// ajoute un cercle pour l'extremite 2 si besoin
+	if (second_end) {
+		QPainterPath t2;
+		t2.addEllipse(secondEndCircleRect());
+		t.addPath(t2.subtracted(t));
+	}
+	
+	return(t);
+}
+
+/**
+	@return une liste contenant les deux points de la droite + les 4 points entourant ces deux points
+*/
+QList<QPointF> PartLine::fourShapePoints() const {
+	const qreal marge = 2.0;
+	// on a donc A(xa , ya) et B(xb, yb)
+	QPointF a = line().p1();
+	QPointF b = line().p2();
+	
+	QList<QPointF> result;
+	
+	// cas particulier : la droite se ramene a un point
+	if (a == b) {
+		result << QPointF(a.x() - marge, a.y() - marge);
+		result << QPointF(a.x() - marge, a.y() + marge);
+		result << QPointF(a.x() + marge, a.y() + marge);
+		result << QPointF(a.x() + marge, a.y() - marge);
+	} else {
+		
+		// on calcule le vecteur AB : (xb-xa, yb-ya)
+		QPointF v_ab = b - a;
+		
+		// et la distance AB : racine des coordonnees du vecteur au carre
+		qreal ab = sqrt(pow(v_ab.x(), 2) + pow(v_ab.y(), 2));
+		
+		// ensuite on definit le vecteur u(a, b) qui est egal au vecteur AB divise
+		// par sa longueur et multiplie par la longueur de la marge  que tu veux
+		// laisser
+		QPointF u = v_ab / ab * marge;
+		
+		// on definit le vecteur v(-b , a) qui est perpendiculaire a AB
+		QPointF v(-u.y(), u.x());
+		QPointF m = -u + v; // on a le vecteur M = -u + v
+		QPointF n = -u - v; // et le vecteur N=-u-v
+		QPointF h =  a + m; // H = A + M
+		QPointF k =  a + n; // K = A + N
+		QPointF i =  b - n; // I = B - N
+		QPointF j =  b - m; // J = B - M
+		
+		result << h << i << j << k;
+	}
+	return(result);
+}
+
+/**
+	@return le rectangle encadrant l'integralite de la premiere extremite
+*/
+QRectF PartLine::firstEndCircleRect() const {
+	QList<QPointF> interesting_points = fourEndPoints(
+		line().p1(),
+		line().p2(),
+		first_length
+	);
+	
+	QRectF end_rect(
+		interesting_points[0] - QPointF(first_length, first_length),
+		QSizeF(2.0 * first_length, 2.0 * first_length)
+	);
+	
+	return(end_rect);
+}
+
+/**
+	@return le rectangle encadrant l'integralite de la seconde extremite
+*/
+QRectF PartLine::secondEndCircleRect() const {
+	QList<QPointF> interesting_points = fourEndPoints(
+		line().p2(),
+		line().p1(),
+		second_length
+	);
+	
+	QRectF end_rect(
+		interesting_points[0] - QPointF(second_length, second_length),
+		QSizeF(2.0 * second_length, 2.0 * second_length)
+	);
+	
+	return(end_rect);
+}
+
+/**
+	Affiche differentes composantes du dessin :
+	  - le boundingRect
+	  - les point speciaux a chaque extremite
+	  - la quadrature du cercle a chaque extremite, meme si celle-ci est d'un
+	  autre type
+*/
+void PartLine::debugPaint(QPainter *painter) {
+	painter -> save();
+	painter -> setPen(Qt::gray);
+	painter -> drawRect(boundingRect());
+	
+	painter -> setPen(Qt::green);
+	painter -> drawRect(firstEndCircleRect());
+	painter -> drawRect(secondEndCircleRect());
+	
+	painter -> setPen(Qt::red);
+	foreach(QPointF pointy, fourEndPoints(line().p1(), line().p2(), first_length)) {
+		painter -> drawEllipse(pointy, 0.1, 0.1);
+	}
+	foreach(QPointF pointy, fourEndPoints(line().p2(), line().p1(), second_length)) {
+		painter -> drawEllipse(pointy, 0.1, 0.1);
+	}
+	
+	painter -> restore();
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartLine::boundingRect() const {
+	QRectF r(QGraphicsLineItem::boundingRect());
+	
+	// le rectangle ainsi obtenu ne doit pas avoir une dimension nulle
+	r.adjust(0.0, 0.0, 0.1, 0.1);
+	
+	// cas special : les embouts sortent largement du bounding rect originel
+	if (first_end != QET::None) {
+		r = r.united(firstEndCircleRect());
+	}
+	
+	if (second_end != QET::None) {
+		r = r.united(secondEndCircleRect());
+	}
+	
+	// la taille du bounding rect est ajustee avec une certaine marge
+	qreal adjust = 1.2;
+	r.adjust(-adjust, -adjust, adjust, adjust);
+	return(r);
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Une ligne est pertinente des lors que ses deux points sont differents
+*/
+bool PartLine::isUseless() const {
+	return(sceneP1() == sceneP2());
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartLine::sceneGeometricRect() const {
+	return(QRectF(sceneP1(), sceneP2()));
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartLine::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	saved_points_.clear();
+	saved_points_ << sceneP1() << sceneP2();
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartLine::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	QList<QPointF> mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_);
+	setLine(QLineF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1))));
+}
+
+/**
+	@param end_type nouveau type d'embout pour l'extremite 1
+*/
+void PartLine::setFirstEndType(const QET::EndType &end_type) {
+	first_end = end_type;
+}
+
+/**
+	@return le type d'embout pour l'extremite 1
+*/
+QET::EndType PartLine::firstEndType() const {
+	return(first_end);
+}
+
+/**
+	@param end_type Nouveau type d'embout pour l'extremite 2
+*/
+void PartLine::setSecondEndType(const QET::EndType &end_type) {
+	second_end = end_type;
+}
+
+/**
+	@return le type d'embout pour l'extremite 2
+*/
+QET::EndType PartLine::secondEndType() const {
+	return(second_end);
+}
+
+/**
+	@return Les quatre points interessants a l'extremite d'une droite
+	Ces points sont, dans l'ordre :
+		* O : point sur la ligne, a une distance length de l'extremite
+		* A : point sur la ligne a une distance 2 x length de l'extremite
+		* B : point a une distance length de O - O est le projete de B sur la droite
+		* C : point a une distance length de O - O est le projete de C sur la droite
+		B et C sont situes de part et d'autre de la ligne
+	@param end_point Extremite concernee
+	@param other_point Autre point permettant de definir une ligne
+	@param length Longueur a utiliser entre l'extremite et le point O
+*/
+QList<QPointF> PartLine::fourEndPoints(const QPointF &end_point, const QPointF &other_point, const qreal &length) {
+	// vecteur et longueur de la ligne 
+	QPointF line_vector = end_point - other_point;
+	qreal line_length = sqrt(pow(line_vector.x(), 2) + pow(line_vector.y(), 2));
+	
+	// vecteur unitaire et vecteur perpendiculaire
+	QPointF u(line_vector / line_length * length);
+	QPointF v(-u.y(), u.x());
+	
+	// points O, A, B et C
+	QPointF o(end_point - u);
+	QPointF a(o - u);
+	QPointF b(o + v);
+	QPointF c(o - v);
+	
+	return(QList<QPointF>() << o << a << b << c);
+}
+
+/**
+	@param length nouvelle longueur de la premiere extremite
+	la longueur de l'extemite ne peut exceder celle de la ligne
+*/
+void PartLine::setFirstEndLength(const qreal &length) {
+	first_length = qMin(qAbs(length), line().length());
+}
+
+/**
+	@return longueur de la premiere extremite
+*/
+qreal PartLine::firstEndLength() const {
+	return(first_length);
+}
+
+/**
+	@param length nouvelle longueur de la seconde extremite
+	la longueur de l'extemite ne peut exceder celle de la ligne
+*/
+void PartLine::setSecondEndLength(const qreal &length) {
+	second_length = qMin(qAbs(length), line().length());
+}
+
+/**
+	@return longueur de la seconde extremite
+*/
+qreal PartLine::secondEndLength() const {
+	return(second_length);
+}

Added: trunk/sources/editor/partline.h
===================================================================
--- trunk/sources/editor/partline.h	                        (rev 0)
+++ trunk/sources/editor/partline.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,93 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_LINE_H
+#define PART_LINE_H
+#include <QtGui>
+#include "customelementgraphicpart.h"
+#include "qet.h"
+/**
+	This class represents a line primitive which may be used to compose the
+	drawing of an electrical element within the element editor. Lines may have
+	specific visual ends (e.g. arrows) through the setFirstEndType and
+	setSecondEndType methods. Their size can be defined using the
+	setFirstEndLength and setSecondEndLength methods. Please note ends are not
+	drawn if the required length for their drawing is longer than the line itself.
+	In case there is room for a single end only, the first one get priority.
+*/
+class PartLine : public QGraphicsLineItem, public CustomElementGraphicPart {
+	// constructors, destructor
+	public:
+	PartLine(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartLine();
+	
+	private:
+	PartLine(const PartLine &);
+	
+	// attributes
+	private:
+	QET::EndType first_end;
+	qreal first_length;
+	QET::EndType second_end;
+	qreal second_length;
+	QList<QPointF> saved_points_;
+	
+	// methods
+	public:
+	enum { Type = UserType + 1104 };
+	
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartLine.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	virtual QString name() const { return(QObject::tr("ligne", "element part name")); }
+	virtual QString xmlName() const { return(QString("line")); }
+	virtual const QDomElement toXml(QDomDocument &) const;
+	virtual void fromXml(const QDomElement &);
+	virtual QPointF sceneP1() const;
+	virtual QPointF sceneP2() const;
+	virtual QPainterPath shape() const;
+	virtual QRectF boundingRect() const;
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	virtual void setFirstEndType(const QET::EndType &);
+	virtual QET::EndType firstEndType() const;
+	virtual void setSecondEndType(const QET::EndType &);
+	virtual QET::EndType secondEndType() const;
+	virtual void setFirstEndLength(const qreal &);
+	virtual qreal firstEndLength() const;
+	virtual void setSecondEndLength(const qreal &);
+	virtual qreal secondEndLength() const;
+	static uint requiredLengthForEndType(const QET::EndType &);
+	static QList<QPointF> fourEndPoints(const QPointF &, const QPointF &, const qreal &);
+	protected:
+	QVariant itemChange(GraphicsItemChange, const QVariant &);
+	
+	private:
+	QList<QPointF> fourShapePoints() const;
+	QRectF firstEndCircleRect() const;
+	QRectF secondEndCircleRect() const;
+	void debugPaint(QPainter *);
+};
+#endif

Added: trunk/sources/editor/partpolygon.cpp
===================================================================
--- trunk/sources/editor/partpolygon.cpp	                        (rev 0)
+++ trunk/sources/editor/partpolygon.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,212 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "partpolygon.h"
+#include "qet.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de ce polygone
+	@param scene La scene sur laquelle figure ce polygone
+*/
+PartPolygon::PartPolygon(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : 
+	QGraphicsPolygonItem(parent, scene),
+	CustomElementGraphicPart(editor),
+	closed(false)
+{
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+/// Destructeur
+PartPolygon::~PartPolygon() {
+}
+
+/**
+	Importe les proprietes d'un polygone depuis un element XML
+	@param qde Element XML a lire
+*/
+void PartPolygon::fromXml(const QDomElement &qde) {
+	stylesFromXml(qde);
+	int i = 1;
+	while(true) {
+		if (
+			QET::attributeIsAReal(qde, QString("x%1").arg(i)) &&\
+			QET::attributeIsAReal(qde, QString("y%1").arg(i))
+		) ++ i;
+		else break;
+	}
+	
+	QPolygonF temp_polygon;
+	for (int j = 1 ; j < i ; ++ j) {
+		temp_polygon << QPointF(
+			qde.attribute(QString("x%1").arg(j)).toDouble(),
+			qde.attribute(QString("y%1").arg(j)).toDouble()
+		);
+	}
+	setPolygon(temp_polygon);
+	
+	closed = qde.attribute("closed") != "false";
+}
+
+/**
+	Exporte le polygone en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant le polygone
+*/
+const QDomElement PartPolygon::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element = xml_document.createElement("polygon");
+	int i = 1;
+	foreach(QPointF point, polygon()) {
+		point = mapToScene(point);
+		xml_element.setAttribute(QString("x%1").arg(i), QString("%1").arg(point.x()));
+		xml_element.setAttribute(QString("y%1").arg(i), QString("%1").arg(point.y()));
+		++ i;
+	}
+	if (!closed) xml_element.setAttribute("closed", "false");
+	stylesToXml(xml_element);
+	return(xml_element);
+}
+
+/**
+	Dessine le polygone
+	@param painter QPainter a utiliser pour rendre le dessin
+	@param options Options pour affiner le rendu
+	@param widget Widget sur lequel le rendu est effectue
+*/
+void PartPolygon::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	Q_UNUSED(widget);
+	applyStylesToQPainter(*painter);
+	QPen t = painter -> pen();
+	t.setCosmetic(options && options -> levelOfDetail < 1.0);
+	if (isSelected()) t.setColor(Qt::red);
+	painter -> setPen(t);
+	if (closed) painter -> drawPolygon(polygon());
+	else painter -> drawPolyline(polygon());
+}
+
+/**
+	Specifie la valeur d'une propriete donnee du polygone
+	@param property propriete a modifier. Valeurs acceptees :
+		* closed : true pour fermer le polygone, false sinon
+	@param value Valeur a attribuer a la propriete
+*/
+void PartPolygon::setProperty(const QString &property, const QVariant &value) {
+	CustomElementGraphicPart::setProperty(property, value);
+	if (property == "closed") closed = value.toBool();
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee de la ligne
+	@param property propriete lue. Valeurs acceptees :
+		* closed : true pour fermer le polygone, false sinon
+	@return La valeur de la propriete property
+*/
+QVariant PartPolygon::property(const QString &property) {
+	// appelle la methode property de CustomElementGraphicpart pour les styles
+	QVariant style_property = CustomElementGraphicPart::property(property);
+	if (style_property != QVariant()) return(style_property);
+	
+	if (property == "closed") return(closed);
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartPolygon::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (scene()) {
+		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsPolygonItem::itemChange(change, value));
+}
+
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Un polygone est pertinent des lors qu'il possede deux points differents.
+*/
+bool PartPolygon::isUseless() const {
+	QPolygonF poly(polygon());
+	
+	if (polygon().count() < 2) return(true);
+	
+	QPointF previous_point;
+	for (int i = 1 ; i < poly.count() ; ++ i) {
+		if (poly[i] != poly[i-1]) return(false);
+	}
+	
+	return(true);
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartPolygon::sceneGeometricRect() const {
+	return(mapToScene(polygon().boundingRect()).boundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartPolygon::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	saved_points_ = mapToScene(polygon()).toList();
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartPolygon::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	QList<QPointF> mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_);
+	setPolygon(mapFromScene(QPolygonF(mapped_points.toVector())));
+}
+
+/**
+	@reimp CustomElementPart::preferredScalingMethod
+	This method is called by the decorator when it needs to determine the best
+	way to interactively scale a primitive. It is typically called when only a
+	single primitive is being scaled.
+	This reimplementation systematically returns QET::RoundScaleRatios.
+*/
+QET::ScalingMethod PartPolygon::preferredScalingMethod() const {
+	return(QET::RoundScaleRatios);
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartPolygon::boundingRect() const {
+	qreal adjust = 1.5;
+	QRectF r(QGraphicsPolygonItem::boundingRect());
+	r.adjust(-adjust, -adjust, adjust, adjust);
+	return(r);
+}

Added: trunk/sources/editor/partpolygon.h
===================================================================
--- trunk/sources/editor/partpolygon.h	                        (rev 0)
+++ trunk/sources/editor/partpolygon.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,87 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_POLYGON_H
+#define PART_POLYGON_H
+#include <QtGui>
+#include "customelementgraphicpart.h"
+/**
+	This class represents a polygon primitive which may be used to compose the
+	drawing of an electrical element within the element editor.
+*/
+class PartPolygon : public QGraphicsPolygonItem, public CustomElementGraphicPart {
+	// constructors, destructor
+	public:
+	PartPolygon(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartPolygon();
+	
+	private:
+	PartPolygon(const PartPolygon &);
+	
+	// attributes
+	private:
+	bool closed;
+	
+	// methods
+	public:
+	enum { Type = UserType + 1105 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartPolygon.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual QString name() const { return(QObject::tr("polygone", "element part name")); }
+	virtual QString xmlName() const { return(QString("polygon")); }
+	void fromXml(const QDomElement &);
+	const QDomElement toXml(QDomDocument &) const;
+	virtual QRectF boundingRect() const;
+	void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+	void setClosed(bool c);
+	bool isClosed() const;
+	void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	virtual QET::ScalingMethod preferredScalingMethod() const;
+	
+	protected:
+	QVariant itemChange(GraphicsItemChange, const QVariant &);
+	
+	private:
+	QList<QPointF> saved_points_;
+};
+
+/**
+	Whether the polygon should be closed.
+	@param c true for the polygon to be closed, false otherwise
+*/
+inline void PartPolygon::setClosed(bool c) {
+	closed = c;
+}
+
+/**
+	Indicate whether the polygon is closed.
+	@return true if the polygon is closed, false otherwise
+*/
+inline bool PartPolygon::isClosed() const {
+	return(closed);
+}
+
+#endif

Added: trunk/sources/editor/partrectangle.cpp
===================================================================
--- trunk/sources/editor/partrectangle.cpp	                        (rev 0)
+++ trunk/sources/editor/partrectangle.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,236 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "partrectangle.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de ce rectangle
+	@param scene La scene sur laquelle figure ce rectangle
+*/
+PartRectangle::PartRectangle(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : QGraphicsRectItem(parent, scene), CustomElementGraphicPart(editor) {
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+/// Destructeur
+PartRectangle::~PartRectangle() {
+}
+
+/**
+	Dessine le rectangle
+	@param painter QPainter a utiliser pour rendre le dessin
+	@param options Options pour affiner le rendu
+	@param widget Widget sur lequel le rendu est effectue
+*/
+void PartRectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	Q_UNUSED(widget);
+	applyStylesToQPainter(*painter);
+	QPen t = painter -> pen();
+	t.setCosmetic(options && options -> levelOfDetail < 1.0);
+	if (isSelected()) {
+		t.setColor(Qt::red);
+	}
+	
+	// force le type de jointures pour les rectangles
+	t.setJoinStyle(Qt::MiterJoin);
+	
+	// force le dessin avec un trait fin si l'une des dimensions du rectangle est nulle
+	if (!rect().width() || !rect().height()) {
+		t.setWidth(0);
+	}
+	
+	painter -> setPen(t);
+	painter -> drawRect(rect());
+	if (isSelected()) {
+		painter -> setRenderHint(QPainter::Antialiasing, false);
+		painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue);
+		QPointF center = rect().center();
+		painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y()));
+		painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0));
+	}
+}
+
+/**
+	Exporte le rectangle en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant le rectangle
+*/
+const QDomElement PartRectangle::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element = xml_document.createElement("rect");
+	QPointF top_left(sceneTopLeft());
+	xml_element.setAttribute("x", QString("%1").arg(top_left.x()));
+	xml_element.setAttribute("y", QString("%1").arg(top_left.y()));
+	xml_element.setAttribute("width",  QString("%1").arg(rect().width()));
+	xml_element.setAttribute("height", QString("%1").arg(rect().height()));
+	stylesToXml(xml_element);
+	return(xml_element);
+}
+
+/**
+	Importe les proprietes d'une rectangle depuis un element XML
+	@param qde Element XML a lire
+*/
+void PartRectangle::fromXml(const QDomElement &qde) {
+	stylesFromXml(qde);
+	setRect(
+		QRectF(
+			mapFromScene(
+				qde.attribute("x", "0").toDouble(),
+				qde.attribute("y", "0").toDouble()
+			),
+			QSizeF(
+				qde.attribute("width",  "0").toDouble(),
+				qde.attribute("height", "0").toDouble()
+			)
+		)
+	);
+}
+
+/**
+	Specifie la valeur d'une propriete donnee du rectangle
+	@param property propriete a modifier. Valeurs acceptees :
+		* x : abscisse du coin superieur gauche du rectangle
+		* y : ordonnee du coin superieur gauche du rectangle
+		* width : largeur du rectangle
+		* height : hauteur du rectangle
+	@param value Valeur a attribuer a la propriete
+*/
+void PartRectangle::setProperty(const QString &property, const QVariant &value) {
+	CustomElementGraphicPart::setProperty(property, value);
+	if (!value.canConvert(QVariant::Double)) return;
+	if (property == "x") {
+		QRectF current_rect = rect();
+		QPointF current_pos = mapToScene(current_rect.topLeft());
+		setRect(current_rect.translated(value.toDouble() - current_pos.x(), 0.0));
+	} else if (property == "y") {
+		QRectF current_rect = rect();
+		QPointF current_pos = mapToScene(current_rect.topLeft());
+		setRect(current_rect.translated(0.0, value.toDouble() - current_pos.y()));
+	} else if (property == "width") {
+		qreal new_width = qAbs(value.toDouble());
+		QRectF current_rect = rect();
+		current_rect.setWidth(new_width);
+		setRect(current_rect);
+	} else if (property == "height") {
+		qreal new_height = qAbs(value.toDouble());
+		QRectF current_rect = rect();
+		current_rect.setHeight(new_height);
+		setRect(current_rect);
+	}
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee du rectangle
+	@param property propriete lue. Valeurs acceptees :
+		* x : abscisse du coin superieur gauche du rectangle
+		* y : ordonnee du coin superieur gauche du rectangle
+		* width : largeur du rectangle
+		* height : hauteur du rectangle
+	@return La valeur de la propriete property
+*/
+QVariant PartRectangle::property(const QString &property) {
+	// appelle la methode property de CustomElementGraphicpart pour les styles
+	QVariant style_property = CustomElementGraphicPart::property(property);
+	if (style_property != QVariant()) return(style_property);
+	
+	if (property == "x") {
+		return(mapToScene(rect().topLeft()).x());
+	} else if (property == "y") {
+		return(mapToScene(rect().topLeft()).y());
+	} else if (property == "width") {
+		return(rect().width());
+	} else if (property == "height") {
+		return(rect().height());
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartRectangle::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (scene()) {
+		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsRectItem::itemChange(change, value));
+}
+
+/**
+	@return le coin superieur gauche du rectangle, dans les coordonnees de la
+	scene.
+*/
+QPointF PartRectangle::sceneTopLeft() const {
+	return(mapToScene(rect().topLeft()));
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Un rectangle est pertinent des lors que ses dimensions ne sont pas nulles.
+*/
+bool PartRectangle::isUseless() const {
+	return(rect().isNull());
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartRectangle::sceneGeometricRect() const {
+	return(mapToScene(rect()).boundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartRectangle::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	// we keep track of our own rectangle at the moment in scene coordinates too
+	saved_points_.clear();
+	saved_points_ << mapToScene(rect().topLeft()) << mapToScene(rect().bottomRight());
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartRectangle::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	QList<QPointF> mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_);
+	setRect(QRectF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1))));
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartRectangle::boundingRect() const {
+	qreal adjust = 1.5;
+	QRectF r(QGraphicsRectItem::boundingRect().normalized());
+	r.adjust(-adjust, -adjust, adjust, adjust);
+	return(r);
+}

Added: trunk/sources/editor/partrectangle.h
===================================================================
--- trunk/sources/editor/partrectangle.h	                        (rev 0)
+++ trunk/sources/editor/partrectangle.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,64 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_RECTANGLE_H
+#define PART_RECTANGLE_H
+#include <QtGui>
+#include "customelementgraphicpart.h"
+/**
+	This class represents a rectangle primitive which may be used to compose the
+	drawing of an electrical element within the element editor.
+*/
+class PartRectangle : public QGraphicsRectItem, public CustomElementGraphicPart {
+	// constructors, destructor
+	public:
+	PartRectangle(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartRectangle();
+	
+	private:
+	PartRectangle(const PartRectangle &);
+	
+	// methods
+	public:
+	enum { Type = UserType + 1109 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartRectangle.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	virtual QString name() const { return(QObject::tr("rectangle", "element part name")); }
+	virtual QString xmlName() const { return(QString("rect")); }
+	virtual const QDomElement toXml(QDomDocument &) const;
+	virtual void fromXml(const QDomElement &);
+	virtual QPointF sceneTopLeft() const;
+	virtual QRectF boundingRect() const;
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	
+	protected:
+	QVariant itemChange(GraphicsItemChange, const QVariant &);
+	
+	private:
+	QList<QPointF> saved_points_;
+};
+#endif

Added: trunk/sources/editor/partterminal.cpp
===================================================================
--- trunk/sources/editor/partterminal.cpp	                        (rev 0)
+++ trunk/sources/editor/partterminal.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,248 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "partterminal.h"
+#include "terminal.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de cette borne
+	@param scene La scene sur laquelle figure cette borne
+*/
+PartTerminal::PartTerminal(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) :
+	CustomElementPart(editor),
+	QGraphicsItem(parent, scene),
+	_orientation(QET::North)
+{
+	updateSecondPoint();
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setZValue(100000);
+}
+
+/// Destructeur
+PartTerminal::~PartTerminal() {
+}
+
+/**
+	Importe les proprietes d'une borne depuis un element XML
+	@param xml_elmt Element XML a lire
+*/
+void PartTerminal::fromXml(const QDomElement &xml_elmt) {
+	// lit la position de la borne
+	qreal term_x = 0.0, term_y = 0.0;
+	QET::attributeIsAReal(xml_elmt, "x", &term_x);
+	QET::attributeIsAReal(xml_elmt, "y", &term_y);
+	setPos(QPointF(term_x, term_y));
+	
+	// lit l'orientation de la borne
+	_orientation = QET::orientationFromString(xml_elmt.attribute("orientation"));
+	updateSecondPoint();
+}
+
+/**
+	Exporte la borne en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant la borne
+*/
+const QDomElement PartTerminal::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element = xml_document.createElement("terminal");
+	
+	// ecrit la position de la borne
+	xml_element.setAttribute("x", QString("%1").arg(scenePos().x()));
+	xml_element.setAttribute("y", QString("%1").arg(scenePos().y()));
+	
+	// ecrit l'orientation de la borne
+	xml_element.setAttribute("orientation", orientationToString(_orientation));
+	
+	return(xml_element);
+}
+
+/**
+	Dessine la borne
+	@param p QPainter a utiliser pour rendre le dessin
+	@param options Options pour affiner le rendu
+	@param widget Widget sur lequel le rendu est effectue
+*/
+void PartTerminal::paint(QPainter *p, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	Q_UNUSED(widget);
+	p -> save();
+	
+	// annulation des renderhints
+	p -> setRenderHint(QPainter::Antialiasing,          false);
+	p -> setRenderHint(QPainter::TextAntialiasing,      false);
+	p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
+	
+	QPen t;
+	t.setWidthF(1.0);
+	t.setCosmetic(options && options -> levelOfDetail < 1.0);
+	
+	// dessin de la borne en rouge
+	t.setColor(isSelected() ? Terminal::neutralColor : Qt::red);
+	p -> setPen(t);
+	p -> drawLine(QPointF(0.0, 0.0), second_point);
+	
+	// dessin du point d'amarrage au conducteur en bleu
+	t.setColor(isSelected() ? Qt::red : Terminal::neutralColor);
+	p -> setPen(t);
+	p -> setBrush(Terminal::neutralColor);
+	p -> drawPoint(QPointF(0.0, 0.0));
+	p -> restore();
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartTerminal::boundingRect() const {
+	QPointF p1, p2;
+	if (second_point.x() <= 0.0 && second_point.y() <= 0.0) {
+		p1 = second_point;
+		p2 = QPointF(0.0, 0.0);
+	} else {
+		p1 = QPointF(0.0, 0.0);
+		p2 = second_point;
+	}
+	QRectF br;
+	br.setTopLeft    (p1 - QPointF(2.0, 2.0));
+	br.setBottomRight(p2 + QPointF(2.0, 2.0));
+	return(br);
+}
+
+/**
+	@return L'orientation de la borne
+*/
+QET::Orientation PartTerminal::orientation() const {
+	return(_orientation);
+}
+
+/**
+	Definit l'orientation de la borne
+	@param ori la nouvelle orientation de la borne
+*/
+void PartTerminal::setOrientation(QET::Orientation ori) {
+	prepareGeometryChange();
+	_orientation = ori;
+	updateSecondPoint();
+}
+
+/**
+	Specifie la valeur d'une propriete donnee de la borne
+	@param property propriete a modifier. Valeurs acceptees :
+		* x : abscisse de la borne
+		* y : ordonnee de la borne
+		* orientation : orientation de la borne
+	@param value Valeur a attribuer a la propriete
+*/
+void PartTerminal::setProperty(const QString &property, const QVariant &value) {
+	if (property == "x") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setPos(value.toDouble(), pos().y());
+	} else if (property == "y") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setPos(pos().x(), value.toDouble());
+	} else if (property == "orientation") {
+		if (!value.canConvert(QVariant::Int)) return;
+		setOrientation(static_cast<QET::Orientation>(value.toInt()));
+	}
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee de la borne
+	@param property propriete lue. Valeurs acceptees :
+		* x : abscisse de la borne
+		* y : ordonnee de la borne
+		* orientation : orientation de la borne
+	@return La valeur de la propriete property
+*/
+QVariant PartTerminal::property(const QString &property) {
+	if (property == "x") {
+		return(scenePos().x());
+	} else if (property == "y") {
+		return(scenePos().y());
+	} else if (property == "orientation") {
+		return(_orientation);
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartTerminal::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (scene()) {
+		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsItem::itemChange(change, value));
+}
+
+/**
+	Met a jour la position du second point en fonction de la position et de
+	l'orientation de la borne.
+*/
+void PartTerminal::updateSecondPoint() {
+	qreal ts = 4.0; // terminal size
+	switch(_orientation) {
+		case QET::North: second_point = QPointF(0.0,  ts); break;
+		case QET::East : second_point = QPointF(-ts, 0.0); break;
+		case QET::South: second_point = QPointF(0.0, -ts); break;
+		case QET::West : second_point = QPointF(ts,  0.0); break;
+	}
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Une borne est toujours pertinente ; cette fonction renvoie donc
+	toujours false
+*/
+bool PartTerminal::isUseless() const {
+	return(false);
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartTerminal::sceneGeometricRect() const {
+	return(sceneBoundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartTerminal::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	saved_position_ = scenePos();
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartTerminal::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	QPointF mapped_point = mapPoints(initial_selection_rect, new_selection_rect, QList<QPointF>() << saved_position_).first();
+	setPos(mapped_point);
+}

Added: trunk/sources/editor/partterminal.h
===================================================================
--- trunk/sources/editor/partterminal.h	                        (rev 0)
+++ trunk/sources/editor/partterminal.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,73 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_TERMINAL_H
+#define PART_TERMINAL_H
+#include "customelementpart.h"
+#include "qet.h"
+#include <QtGui>
+/**
+	This class represents a terminal which may be used to compose the drawing of
+	an electrical element within the element editor.
+*/
+class PartTerminal : public CustomElementPart, public QGraphicsItem {
+	public:
+	// constructors, destructor
+	PartTerminal(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartTerminal();
+	private:
+	PartTerminal(const PartTerminal &);
+	
+	// attributes
+	private:
+	QET::Orientation _orientation;
+	QPointF second_point;
+	
+	// methods
+	public:
+	enum { Type = UserType + 1106 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartTerminal.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual QString name() const { return(QObject::tr("borne", "element part name")); }
+	virtual QString xmlName() const { return(QString("terminal")); }
+	virtual void fromXml(const QDomElement &);
+	virtual const QDomElement toXml(QDomDocument &) const;
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+	virtual QRectF boundingRect() const;
+	QET::Orientation orientation() const;
+	void setOrientation(QET::Orientation);
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	
+	protected:
+	QVariant itemChange(GraphicsItemChange, const QVariant &);
+	
+	private:
+	void updateSecondPoint();
+	
+	private:
+	QPointF saved_position_;
+};
+#endif

Added: trunk/sources/editor/parttext.cpp
===================================================================
--- trunk/sources/editor/parttext.cpp	                        (rev 0)
+++ trunk/sources/editor/parttext.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,577 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "parttext.h"
+#include "texteditor.h"
+#include "editorcommands.h"
+#include "elementprimitivedecorator.h"
+#include "elementscene.h"
+#include "qetapp.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de ce texte statique
+	@param scene La scene sur laquelle figure ce texte statique
+*/
+PartText::PartText(QETElementEditor *editor, QGraphicsItem *parent, ElementScene *scene) :
+	QGraphicsTextItem(parent, scene),
+	CustomElementPart(editor),
+	previous_text(),
+	decorator_(0)
+{
+#if QT_VERSION >= 0x040500
+	document() -> setDocumentMargin(1.0);
+#endif
+	setDefaultTextColor(Qt::black);
+	setFont(QETApp::diagramTextsFont());
+	real_font_size_ = font().pointSize();
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptHoverEvents(true);
+	setDefaultTextColor(Qt::black);
+	setPlainText(QObject::tr("T", "default text when adding a text in the element editor"));
+	
+	adjustItemPosition(1);
+	// adjust textfield position after line additions/deletions
+	connect(document(), SIGNAL(blockCountChanged(int)), this, SLOT(adjustItemPosition(int)));
+	connect(document(), SIGNAL(contentsChanged()),      this, SLOT(adjustItemPosition()));
+}
+
+/// Destructeur
+PartText::~PartText() {
+}
+
+/**
+	Importe les proprietes d'un texte statique depuis un element XML
+	@param xml_element Element XML a lire
+*/
+void PartText::fromXml(const QDomElement &xml_element) {
+	bool ok;
+	int font_size = xml_element.attribute("size").toInt(&ok);
+	if (!ok || font_size < 1) font_size = 20;
+	
+	setBlack(xml_element.attribute("color") != "white");
+	setProperty("size" , font_size);
+	setPlainText(xml_element.attribute("text"));
+	
+	qreal default_rotation_angle = 0.0;
+	if (QET::attributeIsAReal(xml_element, "rotation", &default_rotation_angle)) {
+		setRotationAngle(default_rotation_angle);
+	}
+	
+	setPos(
+		xml_element.attribute("x").toDouble(),
+		xml_element.attribute("y").toDouble()
+	);
+}
+
+/**
+	Exporte le texte statique en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant le texte statique
+*/
+const QDomElement PartText::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element = xml_document.createElement("text");
+	xml_element.setAttribute("x", QString("%1").arg(pos().x()));
+	xml_element.setAttribute("y", QString("%1").arg(pos().y()));
+	xml_element.setAttribute("text", toPlainText());
+	xml_element.setAttribute("size", font().pointSize());
+	// angle de rotation du champ de texte
+	if (rotationAngle()) {
+		xml_element.setAttribute("rotation", QString("%1").arg(rotationAngle()));
+	}
+	if (!isBlack()) {
+		xml_element.setAttribute("color", "white");
+	}
+	return(xml_element);
+}
+
+/**
+	@return l'angle de rotation de ce champ de texte
+*/
+qreal PartText::rotationAngle() const {
+	return(rotation());
+}
+
+/**
+	@param angle Le nouvel angle de rotation de ce champ de texte
+*/
+void PartText::setRotationAngle(const qreal &angle) {
+	setRotation(QET::correctAngle(angle));
+}
+
+/**
+	@return true or false if this static text is rendered black or white,
+	respectively.
+*/
+bool PartText::isBlack() const {
+	return(defaultTextColor() == Qt::black);
+}
+
+/**
+	@param color whether this static text should be rendered black (true) or white
+	(false).
+*/
+void PartText::setBlack(bool color) {
+	setDefaultTextColor(color ? Qt::black : Qt::white);
+}
+
+/**
+	@return Les coordonnees du point situe en bas a gauche du texte.
+*/
+QPointF PartText::margin() const {
+	QFont used_font = font();
+	QFontMetrics qfm(used_font);
+	
+	// marge du texte
+#if QT_VERSION >= 0x040500
+	qreal document_margin = document() -> documentMargin();
+#else
+	// il semblerait qu'avant Qt 4.5, ceci vaille non pas 4.0 mais 2.0
+	qreal document_margin = 2.0;
+#endif
+	
+	QPointF margin(
+		// marge autour du texte
+		document_margin,
+		// marge au-dessus du texte + distance entre le plafond du texte et la baseline
+		document_margin + qfm.ascent()
+	);
+	return(margin);
+}
+
+/**
+	@reimp QGraphicsItem::focusInEvent(QFocusEvent *)
+	@param e The QFocusEvent object describing the focus gain.
+	Start text edition when the item gains focus.
+*/
+void PartText::focusInEvent(QFocusEvent *e) {
+	startEdition();
+	QGraphicsTextItem::focusInEvent(e);
+}
+
+
+/**
+	@reimp QGraphicsItem::focusOutEvent(QFocusEvent *)
+	@param e The QFocusEvent object describing the focus loss.
+	End text edition when the item loses focus.
+*/
+void PartText::focusOutEvent(QFocusEvent *e) {
+	QGraphicsTextItem::focusOutEvent(e);
+	endEdition();
+}
+
+/**
+	@reimp QGraphicsTextItem::keyPressEvent()
+	Used to handle the escape key when the event is delivered to the field, not
+	to the decorator.
+*/
+void PartText::keyPressEvent(QKeyEvent *event) {
+	if (event -> key() == Qt::Key_Escape) {
+		endEdition();
+	}
+	else {
+		QGraphicsTextItem::keyPressEvent(event);
+	}
+}
+
+/**
+	Permet a l'element texte de devenir editable lorsqu'on double-clique dessus
+	@param e Le QGraphicsSceneMouseEvent qui decrit le double-clic
+*/
+void PartText::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e) {
+	QGraphicsTextItem::mouseDoubleClickEvent(e);
+	if (e -> button() == Qt::LeftButton) {
+		setEditable(true);
+	}
+}
+
+/**
+	Specifie la valeur d'une propriete donnee du texte statique
+	@param property propriete a modifier. Valeurs acceptees :
+		* x : abscisse de la position
+		* y : ordonnee de la position
+		* size : taille du texte
+		* text : texte
+		* "rotation angle" : amgle de rotation
+	@param value Valeur a attribuer a la propriete
+*/
+void PartText::setProperty(const QString &property, const QVariant &value) {
+	if (property == "x") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setPos(value.toDouble(), pos().y());
+	} else if (property == "y") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setPos(pos().x(), value.toDouble());
+	} else if (property == "size") {
+		if (!value.canConvert(QVariant::Int)) return;
+		setFont(QETApp::diagramTextsFont(value.toInt()));
+		real_font_size_ = value.toInt();
+	} else if (property == "real_size") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setFont(QETApp::diagramTextsFont(value.toInt()));
+		real_font_size_ = value.toDouble();
+	} else if (property == "text") {
+		setPlainText(value.toString());
+	} else if (property == "rotation angle") {
+		setRotationAngle(value.toDouble());
+	} else if (property == "color") {
+		setBlack(value.toBool());
+	}
+	// adjust item position, especially useful when changing text or size
+	adjustItemPosition();
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee du texte statique
+	@param property propriete lue. Valeurs acceptees :
+		* x : abscisse de la position
+		* y : ordonnee de la position
+		* size : taille du texte
+		* text : texte
+		* "rotation angle" : amgle de rotation
+	@return La valeur de la propriete property
+*/
+QVariant PartText::property(const QString &property) {
+	if (property == "x") {
+		return(pos().x());
+	} else if (property == "y") {
+		return(pos().y());
+	} else if (property == "size") {
+		return(font().pointSize());
+	} else if (property == "real_size") {
+		return(real_font_size_);
+	} else if (property == "text") {
+		return(toPlainText());
+	} else if (property == "rotation angle") {
+		return(rotation());
+	} else if (property == "color") {
+		return(isBlack());
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartText::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (change == QGraphicsItem::ItemPositionHasChanged || change == QGraphicsItem::ItemSceneHasChanged) {
+		updateCurrentPartEditor();
+	} else if (change == QGraphicsItem::ItemSelectedHasChanged) {
+		if (value.toBool() == true) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsTextItem::itemChange(change, value));
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartText::boundingRect() const {
+	QRectF r = QGraphicsTextItem::boundingRect();
+	r.adjust(0.0, -1.1, 0.0, 0.0);
+	return(r);
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Un texte statique n'est pas pertinent lorsque son texte est vide.
+*/
+bool PartText::isUseless() const {
+	return(toPlainText().isEmpty());
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartText::sceneGeometricRect() const {
+	return(sceneBoundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a rect bounding rectangle.
+*/
+void PartText::startUserTransformation(const QRectF &rect) {
+	Q_UNUSED(rect)
+	saved_point_ = pos(); // scene coordinates, no need to mapFromScene()
+	saved_font_size_ = real_font_size_;
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartText::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	// let's try the naive approach
+	QPointF new_pos = mapPoints(initial_selection_rect, new_selection_rect, QList<QPointF>() << saved_point_).first();
+	setPos(new_pos);
+	
+	// adjust the font size following the vertical scale factor
+	qreal sy = new_selection_rect.height() / initial_selection_rect.height();
+	qreal new_font_size = saved_font_size_ * sy;
+	setProperty("real_size", qMax(1, qRound(new_font_size)));
+}
+
+/**
+	Dessine le texte statique.
+	@param painter QPainter a utiliser pour effectuer le rendu
+	@param qsogi   Pptions de dessin
+	@param widget  Widget sur lequel on dessine (facultatif)
+*/
+void PartText::paint(QPainter *painter, const QStyleOptionGraphicsItem *qsogi, QWidget *widget) {
+	// According to the source code of QGraphicsTextItem::paint(), this should
+	// avoid the drawing of the dashed rectangle around the text.
+	QStyleOptionGraphicsItem our_qsogi(*qsogi);
+	our_qsogi.state = QStyle::State_None;
+	
+	QGraphicsTextItem::paint(painter, &our_qsogi, widget);
+	
+#ifdef QET_DEBUG_EDITOR_TEXTS
+	painter -> setPen(Qt::blue);
+	painter -> drawRect(boundingRect());
+	
+	painter -> setPen(Qt::red);
+	drawPoint(painter, QPointF(0, 0));
+	
+	painter -> setPen(Qt::green);
+	drawPoint(painter, mapFromScene(pos()));
+#endif
+}
+
+/**
+	Handle context menu events.
+	@param event Object describing the context menu event to handle.
+*/
+void PartText::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
+	Q_UNUSED(event);
+}
+
+/**
+	Handle events generated when the mouse hovers over the decorator.
+	@param event Object describing the hover event.
+*/
+void PartText::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+	// force the cursor when the text is being edited
+	if (hasFocus() && decorator_) {
+		decorator_ -> setCursor(Qt::IBeamCursor);
+	}
+	QGraphicsTextItem::hoverMoveEvent(event);
+}
+
+/**
+	@reimp CustomElementPart::setDecorator(ElementPrimitiveDecorator *)
+	Install or remove a sceneEventFilter on the decorator and ensure it will
+	adjust itself while the text is being edited.
+*/
+void PartText::setDecorator(ElementPrimitiveDecorator *decorator) {
+	if (decorator) {
+		decorator -> installSceneEventFilter(this);
+		// ensure the decorator will adjust itself when the text area expands or shrinks
+		connect(document(), SIGNAL(contentsChanged()), decorator, SLOT(adjust()));
+	}
+	else {
+		decorator_ -> removeSceneEventFilter(this);
+		endEdition();
+	}
+	decorator_ = decorator;
+}
+
+/**
+	@reimp QGraphicsItem::sceneEventFilter(QGraphicsItem *, QEvent *).
+	Intercepts events before they reach the watched target, i.e. typically the
+	primitives decorator.
+	This method mainly works with key strokes (F2, escape) and double clicks to
+	begin or end text edition.
+*/
+bool PartText::sceneEventFilter(QGraphicsItem *watched, QEvent *event) {
+	if (watched != decorator_) return(false);
+	
+	QPointF event_scene_pos = QET::graphicsSceneEventPos(event);
+	if (!event_scene_pos.isNull()) {
+		if (contains(mapFromScene(event_scene_pos))) {
+			if (hasFocus()) {
+				return sceneEvent(event); // manually deliver the event to this item
+				return(true); // prevent this event from being delivered to any item
+			} else {
+				if (event -> type() == QEvent::GraphicsSceneMouseDoubleClick) {
+					mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+				}
+			}
+		}
+	}
+	else if (event -> type() == QEvent::KeyRelease || event -> type() == QEvent::KeyPress) {
+		// Intercept F2 and escape keystrokes to focus in and out
+		QKeyEvent *key_event = static_cast<QKeyEvent *>(event);
+		if (!hasFocus() && key_event -> key() == Qt::Key_F2) {
+			setEditable(true);
+			QTextCursor qtc = textCursor();
+			qtc.setPosition(qMax(0, document()->characterCount() - 1));
+			setTextCursor(qtc);
+		} else if (hasFocus() && key_event -> key() == Qt::Key_Escape) {
+			endEdition();
+		}
+		if (hasFocus()) {
+			sceneEvent(event); // manually deliver the event to this item
+			return(true); // prevent this event from being delivered to any item
+		}
+	}
+	return(false);
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartText::singleItemPressEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	Q_UNUSED(event)
+	return(hasFocus());
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartText::singleItemMoveEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	Q_UNUSED(event)
+	return(hasFocus());
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartText::singleItemReleaseEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	Q_UNUSED(event)
+	return(hasFocus());
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartText::singleItemDoubleClickEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	// calling mouseDoubleClickEvent() will set this text item editable and grab keyboard focus
+	if (event -> button() == Qt::LeftButton) {
+		mouseDoubleClickEvent(event);
+		return(true);
+	}
+	return(false);
+}
+
+/**
+	Cette methode s'assure que la position du champ de texte est coherente
+	en repositionnant son origine (c-a-d le milieu du bord gauche du champ de
+	texte) a la position originale. Cela est notamment utile lorsque le champ
+	de texte est agrandi ou retreci verticalement (ajout ou retrait de lignes).
+	@param new_block_count Nombre de blocs dans le PartText
+*/
+void PartText::adjustItemPosition(int new_block_count) {
+	Q_UNUSED(new_block_count);
+	QPointF origin_offset = margin();
+	
+	QTransform base_translation;
+	base_translation.translate(-origin_offset.x(), -origin_offset.y());
+	setTransform(base_translation, false);
+	setTransformOriginPoint(origin_offset);
+}
+
+/**
+	@param editable Whether this text item should be interactively editable.
+*/
+void PartText::setEditable(bool editable) {
+	if (editable) {
+		setFlag(QGraphicsItem::ItemIsFocusable, true);
+		setTextInteractionFlags(Qt::TextEditorInteraction);
+		setFocus(Qt::MouseFocusReason);
+	}
+	else {
+		setTextInteractionFlags(Qt::NoTextInteraction);
+		setFlag(QGraphicsItem::ItemIsFocusable, false);
+	}
+}
+
+/**
+	Start text edition by storing the former value of the text.
+*/
+void PartText::startEdition() {
+	// !previous_text.isNull() means the text is being edited
+	previous_text = toPlainText();
+}
+
+/**
+	End text edition, potentially generating a ChangePartCommand if the text
+	has changed.
+*/
+void PartText::endEdition() {
+	if (!previous_text.isNull()) {
+		// the text was being edited
+		QString new_text = toPlainText();
+		if (previous_text != new_text) {
+			// the text was changed
+			ChangePartCommand *text_change = new ChangePartCommand(
+				TextEditor::tr("contenu") + " " + name(),
+				this,
+				"text",
+				previous_text,
+				new_text
+			);
+			previous_text = QString();
+			undoStack().push(text_change);
+		}
+	}
+	
+	// deselectionne le texte
+	QTextCursor qtc = textCursor();
+	qtc.clearSelection();
+	setTextCursor(qtc);
+	
+	setEditable(false);
+	if (decorator_) {
+		decorator_ -> setFocus();
+	}
+}
+
+#ifdef QET_DEBUG_EDITOR_TEXTS
+/**
+	Dessine deux petites fleches pour mettre un point en valeur
+	@param painter QPainter a utiliser pour effectuer le rendu
+	@param point   Point a dessiner
+*/
+void PartText::drawPoint(QPainter *painter, const QPointF &point) {
+	qreal px = point.x();
+	qreal py = point.y();
+	qreal size_1 = 5.0;
+	qreal size_2 = 1.0;
+	painter -> drawLine(QLineF(px, py, px + size_1, py));
+	painter -> drawLine(QLineF(px + size_1 - size_2, py - size_2, px + size_1, py));
+	painter -> drawLine(QLineF(px + size_1 - size_2, py + size_2, px + size_1, py));
+	painter -> drawLine(QLineF(px, py, px, py + size_1));
+	painter -> drawLine(QLineF(px, py + size_1, px - size_2, py + size_1 - size_2));
+	painter -> drawLine(QLineF(px, py + size_1, px + size_2, py + size_1 - size_2));
+}
+#endif

Added: trunk/sources/editor/parttext.h
===================================================================
--- trunk/sources/editor/parttext.h	                        (rev 0)
+++ trunk/sources/editor/parttext.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,98 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_TEXT_H
+#define PART_TEXT_H
+#include <QtGui>
+#include "customelementpart.h"
+class TextEditor;
+class ElementPrimitiveDecorator;
+/**
+	This class represents an static text primitive which may be used to compose
+	the drawing of an electrical element within the element editor.
+*/
+class PartText : public QGraphicsTextItem, public CustomElementPart {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	PartText(QETElementEditor *, QGraphicsItem * = 0, ElementScene * = 0);
+	virtual ~PartText();
+	
+	private:
+	PartText(const PartText &);
+	
+	// methods
+	public:
+	enum { Type = UserType + 1107 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartText.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual QString name() const { return(QObject::tr("texte", "element part name")); }
+	virtual QString xmlName() const { return(QString("text")); }
+	void fromXml(const QDomElement &);
+	const QDomElement toXml(QDomDocument &) const;
+	qreal rotationAngle() const;
+	void setRotationAngle(const qreal &);
+	bool isBlack() const;
+	void setBlack(bool);
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0 );
+	
+	virtual void setDecorator(ElementPrimitiveDecorator *);
+	virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	
+	public slots:
+	void adjustItemPosition(int = 0);
+	void setEditable(bool);
+	void startEdition();
+	void endEdition();
+	
+	protected:
+	virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *);
+	virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *);
+	virtual bool sceneEventFilter(QGraphicsItem *, QEvent *);
+	virtual void focusInEvent(QFocusEvent *);
+	virtual void focusOutEvent(QFocusEvent *);
+	virtual void keyPressEvent(QKeyEvent *);
+	virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
+	virtual QVariant itemChange(GraphicsItemChange, const QVariant &);
+	QRectF boundingRect() const;
+	
+	private:
+	QPointF margin() const;
+#ifdef QET_DEBUG_EDITOR_TEXTS
+	void drawPoint(QPainter *, const QPointF &);
+#endif
+	QString previous_text;
+	qreal real_font_size_;
+	QPointF saved_point_;
+	qreal saved_font_size_;
+	QGraphicsItem *decorator_;
+};
+#endif

Added: trunk/sources/editor/parttextfield.cpp
===================================================================
--- trunk/sources/editor/parttextfield.cpp	                        (rev 0)
+++ trunk/sources/editor/parttextfield.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,556 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "parttextfield.h"
+#include "textfieldeditor.h"
+#include "editorcommands.h"
+#include "elementprimitivedecorator.h"
+#include "qetapp.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param parent Le QGraphicsItem parent de ce champ de texte
+	@param scene La scene sur laquelle figure ce champ de texte
+*/
+PartTextField::PartTextField(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) :
+	QGraphicsTextItem(parent, scene),
+	CustomElementPart(editor),
+	follow_parent_rotations(true),
+	previous_text(),
+	decorator_(0)
+{
+	setDefaultTextColor(Qt::black);
+	setFont(QETApp::diagramTextsFont());
+	real_font_size_ = font().pointSize();
+	setFlags(QGraphicsItem::ItemIsSelectable);
+#if QT_VERSION >= 0x040600
+	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+#endif
+	setAcceptHoverEvents(true);
+	setPlainText(QObject::tr("_", "default text when adding a textfield in the element editor"));
+	
+	adjustItemPosition(1);
+	// adjust textfield position after line additions/deletions
+	connect(document(), SIGNAL(blockCountChanged(int)), this, SLOT(adjustItemPosition(int)));
+	connect(document(), SIGNAL(contentsChanged()),      this, SLOT(adjustItemPosition()));
+}
+
+/// Destructeur
+PartTextField::~PartTextField() {
+}
+
+/**
+	Importe les proprietes d'un champ de texte depuis un element XML
+	@param xml_element Element XML a lire
+*/
+void PartTextField::fromXml(const QDomElement &xml_element) {
+	bool ok;
+	int font_size = xml_element.attribute("size").toInt(&ok);
+	if (!ok || font_size < 1) font_size = 20;
+	
+	setProperty("size", font_size);
+	setPlainText(xml_element.attribute("text"));
+	
+	qreal default_rotation_angle = 0.0;
+	if (QET::attributeIsAReal(xml_element, "rotation", &default_rotation_angle)) {
+		setRotationAngle(default_rotation_angle);
+	}
+	
+	setPos(
+		xml_element.attribute("x").toDouble(),
+		xml_element.attribute("y").toDouble()
+	);
+	
+	follow_parent_rotations = (xml_element.attribute("rotate") == "true");
+}
+
+/**
+	Exporte le champ de texte en XML
+	@param xml_document Document XML a utiliser pour creer l'element XML
+	@return un element XML decrivant le champ de texte
+*/
+const QDomElement PartTextField::toXml(QDomDocument &xml_document) const {
+	QDomElement xml_element = xml_document.createElement("input");
+	xml_element.setAttribute("x", QString("%1").arg(pos().x()));
+	xml_element.setAttribute("y", QString("%1").arg(pos().y()));
+	xml_element.setAttribute("text", toPlainText());
+	xml_element.setAttribute("size", font().pointSize());
+	// angle de rotation du champ de texte
+	if (rotationAngle()) {
+		xml_element.setAttribute("rotation", QString("%1").arg(rotationAngle()));
+	}
+	// suivi (ou non) des rotations de l'element parent par le champ de texte
+	if (follow_parent_rotations) {
+		xml_element.setAttribute("rotate", "true");
+	}
+	return(xml_element);
+}
+
+/**
+	@return l'angle de rotation de ce champ de texte
+*/
+qreal PartTextField::rotationAngle() const {
+	return(rotation());
+}
+
+/**
+	@param angle Le nouvel angle de rotation de ce champ de texte
+*/
+void PartTextField::setRotationAngle(const qreal &angle) {
+	setRotation(QET::correctAngle(angle));
+}
+
+/**
+	@return true si le champ de texte suit les rotation de l'element, false
+	sinon
+*/
+bool PartTextField::followParentRotations() {
+	return(follow_parent_rotations);
+}
+
+/**
+	@param  fpr true pour que le champ de texte suive les rotation de
+	l'element, false sinon
+*/
+void PartTextField::setFollowParentRotations(bool fpr) {
+	follow_parent_rotations = fpr;
+}
+
+/**
+	@return le decalage entre l'origine du QGraphicsItem et l'origine du champ de
+	texte.
+*/
+QPointF PartTextField::margin() const {
+	return(QPointF(0.0, boundingRect().bottom() / 2.0));
+}
+
+/**
+	Handle context menu events.
+	@param event Object describing the context menu event to handle.
+*/
+void PartTextField::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
+	Q_UNUSED(event);
+}
+
+/**
+	Handle events generated when the mouse hovers over the decorator.
+	@param event Object describing the hover event.
+*/
+void PartTextField::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+	// force the cursor when the text is being edited
+	if (hasFocus() && decorator_) {
+		decorator_ -> setCursor(Qt::IBeamCursor);
+	}
+	QGraphicsTextItem::hoverMoveEvent(event);
+}
+
+/**
+	@reimp QGraphicsItem::sceneEventFilter(QGraphicsItem *, QEvent *).
+	Intercepts events before they reach the watched target, i.e. typically the
+	primitives decorator.
+	This method mainly works with key strokes (F2, escape) and double clicks to
+	begin or end text edition.
+*/
+bool PartTextField::sceneEventFilter(QGraphicsItem *watched, QEvent *event) {
+	if (watched != decorator_) return(false);
+	
+	QPointF event_scene_pos = QET::graphicsSceneEventPos(event);
+	if (!event_scene_pos.isNull()) {
+		if (contains(mapFromScene(event_scene_pos))) {
+			if (hasFocus()) {
+				return sceneEvent(event); // manually deliver the event to this item
+				return(true); // prevent this event from being delivered to any item
+			} else {
+				if (event -> type() == QEvent::GraphicsSceneMouseDoubleClick) {
+					mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+				}
+			}
+		}
+	}
+	else if (event -> type() == QEvent::KeyRelease || event -> type() == QEvent::KeyPress) {
+		// Intercept F2 and escape keystrokes to focus in and out
+		QKeyEvent *key_event = static_cast<QKeyEvent *>(event);
+		if (!hasFocus() && key_event -> key() == Qt::Key_F2) {
+			setEditable(true);
+			QTextCursor qtc = textCursor();
+			qtc.setPosition(qMax(0, document()->characterCount() - 1));
+			setTextCursor(qtc);
+		} else if (hasFocus() && key_event -> key() == Qt::Key_Escape) {
+			endEdition();
+		}
+		if (hasFocus()) {
+			sceneEvent(event); // manually deliver the event to this item
+			return(true); // prevent this event from being delivered to any item
+		}
+	}
+	return(false);
+}
+
+/*
+	@reimp QGraphicsItem::focusInEvent(QFocusEvent *)
+	@param e The QFocusEvent object describing the focus gain.
+	Start text edition when the item gains focus.
+*/
+void PartTextField::focusInEvent(QFocusEvent *e) {
+	startEdition();
+	QGraphicsTextItem::focusInEvent(e);
+}
+
+/**
+	Permet a l'element texte de redevenir deplacable a la fin de l'edition de texte
+	@param e Le QFocusEvent decrivant la perte de focus
+*/
+void PartTextField::focusOutEvent(QFocusEvent *e) {
+	QGraphicsTextItem::focusOutEvent(e);
+	endEdition();
+}
+
+/**
+	@reimp QGraphicsTextItem::keyPressEvent()
+	Used to handle the escape key when the event is delivered to the field, not
+	to the decorator.
+*/
+void PartTextField::keyPressEvent(QKeyEvent *event) {
+	if (event -> key() == Qt::Key_Escape) {
+		endEdition();
+	}
+	else {
+		QGraphicsTextItem::keyPressEvent(event);
+	}
+}
+
+/**
+	Permet a l'element texte de devenir editable lorsqu'on double-clique dessus
+	@param e Le QGraphicsSceneMouseEvent qui decrit le double-clic
+*/
+void PartTextField::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e) {
+	QGraphicsTextItem::mouseDoubleClickEvent(e);
+	if (e -> button() == Qt::LeftButton) {
+		setEditable(true);
+	}
+}
+
+/**
+	Specifie la valeur d'une propriete donnee du champ de texte
+	@param property propriete a modifier. Valeurs acceptees :
+		* x : abscisse de la position
+		* y : ordonnee de la position
+		* size : taille du texte
+		* text : texte
+		* rotate : suivi de la rotation de l'element parent
+	@param value Valeur a attribuer a la propriete
+*/
+void PartTextField::setProperty(const QString &property, const QVariant &value) {
+	if (property == "x") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setPos(value.toDouble(), pos().y());
+	} else if (property == "y") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setPos(pos().x(), value.toDouble());
+	} else if (property == "size") {
+		if (!value.canConvert(QVariant::Int)) return;
+		setFont(QETApp::diagramTextsFont(value.toInt()));
+		real_font_size_ = value.toInt();
+	} else if (property == "real_size") {
+		if (!value.canConvert(QVariant::Double)) return;
+		setFont(QETApp::diagramTextsFont(value.toInt()));
+		real_font_size_ = value.toDouble();
+	} else if (property == "text") {
+		setPlainText(value.toString());
+	} else if (property == "rotation angle") {
+		setRotationAngle(value.toDouble());
+	} else if (property == "rotate") {
+		follow_parent_rotations = value.toBool();
+	}
+	// adjust item position, especially useful when changing text or size
+	adjustItemPosition();
+	update();
+}
+
+/**
+	Permet d'acceder a la valeur d'une propriete donnee du champ de texte
+	@param property propriete lue. Valeurs acceptees :
+		* x : abscisse de la position
+		* y : ordonnee de la position
+		* size : taille du texte
+		* text : texte
+		* rotate : suivi de la rotation de l'element parent
+	@return La valeur de la propriete property
+*/
+QVariant PartTextField::property(const QString &property) {
+	if (property == "x") {
+		return(pos().x());
+	} else if (property == "y") {
+		return(pos().y());
+	} else if (property == "size") {
+		return(font().pointSize());
+	} else if (property == "real_size") {
+		return(real_font_size_);
+	} else if (property == "text") {
+		return(toPlainText());
+	} else if (property == "rotation angle") {
+		return(rotation());
+	} else if (property == "rotate") {
+		return(follow_parent_rotations);
+	}
+	return(QVariant());
+}
+
+/**
+	Gere les changements intervenant sur cette partie
+	@param change Type de changement
+	@param value Valeur numerique relative au changement
+*/
+QVariant PartTextField::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (change == QGraphicsItem::ItemPositionHasChanged || change == QGraphicsItem::ItemSceneHasChanged) {
+		updateCurrentPartEditor();
+	} else if (change == QGraphicsItem::ItemSelectedHasChanged) {
+		if (value.toBool() == true) {
+			updateCurrentPartEditor();
+		}
+	}
+	return(QGraphicsTextItem::itemChange(change, value));
+}
+
+/**
+	@return le rectangle delimitant cette partie.
+*/
+QRectF PartTextField::boundingRect() const {
+	QRectF r = QGraphicsTextItem::boundingRect();
+	r.adjust(0.0, -1.1, 0.0, 0.0);
+	return(r);
+}
+
+/**
+	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
+	conservee / enregistree.
+	Un champ de texte est toujours pertinent ; cette fonction renvoie donc
+	toujours false
+*/
+bool PartTextField::isUseless() const {
+	return(false);
+}
+
+/**
+	@return the minimum, margin-less rectangle this part can fit into, in scene
+	coordinates. It is different from boundingRect() because it is not supposed
+	to imply any margin, and it is different from shape because it is a regular
+	rectangle, not a complex shape.
+*/
+QRectF PartTextField::sceneGeometricRect() const {
+	return(sceneBoundingRect());
+}
+
+/**
+	Start the user-induced transformation, provided this primitive is contained
+	within the \a initial_selection_rect bounding rectangle.
+*/
+void PartTextField::startUserTransformation(const QRectF &initial_selection_rect) {
+	Q_UNUSED(initial_selection_rect)
+	saved_point_ = pos(); // scene coordinates, no need to mapFromScene()
+	saved_font_size_ = real_font_size_;
+}
+
+/**
+	Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
+*/
+void PartTextField::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) {
+	// let's try the naive approach
+	QPointF new_pos = mapPoints(initial_selection_rect, new_selection_rect, QList<QPointF>() << saved_point_).first();
+	setPos(new_pos);
+	
+	// adjust the font size following the vertical scale factor
+	qreal sy = new_selection_rect.height() / initial_selection_rect.height();
+	qreal new_font_size = saved_font_size_ * sy;
+	setProperty("real_size", qMax(1, qRound(new_font_size)));
+}
+/**
+	Dessine le texte statique.
+	@param painter QPainter a utiliser pour effectuer le rendu
+	@param qsogi   Pptions de dessin
+	@param widget  Widget sur lequel on dessine (facultatif)
+*/
+void PartTextField::paint(QPainter *painter, const QStyleOptionGraphicsItem *qsogi, QWidget *widget) {
+	// According to the source code of QGraphicsTextItem::paint(), this should
+	// avoid the drawing of the dashed rectangle around the text.
+	QStyleOptionGraphicsItem our_qsogi(*qsogi);
+	our_qsogi.state = QStyle::State_None;
+	
+	QGraphicsTextItem::paint(painter, &our_qsogi, widget);
+#ifdef QET_DEBUG_EDITOR_TEXTS
+	painter -> setPen(Qt::blue);
+	painter -> drawRect(boundingRect());
+	
+	painter -> setPen(Qt::red);
+	drawPoint(painter, QPointF(0, 0));
+	
+	painter -> setPen(QColor("#800000"));
+	drawPoint(painter, mapFromScene(pos()));
+#endif
+}
+
+/**
+	@reimp CustomElementPart::setDecorator(ElementPrimitiveDecorator *)
+	Install or remove a sceneEventFilter on the decorator and ensure it will
+	adjust itself while the text is being edited.
+*/
+void PartTextField::setDecorator(ElementPrimitiveDecorator *decorator) {
+	if (decorator) {
+		decorator -> installSceneEventFilter(this);
+		// ensure the decorator will adjust itself when the text area expands or shrinks
+		connect(document(), SIGNAL(contentsChanged()), decorator, SLOT(adjust()));
+	}
+	else {
+		decorator_ -> removeSceneEventFilter(this);
+		endEdition();
+	}
+	decorator_ = decorator;
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartTextField::singleItemPressEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	Q_UNUSED(event)
+	return(hasFocus());
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartTextField::singleItemMoveEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	Q_UNUSED(event)
+	return(hasFocus());
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartTextField::singleItemReleaseEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	Q_UNUSED(event)
+	return(hasFocus());
+}
+
+/**
+	Accept the mouse \a event relayed by \a decorator if this text item has focus.
+*/
+bool PartTextField::singleItemDoubleClickEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
+	Q_UNUSED(decorator)
+	// calling mouseDoubleClickEvent() will set this text item editable and grab keyboard focus
+	if (event -> button() == Qt::LeftButton) {
+		mouseDoubleClickEvent(event);
+		return(true);
+	}
+	return(false);
+}
+
+/**
+	Cette methode s'assure que la position du champ de texte est coherente
+	en repositionnant son origine (c-a-d le milieu du bord gauche du champ de
+	texte) a la position originale. Cela est notamment utile lorsque le champ
+	de texte est agrandi ou retreci verticalement (ajout ou retrait de lignes).
+	@param new_block_count Nombre de blocs dans le PartTextField
+*/
+void PartTextField::adjustItemPosition(int new_block_count) {
+	Q_UNUSED(new_block_count);
+	qreal origin_offset = boundingRect().bottom() / 2.0;
+	
+	QTransform base_translation;
+	base_translation.translate(0.0, -origin_offset);
+	setTransform(base_translation, false);
+	setTransformOriginPoint(0.0, origin_offset);
+}
+
+/**
+	@param editable Whether this text item should be interactively editable.
+*/
+void PartTextField::setEditable(bool editable) {
+	if (editable) {
+		setFlag(QGraphicsItem::ItemIsFocusable, true);
+		setTextInteractionFlags(Qt::TextEditorInteraction);
+		setFocus(Qt::MouseFocusReason);
+	}
+	else {
+		setTextInteractionFlags(Qt::NoTextInteraction);
+		setFlag(QGraphicsItem::ItemIsFocusable, false);
+	}
+}
+
+/**
+	Start text edition by storing the former value of the text.
+*/
+void PartTextField::startEdition() {
+	// !previous_text.isNull() means the text is being edited
+	previous_text = toPlainText();
+}
+
+/**
+	End text edition, potentially generating a ChangePartCommand if the text
+	has changed.
+*/
+void PartTextField::endEdition() {
+	if (!previous_text.isNull()) {
+		// the text was being edited
+		QString new_text = toPlainText();
+		if (previous_text != new_text) {
+			// the text was changed
+			ChangePartCommand *text_change = new ChangePartCommand(
+				TextFieldEditor::tr("contenu") + " " + name(),
+				this,
+				"text",
+				previous_text,
+				new_text
+			);
+			previous_text = QString();
+			undoStack().push(text_change);
+		}
+	}
+	
+	// deselectionne le texte
+	QTextCursor qtc = textCursor();
+	qtc.clearSelection();
+	setTextCursor(qtc);
+	
+	setEditable(false);
+	if (decorator_) {
+		decorator_ -> setFocus();
+	}
+}
+
+#ifdef QET_DEBUG_EDITOR_TEXTS
+/**
+	Dessine deux petites fleches pour mettre un point en valeur
+	@param painter QPainter a utiliser pour effectuer le rendu
+	@param point   Point a dessiner
+*/
+void PartTextField::drawPoint(QPainter *painter, const QPointF &point) {
+	qreal px = point.x();
+	qreal py = point.y();
+	qreal size_1 = 5.0;
+	qreal size_2 = 1.0;
+	painter -> drawLine(QLineF(px, py, px + size_1, py));
+	painter -> drawLine(QLineF(px + size_1 - size_2, py - size_2, px + size_1, py));
+	painter -> drawLine(QLineF(px + size_1 - size_2, py + size_2, px + size_1, py));
+	painter -> drawLine(QLineF(px, py, px, py + size_1));
+	painter -> drawLine(QLineF(px, py + size_1, px - size_2, py + size_1 - size_2));
+	painter -> drawLine(QLineF(px, py + size_1, px + size_2, py + size_1 - size_2));
+}
+#endif

Added: trunk/sources/editor/parttextfield.h
===================================================================
--- trunk/sources/editor/parttextfield.h	                        (rev 0)
+++ trunk/sources/editor/parttextfield.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,104 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PART_TEXTFIELD_H
+#define PART_TEXTFIELD_H
+#include <QtGui>
+#include "customelementpart.h"
+class TextFieldEditor;
+class QETElementEditor;
+class ElementPrimitiveDecorator;
+/**
+	This class represents an editable text field which may be used to compose the
+	drawing of an electrical element within the element editor. Users may specify
+	a default value. The field will remain editable once the element is added onto
+	a diagram. lorsque l'element sera pose sur un schema.
+*/
+class PartTextField : public QGraphicsTextItem, public CustomElementPart {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	PartTextField(QETElementEditor *, QGraphicsItem * = 0, QGraphicsScene * = 0);
+	virtual ~PartTextField();
+	
+	private:
+	PartTextField(const PartTextField &);
+	
+	// attributes
+	bool follow_parent_rotations;
+	
+	// methods
+	public:
+	enum { Type = UserType + 1108 };
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		PartTextField.
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	virtual QString name() const { return(QObject::tr("champ de texte", "element part name")); }
+	virtual QString xmlName() const { return(QString("input")); }
+	void fromXml(const QDomElement &);
+	const QDomElement toXml(QDomDocument &) const;
+	qreal rotationAngle() const;
+	void setRotationAngle(const qreal &);
+	bool followParentRotations();
+	void setFollowParentRotations(bool);
+	virtual void setProperty(const QString &, const QVariant &);
+	virtual QVariant property(const QString &);
+	virtual bool isUseless() const;
+	virtual QRectF sceneGeometricRect() const;
+	virtual void startUserTransformation(const QRectF &);
+	virtual void handleUserTransformation(const QRectF &, const QRectF &);
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0 );
+	
+	virtual void setDecorator(ElementPrimitiveDecorator *);
+	virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	virtual bool singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
+	
+	public slots:
+	void adjustItemPosition(int = 0);
+	void setEditable(bool);
+	void startEdition();
+	void endEdition();
+	
+	protected:
+	virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *);
+	virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *);
+	virtual bool sceneEventFilter(QGraphicsItem *, QEvent *);
+	virtual void focusInEvent(QFocusEvent *);
+	virtual void focusOutEvent(QFocusEvent *);
+	virtual void keyPressEvent(QKeyEvent *);
+	virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
+	virtual QVariant itemChange(GraphicsItemChange, const QVariant &);
+	QRectF boundingRect() const;
+	
+	private:
+	QPointF margin() const;
+#ifdef QET_DEBUG_EDITOR_TEXTS
+	void drawPoint(QPainter *, const QPointF &);
+#endif
+	QString previous_text;
+	qreal real_font_size_;
+	QPointF saved_point_;
+	qreal saved_font_size_;
+	QGraphicsItem *decorator_;
+};
+#endif

Added: trunk/sources/editor/polygoneditor.cpp
===================================================================
--- trunk/sources/editor/polygoneditor.cpp	                        (rev 0)
+++ trunk/sources/editor/polygoneditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,194 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "polygoneditor.h"
+#include "partpolygon.h"
+#include "elementscene.h"
+#include "editorcommands.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param p Le polygone a editer
+	@param parent le Widget parent
+*/
+PolygonEditor::PolygonEditor(QETElementEditor *editor, PartPolygon *p, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(p),
+	points_list(this),
+	close_polygon(tr("Polygone ferm\351"), this)
+{
+	style_ = new StyleEditor(editor);
+	
+	// prepare la liste de points
+	points_list.setColumnCount(2);
+	QStringList headers;
+	headers << tr("x") << tr("y");
+	points_list.setHeaderLabels(headers);
+	points_list.setRootIsDecorated(false);
+	updateForm();
+	
+	// layout
+	QVBoxLayout *layout = new QVBoxLayout(this);
+	layout -> addWidget(style_);
+	layout -> addWidget(new QLabel(tr("Points du polygone :")));
+	layout -> addWidget(&points_list);
+	layout -> addWidget(&close_polygon);
+	
+	updateForm();
+}
+
+/// Destructeur
+PolygonEditor::~PolygonEditor() {
+}
+
+/**
+	Met a jour le polygone a partir des donnees du formulaire : points et etat ferme ou non
+*/
+void PolygonEditor::updatePolygon() {
+	updatePolygonPoints();
+	updatePolygonClosedState();
+}
+
+/**
+	Met a jour les points du polygone et cree un objet d'annulation
+*/
+void PolygonEditor::updatePolygonPoints() {
+	if (!part) return;
+	QVector<QPointF> points = getPointsFromTree();
+	if (points.count() < 2) {
+		QET::MessageBox::warning(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Le polygone doit comporter au moins deux points.", "message box content")
+		);
+		return;
+	}
+	undoStack().push(new ChangePolygonPointsCommand(part, part -> polygon(), points));
+}
+
+/**
+	Met a jour l'etat ferme ou non du polygone
+*/
+void PolygonEditor::updatePolygonClosedState() {
+	if (!part) return;
+	undoStack().push(
+		new ChangePartCommand(
+			tr("fermeture du polygone"),
+			part,
+			"closed",
+			QVariant(!close_polygon.isChecked()),
+			QVariant(close_polygon.isChecked())
+		)
+	);
+}
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void PolygonEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	while(points_list.takeTopLevelItem(0)) {}
+	foreach(QPointF point, part -> polygon()) {
+		point = part -> mapToScene(point);
+		QStringList qsl;
+		qsl << QString("%1").arg(point.x()) << QString("%1").arg(point.y());
+		QTreeWidgetItem *qtwi = new QTreeWidgetItem(qsl);
+		qtwi -> setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
+		points_list.addTopLevelItem(qtwi);
+	}
+	close_polygon.setChecked(part -> isClosed());
+	activeConnections(true);
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de polygone acceptera d'editer la primitive new_part s'il s'agit
+	d'un objet de la classe PartPolygon.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool PolygonEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		style_ -> setPart(0);
+		return(true);
+	}
+	if (PartPolygon *part_polygon = dynamic_cast<PartPolygon *>(new_part)) {
+		part = part_polygon;
+		style_ -> setPart(part);
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *PolygonEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	@return Un vecteur contenant les points composant le polygone a partir du
+	formulaire d'edition
+*/
+QVector<QPointF> PolygonEditor::getPointsFromTree() {
+	if (!part) return(QVector<QPointF>());
+	QVector<QPointF> points;
+	for(int i = 0 ; i < points_list.topLevelItemCount() ; ++ i) {
+		QTreeWidgetItem *qtwi = points_list.topLevelItem(i);
+		bool x_convert_ok, y_convert_ok;
+		qreal x = qtwi -> text(0).toDouble(&x_convert_ok);
+		qreal y = qtwi -> text(1).toDouble(&y_convert_ok);
+		if (!x_convert_ok || !y_convert_ok) continue;
+		points << part -> mapFromScene(QPointF(x, y));
+	}
+	return(points);
+}
+
+/**
+	@param qtwi QTreeWidgetItem a valider
+	@param column Colonne exacte du QTreeWidgetItem a valider
+*/
+void PolygonEditor::validColumn(QTreeWidgetItem *qtwi, int column) {
+	bool convert_ok;
+	qtwi -> text(column).toDouble(&convert_ok);
+	if (convert_ok) {
+		points_list.closePersistentEditor(qtwi, column);
+		updatePolygonPoints();
+	} else points_list.openPersistentEditor(qtwi, column);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void PolygonEditor::activeConnections(bool active) {
+	if (active) {
+		connect(&close_polygon, SIGNAL(stateChanged(int)),                   this, SLOT(updatePolygonClosedState()));
+		connect(&points_list,   SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(validColumn(QTreeWidgetItem *, int)));
+	} else {
+		disconnect(&close_polygon, SIGNAL(stateChanged(int)),                   this, SLOT(updatePolygonClosedState()));
+		disconnect(&points_list,   SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(validColumn(QTreeWidgetItem *, int)));
+	}
+}

Added: trunk/sources/editor/polygoneditor.h
===================================================================
--- trunk/sources/editor/polygoneditor.h	                        (rev 0)
+++ trunk/sources/editor/polygoneditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,63 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef POLYGON_EDITOR_H
+#define POLYGON_EDITOR_H
+#include "elementitemeditor.h"
+class PartPolygon;
+class StyleEditor;
+/**
+	This class provides a widget to edit polygons within the element editor.
+*/
+class PolygonEditor : public ElementItemEditor {
+	
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	PolygonEditor(QETElementEditor *, PartPolygon * = 0, QWidget * = 0);
+	virtual ~PolygonEditor();
+	
+	private:
+	PolygonEditor(const PolygonEditor &);
+	
+	// attributes
+	private:
+	PartPolygon *part;
+	StyleEditor *style_;
+	QTreeWidget points_list;
+	QCheckBox close_polygon;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	private:
+	QVector<QPointF> getPointsFromTree();
+	
+	public slots:
+	void updatePolygon();
+	void updatePolygonPoints();
+	void updatePolygonClosedState();
+	void updateForm();
+	void validColumn(QTreeWidgetItem *qtwi, int column);
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/qetelementeditor.cpp
===================================================================
--- trunk/sources/editor/qetelementeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/qetelementeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1392 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetelementeditor.h"
+#include "qet.h"
+#include "qetapp.h"
+#include "elementscene.h"
+#include "elementview.h"
+#include "customelementpart.h"
+#include "newelementwizard.h"
+#include "elementitemeditor.h"
+#include "elementdefinition.h"
+#include "elementdialog.h"
+#include "recentfiles.h"
+#include "qeticons.h"
+#include "qetmessagebox.h"
+
+// editeurs de primitives
+#include "arceditor.h"
+#include "ellipseeditor.h"
+#include "lineeditor.h"
+#include "polygoneditor.h"
+#include "rectangleeditor.h"
+#include "terminaleditor.h"
+#include "texteditor.h"
+#include "textfieldeditor.h"
+
+#include <QMessageBox>
+/*
+	Nombre maximum de primitives affichees par la "liste des parties"
+	Au-dela, un petit message est affiche, indiquant que ce nombre a ete depasse
+	et que la liste ne sera donc pas mise a jour.
+*/
+#define QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST 200
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+QETElementEditor::QETElementEditor(QWidget *parent) :
+	QETMainWindow(parent),
+	read_only(false),
+	min_title(tr("QElectroTech - \311diteur d'\351l\351ment", "window title")),
+	opened_from_file(false)
+{
+	setWindowTitle(min_title);
+	setWindowIcon(QET::Icons::QETLogo);
+	
+	setupInterface();
+	setupActions();
+	setupMenus();
+	
+	// la fenetre est maximisee par defaut
+	setMinimumSize(QSize(500, 350));
+	setWindowState(Qt::WindowMaximized);
+	
+	// lecture des parametres
+	readSettings();
+	slot_updateMenus();
+	
+	// affichage
+	show();
+}
+
+/// Destructeur
+QETElementEditor::~QETElementEditor() {
+	/*
+		retire le widget d'edition de primitives affiche par le dock
+		cela evite qu'il ne soit supprime par son widget parent
+	*/
+	clearToolsDock();
+	
+	// supprime les editeurs de primitives
+	qDeleteAll(editors_.begin(), editors_.end());
+	editors_.clear();
+}
+
+/**
+	@param el Le nouvel emplacement de l'element edite
+*/
+void QETElementEditor::setLocation(const ElementsLocation &el) {
+	location_ = el;
+	opened_from_file = false;
+	// modifie le mode lecture seule si besoin
+	ElementsCollectionItem *item = QETApp::collectionItem(location_);
+	bool must_be_read_only = item && !item -> isWritable();
+	if (isReadOnly() != must_be_read_only) {
+		setReadOnly(must_be_read_only);
+	}
+	slot_updateTitle();
+}
+
+/**
+	@param fn Le nouveau nom de fichier de l'element edite
+*/
+void QETElementEditor::setFileName(const QString &fn) {
+	filename_ = fn;
+	opened_from_file = true;
+	// modifie le mode lecture seule si besoin
+	bool must_be_read_only = !QFileInfo(filename_).isWritable();
+	if (isReadOnly() != must_be_read_only) {
+		setReadOnly(must_be_read_only);
+	}
+	slot_updateTitle();
+}
+
+/**
+	Met en place les actions
+*/
+void QETElementEditor::setupActions() {
+	new_element     = new QAction(QET::Icons::DocumentNew,          tr("&Nouveau"),                                  this);
+	open            = new QAction(QET::Icons::DocumentOpen,         tr("&Ouvrir"),                                   this);
+	open_file       = new QAction(QET::Icons::DocumentOpen,         tr("&Ouvrir depuis un fichier"),                 this);
+	save            = new QAction(QET::Icons::DocumentSave,         tr("&Enregistrer"),                              this);
+	save_as         = new QAction(QET::Icons::DocumentSaveAs,       tr("Enregistrer sous"),                          this);
+	save_as_file    = new QAction(QET::Icons::DocumentSaveAs,       tr("Enregistrer dans un fichier"),               this);
+	reload          = new QAction(QET::Icons::ViewRefresh,          tr("Recharger"),                                 this);
+	quit            = new QAction(QET::Icons::ApplicationExit,      tr("&Quitter"),                                  this);
+	selectall       = new QAction(QET::Icons::EditSelectAll,        tr("Tout s\351lectionner"),                      this);
+	deselectall     = new QAction(                                  tr("D\351s\351lectionner tout"),                 this);
+	cut             = new QAction(QET::Icons::EditCut,              tr("Co&uper"),                                   this);
+	copy            = new QAction(QET::Icons::EditCopy,             tr("Cop&ier"),                                   this);
+	paste           = new QAction(QET::Icons::EditPaste,            tr("C&oller"),                                   this);
+	paste_in_area   = new QAction(QET::Icons::EditPaste,            tr("C&oller dans la zone..."),                   this);
+	paste_from_file = new QAction(QET::Icons::XmlTextFile,          tr("un fichier"),                                this);
+	paste_from_elmt = new QAction(QET::Icons::Element,              tr("un \351l\351ment"),                          this);
+	inv_select      = new QAction(                                  tr("Inverser la s\351lection"),                  this);
+	edit_delete     = new QAction(QET::Icons::EditDelete,           tr("&Supprimer"),                                this);
+	zoom_in         = new QAction(QET::Icons::ZoomIn,               tr("Zoom avant"),                                this);
+	zoom_out        = new QAction(QET::Icons::ZoomOut,              tr("Zoom arri\350re"),                           this);
+	zoom_fit        = new QAction(QET::Icons::ZoomFitBest,          tr("Zoom adapt\351"),                            this);
+	zoom_reset      = new QAction(QET::Icons::ZoomOriginal,         tr("Pas de zoom"),                               this);
+	edit_names      = new QAction(QET::Icons::Names,                tr("\311diter les noms"),                        this);
+	edit_author     = new QAction(QET::Icons::UserInformations,     tr("\311diter les informations sur l'auteur"),   this);
+	edit_raise      = new QAction(QET::Icons::Raise,                tr("Rapprocher"),                                this);
+	edit_lower      = new QAction(QET::Icons::Lower,                tr("\311loigner"),                               this);
+	edit_backward   = new QAction(QET::Icons::SendBackward,         tr("Envoyer au fond"),                           this);
+	edit_forward    = new QAction(QET::Icons::BringForward,         tr("Amener au premier plan"),                    this);
+	move            = new QAction(QET::Icons::PartSelect,           tr("D\351placer un objet"),                      this);
+	add_line        = new QAction(QET::Icons::PartLine,             tr("Ajouter une ligne"),                         this);
+	add_rectangle   = new QAction(QET::Icons::PartRectangle,        tr("Ajouter un rectangle"),                      this);
+	add_ellipse     = new QAction(QET::Icons::PartEllipse,          tr("Ajouter une ellipse"),                       this);
+	add_polygon     = new QAction(QET::Icons::PartPolygon,          tr("Ajouter un polygone"),                       this);
+	add_text        = new QAction(QET::Icons::PartText,             tr("Ajouter du texte"),                          this);
+	add_arc         = new QAction(QET::Icons::PartArc,              tr("Ajouter un arc de cercle"),                  this);
+	add_terminal    = new QAction(QET::Icons::Terminal,             tr("Ajouter une borne"),                         this);
+	add_textfield   = new QAction(QET::Icons::PartTextField,        tr("Ajouter un champ de texte"),                 this);
+	
+	QString add_status_tip = tr("Maintenez la touche Shift enfonc\351e pour effectuer plusieurs ajouts d'affil\351e");
+	add_line      -> setStatusTip(add_status_tip);
+	add_rectangle -> setStatusTip(add_status_tip);
+	add_ellipse   -> setStatusTip(add_status_tip);
+	add_text      -> setStatusTip(add_status_tip);
+	add_arc       -> setStatusTip(add_status_tip);
+	add_terminal  -> setStatusTip(add_status_tip);
+	add_textfield -> setStatusTip(add_status_tip);
+	add_polygon   -> setStatusTip(tr("Utilisez le bouton droit de la souris pour poser le dernier point du polygone"));
+	
+	undo = ce_scene -> undoStack().createUndoAction(this, tr("Annuler"));
+	redo = ce_scene -> undoStack().createRedoAction(this, tr("Refaire"));
+	undo -> setIcon(QET::Icons::EditUndo);
+	redo -> setIcon(QET::Icons::EditRedo);
+	undo -> setShortcuts(QKeySequence::Undo);
+	redo -> setShortcuts(QKeySequence::Redo);
+	
+	new_element       -> setShortcut(QKeySequence::New);
+	open              -> setShortcut(QKeySequence::Open);
+	open_file         -> setShortcut(tr("Ctrl+Shift+O"));
+	save              -> setShortcut(QKeySequence::Save);
+	save_as_file      -> setShortcut(tr("Ctrl+Shift+S"));
+	reload            -> setShortcut(Qt::Key_F5);
+	quit              -> setShortcut(QKeySequence(tr("Ctrl+Q")));
+	selectall         -> setShortcut(QKeySequence::SelectAll);
+	deselectall       -> setShortcut(QKeySequence(tr("Ctrl+Shift+A")));
+	inv_select        -> setShortcut(QKeySequence(tr("Ctrl+I")));
+	cut               -> setShortcut(QKeySequence::Cut);
+	copy              -> setShortcut(QKeySequence::Copy);
+	paste             -> setShortcut(QKeySequence::Paste);
+	paste_in_area     -> setShortcut(tr("Ctrl+Shift+V"));
+#ifndef Q_WS_MAC
+	edit_delete       -> setShortcut(QKeySequence(Qt::Key_Delete));
+#else
+	edit_delete       -> setShortcut(QKeySequence(tr("Backspace")));
+#endif
+	
+	zoom_in           -> setShortcut(QKeySequence::ZoomIn);
+	zoom_out          -> setShortcut(QKeySequence::ZoomOut);
+	zoom_fit          -> setShortcut(QKeySequence(tr("Ctrl+9")));
+	zoom_reset        -> setShortcut(QKeySequence(tr("Ctrl+0")));
+	
+	edit_names        -> setShortcut(QKeySequence(tr("Ctrl+E")));
+	edit_author       -> setShortcut(tr("Ctrl+Y"));
+	
+	edit_raise        -> setShortcut(QKeySequence(tr("Ctrl+Shift+Up")));
+	edit_lower        -> setShortcut(QKeySequence(tr("Ctrl+Shift+Down")));
+	edit_backward     -> setShortcut(QKeySequence(tr("Ctrl+Shift+End")));
+	edit_forward      -> setShortcut(QKeySequence(tr("Ctrl+Shift+Home")));
+	
+	connect(new_element,     SIGNAL(triggered()), this,     SLOT(slot_new()));
+	connect(open,            SIGNAL(triggered()), this,     SLOT(slot_open()));
+	connect(open_file,       SIGNAL(triggered()), this,     SLOT(slot_openFile()));
+	connect(save,            SIGNAL(triggered()), this,     SLOT(slot_save()));
+	connect(save_as,         SIGNAL(triggered()), this,     SLOT(slot_saveAs()));
+	connect(save_as_file,    SIGNAL(triggered()), this,     SLOT(slot_saveAsFile()));
+	connect(reload,          SIGNAL(triggered()), this,     SLOT(slot_reload()));
+	connect(quit,            SIGNAL(triggered()), this,     SLOT(close()));
+	connect(selectall,       SIGNAL(triggered()), ce_scene, SLOT(slot_selectAll()));
+	connect(deselectall,     SIGNAL(triggered()), ce_scene, SLOT(slot_deselectAll()));
+	connect(inv_select,      SIGNAL(triggered()), ce_scene, SLOT(slot_invertSelection()));
+	connect(cut,             SIGNAL(triggered()), ce_view,  SLOT(cut()));
+	connect(copy,            SIGNAL(triggered()), ce_view,  SLOT(copy()));
+	connect(paste,           SIGNAL(triggered()), ce_view,  SLOT(paste()));
+	connect(paste_in_area,   SIGNAL(triggered()), ce_view,  SLOT(pasteInArea()));
+	connect(paste_from_file, SIGNAL(triggered()), this,     SLOT(pasteFromFile()));
+	connect(paste_from_elmt, SIGNAL(triggered()), this,     SLOT(pasteFromElement()));
+	connect(zoom_in,         SIGNAL(triggered()), ce_view,  SLOT(zoomIn()));
+	connect(zoom_out,        SIGNAL(triggered()), ce_view,  SLOT(zoomOut()));
+	connect(zoom_fit,        SIGNAL(triggered()), ce_view,  SLOT(zoomFit()));
+	connect(zoom_reset,      SIGNAL(triggered()), ce_view,  SLOT(zoomReset()));
+	connect(edit_delete,     SIGNAL(triggered()), ce_scene, SLOT(slot_delete()));
+	connect(edit_names,      SIGNAL(triggered()), ce_scene, SLOT(slot_editNames()));
+	connect(edit_author,     SIGNAL(triggered()), ce_scene, SLOT(slot_editAuthorInformations()));
+	connect(edit_forward,    SIGNAL(triggered()), ce_scene, SLOT(slot_bringForward()));
+	connect(edit_raise,      SIGNAL(triggered()), ce_scene, SLOT(slot_raise()));
+	connect(edit_lower,      SIGNAL(triggered()), ce_scene, SLOT(slot_lower()));
+	connect(edit_backward,   SIGNAL(triggered()), ce_scene, SLOT(slot_sendBackward()));
+	connect(move,            SIGNAL(triggered()), ce_scene, SLOT(slot_move()));
+	connect(add_line,        SIGNAL(triggered()), ce_scene, SLOT(slot_addLine()));
+	connect(add_rectangle,   SIGNAL(triggered()), ce_scene, SLOT(slot_addRectangle()));
+	connect(add_ellipse,     SIGNAL(triggered()), ce_scene, SLOT(slot_addEllipse()));
+	connect(add_polygon,     SIGNAL(triggered()), ce_scene, SLOT(slot_addPolygon()));
+	connect(add_text,        SIGNAL(triggered()), ce_scene, SLOT(slot_addText()));
+	connect(add_arc,         SIGNAL(triggered()), ce_scene, SLOT(slot_addArc()));
+	connect(add_terminal,    SIGNAL(triggered()), ce_scene, SLOT(slot_addTerminal()));
+	connect(add_textfield,   SIGNAL(triggered()), ce_scene, SLOT(slot_addTextField()));
+	connect(move,            SIGNAL(triggered()), this,     SLOT(slot_setRubberBandToView()));
+	connect(add_line,        SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_rectangle,   SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_ellipse,     SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_polygon,     SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_text,        SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_arc,         SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_terminal,    SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	connect(add_textfield,   SIGNAL(triggered()), this,     SLOT(slot_setNoDragToView()));
+	
+	connect(ce_scene,        SIGNAL(needNormalMode()), this, SLOT(slot_setNormalMode()));
+	
+	move          -> setCheckable(true);
+	add_line      -> setCheckable(true);
+	add_rectangle -> setCheckable(true);
+	add_ellipse   -> setCheckable(true);
+	add_polygon   -> setCheckable(true);
+	add_text      -> setCheckable(true);
+	add_arc       -> setCheckable(true);
+	add_terminal  -> setCheckable(true);
+	add_textfield -> setCheckable(true);
+	
+	parts = new QActionGroup(this);
+	parts -> addAction(move);
+	parts -> addAction(add_line);
+	parts -> addAction(add_rectangle);
+	parts -> addAction(add_ellipse);
+	parts -> addAction(add_polygon);
+	parts -> addAction(add_arc);
+	parts -> addAction(add_text);
+	parts -> addAction(add_textfield);
+	parts -> addAction(add_terminal);
+	parts -> setExclusive(true);
+	
+	parts_toolbar = new QToolBar(tr("Parties", "toolbar title"), this);
+	parts_toolbar -> setObjectName("parts");
+	foreach (QAction *action, parts -> actions()) parts_toolbar -> addAction(action);
+	move -> setChecked(true);
+	parts_toolbar -> setAllowedAreas(Qt::AllToolBarAreas);
+	
+	/*
+	QAction *xml_preview = new QAction(QET::Icons::DialogInformation, tr("XML"), this);
+	connect(xml_preview, SIGNAL(triggered()), this, SLOT(xmlPreview()));
+	parts_toolbar -> addAction(xml_preview);
+	*/
+	
+	main_toolbar = new QToolBar(tr("Outils", "toolbar title"), this);
+	main_toolbar -> setObjectName("main_toolbar");
+	view_toolbar = new QToolBar(tr("Affichage", "toolbar title"), this);
+	view_toolbar -> setObjectName("display");
+	element_toolbar = new QToolBar(tr("\311l\351ment", "toolbar title"), this);
+	element_toolbar -> setObjectName("element_toolbar");
+	depth_toolbar = new QToolBar(tr("Profondeur", "toolbar title"), this);
+	depth_toolbar -> setObjectName("depth_toolbar");
+	
+	main_toolbar -> addAction(new_element);
+	main_toolbar -> addAction(open);
+	main_toolbar -> addAction(save);
+	main_toolbar -> addAction(save_as);
+	main_toolbar -> addAction(reload);
+	main_toolbar -> addSeparator();
+	main_toolbar -> addAction(undo);
+	main_toolbar -> addAction(redo);
+	main_toolbar -> addSeparator();
+	main_toolbar -> addAction(edit_delete);
+	view_toolbar -> addAction(zoom_fit);
+	view_toolbar -> addAction(zoom_reset);
+	element_toolbar -> addAction(edit_names);
+	depth_toolbar -> addAction(edit_forward);
+	depth_toolbar -> addAction(edit_raise);
+	depth_toolbar -> addAction(edit_lower);
+	depth_toolbar -> addAction(edit_backward);
+	
+	addToolBar(Qt::TopToolBarArea, main_toolbar);
+	addToolBar(Qt::TopToolBarArea, view_toolbar);
+	addToolBar(Qt::TopToolBarArea, element_toolbar);
+	addToolBar(Qt::TopToolBarArea, depth_toolbar);
+	addToolBar(Qt::LeftToolBarArea, parts_toolbar);
+	
+	connect(ce_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updateInformations()));
+	connect(ce_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updateMenus()));
+	connect(QApplication::clipboard(),  SIGNAL(dataChanged()),      this, SLOT(slot_updateMenus()));
+	connect(&(ce_scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(slot_updateMenus()));
+	connect(&(ce_scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(slot_updateTitle()));
+	
+	// Annuler ou refaire une action met a jour la liste des primitives ; cela sert notamment pour les
+	// ajouts et suppressions de primitives ainsi que pour les actions entrainant un change
+	connect(&(ce_scene -> undoStack()), SIGNAL(indexChanged(int)),  this, SLOT(slot_updatePartsList()));
+	
+	// Annuler ou refaire une action met a jour les informations affichees sur les primitives selectionnees,
+	// celles-ci etant potentiellement impactees
+	connect(&(ce_scene -> undoStack()), SIGNAL(indexChanged(int)),  this, SLOT(slot_updateInformations()));
+}
+
+/**
+	Met en place les menus.
+*/
+void QETElementEditor::setupMenus() {
+	file_menu    = new QMenu(tr("&Fichier"),       this);
+	edit_menu    = new QMenu(tr("&\311dition"),    this);
+	display_menu = new QMenu(tr("Afficha&ge"),     this);
+	tools_menu   = new QMenu(tr("O&utils"),        this);
+	
+	file_menu    -> setTearOffEnabled(true);
+	edit_menu    -> setTearOffEnabled(true);
+	display_menu -> setTearOffEnabled(true);
+	tools_menu   -> setTearOffEnabled(true);
+	
+	file_menu    -> addAction(new_element);
+	file_menu    -> addAction(open);
+	file_menu    -> addAction(open_file);
+	file_menu    -> addMenu(QETApp::elementsRecentFiles() -> menu());
+	connect(QETApp::elementsRecentFiles(), SIGNAL(fileOpeningRequested(const QString &)), this, SLOT(openRecentFile(const QString &)));
+	file_menu    -> addAction(save);
+	file_menu    -> addAction(save_as);
+	file_menu    -> addAction(save_as_file);
+	file_menu    -> addSeparator();
+	file_menu    -> addAction(reload);
+	file_menu    -> addSeparator();
+	file_menu    -> addAction(quit);
+	
+	paste_from_menu = new QMenu(tr("Coller depuis..."));
+	paste_from_menu -> setIcon(QET::Icons::EditPaste);
+	paste_from_menu -> addAction(paste_from_file);
+	paste_from_menu -> addAction(paste_from_elmt);
+
+	edit_menu -> addAction(undo);
+	edit_menu -> addAction(redo);
+	edit_menu -> addSeparator();
+	edit_menu -> addAction(selectall);
+	edit_menu -> addAction(deselectall);
+	edit_menu -> addAction(inv_select);
+	edit_menu -> addSeparator();
+	edit_menu -> addAction(cut);
+	edit_menu -> addAction(copy);
+	edit_menu -> addAction(paste);
+	edit_menu -> addAction(paste_in_area);
+	edit_menu -> addMenu(paste_from_menu);
+	edit_menu -> addSeparator();
+	edit_menu -> addAction(edit_delete);
+	edit_menu -> addSeparator();
+	edit_menu -> addAction(edit_names);
+	edit_menu -> addAction(edit_author);
+	edit_menu -> addSeparator();
+	edit_menu -> addAction(edit_forward);
+	edit_menu -> addAction(edit_raise);
+	edit_menu -> addAction(edit_lower);
+	edit_menu -> addAction(edit_backward);
+
+	display_menu -> addAction(zoom_in);
+	display_menu -> addAction(zoom_out);
+	display_menu -> addAction(zoom_fit);
+	display_menu -> addAction(zoom_reset);
+	
+	insertMenu(settings_menu_, file_menu);
+	insertMenu(settings_menu_, edit_menu);
+	insertMenu(settings_menu_, display_menu);
+}
+
+/**
+ * @brief QETElementEditor::contextMenuEvent
+ * @param event
+ */
+void QETElementEditor::contextMenu(QContextMenuEvent *event) {
+		QMenu menu(this);
+		menu.addAction(undo);
+		menu.addAction(redo);
+		menu.addAction(selectall);
+		menu.addAction(deselectall);
+		menu.addAction(inv_select);
+		menu.addSeparator();
+		menu.addAction(edit_delete);
+		menu.addAction(cut);
+		menu.addAction(copy);
+		menu.addSeparator();
+		menu.addAction(paste);
+		menu.addAction(paste_in_area);
+		menu.addMenu(paste_from_menu);
+		menu.addSeparator();
+		menu.addAction(edit_forward);
+		menu.addAction(edit_raise);
+		menu.addAction(edit_lower);
+		menu.addAction(edit_backward);
+		menu.exec(event -> globalPos());
+ }
+
+
+/**
+	Met a jour les menus
+*/
+void QETElementEditor::slot_updateMenus() {
+	bool selected_items = !read_only && !ce_scene -> selectedItems().isEmpty();
+	bool clipboard_elmt = !read_only && ElementScene::clipboardMayContainElement();
+	
+	// actions dependant seulement de l'etat "lecture seule" de l'editeur
+	foreach (QAction *action, parts -> actions()) {
+		action -> setEnabled(!read_only);
+	}
+	selectall       -> setEnabled(!read_only);
+	inv_select      -> setEnabled(!read_only);
+	paste_from_file -> setEnabled(!read_only);
+	paste_from_elmt -> setEnabled(!read_only);
+	parts_list      -> setEnabled(!read_only);
+	
+	// actions dependant de la presence de parties selectionnees
+	deselectall     -> setEnabled(selected_items);
+	cut             -> setEnabled(selected_items);
+	copy            -> setEnabled(selected_items);
+	edit_delete     -> setEnabled(selected_items);
+	edit_forward    -> setEnabled(selected_items);
+	edit_raise      -> setEnabled(selected_items);
+	edit_lower      -> setEnabled(selected_items);
+	edit_backward   -> setEnabled(selected_items);
+	
+	// actions dependant du contenu du presse-papiers
+	paste           -> setEnabled(clipboard_elmt);
+	paste_in_area   -> setEnabled(clipboard_elmt);
+	
+	// actions dependant de l'etat de la pile d'annulation
+	save            -> setEnabled(!read_only && !ce_scene -> undoStack().isClean());
+	undo            -> setEnabled(!read_only && ce_scene -> undoStack().canUndo());
+	redo            -> setEnabled(!read_only && ce_scene -> undoStack().canRedo());
+}
+
+/**
+	Met a jour le titre de la fenetre
+*/
+void QETElementEditor::slot_updateTitle() {
+	QString title = min_title;
+	title += " - " + ce_scene -> names().name() + " ";
+	if (!filename_.isEmpty() || !location_.isNull()) {
+		if (!ce_scene -> undoStack().isClean()) title += tr("[Modifi\351]", "window title tag");
+	}
+	if (isReadOnly()) title += tr(" [lecture seule]", "window title tag");
+	setWindowTitle(title);
+}
+
+/**
+	Met en place l'interface
+*/
+void QETElementEditor::setupInterface() {
+	// editeur
+	ce_scene = new ElementScene(this, this);
+	ce_scene -> slot_move();
+	ce_view = new ElementView(ce_scene, this);
+	slot_setRubberBandToView();
+	setCentralWidget(ce_view);
+	
+	// widget par defaut dans le QDockWidget
+	default_informations = new QLabel();
+	
+	// ScrollArea pour accueillir un widget d'edition (change a la volee)
+	tools_dock_scroll_area_ = new QScrollArea();
+	tools_dock_scroll_area_ -> setFrameStyle(QFrame::NoFrame);
+	tools_dock_scroll_area_ -> setAlignment(Qt::AlignHCenter|Qt::AlignTop);
+	
+	// Pile de widgets pour accueillir les deux widgets precedents
+	tools_dock_stack_ = new QStackedWidget();
+	tools_dock_stack_ -> insertWidget(0, default_informations);
+	tools_dock_stack_ -> insertWidget(1, tools_dock_scroll_area_);
+	
+	// widgets d'editions pour les parties
+	editors_["arc"]       = new ArcEditor(this);
+	editors_["ellipse"]   = new EllipseEditor(this);
+	editors_["line"]      = new LineEditor(this);
+	editors_["polygon"]   = new PolygonEditor(this);
+	editors_["rect"]      = new RectangleEditor(this);
+	editors_["terminal"]  = new TerminalEditor(this);
+	editors_["text"]      = new TextEditor(this);
+	editors_["input"]     = new TextFieldEditor(this);
+	
+	// panel sur le cote pour editer les parties
+	tools_dock = new QDockWidget(tr("Informations", "dock title"), this);
+	tools_dock -> setObjectName("informations");
+	tools_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+	tools_dock -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	tools_dock -> setMinimumWidth(380);
+	addDockWidget(Qt::RightDockWidgetArea, tools_dock);
+	tools_dock -> setWidget(tools_dock_stack_);
+	
+	// panel sur le cote pour les annulations
+	undo_dock = new QDockWidget(tr("Annulations", "dock title"), this);
+	undo_dock -> setObjectName("undo");
+	undo_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+	undo_dock -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	undo_dock -> setMinimumWidth(290);
+	addDockWidget(Qt::RightDockWidgetArea, undo_dock);
+	QUndoView* undo_view = new QUndoView(&(ce_scene -> undoStack()), this);
+	undo_view -> setEmptyLabel(tr("Aucune modification"));
+	undo_dock -> setWidget(undo_view);
+	
+	// panel sur le cote pour la liste des parties
+	parts_list = new QListWidget(this);
+	parts_list -> setSelectionMode(QAbstractItemView::ExtendedSelection);
+	connect(ce_scene,   SIGNAL(partsAdded()),           this, SLOT(slot_createPartsList()));
+	connect(ce_scene,   SIGNAL(partsRemoved()),         this, SLOT(slot_createPartsList()));
+	connect(ce_scene,   SIGNAL(partsZValueChanged()),   this, SLOT(slot_createPartsList()));
+	connect(ce_scene,   SIGNAL(selectionChanged()),     this, SLOT(slot_updatePartsList()));
+	connect(parts_list, SIGNAL(itemSelectionChanged()), this, SLOT(slot_updateSelectionFromPartsList()));
+	parts_dock = new QDockWidget(tr("Parties", "dock title"), this);
+	parts_dock -> setObjectName("parts_list");
+	parts_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+	parts_dock -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	parts_dock -> setMinimumWidth(290);
+	tabifyDockWidget(undo_dock, parts_dock);
+	parts_dock -> setWidget(parts_list);
+	
+	slot_updateInformations();
+	slot_createPartsList();
+	
+	// barre d'etat
+	statusBar() -> showMessage(tr("\311diteur d'\351l\351ments", "status bar message"));
+}
+
+/**
+	Passe l'editeur d'element en mode selection : le pointeur deplace les
+	elements selectionnes et il est possible d'utiliser un rectangle de selection.
+*/
+void QETElementEditor::slot_setRubberBandToView() {
+	ce_view -> setDragMode(QGraphicsView::RubberBandDrag);
+}
+
+/**
+	Passe l'editeur d'element en mode immobile (utilise pour la lecture seule)
+*/
+void QETElementEditor::slot_setNoDragToView() {
+	ce_view -> setDragMode(QGraphicsView::NoDrag);
+}
+
+/**
+	Passe l'editeur en mode normal
+*/
+void QETElementEditor::slot_setNormalMode() {
+	if (!move -> isChecked()) move -> setChecked(true);
+	ce_view -> setDragMode(QGraphicsView::RubberBandDrag);
+	ce_scene -> slot_move();
+}
+
+/**
+	Met a jour la zone d'information et d'edition des primitives.
+	Si plusieurs primitives sont selectionnees, seule leur quantite est
+	affichee. Sinon, un widget d'edition approprie est mis en place.
+*/
+void QETElementEditor::slot_updateInformations() {
+	QList<QGraphicsItem *> selected_qgis = ce_scene -> selectedItems();
+	
+	clearToolsDock();
+	
+	// s'il n'y a qu'une seule primitive selectionnee
+	if (selected_qgis.size() == 1) {
+		QGraphicsItem *qgi = selected_qgis.first();
+		if (CustomElementPart *selection = dynamic_cast<CustomElementPart *>(qgi)) {
+			// on en ajoute le widget d'edition
+			QString selection_xml_name = selection -> xmlName();
+			ElementItemEditor *selection_editor = editors_[selection_xml_name];
+			if (selection_editor) {
+				if (selection_editor -> setPart(selection)) {
+					tools_dock_scroll_area_ -> setWidget(selection_editor);
+					tools_dock_stack_ -> setCurrentIndex(1);
+				} else {
+					qDebug() << "Editor refused part.";
+				}
+			}
+		}
+	} else {
+		default_informations -> setText(
+			tr(
+				"%n partie(s) s\351lectionn\351e(s).",
+				"",
+				selected_qgis.size()
+			)
+		);
+		default_informations -> setAlignment(Qt::AlignHCenter | Qt::AlignTop);
+		tools_dock_stack_ -> setCurrentIndex(0);
+	}
+}
+
+/**
+	Affiche le code XML correspondant a l'element dans son etat actuel dans
+	une boite de dialogue.
+*/
+void QETElementEditor::xmlPreview() {
+	QET::MessageBox::information(
+		this,
+		"Export XML",
+		ce_scene -> toXml().toString(4)
+	);
+}
+
+/**
+	Effectue diverses verifications sur l'element et en informe l'utilisateur.
+	@return true si la situation est ok, false sinon
+*/
+bool QETElementEditor::checkElement() {
+	// liste les avertissements applicables
+	typedef QPair<QString, QString> QETWarning;
+	QList<QETWarning> warnings;
+		
+	/// Avertissement #2 : si l'element ne comporte aucune borne
+	if (!ce_scene -> containsTerminals()) {
+		warnings << qMakePair(
+			tr("Absence de borne", "warning title"),
+			tr(
+				"L'\351l\351ment ne comporte aucune borne. Un \351l\351ment "
+				"doit comporter des bornes afin de pouvoir \351tre reli\351 "
+				"\340 d'autres \351l\351ments par l'interm\351diaire de "
+				"conducteurs.",
+				"warning description"
+			)
+		);
+	}
+	
+	if (!warnings.count()) return(true);
+	
+	// affiche les avertissements
+	QString warning_message = tr(
+		"La v\351rification de cet \351l\351ment a g\351n\351r\351 %n avertissement(s)\240:",
+		"message box content",
+		warnings.count()
+	);
+	
+	warning_message += "<ol>";
+	foreach(QETWarning warning, warnings) {
+		warning_message += "<li>";
+		warning_message += QString(
+			tr("<b>%1</b>\240: %2", "warning title: warning description")
+		).arg(warning.first).arg(warning.second);
+		warning_message += "</li>";
+	}
+	warning_message += "</ol>";
+	
+	QMessageBox warnings_message_box(this);
+	warnings_message_box.setWindowModality(Qt::WindowModal);
+	warnings_message_box.setWindowFlags(warnings_message_box.windowFlags() | Qt::Sheet);
+	warnings_message_box.setTextFormat(Qt::RichText);
+	warnings_message_box.setWindowTitle(tr("Avertissements", "messagebox title"));
+	warnings_message_box.setText(warning_message);
+	warnings_message_box.exec();
+	return(false);
+}
+
+/**
+	Charge un fichier
+	@param filepath Chemin du fichier a charger
+*/
+void QETElementEditor::fromFile(const QString &filepath) {
+	bool state = true;
+	QString error_message;
+	
+	// le fichier doit exister
+	QFileInfo infos_file(filepath);
+	if (!infos_file.exists() || !infos_file.isFile()) {
+		state = false;
+		error_message = QString(tr("Le fichier %1 n'existe pas.", "message box content")).arg(filepath);
+	}
+	
+	// le fichier doit etre lisible
+	QFile file(filepath);
+	if (state) {
+		if (!file.open(QIODevice::ReadOnly)) {
+			state = false;
+			error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(filepath);
+		}
+	}
+	
+	// le fichier doit etre un document XML
+	QDomDocument document_xml;
+	if (state) {
+		if (!document_xml.setContent(&file)) {
+			state = false;
+			error_message = tr("Ce fichier n'est pas un document XML valide", "message box content");
+		}
+		file.close();
+	}
+	
+	if (!state) {
+		QET::MessageBox::critical(this, tr("Erreur", "toolbar title"), error_message);
+		return;
+	}
+	
+	// chargement de l'element
+	ce_scene -> fromXml(document_xml);
+	slot_createPartsList();
+	
+	// gestion de la lecture seule
+	if (!infos_file.isWritable()) {
+		QET::MessageBox::warning(
+			this,
+			tr("\311dition en lecture seule", "message box title"),
+			tr("Vous n'avez pas les privil\350ges n\351cessaires pour modifier cet \351lement. Il sera donc ouvert en lecture seule.", "message box content")
+		);
+		setReadOnly(true);
+	} else {
+		setReadOnly(false);
+	}
+	
+	// memorise le fichier
+	setFileName(filepath);
+	QETApp::elementsRecentFiles() -> fileWasOpened(filepath);
+	slot_updateMenus();
+}
+
+/**
+	Enregistre l'element vers un fichier
+	@param fn Chemin du fichier a enregistrer
+	@return true en cas de reussite, false sinon
+*/
+bool QETElementEditor::toFile(const QString &fn) {
+	QDomDocument element_xml = ce_scene -> toXml();
+	bool writing = QET::writeXmlFile(element_xml, fn);
+	if (!writing) {
+		QET::MessageBox::warning(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Impossible d'\351crire dans ce fichier", "message box content")
+		);
+	}
+	return(writing);
+}
+
+/**
+	Enregistre l'element vers un emplacement
+	@param location Emplacement de l'element a enregistrer
+	@return true en cas de reussite, false sinon
+*/
+bool QETElementEditor::toLocation(const ElementsLocation &location) {
+	ElementsCollectionItem *item = QETApp::collectionItem(location);
+	ElementDefinition *element;
+	if (item) {
+		// l'element existe deja
+		element = qobject_cast<ElementDefinition *>(item);
+	} else {
+		// l'element n'existe pas encore, on demande sa creation
+		element = QETApp::createElement(location);
+	}
+	
+	if (!element) {
+		QET::MessageBox::critical(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Impossible d'atteindre l'\351l\351ment", "message box content")
+		);
+		return(false);
+	}
+	
+	// enregistre l'element
+	element -> setXml(ce_scene -> toXml().documentElement());
+	if (!element -> write()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Impossible d'enregistrer l'\351l\351ment", "message box content")
+		);
+		return(false);
+	}
+	
+	return(true);
+}
+
+/**
+	@param provided_location Emplacement d'un element
+	@return true si cet editeur est en train d'editer l'element dont
+	l'emplacement est location, false sinon
+*/
+bool QETElementEditor::isEditing(const ElementsLocation &provided_location) {
+	if (opened_from_file) {
+		return(
+			QET::compareCanonicalFilePaths(
+				filename_,
+				QETApp::realPath(provided_location.toString())
+			)
+		);
+	} else {
+		return(provided_location == location_);
+	}
+}
+
+/**
+	@param provided_filepath Chemin d'un element sur un filesystem
+	@return true si cet editeur est en train d'editer l'element dont
+	le chemin est filepath, false sinon
+*/
+bool QETElementEditor::isEditing(const QString &provided_filepath) {
+	// determine le chemin canonique de l'element actuelle edite, si applicable
+	QString current_filepath;
+	if (opened_from_file) {
+		current_filepath = filename_;
+	} else {
+		current_filepath = QETApp::realPath(location_.toString());
+	}
+	
+	return(
+		QET::compareCanonicalFilePaths(
+			current_filepath,
+			provided_filepath
+		)
+	);
+}
+
+/**
+	specifie si l'editeur d'element doit etre en mode lecture seule
+	@param ro true pour activer le mode lecture seule, false pour le desactiver
+*/
+void QETElementEditor::setReadOnly(bool ro) {
+	read_only = ro;
+	
+	// active / desactive les interactions avec la scene
+	ce_view -> setInteractive(!ro);
+	
+	slot_updateMenus();
+}
+
+/**
+	@return true si l'editeur d'element est en mode lecture seule
+*/
+bool QETElementEditor::isReadOnly() const {
+	return(read_only);
+}
+
+/**
+	Lance l'assistant de creation d'un nouvel element.
+*/
+void QETElementEditor::slot_new() {
+	NewElementWizard new_element_wizard(this);
+	new_element_wizard.exec();
+}
+
+/**
+	Ouvre un element
+*/
+void QETElementEditor::slot_open() {
+	// demande le chemin virtuel de l'element a ouvrir a l'utilisateur
+	ElementsLocation location = ElementDialog::getOpenElementLocation(this);
+	if (location.isNull()) return;
+	QETApp::instance() -> openElementLocations(QList<ElementsLocation>() << location);
+}
+
+/**
+	Ouvre un fichier
+	Demande un fichier a l'utilisateur et ouvre ce fichier
+*/
+void QETElementEditor::slot_openFile() {
+	// repertoire a afficher initialement dans le dialogue
+	QString open_dir = filename_.isEmpty() ? QETApp::customElementsDir() : QDir(filename_).absolutePath();
+	
+	// demande un nom de fichier a ouvrir a l'utilisateur
+	QString user_filename = QETElementEditor::getOpenElementFileName(this, open_dir);
+	
+	// ouvre l'element
+	openElement(user_filename);
+}
+
+/**
+	Slot utilise pour ouvrir un fichier recent.
+	Transfere filepath au slot openElement seulement si cet editeur est actif
+	@param filepath Fichier a ouvrir
+	@see openElement
+*/
+void QETElementEditor::openRecentFile(const QString &filepath) {
+	// small hack to prevent all element editors from trying to topen the required
+	// recent file at the same time
+	if (qApp -> activeWindow() != this) return;
+	openElement(filepath);
+}
+
+/**
+	Ouvre un fichier element dans un nouvel editeur
+	Cette methode ne controle pas si le fichier est deja ouvert
+	@param filepath Fichier a ouvrir
+	@see fromFile
+	@see QETApp::openElementFiles
+*/
+void QETElementEditor::openElement(const QString &filepath) {
+	if (filepath.isEmpty()) return;
+	// we have to test the file existence here because QETApp::openElementFiles()
+	// will discard non-existent files through QFileInfo::canonicalFilePath()
+	if (!QFile::exists(filepath)) {
+		QET::MessageBox::critical(
+			this,
+			tr("Impossible d'ouvrir le fichier", "message box title"),
+			QString(
+				tr("Il semblerait que le fichier %1 que vous essayez d'ouvrir"
+				" n'existe pas ou plus.")
+			).arg(filepath)
+		);
+	}
+	QETApp::instance() -> openElementFiles(QStringList() << filepath);
+}
+
+/**
+	Recharge l'element edite
+*/
+void QETElementEditor::slot_reload() {
+	// s'il ya des modifications, on demande a l'utilisateur s'il est certain
+	// de vouloir recharger
+	if (!ce_scene -> undoStack().isClean()) {
+		QMessageBox::StandardButton answer = QET::MessageBox::question(
+			this,
+			tr("Recharger l'\351l\351ment", "dialog title"),
+			tr("Vous avez efffectu\351 des modifications sur cet \351l\351ment. Si vous le rechargez, ces modifications seront perdues. Voulez-vous vraiment recharger l'\351l\351ment ?", "dialog content"),
+			QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+			QMessageBox::Cancel
+		);
+		if (answer != QMessageBox::Yes) return;
+	}
+	
+	// recharge l'element
+	if (opened_from_file) {
+		// l'element a ete ouvert a partir d'un chemin de fichier
+		ce_scene -> reset();
+		fromFile(filename_);
+	} else {
+		// l'element a ete ouvert a partir d'un emplacement (ElementsLocation)
+		// il peut s'agir aussi bien d'un fichier que d'un element XML
+		if (ElementsCollectionItem *item = QETApp::collectionItem(location_)) {
+			item -> reload();
+			ce_scene -> reset();
+			fromLocation(location_);
+		}
+	}
+}
+
+/**
+	Enregistre l'element en cours d'edition.
+	Si le nom du fichier en cours n'est pas connu, cette methode equivaut a
+	l'action "Enregistrer sous"
+	@see slot_saveAs()
+*/
+bool QETElementEditor::slot_save() {
+	// verification avant d'enregistrer le fichier
+	checkElement();
+	// si on ne connait pas le nom du fichier en cours, enregistrer revient a enregistrer sous
+	if (opened_from_file) {
+		if (filename_.isEmpty()) return(slot_saveAsFile());
+		// sinon on enregistre dans le nom de fichier connu
+		bool result_save = toFile(filename_);
+		if (result_save) ce_scene -> undoStack().setClean();
+		return(result_save);
+	} else {
+		if (location_.isNull()) return(slot_saveAs());
+		// sinon on enregistre a l'emplacement connu
+		bool result_save = toLocation(location_);
+		if (result_save) ce_scene -> undoStack().setClean();
+		return(result_save);
+	}
+}
+
+/**
+	Demande une localisation a l'utilisateur et enregistre l'element
+*/
+bool QETElementEditor::slot_saveAs() {
+	// demande une localisation a l'utilisateur
+	ElementsLocation location = ElementDialog::getSaveElementLocation(this);
+	if (location.isNull()) return(false);
+	
+	// tente l'enregistrement
+	bool result_save = toLocation(location);
+	if (result_save) {
+		setLocation(location);
+		ce_scene -> undoStack().setClean();
+	}
+	
+	// retourne un booleen representatif de la reussite de l'enregistrement
+	return(result_save);
+}
+
+/**
+	Demande un nom de fichier a l'utilisateur et enregistre l'element
+*/
+bool QETElementEditor::slot_saveAsFile() {
+	// demande un nom de fichier a l'utilisateur pour enregistrer l'element
+	QString fn = QFileDialog::getSaveFileName(
+		this,
+		tr("Enregistrer sous", "dialog title"),
+		filename_.isEmpty() ? QETApp::customElementsDir() : QDir(filename_).absolutePath(),
+		tr(
+			"\311l\351ments QElectroTech (*.elmt)",
+			"filetypes allowed when saving an element file"
+		)
+	);
+	// si aucun nom n'est entre, renvoie faux.
+	if (fn.isEmpty()) return(false);
+	// si le nom ne se termine pas par l'extension .elmt, celle-ci est ajoutee
+	if (!fn.endsWith(".elmt", Qt::CaseInsensitive)) fn += ".elmt";
+	// tente d'enregistrer le fichier
+	bool result_save = toFile(fn);
+	// si l'enregistrement reussit, le nom du fichier est conserve
+	if (result_save) {
+		setFileName(fn);
+		QETApp::elementsRecentFiles() -> fileWasOpened(fn);
+		ce_scene -> undoStack().setClean();
+	}
+	// retourne un booleen representatif de la reussite de l'enregistrement
+	return(result_save);
+}
+
+/**
+	@return true si l'element peut etre ferme.
+	Un element peut etre ferme s'il ne comporte aucune modification.
+	Si l'element comporte des modifications, la question est posee a
+	l'utilisateur.
+*/
+bool QETElementEditor::canClose() {
+	if (ce_scene -> undoStack().isClean()) return(true);
+	// demande d'abord a l'utilisateur s'il veut enregistrer l'element en cours
+	QMessageBox::StandardButton answer = QET::MessageBox::question(
+		this,
+		tr("Enregistrer l'\351l\351ment en cours ?", "dialog title"),
+		QString(
+			tr(
+				"Voulez-vous enregistrer l'\351l\351ment %1 ?",
+				"dialog content - %1 is an element name"
+			)
+		).arg(ce_scene -> names().name()),
+		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+		QMessageBox::Cancel
+	);
+	bool result;
+	switch(answer) {
+		case QMessageBox::Cancel: result = false;         break; // l'utilisateur annule : echec de la fermeture
+		case QMessageBox::Yes:    result = slot_save();   break; // l'utilisateur dit oui : la reussite depend de l'enregistrement
+		default:                  result = true;                 // l'utilisateur dit non ou ferme le dialogue: c'est reussi
+	}
+	return(result);
+}
+
+/**
+	Enleve et cache le widget affiche par le dock permettant d'editer les
+	parties.
+	@return le widget enleve, ou 0 s'il n'y avait pas de widget a enlever
+*/
+QWidget *QETElementEditor::clearToolsDock() {
+	if (QWidget *previous_widget = tools_dock_scroll_area_ -> takeWidget()) {
+		previous_widget -> setParent(0);
+		previous_widget -> hide();
+		return(previous_widget);
+	}
+	return(0);
+}
+
+/**
+	Exporte le document XML xml_document vers le presse-papier puis declenche
+	son collage dans l'editeur courant, avec selection de la zone de collage
+	@param xml_document Document XML a copier/coller
+	@see ElementView::pasteInArea
+*/
+void QETElementEditor::copyAndPasteXml(const QDomDocument &xml_document) {
+	// accede au presse-papier
+	QClipboard *clipboard = QApplication::clipboard();
+	
+	// genere la description XML de la selection
+	QString clipboard_content = xml_document.toString(4);
+	
+	// met la description XML dans le presse-papier
+	if (clipboard -> supportsSelection()) {
+		clipboard -> setText(clipboard_content, QClipboard::Selection);
+	}
+	clipboard -> setText(clipboard_content);
+	
+	ce_view -> pasteInArea();
+}
+
+/**
+	Permet de quitter l'editeur lors de la fermeture de la fenetre principale
+	@param qce Le QCloseEvent correspondant a l'evenement de fermeture
+*/
+void QETElementEditor::closeEvent(QCloseEvent *qce) {
+	if (canClose()) {
+		writeSettings();
+		setAttribute(Qt::WA_DeleteOnClose);
+		ce_scene -> reset();
+		qce -> accept();
+	} else qce -> ignore();
+}
+
+/**
+	Executed the first time the window editor is displayed.
+*/
+void QETElementEditor::firstActivation(QEvent *event) {
+	Q_UNUSED(event)
+	QTimer::singleShot(250, ce_view, SLOT(zoomFit()));
+}
+
+/**
+	Remplit la liste des parties
+*/
+void QETElementEditor::slot_createPartsList() {
+	parts_list -> blockSignals(true);
+	parts_list -> clear();
+	QList<QGraphicsItem *> qgis = ce_scene -> zItems();
+	
+	// on ne construit plus la liste a partir de 200 primitives
+	// c'est ingerable : la maj de la liste prend trop de temps et le resultat
+	// est inexploitable
+	if (qgis.count() <= QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST) {
+		for (int j = qgis.count() - 1 ; j >= 0 ; -- j) {
+			QGraphicsItem *qgi = qgis[j];
+			if (CustomElementPart *cep = dynamic_cast<CustomElementPart *>(qgi)) {
+				QString part_desc = cep -> name();
+				QListWidgetItem *qlwi = new QListWidgetItem(part_desc);
+				QVariant v;
+				v.setValue<QGraphicsItem *>(qgi);
+				qlwi -> setData(42, v);
+				parts_list -> addItem(qlwi);
+				qlwi -> setSelected(qgi -> isSelected());
+			}
+		}
+	} else {
+		parts_list -> addItem(new QListWidgetItem(tr("Trop de primitives, liste non g\351n\351r\351e.")));
+	}
+	parts_list -> blockSignals(false);
+}
+
+/**
+	Met a jour la selection dans la liste des parties
+*/
+void QETElementEditor::slot_updatePartsList() {
+	int items_count = ce_scene -> items().count();
+	if (parts_list -> count() != items_count) {
+		slot_createPartsList();
+	} else if (items_count <= QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST) {
+		parts_list -> blockSignals(true);
+		int i = 0;
+		QList<QGraphicsItem *> items = ce_scene -> zItems();
+		for (int j = items.count() - 1 ; j >= 0 ; -- j) {
+			QGraphicsItem *qgi = items[j];
+			QListWidgetItem *qlwi = parts_list -> item(i);
+			if (qlwi) qlwi -> setSelected(qgi -> isSelected());
+			++ i;
+		}
+		parts_list -> blockSignals(false);
+	}
+}
+
+/**
+	Met a jour la selection des parties de l'element a partir de la liste des
+	parties
+*/
+void QETElementEditor::slot_updateSelectionFromPartsList() {
+	ce_scene  -> blockSignals(true);
+	parts_list -> blockSignals(true);
+	for (int i = 0 ; i < parts_list -> count() ; ++ i) {
+		QListWidgetItem *qlwi = parts_list -> item(i);
+		QGraphicsItem *qgi = qlwi -> data(42).value<QGraphicsItem *>();
+		if (qgi) {
+			qgi -> setSelected(qlwi -> isSelected());
+		}
+	}
+	parts_list -> blockSignals(false);
+	ce_scene -> blockSignals(false);
+	slot_updateInformations();
+	slot_updateMenus();
+}
+
+/// Lit les parametres de l'editeur d'element
+void QETElementEditor::readSettings() {
+	QSettings &settings = QETApp::settings();
+	
+	// dimensions et position de la fenetre
+	QVariant geometry = settings.value("elementeditor/geometry");
+	if (geometry.isValid()) restoreGeometry(geometry.toByteArray());
+	
+	// etat de la fenetre (barres d'outils, docks...)
+	QVariant state = settings.value("elementeditor/state");
+	if (state.isValid()) restoreState(state.toByteArray());
+	
+	// informations complementaires de l'element : valeur par defaut
+	ce_scene -> setInformations(settings.value("elementeditor/default-informations", "").toString());
+}
+
+/// Enregistre les parametres de l'editeur d'element
+void QETElementEditor::writeSettings() {
+	QSettings &settings = QETApp::settings();
+	settings.setValue("elementeditor/geometry", saveGeometry());
+	settings.setValue("elementeditor/state", saveState());
+}
+
+/**
+	@return les decalages horizontaux et verticaux (sous la forme d'un point) a
+	utiliser lors d'un copier/coller avec decalage.
+*/
+QPointF QETElementEditor::pasteOffset() {
+	QPointF paste_offset(5.0, 0.0);
+	return(paste_offset);
+}
+
+/**
+	Demande a l'utilisateur d'ouvrir un fichier sense etre un element.
+	@param parent QWidget parent du dialogue d'ouverture de fichier
+	@param initial_dir Repertoire a afficher initialement - si une chaine vide
+	est fournie, QETApp::customElementsDir() sera utilise.
+	@return Le chemin du fichier choisi ou une chaine vide si l'utilisateur a
+	clique sur le bouton "Annuler".
+	@see QETApp::customElementsDir()
+*/
+QString QETElementEditor::getOpenElementFileName(QWidget *parent, const QString &initial_dir) {
+	// demande un nom de fichier a ouvrir a l'utilisateur
+	QString user_filename = QFileDialog::getOpenFileName(
+		parent,
+		tr("Ouvrir un fichier", "dialog title"),
+		initial_dir.isEmpty() ? QETApp::customElementsDir() : initial_dir,
+		tr(
+			"\311l\351ments QElectroTech (*.elmt);;"
+			"Fichiers XML (*.xml);;"
+			"Tous les fichiers (*)",
+			"filetypes allowed when opening an element file"
+		)
+	);
+	return(user_filename);
+}
+
+/**
+	@param location Emplacement de l'element a editer
+*/
+void QETElementEditor::fromLocation(const ElementsLocation &location) {
+	
+	// l'element doit exister
+	ElementsCollectionItem *item = QETApp::collectionItem(location);
+	ElementDefinition *element = 0;
+	if (!item) {
+		QET::MessageBox::critical(
+			this,
+			tr("\311l\351ment inexistant.", "message box title"),
+			tr("L'\351l\351ment n'existe pas.", "message box content")
+		);
+		return;
+	}
+	
+	if (!item -> isElement() || !(element = qobject_cast<ElementDefinition *>(item)) || element -> isNull()) {
+		QET::MessageBox::critical(
+			this,
+			tr("\311l\351ment inexistant.", "message box title"),
+			tr("Le chemin virtuel choisi ne correspond pas \340 un \351l\351ment.", "message box content")
+		);
+		return;
+	}
+	
+	// le fichier doit etre un document XML
+	QDomDocument document_xml;
+	QDomNode node = document_xml.importNode(element -> xml(), true);
+	document_xml.appendChild(node);
+	
+	// chargement de l'element
+	ce_scene -> fromXml(document_xml);
+	slot_createPartsList();
+	
+	// gestion de la lecture seule
+	if (!element -> isWritable()) {
+		QET::MessageBox::warning(
+			this,
+			tr("\311dition en lecture seule", "message box title"),
+			tr("Vous n'avez pas les privil\350ges n\351cessaires pour modifier cet \351lement. Il sera donc ouvert en lecture seule.", "message box content")
+		);
+		setReadOnly(true);
+	} else {
+		setReadOnly(false);
+	}
+	
+	// memorise le fichier
+	setLocation(location);
+	slot_updateMenus();
+}
+
+/**
+	Demande un fichier a l'utilisateur, l'ouvre en tant que fichier element,
+	met son contenu dans le presse-papiers, et appelle ElementView::PasteInArea
+*/
+void QETElementEditor::pasteFromFile() {
+	// demande le chemin du fichier a ouvrir a l'utilisateur
+	QString element_file_path = getOpenElementFileName(this);
+	if (element_file_path.isEmpty()) return;
+	
+	QString error_message;
+	QDomDocument xml_document;
+	QFile element_file(element_file_path);
+	// le fichier doit etre lisible
+	if (!element_file.open(QIODevice::ReadOnly)) {
+		error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(element_file_path);
+	} else {
+		// le fichier doit etre un document XML
+		if (!xml_document.setContent(&element_file)) {
+			error_message = tr("Ce fichier n'est pas un document XML valide", "message box content");
+		}
+		element_file.close();
+	}
+	
+	if (!error_message.isEmpty()) {
+		QET::MessageBox::critical(this, tr("Erreur", "toolbar title"), error_message);
+	}
+	copyAndPasteXml(xml_document);
+}
+
+/**
+	Denande un element a l'utilisateur, met son contenu dans le presse-papiers,
+	et appelle ElementView::PasteInArea
+*/
+void QETElementEditor::pasteFromElement() {
+	// demande le chemin virtuel de l'element a ouvrir a l'utilisateur
+	ElementsLocation location = ElementDialog::getOpenElementLocation(this);
+	if (location.isNull()) return;
+	
+	// verifie l'existence de l'element choisi
+	ElementsCollectionItem *item = QETApp::collectionItem(location);
+	ElementDefinition *element = 0;
+	if (!item) {
+		QET::MessageBox::critical(
+			this,
+			tr("\311l\351ment inexistant.", "message box title"),
+			tr("L'\351l\351ment n'existe pas.", "message box content")
+		);
+		return;
+	}
+	
+	if (!item -> isElement() || !(element = qobject_cast<ElementDefinition *>(item)) || element -> isNull()) {
+		QET::MessageBox::critical(
+			this,
+			tr("\311l\351ment inexistant.", "message box title"),
+			tr("Le chemin virtuel choisi ne correspond pas \340 un \351l\351ment.", "message box content")
+		);
+		return;
+	}
+	
+	// creation d'un document XML a partir de la description XML de l'element
+	QDomDocument document_xml;
+	QDomNode node = document_xml.importNode(element -> xml(), true);
+	document_xml.appendChild(node);
+	
+	copyAndPasteXml(document_xml);
+}
+
+/**
+	Met a jour l'editeur de primitive actuellement visible.
+	Si aucun editeur de primitive n'est visible, ce slot ne fait rien.
+*/
+void QETElementEditor::updateCurrentPartEditor() {
+	// si aucun widget d'edition n'est affiche, on ne fait rien
+	if (!tools_dock_stack_ -> currentIndex()) return;
+	
+	// s'il y a un widget d'edition affiche, on le met a jour
+	if (ElementItemEditor *current_editor = dynamic_cast<ElementItemEditor *>(tools_dock_scroll_area_ -> widget())) {
+		current_editor -> updateForm();
+	}
+}

Added: trunk/sources/editor/qetelementeditor.h
===================================================================
--- trunk/sources/editor/qetelementeditor.h	                        (rev 0)
+++ trunk/sources/editor/qetelementeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,182 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef CUSTOM_ELEMENT_EDITOR_H
+#define CUSTOM_ELEMENT_EDITOR_H
+#include <QtGui>
+#include "qetmainwindow.h"
+#include "qet.h"
+#include "elementscene.h"
+#include "orientationset.h"
+#include "elementslocation.h"
+class ElementItemEditor;
+class ElementView;
+/**
+	This class represents an element editor, allowing users to draw, change and
+	configure a particular electrical element.
+*/
+class QETElementEditor : public QETMainWindow {
+	Q_OBJECT
+	
+	// constructor, destructor
+	public:
+	QETElementEditor(QWidget * = 0);
+	virtual ~QETElementEditor();
+	private:
+	QETElementEditor(const QETElementEditor &);
+	
+	// attributes
+	private:
+	/// whether the editor is "read-only"
+	bool read_only;
+	/// menus
+	QMenu *file_menu, *edit_menu, *paste_from_menu, *display_menu, *tools_menu;
+	/// view widget for the editing scene
+	ElementView *ce_view;
+	/// editing scene
+	ElementScene *ce_scene;
+	/// container for widgets dedicated to primitive edition
+	QDockWidget *tools_dock;
+	/// Stack of widgets for tools_dock
+	QStackedWidget *tools_dock_stack_;
+	/// label displayed when several primitives are selected
+	QLabel *default_informations;
+	/// Hash associating primitive names with their matching edition widget
+	QHash<QString, ElementItemEditor *> editors_;
+	/// ScrollArea for the tools_dock DockWidget
+	QScrollArea *tools_dock_scroll_area_;
+	/// container for the undo list
+	QDockWidget *undo_dock;
+	/// Container for the list of existing primitives
+	QDockWidget *parts_dock;
+	/// List of primitives
+	QListWidget *parts_list;
+	/// actions for the "file" menu
+	QAction *new_element, *open, *open_file, *save, *save_as, *save_as_file, *reload, *quit;
+	/// actions for the "edit" menu
+	QAction *selectall, *deselectall, *inv_select;
+	QAction *cut, *copy, *paste, *paste_in_area, *paste_from_file, *paste_from_elmt;
+	QAction *undo, *redo;
+	QAction *edit_delete, *edit_size_hs, *edit_names, *edit_author;
+	QAction *edit_raise, *edit_lower, *edit_backward, *edit_forward;
+	/// actions for the "display" menu
+	QAction *zoom_in, *zoom_out, *zoom_fit, *zoom_reset;
+	/// toolbars
+	QToolBar *parts_toolbar, *main_toolbar, *view_toolbar, *depth_toolbar, *element_toolbar;
+	/// toolbars actions
+	QActionGroup *parts;
+	QAction *move, *add_line, *add_rectangle, *add_ellipse, *add_polygon, *add_text;
+	QAction *add_arc, *add_terminal, *add_textfield;
+	/// minimum window title
+	QString min_title;
+	/// filename of the currently edited element
+	QString filename_;
+	/// location of the currently edited element
+	ElementsLocation location_;
+	/// whether the currently edited element comes from a file or a location
+	bool opened_from_file;
+	
+	// methods
+	public:
+	void setNames(const NamesList &);
+	OrientationSet orientations() const;
+	void setLocation(const ElementsLocation &);
+	ElementsLocation location() const;
+	void setFileName(const QString &);
+	QString fileName() const;
+	void setReadOnly(bool);
+	bool isReadOnly() const;
+	void fromFile(const QString &);
+	void fromLocation(const ElementsLocation &);
+	bool toFile(const QString &);
+	bool toLocation(const ElementsLocation &);
+	bool isEditing(const ElementsLocation &);
+	bool isEditing(const QString &);
+	ElementScene *elementScene() const;
+	void readSettings();
+	void writeSettings();
+	static QPointF pasteOffset();
+	static QString getOpenElementFileName(QWidget * = 0, const QString & = QString());
+	void contextMenu(QContextMenuEvent *event);
+
+	protected:
+	void closeEvent(QCloseEvent *);
+	virtual void firstActivation(QEvent *);
+
+	private:
+	void setupActions();
+	void setupMenus();
+	void setupInterface();
+	bool canClose();
+	QWidget *clearToolsDock();
+	void copyAndPasteXml(const QDomDocument &);
+	
+	public slots:
+	void slot_new();
+	void slot_open();
+	void slot_openFile();
+	void openRecentFile(const QString &);
+	void openElement(const QString &);
+	void slot_reload();
+	bool slot_save();
+	bool slot_saveAs();
+	bool slot_saveAsFile();
+	void slot_setRubberBandToView();
+	void slot_setNoDragToView();
+	void slot_setNormalMode();
+	void slot_updateInformations();
+	void slot_updateMenus();
+	void slot_updateTitle();
+	void slot_createPartsList();
+	void slot_updatePartsList();
+	void slot_updateSelectionFromPartsList();
+	void xmlPreview();
+	bool checkElement();
+	void pasteFromFile();
+	void pasteFromElement();
+	void updateCurrentPartEditor();
+};
+
+/**
+	@param nameslist the new list of names for the currently edited element
+*/
+inline void QETElementEditor::setNames(const NamesList &nameslist) {
+	ce_scene -> setNames(nameslist);
+}
+
+/**
+	@return the location of the currently edited element
+*/
+inline ElementsLocation QETElementEditor::location() const {
+	return(location_);
+}
+
+/**
+	@return the filename of the currently edited element
+*/
+inline QString QETElementEditor::fileName() const {
+	return(filename_);
+}
+
+/**
+	@return the editing scene
+*/
+inline ElementScene *QETElementEditor::elementScene() const {
+	return(ce_scene);
+}
+
+#endif

Added: trunk/sources/editor/rectangleeditor.cpp
===================================================================
--- trunk/sources/editor/rectangleeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/rectangleeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,148 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "rectangleeditor.h"
+#include "partrectangle.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param rect Le rectangle a editer
+	@param parent le Widget parent
+*/
+RectangleEditor::RectangleEditor(QETElementEditor *editor, PartRectangle *rect, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(rect)
+{
+	style_ = new StyleEditor(editor);
+	
+	x = new QLineEdit();
+	y = new QLineEdit();
+	w = new QLineEdit();
+	h = new QLineEdit();
+	
+	x -> setValidator(new QDoubleValidator(x));
+	y -> setValidator(new QDoubleValidator(y));
+	w -> setValidator(new QDoubleValidator(w));
+	h -> setValidator(new QDoubleValidator(h));
+	
+	QVBoxLayout *v_layout = new QVBoxLayout(this);
+	
+	QGridLayout *grid = new QGridLayout();
+	grid -> addWidget(new QLabel(tr("Coin sup\351rieur gauche\240: ")), 0, 0, 1, 4);
+	grid -> addWidget(new QLabel("x"),                                  1, 0, Qt::AlignRight);
+	grid -> addWidget(x,                                                1, 1);
+	grid -> addWidget(new QLabel("y"),                                  1, 2);
+	grid -> addWidget(y,                                                1, 3);
+	grid -> addWidget(new QLabel(tr("Dimensions\240: ")),               2, 0, 1, 4);
+	grid -> addWidget(new QLabel(tr("Largeur\240:")),                   3, 0);
+	grid -> addWidget(w,                                                3, 1);
+	grid -> addWidget(new QLabel(tr("Hauteur\240:")),                   4, 0);
+	grid -> addWidget(h,                                                4, 1);
+	
+	v_layout -> addWidget(style_);
+	v_layout -> addLayout(grid);
+	
+	activeConnections(true);
+	updateForm();
+}
+
+/// Destructeur
+RectangleEditor::~RectangleEditor() {
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de rectangle acceptera d'editer la primitive new_part s'il s'agit
+	d'un objet de la classe PartRectangle.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool RectangleEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		style_ -> setPart(0);
+		return(true);
+	}
+	if (PartRectangle *part_rectangle = dynamic_cast<PartRectangle *>(new_part)) {
+		part = part_rectangle;
+		style_ -> setPart(part);
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *RectangleEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour le rectangle a partir des donnees du formulaire
+*/
+void RectangleEditor::updateRectangle() {
+	if (!part) return;
+	part -> setProperty("x",      x -> text().toDouble());
+	part -> setProperty("y",      y -> text().toDouble());
+	part -> setProperty("width",  w -> text().toDouble());
+	part -> setProperty("height", h -> text().toDouble());
+}
+
+/// Met a jour l'abscisse du coin superieur gauche du rectangle et cree un objet d'annulation
+void RectangleEditor::updateRectangleX() { addChangePartCommand(tr("abscisse"),               part, "x",           x -> text().toDouble());       }
+/// Met a jour l'ordonnee du coin superieur gauche du rectangle et cree un objet d'annulation
+void RectangleEditor::updateRectangleY() { addChangePartCommand(tr("ordonn\351e"),            part, "y",           y -> text().toDouble());       }
+/// Met a jour la largeur du rectangle et cree un objet d'annulation
+void RectangleEditor::updateRectangleW() { addChangePartCommand(tr("largeur"),                part, "width",       w -> text().toDouble());       }
+/// Met a jour la hauteur du rectangle et cree un objet d'annulation
+void RectangleEditor::updateRectangleH() { addChangePartCommand(tr("hauteur"),                part, "height",      h -> text().toDouble());       }
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void RectangleEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	x -> setText(part -> property("x").toString());
+	y -> setText(part -> property("y").toString());
+	w -> setText(part -> property("width").toString());
+	h -> setText(part -> property("height").toString());
+	activeConnections(true);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void RectangleEditor::activeConnections(bool active) {
+	if (active) {
+		connect(x, SIGNAL(editingFinished()), this, SLOT(updateRectangleX()));
+		connect(y, SIGNAL(editingFinished()), this, SLOT(updateRectangleY()));
+		connect(w, SIGNAL(editingFinished()), this, SLOT(updateRectangleW()));
+		connect(h, SIGNAL(editingFinished()), this, SLOT(updateRectangleH()));
+	} else {
+		disconnect(x, SIGNAL(editingFinished()), this, SLOT(updateRectangleX()));
+		disconnect(y, SIGNAL(editingFinished()), this, SLOT(updateRectangleY()));
+		disconnect(w, SIGNAL(editingFinished()), this, SLOT(updateRectangleW()));
+		disconnect(h, SIGNAL(editingFinished()), this, SLOT(updateRectangleH()));
+	}
+}

Added: trunk/sources/editor/rectangleeditor.h
===================================================================
--- trunk/sources/editor/rectangleeditor.h	                        (rev 0)
+++ trunk/sources/editor/rectangleeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,58 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef RECTANGLE_EDITOR_H
+#define RECTANGLE_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartRectangle;
+class StyleEditor;
+/**
+	This class provides a widget to edit rectangles within the element editor.
+*/
+class RectangleEditor : public ElementItemEditor {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	RectangleEditor(QETElementEditor *, PartRectangle * = 0, QWidget * = 0);
+	virtual ~RectangleEditor();
+	private:
+	RectangleEditor(const RectangleEditor &);
+	
+	// attributes
+	private:
+	PartRectangle *part;
+	StyleEditor *style_;
+	QLineEdit *x, *y, *w, *h;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateRectangle();
+	void updateRectangleX();
+	void updateRectangleY();
+	void updateRectangleW();
+	void updateRectangleH();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/styleeditor.cpp
===================================================================
--- trunk/sources/editor/styleeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/styleeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,212 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "styleeditor.h"
+#include "customelementgraphicpart.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param p La partie a editer
+	@param parent le Widget parent
+*/
+StyleEditor::StyleEditor(QETElementEditor *editor, CustomElementGraphicPart *p, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(p)
+{
+	// couleur
+	outline_color = new QComboBox(this);
+	outline_color -> addItem(tr("Noir", "element part color"), CustomElementGraphicPart::BlackColor);
+	outline_color -> addItem(tr("Blanc", "element part color"), CustomElementGraphicPart::WhiteColor);
+	outline_color -> addItem(tr("Vert", "element part color"), CustomElementGraphicPart::GreenColor);
+	outline_color -> addItem(tr("Rouge", "element part color"), CustomElementGraphicPart::RedColor);
+	outline_color -> addItem(tr("Bleu", "element part color"), CustomElementGraphicPart::BlueColor);
+
+	// style
+	style = new QButtonGroup(this);
+	style -> addButton(normal_style = new QRadioButton(tr("Normal",       "element part line style")), CustomElementGraphicPart::NormalStyle);
+	style -> addButton(dashed_style = new QRadioButton(tr("Tiret",        "element part line style")), CustomElementGraphicPart::DashedStyle);
+	style -> addButton(dotted_style = new QRadioButton(tr("Pointill\351", "element part line style")), CustomElementGraphicPart::DottedStyle);
+	style -> addButton(dashdotted_style = new QRadioButton(tr("Traits et points", "element part line style")), CustomElementGraphicPart::DashdottedStyle);
+	normal_style -> setChecked(true);
+	
+	// epaisseur
+	weight = new QButtonGroup(this);
+	weight -> addButton(none_weight   = new QRadioButton(tr("Nulle", "element part weight")),   CustomElementGraphicPart::NoneWeight);
+	weight -> addButton(thin_weight   = new QRadioButton(tr("Fine", "element part weight")),    CustomElementGraphicPart::ThinWeight);
+	weight -> addButton(normal_weight = new QRadioButton(tr("Normale", "element part weight")), CustomElementGraphicPart::NormalWeight);
+	
+	// remplissage
+	filling_color = new QComboBox (this);
+	filling_color -> addItem(tr("Aucun", "element part filling"), CustomElementGraphicPart::NoneFilling);
+	filling_color -> addItem(tr("Noir", "element part filling"), CustomElementGraphicPart::BlackFilling);
+	filling_color -> addItem(tr("Blanc", "element part filling"), CustomElementGraphicPart::WhiteFilling);
+	filling_color -> addItem(tr("Vert", "element part filling"), CustomElementGraphicPart::GreenFilling);
+	filling_color -> addItem(tr("Rouge", "element part filling"), CustomElementGraphicPart::RedFilling);
+	filling_color -> addItem(tr("Bleu", "element part filling"), CustomElementGraphicPart::BlueFilling);
+
+	// antialiasing
+	antialiasing = new QCheckBox(tr("Antialiasing"));
+	
+	updateForm();
+	
+	main_layout = new QVBoxLayout();
+	main_layout -> setMargin(0);
+	
+	main_layout -> addWidget(new QLabel("<u>" + tr("Apparence :") + "</u> "));
+	
+	QHBoxLayout *color_layout = new QHBoxLayout();
+	color_layout -> addWidget(new QLabel(tr("Contour :")), 0, Qt::AlignRight);
+	color_layout -> addWidget(outline_color);
+	color_layout -> addSpacing(10);
+	color_layout -> addWidget(new QLabel(tr("Remplissage :")), 0, Qt::AlignRight);
+	color_layout -> addWidget(filling_color);
+	main_layout -> addLayout(color_layout);
+	
+	QHBoxLayout *style_layout = new QHBoxLayout();
+	style_layout -> addWidget(new QLabel(tr("Style : ")));
+	style_layout -> addWidget(normal_style);
+	style_layout -> addWidget(dashed_style);
+	style_layout -> addWidget(dashdotted_style);
+	style_layout -> addWidget(dotted_style);
+	style_layout -> addStretch();
+	main_layout -> addLayout(style_layout);
+	
+	QHBoxLayout *weight_layout = new QHBoxLayout();
+	weight_layout -> addWidget(new QLabel(tr("\311paisseur : ")));
+	weight_layout -> addWidget(none_weight);
+	weight_layout -> addWidget(thin_weight);
+	weight_layout -> addWidget(normal_weight);
+	weight_layout -> addStretch();
+	main_layout -> addLayout(weight_layout);
+	main_layout -> addWidget(antialiasing);
+
+	main_layout -> addSpacing(10);
+	main_layout -> addWidget(new QLabel("<u>" + tr("G\351om\351trie :") + "</u> "));
+	main_layout -> addStretch();
+
+
+	setLayout(main_layout);
+}
+
+/// Destructeur
+StyleEditor::~StyleEditor() {
+}
+
+/**
+	Met a jour le style de la partie a partir des donnees du formulaire
+*/
+void StyleEditor::updatePart() {
+	if (!part) return;
+	// applique l'antialiasing
+	part -> setAntialiased(antialiasing -> isChecked());
+	
+	// applique la couleur
+	part -> setColor(static_cast<CEGP::Color>(outline_color -> currentIndex()));
+	
+	// applique le style
+	part -> setLineStyle(static_cast<CEGP::LineStyle>(style -> checkedId()));
+	
+	// applique l'epaisseur
+	part -> setLineWeight(static_cast<CEGP::LineWeight>(weight -> checkedId()));
+	
+	// applique le remplissage
+	part -> setFilling(static_cast<CEGP::Filling>(filling_color -> currentIndex()));
+}
+
+/// Met a jour l'antialiasing et cree un objet d'annulation
+void StyleEditor::updatePartAntialiasing()   { addChangePartCommand(tr("style antialiasing"), part, "antialias",   antialiasing -> isChecked()); }
+/// Met a jour la couleur du trait et cree un objet d'annulation
+void StyleEditor::updatePartColor()          { addChangePartCommand(tr("style couleur"),      part, "color",       outline_color -> currentIndex());}
+/// Met a jour le style du trait et cree un objet d'annulation
+void StyleEditor::updatePartLineStyle()      { addChangePartCommand(tr("style ligne"),        part, "line-style",  style -> checkedId());        }
+/// Met a jour l'epaisseur du trait et cree un objet d'annulation
+void StyleEditor::updatePartLineWeight()     { addChangePartCommand(tr("style epaisseur"),    part, "line-weight", weight -> checkedId());       }
+/// Met a jour la couleur de fond et cree un objet d'annulation
+void StyleEditor::updatePartFilling()        { addChangePartCommand(tr("style remplissage"),  part, "filling",     filling_color -> currentIndex());}
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void StyleEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	// lit l'antialiasing
+	antialiasing -> setChecked(part -> antialiased());
+	
+	// lit la couleur
+	outline_color -> setCurrentIndex(part -> color());
+	
+	// lit le style
+	style -> button(part -> lineStyle()) -> setChecked(true);
+	
+	// lit l'epaisseur
+	weight -> button(part -> lineWeight()) -> setChecked(true);
+	
+	// lit le remplissage
+	filling_color -> setCurrentIndex(part -> filling());
+	activeConnections(true);
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de ligne acceptera d'editer la primitive new_part s'il s'agit d'un
+	objet de la classe CustomElementGraphicPart.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool StyleEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		return(true);
+	}
+	if (CustomElementGraphicPart *part_graphic = dynamic_cast<CustomElementGraphicPart *>(new_part)) {
+		part = part_graphic;
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *StyleEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void StyleEditor::activeConnections(bool active) {
+	if (active) {
+		connect (outline_color, SIGNAL(activated(int)), this, SLOT(updatePartColor()));
+		connect(style,        SIGNAL(buttonClicked(int)), this, SLOT(updatePartLineStyle()));
+		connect(weight,       SIGNAL(buttonClicked(int)), this, SLOT(updatePartLineWeight()));
+		connect(filling_color, SIGNAL(activated(int)), this, SLOT(updatePartFilling()));
+		connect(antialiasing, SIGNAL(stateChanged(int)),  this, SLOT(updatePartAntialiasing()));
+	} else {
+		disconnect(outline_color, SIGNAL(activated(int)), this, SLOT(updatePartColor()));
+		disconnect(style,        SIGNAL(buttonClicked(int)), this, SLOT(updatePartLineStyle()));
+		disconnect(weight,       SIGNAL(buttonClicked(int)), this, SLOT(updatePartLineWeight()));
+		disconnect(filling_color, SIGNAL(activated(int)), this, SLOT(updatePartFilling()));
+		disconnect(antialiasing, SIGNAL(stateChanged(int)),  this, SLOT(updatePartAntialiasing()));
+	}
+}

Added: trunk/sources/editor/styleeditor.h
===================================================================
--- trunk/sources/editor/styleeditor.h	                        (rev 0)
+++ trunk/sources/editor/styleeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,67 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef STYLE_EDITOR_H
+#define STYLE_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class CustomElementGraphicPart;
+/**
+	This class provides a widget to edit styles (color, pen style and thickness,
+	filling, antialiasing) common to most primitives within the element editor.
+	Its appendWidget() method makes the insertion of another widget below it
+	easier.
+*/
+class StyleEditor : public ElementItemEditor {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	StyleEditor(QETElementEditor *, CustomElementGraphicPart * = 0, QWidget * = 0);
+	virtual ~StyleEditor();
+	
+	private:
+	StyleEditor(const StyleEditor &);
+	
+	// attributes
+	private:
+	CustomElementGraphicPart *part;
+	QVBoxLayout *main_layout;
+	QButtonGroup *style, *weight;
+	QRadioButton *black_color, *white_color,  *normal_style, *dashed_style, *dashdotted_style, *dotted_style, *green_color, *red_color, *blue_color;
+	QRadioButton *none_weight, *thin_weight, *normal_weight, *no_filling;
+	QRadioButton *black_filling, *white_filling, *green_filling, *red_filling, *blue_filling;
+	QCheckBox *antialiasing;
+	QComboBox *filling_color, *outline_color;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updatePart();
+	void updateForm();
+	void updatePartAntialiasing();
+	void updatePartColor();
+	void updatePartLineStyle();
+	void updatePartLineWeight();
+	void updatePartFilling();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/terminaleditor.cpp
===================================================================
--- trunk/sources/editor/terminaleditor.cpp	                        (rev 0)
+++ trunk/sources/editor/terminaleditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,146 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "terminaleditor.h"
+#include "partterminal.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param term La borne a editer
+	@param parent QWidget parent de ce widget
+*/
+TerminalEditor::TerminalEditor(QETElementEditor *editor, PartTerminal *term, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(term)
+{
+	qle_x = new QLineEdit();
+	qle_y = new QLineEdit();
+	
+	qle_x -> setValidator(new QDoubleValidator(qle_x));
+	qle_y -> setValidator(new QDoubleValidator(qle_y));
+	
+	orientation = new QComboBox();
+	orientation -> addItem(QET::Icons::North, tr("Nord"),  QET::North);
+	orientation -> addItem(QET::Icons::East,  tr("Est"),   QET::East);
+	orientation -> addItem(QET::Icons::South, tr("Sud"),   QET::South);
+	orientation -> addItem(QET::Icons::West,  tr("Ouest"), QET::West);
+	
+	QVBoxLayout *main_layout = new QVBoxLayout();
+	main_layout -> addWidget(new QLabel(tr("Position : ")));
+	
+	QHBoxLayout *position = new QHBoxLayout();
+	position -> addWidget(new QLabel(tr("x : ")));
+	position -> addWidget(qle_x                 );
+	position -> addWidget(new QLabel(tr("y : ")));
+	position -> addWidget(qle_y                 );
+	main_layout -> addLayout(position);
+	
+	QHBoxLayout *ori = new QHBoxLayout();
+	ori -> addWidget(new QLabel(tr("Orientation : ")));
+	ori -> addWidget(orientation                     );
+	main_layout -> addLayout(ori);
+	main_layout -> addStretch();
+	setLayout(main_layout);
+	
+	activeConnections(true);
+	updateForm();
+}
+
+/// Destructeur
+TerminalEditor::~TerminalEditor() {
+};
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de borne acceptera d'editer la primitive new_part s'il s'agit d'un
+	objet de la classe PartTerminal.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool TerminalEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		return(true);
+	}
+	if (PartTerminal *part_terminal = dynamic_cast<PartTerminal *>(new_part)) {
+		part = part_terminal;
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *TerminalEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour la borne a partir des donnees du formulaire
+*/
+void TerminalEditor::updateTerminal() {
+	if (!part) return;
+	part -> setPos(qle_x -> text().toDouble(), qle_y -> text().toDouble());
+	part -> setOrientation(
+		static_cast<QET::Orientation>(
+			orientation -> itemData(
+				orientation -> currentIndex()
+			).toInt()
+		)
+	);
+}
+
+/// Met a jour l'abscisse de la position de la borne et cree un objet d'annulation
+void TerminalEditor::updateTerminalX() { addChangePartCommand(tr("abscisse"),    part, "x",           qle_x -> text().toDouble()); updateForm(); }
+/// Met a jour l'ordonnee de la position de la borne et cree un objet d'annulation
+void TerminalEditor::updateTerminalY() { addChangePartCommand(tr("ordonn\351e"), part, "y",           qle_y -> text().toDouble()); updateForm(); }
+/// Met a jour l'orientation de la borne et cree un objet d'annulation
+void TerminalEditor::updateTerminalO() { addChangePartCommand(tr("orientation"), part, "orientation", orientation -> itemData(orientation -> currentIndex()).toInt()); }
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void TerminalEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	qle_x -> setText(part -> property("x").toString());
+	qle_y -> setText(part -> property("y").toString());
+	orientation -> setCurrentIndex(static_cast<int>(part -> orientation()));
+	activeConnections(true);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void TerminalEditor::activeConnections(bool active) {
+	if (active) {
+		connect(qle_x,       SIGNAL(editingFinished()), this, SLOT(updateTerminalX()));
+		connect(qle_y,       SIGNAL(editingFinished()), this, SLOT(updateTerminalY()));
+		connect(orientation, SIGNAL(activated(int)),    this, SLOT(updateTerminalO()));
+	} else {
+		disconnect(qle_x,       SIGNAL(editingFinished()), this, SLOT(updateTerminalX()));
+		disconnect(qle_y,       SIGNAL(editingFinished()), this, SLOT(updateTerminalY()));
+		disconnect(orientation, SIGNAL(activated(int)),    this, SLOT(updateTerminalO()));
+	}
+}

Added: trunk/sources/editor/terminaleditor.h
===================================================================
--- trunk/sources/editor/terminaleditor.h	                        (rev 0)
+++ trunk/sources/editor/terminaleditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,56 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TERMINAL_EDITOR_H
+#define TERMINAL_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartTerminal;
+/**
+	This class provides a widget to edit terminals within the element editor.
+*/
+class TerminalEditor : public ElementItemEditor {
+	Q_OBJECT
+	// Constructors, destructor
+	public:
+	TerminalEditor(QETElementEditor *, PartTerminal * = 0, QWidget * = 0);
+	virtual ~TerminalEditor();
+	private:
+	TerminalEditor(const TerminalEditor &);
+	
+	// attributes
+	private:
+	PartTerminal *part;
+	QLineEdit *qle_x, *qle_y;
+	QComboBox *orientation;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateTerminal();
+	void updateTerminalX();
+	void updateTerminalY();
+	void updateTerminalO();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/texteditor.cpp
===================================================================
--- trunk/sources/editor/texteditor.cpp	                        (rev 0)
+++ trunk/sources/editor/texteditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,178 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "texteditor.h"
+#include "parttext.h"
+#include "qetapp.h"
+#include "qtextorientationspinboxwidget.h"
+
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param text Champ de texte a editer
+	@param parent QWidget parent de ce widget
+*/
+TextEditor::TextEditor(QETElementEditor *editor, PartText *text, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(text)
+{
+	qle_x     = new QLineEdit();
+	qle_y     = new QLineEdit();
+	qle_text  = new QLineEdit();
+	font_size = new QSpinBox();
+	font_size -> setRange(0, 144);
+	black_color_ = new QRadioButton(tr("Noir", "element text part color"));
+	white_color_ = new QRadioButton(tr("Blanc", "element text part color"));
+	color_ = new QButtonGroup(this);
+	color_ -> addButton(black_color_, true);
+	color_ -> addButton(white_color_, false);
+	connect(color_, SIGNAL(buttonClicked(int)), this, SLOT(updateTextC()));
+	QLabel *rotation_angle_label = new QLabel(tr("Angle de rotation : "));
+	rotation_angle_label -> setWordWrap(true);
+	rotation_angle_ = QETApp::createTextOrientationSpinBoxWidget();
+	
+	qle_x -> setValidator(new QDoubleValidator(qle_x));
+	qle_y -> setValidator(new QDoubleValidator(qle_y));
+	
+	QVBoxLayout *main_layout = new QVBoxLayout();
+	main_layout -> addWidget(new QLabel(tr("Position : ")));
+	
+	QHBoxLayout *position = new QHBoxLayout();
+	position -> addWidget(new QLabel(tr("x : ")));
+	position -> addWidget(qle_x                 );
+	position -> addWidget(new QLabel(tr("y : ")));
+	position -> addWidget(qle_y                 );
+	main_layout -> addLayout(position);
+	
+	QHBoxLayout *fs = new QHBoxLayout();
+	fs -> addWidget(new QLabel(tr("Taille : ")));
+	fs -> addWidget(font_size);
+	main_layout -> addLayout(fs);
+	
+	QHBoxLayout *color_layout = new QHBoxLayout();
+	color_layout -> addWidget(new QLabel(tr("Couleur : ")));
+	color_layout -> addWidget(black_color_);
+	color_layout -> addWidget(white_color_);
+	color_layout -> addStretch();
+	main_layout -> addLayout(color_layout);
+	
+	QHBoxLayout *t = new QHBoxLayout();
+	t -> addWidget(new QLabel(tr("Texte : ")));
+	t -> addWidget(qle_text);
+	
+	QHBoxLayout *rotation_angle_layout = new QHBoxLayout();
+	rotation_angle_layout -> addWidget(rotation_angle_label);
+	rotation_angle_layout -> addWidget(rotation_angle_);
+	
+	main_layout -> addLayout(t);
+	main_layout -> addLayout(rotation_angle_layout);
+	main_layout -> addStretch();
+	setLayout(main_layout);
+	
+	updateForm();
+}
+
+/**
+	Destructeur
+*/
+TextEditor::~TextEditor() {
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de texte statique acceptera d'editer la primitive new_part s'il
+	s'agit d'un objet de la classe PartText.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool TextEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		return(true);
+	}
+	if (PartText *part_text = dynamic_cast<PartText *>(new_part)) {
+		part = part_text;
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *TextEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour le champ de texte a partir des donnees du formulaire
+*/
+void TextEditor::updateText() {
+	if (!part) return;
+	part -> setProperty("size", font_size -> value());
+	part -> setPlainText(qle_text -> text());
+	part -> setPos(qle_x -> text().toDouble(), qle_y -> text().toDouble());
+}
+
+/// Met a jour l'abscisse de la position du texte et cree un objet d'annulation
+void TextEditor::updateTextX() { addChangePartCommand(tr("abscisse"),    part, "x",    qle_x -> text().toDouble()); updateForm(); }
+/// Met a jour l'ordonnee de la position du texte et cree un objet d'annulation
+void TextEditor::updateTextY() { addChangePartCommand(tr("ordonn\351e"), part, "y",    qle_y -> text().toDouble()); updateForm(); }
+/// Met a jour le texte et cree un objet d'annulation
+void TextEditor::updateTextT() { addChangePartCommand(tr("contenu"),     part, "text", qle_text -> text());         }
+/// Met a jour la taille du texte et cree un objet d'annulation
+void TextEditor::updateTextS() { addChangePartCommand(tr("taille"),      part, "size", font_size -> value());       }
+/// Update the text color and create an undo object
+void TextEditor::updateTextC() { addChangePartCommand(tr("couleur", "undo caption"), part, "color", color_ -> checkedId()); }
+/// Met a jour l'angle de rotation du champ de texte et cree un objet d'annulation
+void TextEditor::updateTextRotationAngle() { addChangePartCommand(tr("angle de rotation"), part, "rotation angle", rotation_angle_ -> value()); }
+
+/**
+	Met a jour le formulaire a partir du champ de texte
+*/
+void TextEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	qle_x     -> setText(part -> property("x").toString());
+	qle_y     -> setText(part -> property("y").toString());
+	qle_text  -> setText(part -> property("text").toString());
+	font_size -> setValue(part -> property("size").toInt());
+	if (QAbstractButton *button = color_ -> button(part -> property("color").toBool())) {
+		button -> setChecked(true);
+	}
+	rotation_angle_ -> setValue(part -> property("rotation angle").toDouble());
+	activeConnections(true);
+}
+
+void TextEditor::activeConnections(bool active) {
+	if (active) {
+		connect(qle_x,     SIGNAL(editingFinished()), this, SLOT(updateTextX()));
+		connect(qle_y,     SIGNAL(editingFinished()), this, SLOT(updateTextY()));
+		connect(qle_text,  SIGNAL(editingFinished()), this, SLOT(updateTextT()));
+		connect(font_size, SIGNAL(editingFinished()), this, SLOT(updateTextS()));
+		connect(rotation_angle_, SIGNAL(editingFinished()), this, SLOT(updateTextRotationAngle()));
+	} else {
+		disconnect(qle_x,     SIGNAL(editingFinished()), this, SLOT(updateTextX()));
+		disconnect(qle_y,     SIGNAL(editingFinished()), this, SLOT(updateTextY()));
+		disconnect(qle_text,  SIGNAL(editingFinished()), this, SLOT(updateTextT()));
+		disconnect(font_size, SIGNAL(editingFinished()), this, SLOT(updateTextS()));
+		disconnect(rotation_angle_, SIGNAL(editingFinished()), this, SLOT(updateTextRotationAngle()));
+	}
+}

Added: trunk/sources/editor/texteditor.h
===================================================================
--- trunk/sources/editor/texteditor.h	                        (rev 0)
+++ trunk/sources/editor/texteditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,64 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TEXT_EDITOR_H
+#define TEXT_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartText;
+class QTextOrientationSpinBoxWidget;
+/**
+	This class provides a widget to edit static texts within the element
+	editor.
+*/
+class TextEditor : public ElementItemEditor {
+	Q_OBJECT
+	// Constructors, destructor
+	public:
+	TextEditor(QETElementEditor *, PartText * = 0, QWidget * = 0);
+	virtual ~TextEditor();
+	private:
+	TextEditor(const TextEditor &);
+	
+	// attributes
+	private:
+	PartText *part;
+	QLineEdit *qle_x, *qle_y, *qle_text;
+	QSpinBox *font_size;
+	QButtonGroup *color_;
+	QRadioButton *black_color_, *white_color_;
+	QTextOrientationSpinBoxWidget *rotation_angle_;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateText();
+	void updateTextX();
+	void updateTextY();
+	void updateTextT();
+	void updateTextS();
+	void updateTextC();
+	void updateTextRotationAngle();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/editor/textfieldeditor.cpp
===================================================================
--- trunk/sources/editor/textfieldeditor.cpp	                        (rev 0)
+++ trunk/sources/editor/textfieldeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,172 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "textfieldeditor.h"
+#include "parttextfield.h"
+#include "qtextorientationspinboxwidget.h"
+#include "qetapp.h"
+/**
+	Constructeur
+	@param editor L'editeur d'element concerne
+	@param textfield Le champ de texte a editer
+	@param parent QWidget parent
+*/
+TextFieldEditor::TextFieldEditor(QETElementEditor *editor, PartTextField *textfield, QWidget *parent) :
+	ElementItemEditor(editor, parent),
+	part(textfield)
+{
+	qle_x     = new QLineEdit();
+	qle_y     = new QLineEdit();
+	qle_text  = new QLineEdit();
+	font_size = new QSpinBox();
+	font_size -> setRange(0, 144);
+	rotate    = new QCheckBox(tr("Ne pas subir les rotations de l'\351l\351ment parent"));
+	rotate -> setChecked(true);
+	QLabel *rotation_angle_label = new QLabel(tr("Angle de rotation par d\351faut : "));
+	rotation_angle_label -> setWordWrap(true);
+	rotation_angle_ = QETApp::createTextOrientationSpinBoxWidget();
+	
+	qle_x -> setValidator(new QDoubleValidator(qle_x));
+	qle_y -> setValidator(new QDoubleValidator(qle_y));
+	
+	QVBoxLayout *main_layout = new QVBoxLayout();
+	main_layout -> addWidget(new QLabel(tr("Position : ")));
+	
+	QHBoxLayout *position = new QHBoxLayout();
+	position -> addWidget(new QLabel(tr("x : ")));
+	position -> addWidget(qle_x                 );
+	position -> addWidget(new QLabel(tr("y : ")));
+	position -> addWidget(qle_y                 );
+	main_layout -> addLayout(position);
+	
+	QHBoxLayout *fs = new QHBoxLayout();
+	fs -> addWidget(new QLabel(tr("Taille : ")));
+	fs -> addWidget(font_size);
+	main_layout -> addLayout(fs);
+	
+	QHBoxLayout *t = new QHBoxLayout();
+	t -> addWidget(new QLabel(tr("Texte par d\351faut : ")));
+	t -> addWidget(qle_text);
+	main_layout -> addLayout(t);
+	
+	QHBoxLayout *rotation_angle_layout = new QHBoxLayout();
+	rotation_angle_layout -> addWidget(rotation_angle_label);
+	rotation_angle_layout -> addWidget(rotation_angle_);
+	main_layout -> addLayout(rotation_angle_layout);
+	
+	QHBoxLayout *r = new QHBoxLayout();
+	r -> addWidget(rotate);
+	main_layout -> addLayout(r);
+	
+	main_layout -> addStretch();
+	setLayout(main_layout);
+	updateForm();
+}
+
+/// Destructeur
+TextFieldEditor::~TextFieldEditor() {
+}
+
+/**
+	Permet de specifier a cet editeur quelle primitive il doit editer. A noter
+	qu'un editeur peut accepter ou refuser d'editer une primitive.
+	L'editeur de texte dynamique acceptera d'editer la primitive new_part s'il
+	s'agit d'un objet de la classe PartTextField.
+	@param new_part Nouvelle primitive a editer
+	@return true si l'editeur a accepter d'editer la primitive, false sinon
+*/
+bool TextFieldEditor::setPart(CustomElementPart *new_part) {
+	if (!new_part) {
+		part = 0;
+		return(true);
+	}
+	if (PartTextField *part_textfield = dynamic_cast<PartTextField *>(new_part)) {
+		part = part_textfield;
+		updateForm();
+		return(true);
+	} else {
+		return(false);
+	}
+}
+
+/**
+	@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
+*/
+CustomElementPart *TextFieldEditor::currentPart() const {
+	return(part);
+}
+
+/**
+	Met a jour le champ de texte a partir des donnees du formulaire
+*/
+void TextFieldEditor::updateTextField() {
+	if (!part) return;
+	part -> setProperty("size", font_size -> value());
+	part -> setPlainText(qle_text -> text());
+	part -> setPos(qle_x -> text().toDouble(), qle_y -> text().toDouble());
+	part -> setFollowParentRotations(!rotate -> isChecked());
+}
+
+/// Met a jour l'abscisse de la position du champ de texte et cree un objet d'annulation
+void TextFieldEditor::updateTextFieldX() { addChangePartCommand(tr("abscisse"),        part, "x",      qle_x -> text().toDouble()); updateForm(); }
+/// Met a jour l'ordonnee de la position du champ de texte et cree un objet d'annulation
+void TextFieldEditor::updateTextFieldY() { addChangePartCommand(tr("ordonn\351e"),     part, "y",      qle_y -> text().toDouble()); updateForm(); }
+/// Met a jour le texte du champ de texte et cree un objet d'annulation
+void TextFieldEditor::updateTextFieldT() { addChangePartCommand(tr("contenu"),         part, "text",   qle_text -> text());         }
+/// Met a jour la taille du champ de texte et cree un objet d'annulation
+void TextFieldEditor::updateTextFieldS() { addChangePartCommand(tr("taille"),          part, "size",   font_size -> value());       }
+/// Met a jour la taille du champ de texte et cree un objet d'annulation
+void TextFieldEditor::updateTextFieldR() { addChangePartCommand(tr("propri\351t\351"), part, "rotate", !rotate -> isChecked());     }
+/// Met a jour l'angle de rotation du champ de texte et cree un objet d'annulation
+void TextFieldEditor::updateTextFieldRotationAngle() { addChangePartCommand(tr("angle de rotation"), part, "rotation angle", rotation_angle_ -> value()); }
+
+/**
+	Met a jour le formulaire d'edition
+*/
+void TextFieldEditor::updateForm() {
+	if (!part) return;
+	activeConnections(false);
+	qle_x     -> setText(part -> property("x").toString());
+	qle_y     -> setText(part -> property("y").toString());
+	qle_text  -> setText(part -> property("text").toString());
+	font_size -> setValue(part -> property("size").toInt());
+	rotate  -> setChecked(!part -> property("rotate").toBool());
+	rotation_angle_ -> setValue(part -> property("rotation angle").toDouble());
+	activeConnections(true);
+}
+
+/**
+	Active ou desactive les connexionx signaux/slots entre les widgets internes.
+	@param active true pour activer les connexions, false pour les desactiver
+*/
+void TextFieldEditor::activeConnections(bool active) {
+	if (active) {
+		connect(qle_x,     SIGNAL(editingFinished()), this, SLOT(updateTextFieldX()));
+		connect(qle_y,     SIGNAL(editingFinished()), this, SLOT(updateTextFieldY()));
+		connect(qle_text,  SIGNAL(editingFinished()), this, SLOT(updateTextFieldT()));
+		connect(font_size, SIGNAL(editingFinished()), this, SLOT(updateTextFieldS()));
+		connect(rotate,    SIGNAL(stateChanged(int)), this, SLOT(updateTextFieldR()));
+		connect(rotation_angle_, SIGNAL(editingFinished()), this, SLOT(updateTextFieldRotationAngle()));
+	} else {
+		disconnect(qle_x,     SIGNAL(editingFinished()), this, SLOT(updateTextFieldX()));
+		disconnect(qle_y,     SIGNAL(editingFinished()), this, SLOT(updateTextFieldY()));
+		disconnect(qle_text,  SIGNAL(editingFinished()), this, SLOT(updateTextFieldT()));
+		disconnect(font_size, SIGNAL(editingFinished()), this, SLOT(updateTextFieldS()));
+		disconnect(rotate,    SIGNAL(stateChanged(int)), this, SLOT(updateTextFieldR()));
+		disconnect(rotation_angle_, SIGNAL(editingFinished()), this, SLOT(updateTextFieldRotationAngle()));
+	}
+}

Added: trunk/sources/editor/textfieldeditor.h
===================================================================
--- trunk/sources/editor/textfieldeditor.h	                        (rev 0)
+++ trunk/sources/editor/textfieldeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,62 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TEXTFIELD_EDITOR_H
+#define TEXTFIELD_EDITOR_H
+#include <QtGui>
+#include "elementitemeditor.h"
+class PartTextField;
+class QTextOrientationSpinBoxWidget;
+/**
+	This class provides a widget to edit text fields within the element editor.
+*/
+class TextFieldEditor : public ElementItemEditor {
+	Q_OBJECT
+	// Constructors, destructor
+	public:
+	TextFieldEditor(QETElementEditor *, PartTextField * = 0, QWidget * = 0);
+	virtual ~TextFieldEditor();
+	private:
+	TextFieldEditor(const TextFieldEditor &);
+	
+	// attributes
+	private:
+	PartTextField *part;
+	QLineEdit *qle_x, *qle_y, *qle_text;
+	QSpinBox *font_size;
+	QCheckBox *rotate;
+	QTextOrientationSpinBoxWidget *rotation_angle_;
+	
+	// methods
+	public:
+	virtual bool setPart(CustomElementPart *);
+	virtual CustomElementPart *currentPart() const;
+	
+	public slots:
+	void updateTextField();
+	void updateTextFieldX();
+	void updateTextFieldY();
+	void updateTextFieldT();
+	void updateTextFieldS();
+	void updateTextFieldR();
+	void updateTextFieldRotationAngle();
+	void updateForm();
+	
+	private:
+	void activeConnections(bool);
+};
+#endif

Added: trunk/sources/elementdefinition.cpp
===================================================================
--- trunk/sources/elementdefinition.cpp	                        (rev 0)
+++ trunk/sources/elementdefinition.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,476 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementdefinition.h"
+#include "elementscollection.h"
+#include "moveelementshandler.h"
+#include "moveelementsdescription.h"
+
+/**
+	@return true si l'element est rattache a une collection d'elements
+	Un element appartenant a une collection a forcement un chemin virtuel.
+*/
+bool ElementDefinition::hasParentCategory() {
+	return(parent_category_);
+}
+
+/**
+	@return la categorie a laquelle appartient cet element
+*/
+ElementsCategory *ElementDefinition::parentCategory() {
+	return(parent_category_);
+}
+
+/**
+	@return la liste des categories parentes de cet item.
+*/
+QList<ElementsCategory *> ElementDefinition::parentCategories() {
+	QList<ElementsCategory *> cat_list;
+	if (ElementsCategory *par_cat = parentCategory()) {
+		cat_list << par_cat -> parentCategories() << par_cat;
+	}
+	return(cat_list);
+}
+
+/**
+	@return true si l'element est rattache a une collection d'elements
+	Un element appartenant a une collection a forcement un chemin virtuel.
+*/
+bool ElementDefinition::hasParentCollection() {
+	return(parent_collection_);
+}
+
+/**
+	@param other_item Autre item
+	@return true si other_item est parent (direct ou indirect) de cet item, false sinon
+*/
+bool ElementDefinition::isChildOf(ElementsCollectionItem *other_item) {
+	// soit l'autre item est le parent direct de cet element
+	if (ElementsCategory *other_item_cat = other_item -> toCategory()) {
+		if (other_item_cat == parentCategory()) {
+			return(true);
+		}
+	}
+	
+	// soit il est un parent indirect, auquel cas, on peut demander a la categorie parente de repondre a la question
+	if (ElementsCategory *parent_cat = parentCategory()) {
+		return(parent_cat -> isChildOf(other_item));
+	}
+	
+	// arrive ici, l'autre item n'est pas parent de cet item
+	return(false);
+}
+
+/**
+	@return la collection d'element a laquelle appartient cet element
+*/
+ElementsCollection *ElementDefinition::parentCollection() {
+	return(parent_collection_);
+}
+
+/**
+	@return le projet auquel appartient cette categorie, si celle-ci
+	appartient a une collection.
+*/
+QETProject *ElementDefinition::project() {
+	if (hasParentCollection()) {
+		return(parentCollection() -> project());
+	}
+	return(0);
+}
+
+/**
+	Ne fait rien ; le projet doit etre defini au niveau d'une collection
+*/
+void ElementDefinition::setProject(QETProject *) {
+}
+
+/**
+	@return le protocole utilise par la collection a laquelle appartient cet element
+*/
+QString ElementDefinition::protocol() {
+	// il n'est pas possible d'avoir un protocole sans appartenir a une collection
+	if (!hasParentCollection()) return(QString());
+	
+	return(parentCollection() -> protocol());
+}
+
+/**
+	Ne fait rien
+*/
+void ElementDefinition::setProtocol(const QString &) {
+}
+
+/**
+	@return le chemin virtuel complet de cet element (protocole + chemin)
+*/
+QString ElementDefinition::fullVirtualPath() {
+	// il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection
+	if (!hasParentCollection()) return(QString());
+	
+	return(protocol() + "://" + virtualPath());
+}
+
+/**
+	@return l'emplacement de l'element
+*/
+ElementsLocation ElementDefinition::location() {
+	return(ElementsLocation(fullVirtualPath(), project()));
+}
+
+/**
+	@return une liste vide - un element ne possede pas de categorie
+*/
+QList<ElementsCategory *> ElementDefinition::categories() {
+	return(QList<ElementsCategory *>());
+}
+
+/**
+	@return toujours 0 - un element ne possede pas de categorie
+*/
+ElementsCategory *ElementDefinition::category(const QString &) {
+	return(0);
+}
+
+/**
+	@return toujours 0 - un element ne possede pas de categorie
+*/
+ElementsCategory *ElementDefinition::createCategory(const QString &) {
+	return(0);
+}
+
+/**
+	@return une liste contenant seulement cet element
+*/
+QList<ElementDefinition *> ElementDefinition::elements() {
+	return(QList<ElementDefinition *>() << this);
+}
+
+/**
+	@return cet element si path est vide, 0 sinon
+*/
+ElementDefinition *ElementDefinition::element(const QString &path) {
+	if (path.isEmpty()) return(this);
+	return(0);
+}
+
+/**
+	@return toujours 0 - un element n'en cree pas d'autre
+*/
+ElementDefinition *ElementDefinition::createElement(const QString &) {
+	return(0);
+}
+
+/**
+	@return always true - an element contains nothing but itself
+*/
+bool ElementDefinition::isEmpty() {
+	return(false);
+}
+
+/**
+	@return always 1
+*/
+int ElementDefinition::count() {
+	return(1);
+}
+
+/**
+	@return toujours 0 - un element n'est pas une collection
+*/
+ElementsCollection *ElementDefinition::toCollection() {
+	return(0);
+}
+
+/**
+	@return la categorie parente de cet element
+*/
+ElementsCategory *ElementDefinition::toCategory() {
+	return(parentCategory());
+}
+
+/**
+	@return toujours 0 - un element n'est pas une categorie
+*/
+ElementsCategory *ElementDefinition::toPureCategory() {
+	return(0);
+}
+
+/**
+	@return un pointeur ElementDefinition * sur cet element
+*/
+ElementDefinition *ElementDefinition::toElement() {
+	return(this);
+}
+
+/**
+	@return true si cette definition d'element est egale (en termes de contenu)
+	a la definition d'element other, false sinon.
+*/
+bool ElementDefinition::equals(ElementDefinition &other) {
+	/*
+		Pour le moment, cette methode compare simplement l'export au format
+		texte des documents XML. Cela peut entrainer de faux positifs.
+		Exemple : un espace de plus ou de moins dans le XML n'en change pas
+		forcement la semantique. Mais cela changera l'export au format texte.
+	*/
+	QDomDocument this_xml_document;
+	this_xml_document.appendChild(this_xml_document.importNode(xml(), true));
+	QString this_text = this_xml_document.toString(0);
+	
+	QDomDocument other_xml_document;
+	other_xml_document.appendChild(other_xml_document.importNode(other.xml(), true));
+	QString other_text = other_xml_document.toString(0);
+	
+	return(other_text == this_text);
+}
+
+/**
+	@param target_category Categorie cible pour la copie ; elle doit exister
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer la copie
+	@param deep_copy Argument ignore - une copie "recursive" n'a pas de sens pour un element
+	@return La copie de l'element ou 0 si le processus a echoue
+*/
+ElementsCollectionItem *ElementDefinition::copy(ElementsCategory *target_category, MoveElementsHandler *handler, bool deep_copy) {
+	Q_UNUSED(deep_copy);
+	if (!target_category) return(0);
+	
+	// echec si le path name de cet element est vide
+	QString elmt_name(pathName());
+	if (elmt_name.isEmpty()) return(0);
+	
+	// cree une description du mouvement a effectuer
+	MoveElementsDescription mvt_desc;
+	mvt_desc.setDestinationParentCategory(target_category);
+	// on tente une copie avec le meme nom interne
+	mvt_desc.setOriginalDestinationInternalName(pathName());
+	mvt_desc.setFinalDestinationInternalName(pathName());
+	mvt_desc.setHandler(handler);
+	
+	copy(&mvt_desc);
+	return(mvt_desc.createdItem());
+}
+
+/**
+	Methode privee effectuant une copie de cet element a partir d'une
+	description du mouvement.
+	@param mvt_desc Description du mouvement
+*/
+void ElementDefinition::copy(MoveElementsDescription *mvt_desc) {
+	// quelques pointeurs pour simplifier l'ecriture de la methode
+	MoveElementsHandler *handler = mvt_desc -> handler();
+	ElementsCategory *target_category = mvt_desc -> destinationParentCategory();
+	
+	ElementDefinition *element_copy = 0;
+	
+	// verifie que la categorie parente cible est accessible en lecture
+	if (!target_category -> isReadable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				QET::Action todo = handler -> categoryIsNotReadable(target_category);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!target_category -> isReadable());
+		}
+	}
+	
+	// verifie que la categorie parente cible est accessible en ecriture
+	if (!target_category -> isWritable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				QET::Action todo = handler -> categoryIsNotWritable(target_category);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!target_category -> isWritable());
+		}
+	}
+	
+	// verifie que la cible n'existe pas deja
+	if ((element_copy = target_category -> element(mvt_desc -> finalDestinationInternalName()))) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				// la cible existe deja : on demande au Handler ce qu'on doit faire
+				QET::Action todo = handler -> elementAlreadyExists(this, element_copy);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Erase) {
+					break;
+				} else if (todo == QET::Rename) {
+					mvt_desc -> setFinalDestinationInternalName(handler -> nameForRenamingOperation());
+				}
+			} while ((element_copy = target_category -> element(mvt_desc -> finalDestinationInternalName())));
+		}
+	}
+	
+	/*
+		A ce stade, on peut creer l'element cible : soit il n'existe pas, soit
+		on a l'autorisation de l'ecraser
+	*/
+	
+	// si la cible existe deja, verifie qu'elle est accessible en ecriture
+	element_copy = target_category -> element(mvt_desc -> finalDestinationInternalName());
+	if (element_copy && !element_copy -> isWritable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				// la cible n'est pas accessible en ecriture : on demande au Handler ce qu'on doit faire
+				QET::Action todo = handler -> elementIsNotWritable(element_copy);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!element_copy -> isWritable());
+		}
+	}
+	
+	// cree l'element cible
+	element_copy = target_category -> createElement(mvt_desc -> finalDestinationInternalName());
+	if (!element_copy) {
+		if (handler) {
+			handler -> errorWithAnElement(this, tr("L'\351l\351ment cible n'a pu \352tre cr\351\351."));
+		}
+		return;
+	}
+	
+	// recopie la definition de l'element
+	element_copy -> setXml(xml());
+	element_copy -> write();
+	mvt_desc -> setCreatedItem(element_copy);
+}
+
+/**
+	@param target_category Categorie cible pour le deplacement ; elle doit exister
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer le deplacement
+	@return L'element apres deplacement ou 0 si le processus a echoue
+	
+*/
+ElementsCollectionItem *ElementDefinition::move(ElementsCategory *target_category, MoveElementsHandler *handler) {
+	if (!target_category) return(0);
+	
+	// echec si le path name de cet element est vide
+	QString elmt_name(pathName());
+	if (elmt_name.isEmpty()) return(0);
+	
+	// cree une description du mouvement a effectuer
+	MoveElementsDescription mvt_desc;
+	mvt_desc.setDestinationParentCategory(target_category);
+	// on tente un deplacement avec le meme nom interne
+	mvt_desc.setOriginalDestinationInternalName(pathName());
+	mvt_desc.setFinalDestinationInternalName(pathName());
+	mvt_desc.setHandler(handler);
+	
+	move(&mvt_desc);
+	return(mvt_desc.createdItem());
+}
+
+/**
+	Methode privee effectuant un delacement de cet element a partir d'une
+	description du mouvement.
+	Pour etre plus exact, cette methode effectue d'abord une copie de l'element,
+	puis, si celle-ci a reussi, il supprime l'element d'origine.
+	@param mvt_desc Description du mouvement
+*/
+void ElementDefinition::move(MoveElementsDescription *mvt_desc) {
+	// effectue une copie de l'element
+	copy(mvt_desc);
+	ElementsCollectionItem *item_copy = mvt_desc -> createdItem();
+	if (!item_copy) return;
+	ElementDefinition *element_copy = item_copy -> toElement();
+	if (!element_copy) return;
+	
+	// supprime cet element
+	MoveElementsHandler *handler = mvt_desc -> handler();
+	
+	// cet element doit etre accessible en ecriture pour etre supprime
+	if (!isWritable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				// on demande au Handler ce qu'on doit faire
+				QET::Action todo = handler -> elementIsNotWritable(this);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!isWritable());
+		}
+	}
+	
+	// supprime cet element (sinon il ne s'agirait que d'une copie, pas d'un deplacement)
+	bool element_deletion = remove();
+	mvt_desc -> setSourceItemDeleted(element_deletion);
+	if (!element_deletion && handler) {
+		handler -> errorWithAnElement(this, tr("La suppression de cet \351l\351ment a \351chou\351."));
+	}
+}
+
+/**
+	Cette methode n'a aucun effet
+	@return toujours true
+*/
+bool ElementDefinition::removeContent() {
+	return(true);
+}

Added: trunk/sources/elementdefinition.h
===================================================================
--- trunk/sources/elementdefinition.h	                        (rev 0)
+++ trunk/sources/elementdefinition.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,128 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_DEFINITION_H
+#define ELEMENT_DEFINITION_H
+#include <QtXml>
+#include "elementscategory.h"
+class ElementsCollection;
+class MoveElementsHandler;
+/**
+	This abstract class represents a way to access the XML definition of an
+	element, be it from a .elmt file or from a QET project file.
+*/
+class ElementDefinition : public ElementsCollectionItem {
+	Q_OBJECT
+	
+	public:
+	/**
+		Constructor
+	*/
+	ElementDefinition(ElementsCategory *category = 0, ElementsCollection *collection = 0) : ElementsCollectionItem(category), parent_category_(category), parent_collection_(collection) {};
+	
+	/**
+		Destructor
+	*/
+	virtual ~ElementDefinition() {};
+	
+	/**
+		@return the XML definition of a particular element
+	*/
+	virtual QDomElement xml() = 0;
+	
+	/**
+		Specify the XML definition of a particular element
+		@param xml_element New XML definition
+		@return true if everything went well, false otherwise
+	*/
+	virtual bool setXml(const QDomElement &xml_element) = 0;
+	
+	/**
+		@return true if the definition is not available
+	*/
+	virtual bool isNull() const = 0;
+	
+	virtual ElementsCategory *parentCategory();
+	virtual QList<ElementsCategory *> parentCategories();
+	virtual bool hasParentCategory();
+	
+	/**
+		@return whether the element is attached to an elements collection
+		An elemet which belongs to a collection always has a virtual path.
+	*/
+	virtual bool hasParentCollection();
+	virtual bool isChildOf(ElementsCollectionItem *);
+	
+	/**
+		@return the elements collections this element belongs to
+	*/
+	virtual ElementsCollection *parentCollection();
+	
+	virtual QETProject *project();
+	virtual void setProject(QETProject *);
+	/**
+		@return the "protocol" used by the parent collection
+	*/
+	virtual QString protocol();
+	/**
+		Has no effect.
+	*/
+	virtual void setProtocol(const QString &);
+	
+	/**
+		@return the full virtual path for this element (i.e. "protocol" + path)
+	*/
+	virtual QString fullVirtualPath();
+	
+	/**
+		@return the location of this element, as an ElementsLocation object.
+		@see ElementsLocation
+	*/
+	virtual ElementsLocation location();
+	
+	virtual QList<ElementsCategory *> categories();
+	virtual ElementsCategory *category(const QString &);
+	virtual ElementsCategory *createCategory(const QString &);
+	
+	virtual QList<ElementDefinition *> elements();
+	virtual ElementDefinition *element(const QString &);
+	virtual ElementDefinition *createElement(const QString &);
+	virtual bool isEmpty();
+	virtual int count();
+	virtual ElementsCollectionItem *copy(ElementsCategory *, MoveElementsHandler *, bool = true);
+	virtual ElementsCollectionItem *move(ElementsCategory *, MoveElementsHandler *);
+	
+	virtual bool isCollection()   const { return(false); } ///< @return always false
+	virtual bool isRootCategory() const { return(false); } ///< @return always false
+	virtual bool isCategory()     const { return(false); } ///< @return always false
+	virtual bool isElement()      const { return(true ); } ///< @return always  true
+	virtual ElementsCollection *toCollection();
+	virtual ElementsCategory *toCategory();
+	virtual ElementsCategory *toPureCategory();
+	virtual ElementDefinition *toElement();
+	virtual bool equals(ElementDefinition &);
+	virtual bool removeContent();
+	virtual QDateTime modificationTime() const = 0;
+	void copy(MoveElementsDescription *);
+	void move(MoveElementsDescription *);
+	
+	// attributes
+	private:
+	ElementsCategory *parent_category_;
+	ElementsCollection *parent_collection_;
+};
+#endif

Added: trunk/sources/elementdeleter.cpp
===================================================================
--- trunk/sources/elementdeleter.cpp	                        (rev 0)
+++ trunk/sources/elementdeleter.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,76 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementdeleter.h"
+#include "qetapp.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur
+	@param elmt_path Chemin virtuel du fichier representant l'element a supprimer
+	@param parent QWidget parent
+*/
+ElementDeleter::ElementDeleter(const ElementsLocation &elmt_path, QWidget *parent) :
+	QWidget(parent),
+	element(0)
+{
+	// recupere l'element a supprimer
+	ElementsCollectionItem *element_item = QETApp::collectionItem(elmt_path);
+	if (!element_item) return;
+	
+	// on exige un element
+	if (!element_item -> isElement()) return;
+	
+	element = element_item;
+}
+
+/// Destructeur
+ElementDeleter::~ElementDeleter() {
+}
+
+/**
+	Supprime l'element : verifie l'existence du fichier, demande confirmation a
+	l'utilisateur et avertit ce dernier si la suppression a echoue.
+*/
+bool ElementDeleter::exec() {
+	// verifie l'existence de l'element
+	if (!element || !element -> isElement()) return(false);
+	
+	// confirmation #1
+	QMessageBox::StandardButton answer_1 = QET::MessageBox::question(
+		this,
+		tr("Supprimer l'\351l\351ment ?", "message box title"),
+		tr("\312tes-vous s\373r de vouloir supprimer cet \351l\351ment ?\n", "message box content"),
+		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
+	);
+	if (answer_1 != QMessageBox::Yes) return(false);
+	
+	/**
+		@todo Regression : rafficher le chemin de l'element
+	*/
+	
+	// supprime l'element
+	if (!element -> remove()) {
+		QET::MessageBox::warning(
+			this,
+			tr("Suppression de l'\351l\351ment", "message box title"),
+			tr("La suppression de l'\351l\351ment a \351chou\351.", "message box content")
+		);
+		return(false);
+	}
+	return(true);
+}

Added: trunk/sources/elementdeleter.h
===================================================================
--- trunk/sources/elementdeleter.h	                        (rev 0)
+++ trunk/sources/elementdeleter.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,44 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_DELETER_H
+#define ELEMENT_DELETER_H
+#include "elementscategory.h"
+#include "elementslocation.h"
+#include <QtGui>
+/**
+	This class provides an abstract way to delete an element from its parent
+	collection. Especially, it requires a confirmation from users.
+*/
+class ElementDeleter : public QWidget {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ElementDeleter(const ElementsLocation &, QWidget * = 0);
+	virtual ~ElementDeleter();
+	private:
+	ElementDeleter(const ElementsCategory &);
+	
+	// methods
+	public slots:
+	bool exec();
+	
+	// attributes
+	private:
+	ElementsCollectionItem *element;
+};
+#endif

Added: trunk/sources/elementdialog.cpp
===================================================================
--- trunk/sources/elementdialog.cpp	                        (rev 0)
+++ trunk/sources/elementdialog.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,377 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementdialog.h"
+#include <QtGui>
+#include "qetapp.h"
+#include "elementscategorieslist.h"
+#include "elementscollectionitem.h"
+#include "qfilenameedit.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur par defaut.
+	Construit un dialogue permettant d'ouvrir un element
+	@param mode Mode du dialogue
+	@see ElementDialog::Mode
+	@param parentWidget QWidget parent
+	@param parent QObject parent
+	
+*/
+ElementDialog::ElementDialog(uint mode, QWidget *parentWidget, QObject *parent) :
+	QObject(parent),
+	mode_(mode),
+	buttons_(0),
+	list_(0),
+	textfield_(0)
+{
+	dialog_  = new QDialog(parentWidget);
+	dialog_ -> setWindowModality(Qt::WindowModal);
+#ifdef Q_WS_MAC
+	dialog_ -> setWindowFlags(Qt::Sheet);
+#endif
+	buttons_ = new QDialogButtonBox();
+	
+	// types selectionnables dans la liste
+	bool display_elements = (mode_ == OpenElement || mode_ == SaveElement);
+	int selectables = 0;
+	switch(mode_) {
+		case OpenElement:  selectables = QET::Element; break;
+		case SaveElement:  selectables = QET::ElementsCollectionItem; break;
+		case OpenCategory: selectables = QET::ElementsContainer; break;
+		case SaveCategory: selectables = QET::ElementsContainer; break;
+	}
+	list_    = new ElementsCategoriesList(display_elements, selectables);
+	connect(list_, SIGNAL(locationChanged(const ElementsLocation &)), this, SLOT(locationChanged(const ElementsLocation &)));
+	
+	// titre et label
+	if (!mode) {
+		title_ = tr("Ouvrir un \351l\351ment", "dialog title");
+		label_ = tr("Choisissez l'\351l\351ment que vous souhaitez ouvrir.", "dialog content");
+	} else if (mode == 1) {
+		title_ = tr("Enregistrer un \351l\351ment", "dialog title");
+		label_ = tr("Choisissez l'\351l\351ment dans lequel vous souhaitez enregistrer votre d\351finition.", "dialog content");
+	} else if (mode == 2) {
+		title_ = tr("Ouvrir une cat\351gorie", "dialog title");
+		label_ = tr("Choisissez une cat\351gorie.", "dialog content");
+	} else {
+		title_ = tr("Enregistrer une cat\351gorie", "dialog title");
+		label_ = tr("Choisissez une cat\351gorie.", "dialog content");
+	}
+	
+	// mode ouverture / enregistrement
+	if (mode_ == SaveCategory || mode_ == SaveElement) {
+		buttons_ -> setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
+		textfield_ = new QFileNameEdit();
+		connect(textfield_, SIGNAL(textChanged(const QString &)), this, SLOT(textFieldChanged(const QString &)));
+	} else {
+		buttons_ -> setStandardButtons(QDialogButtonBox::Open | QDialogButtonBox::Cancel);
+	}
+	
+	// connexions boutons -> dialogue
+	connect(buttons_, SIGNAL(accepted()), this,    SLOT(checkDialog()));
+	connect(buttons_, SIGNAL(rejected()), dialog_, SLOT(reject()));
+	
+	// connexions dialogue -> classe
+	connect(dialog_,  SIGNAL(accepted()), this, SLOT(dialogAccepted()));
+	connect(dialog_,  SIGNAL(rejected()), this, SLOT(dialogRejected()));
+	
+	makeInterface();
+}
+
+/**
+	Destructeur
+*/
+ElementDialog::~ElementDialog() {
+	dialog_ -> setParent(0);
+	delete dialog_;
+}
+
+/**
+	Affiche un dialogue permettant a l'utilisateur de selectionner une categorie existant deja
+	@param parentWidget QWidget parent
+	@return le chemin virtuel de cette categorie
+*/
+ElementsLocation ElementDialog::getExistingCategoryLocation(QWidget *parentWidget) {
+	return(ElementDialog::execConfiguredDialog(ElementDialog::OpenCategory, parentWidget));
+}
+
+/**
+	Affiche un dialogue permettant a l'utilisateur de selectionner une nouvelle categorie
+	@param parentWidget QWidget parent
+	@return le chemin virtuel de cette categorie
+*/
+ElementsLocation ElementDialog::getNewCategoryLocation(QWidget *parentWidget) {
+	return(ElementDialog::execConfiguredDialog(ElementDialog::SaveCategory, parentWidget));
+}
+
+/**
+	Affiche un dialogue permettant a l'utilisateur de selectionner un element a ouvrir
+	@param parentWidget QWidget parent
+	@return le chemin virtuel de cet element
+*/
+ElementsLocation ElementDialog::getOpenElementLocation(QWidget *parentWidget) {
+	return(ElementDialog::execConfiguredDialog(ElementDialog::OpenElement, parentWidget));
+}
+
+/**
+	Affiche un dialogue permettant a l'utilisateur de selectionner un element (existant ou non)
+	qu'il souhaite enregistrer
+	@param parentWidget QWidget parent
+	@return le chemin virtuel de cet element
+*/
+ElementsLocation ElementDialog::getSaveElementLocation(QWidget *parentWidget) {
+	return(ElementDialog::execConfiguredDialog(ElementDialog::SaveElement, parentWidget));
+}
+
+/**
+	Lance un dialogue selon la configuration mode
+	@param mode Mode du dialogue
+	@param parentWidget QWidget parent
+*/
+ElementsLocation ElementDialog::execConfiguredDialog(int mode, QWidget *parentWidget) {
+	ElementDialog element_dialog(mode, parentWidget);
+	element_dialog.exec();
+	return(element_dialog.location());
+}
+
+/**
+	Assemble les widgets pour obtenir le dialogue final
+*/
+void ElementDialog::makeInterface() {
+	dialog_ -> setWindowTitle(title_);
+	
+	// disposition verticale
+	QVBoxLayout *layout = new QVBoxLayout(dialog_);
+	layout -> addWidget(new QLabel(label_));
+	layout -> addWidget(list_);
+	if (textfield_) {
+		QHBoxLayout *filename_layout = new QHBoxLayout();
+		filename_layout -> addWidget(new QLabel(tr("Nom :")));
+		filename_layout -> addWidget(textfield_);
+		layout -> addLayout(filename_layout);
+	}
+	layout -> addWidget(buttons_);
+}
+
+/**
+	Execute le dialogue
+	@return QDialog::Accepted si le dialogue a ete accepte, false sinon
+	@see DialogCode
+*/
+int ElementDialog::exec() {
+	return(dialog_ -> exec());
+}
+
+/**
+	@return le chemin virtuel choisi via le dialogue
+	Si l'utilisateur n'a pas pu faire son choix, une chaine vide est retournee.
+*/
+ElementsLocation ElementDialog::location() const {
+	return(location_);
+}
+
+/**
+	gere le changement de chemin virtuel par l'utilisateur
+	@param new_loc le nouveau chemin virtuel choisi par l'utilisateur
+*/
+void ElementDialog::locationChanged(const ElementsLocation &new_loc) {
+	ElementsCollectionItem *item = QETApp::collectionItem(new_loc);
+	if (!item) return;
+	if (mode_ == OpenElement) {
+		buttons_ -> button(QDialogButtonBox::Open) -> setEnabled(item -> isElement());
+	} else if (mode_ == SaveElement) {
+		// si l'utilisateur choisit un element existant, on desactive le champ
+		textfield_ -> setEnabled(!item -> isElement());
+		// il faut soit un element selectionne soit une categorie et un nom
+		buttons_ -> button(QDialogButtonBox::Save) -> setEnabled(
+			((item -> isCategory() || item -> isCollection()) && !textfield_ -> text().isEmpty()) ||\
+			item -> isElement()
+		);
+	} else if (mode_ == OpenCategory) {
+		/// @todo
+	} else if (mode_ == SaveCategory) {
+		/// @todo
+	}
+	location_ = new_loc;
+}
+
+/**
+	Gere le changement de contenu dans le champ de texte
+	@param text Contenu du champ de texte
+*/
+void ElementDialog::textFieldChanged(const QString &text) {
+	ElementsCollectionItem *item = QETApp::collectionItem(list_ -> selectedLocation());
+	if (!item) return;
+	if (mode_ == SaveElement) {
+		// il faut soit un element selectionne soit une categorie et un nom
+		buttons_ -> button(QDialogButtonBox::Save) -> setEnabled(
+			((item -> isCategory() || item -> isCollection()) && !text.isEmpty()) ||\
+			item -> isElement()
+		);
+	} else if (mode_ == SaveCategory) {
+		// il faut forcement un nom pour la nouvelle categorie
+		buttons_ -> button(QDialogButtonBox::Save) -> setEnabled(!text.isEmpty());
+	}
+}
+
+/**
+	Verifie l'etat du dialogue au moment ou l'utilisateur le valide.
+*/
+void ElementDialog::checkDialog() {
+	// verifie si ce qui a ete selectionne par l'utilisateur correspond au mode du widget
+	if (mode_ == OpenElement) {
+		// l'utilisateur doit avoir choisi un element existant
+		
+		// on verifie d'abord que l'utilisateur a choisi quelque chose
+		ElementsLocation location = list_ -> selectedLocation();
+		if (location.isNull()) {
+			QET::MessageBox::critical(
+				dialog_,
+				tr("Pas de s\351lection", "message box title"),
+				tr("Vous devez s\351lectionner un \351l\351ment.", "message box content")
+			);
+			return;
+		}
+		
+		// on verifie donc que la selection existe
+		ElementsCollectionItem *item = QETApp::collectionItem(location);
+		if (!item) {
+			QET::MessageBox::critical(
+				dialog_,
+				tr("S\351lection inexistante", "message box title"),
+				tr("La s\351lection n'existe pas.", "message box content")
+			);
+			return;
+		}
+		
+		// puis on verifie qu'il s'agit bien d'un element
+		if (!item -> isElement()) {
+			QET::MessageBox::critical(
+				dialog_,
+				tr("S\351lection incorrecte", "message box title"),
+				tr("La s\351lection n'est pas un \351l\351ment.", "message box content")
+			);
+			return;
+		}
+		
+		location_ = location;
+	} else if (mode_ == SaveElement) {
+		/*
+			l'utilisateur doit avoir choisi soit :
+			-une categorie et un nom d'element
+			-un element existant
+		*/
+		ElementsLocation location = list_ -> selectedLocation();
+		if (location.isNull()) {
+			QET::MessageBox::critical(
+				dialog_,
+				tr("Pas de s\351lection", "message box title"),
+				tr("Vous devez s\351lectionner une cat\351gorie ou un \351l\351ment.", "message box content")
+			);
+			return;
+		}
+		
+		// on verifie donc que la selection existe
+		ElementsCollectionItem *item = QETApp::collectionItem(location);
+		if (!item) {
+			QET::MessageBox::critical(
+				dialog_,
+				tr("S\351lection inexistante", "message box title"),
+				tr("La s\351lection n'existe pas.", "message box content")
+			);
+			return;
+		}
+		
+		ElementsLocation final_location(location);
+		if (!item -> isElement()) {
+			QString element_name(textfield_ -> text());
+			// si on a une categorie (ou une collection), il nous faut un nom d'element
+			if (element_name.isEmpty()) {
+				QET::MessageBox::critical(
+					dialog_,
+					tr("Nom manquant", "message box title"),
+					tr("Vous devez entrer un nom pour l'\351l\351ment", "message box content")
+				);
+				return;
+			}
+			
+			// ce nom d'element doit etre valide
+			if (QET::containsForbiddenCharacters(element_name)) {
+				QET::MessageBox::critical(
+					dialog_,
+					tr("Nom invalide", "message box title"),
+					QString(
+						tr(
+							"Vous ne pouvez pas utiliser les caract\350res "
+							"suivants dans le nom de l'\351l\351ment : %1"
+						)
+					).arg(QET::forbiddenCharactersString(true))
+				);
+				return;
+			}
+			
+			// ajoute .elmt a la fin du nom si necessaire
+			if (!element_name.endsWith(".elmt", Qt::CaseInsensitive)) {
+				element_name += ".elmt";
+			}
+			final_location.addToPath(element_name);
+		}
+		
+		// determine si l'element n'existe pas deja
+		bool element_already_exists = (
+			item -> isElement() ||\
+			QETApp::collectionItem(final_location)
+		);
+		
+		// si l'element existe, on demande confirmation pour son ecrasement
+		if (element_already_exists) {
+			QMessageBox::StandardButton answer = QET::MessageBox::question(
+				dialog_,
+				tr("\311craser l'\351l\351ment ?", "message box title"),
+				tr("L'\351l\351ment existe d\351j\340. Voulez-vous l'\351craser ?", "message box content"),
+				QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+				QMessageBox::No
+			);
+			if (answer != QMessageBox::Yes) return;
+		}
+		
+		location_ = final_location;
+	} else if (mode_ == OpenCategory) {
+		// l'utilisateur doit avoir choisi une categorie ou une collection existante
+		/// @todo effectuer les verifications necessaires
+	} else if (mode_ == SaveCategory) {
+		// l'utilisateur doit avoir choisi une categorie inexistante
+		/// @todo effectuer les verifications necessaires
+	}
+	
+	// le dialogue est verifie, il peut etre accepte
+	dialog_ -> accept();
+}
+
+/**
+	Slot execute apres la validation du dialogue par l'utilisateur
+*/
+void ElementDialog::dialogAccepted() {
+}
+
+/**
+	Gere le rejet du dialogue par l'utilisateur.
+*/
+void ElementDialog::dialogRejected() {
+	location_ = ElementsLocation();
+}

Added: trunk/sources/elementdialog.h
===================================================================
--- trunk/sources/elementdialog.h	                        (rev 0)
+++ trunk/sources/elementdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,83 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_DIALOG_H
+#define ELEMENT_DIALOG_H
+#include <QtCore>
+#include "elementslocation.h"
+class QDialog;
+class QDialogButtonBox;
+class ElementsCategoriesList;
+class QFileNameEdit;
+/**
+	This class provides several dialogs to select an element or a category
+	(e.g. new or existing, for opening or for saving...).
+*/
+class ElementDialog : public QObject {
+	Q_OBJECT
+	// enumerations
+	/**
+		This enum represents the available configurations for the required dialog
+	*/
+	enum {
+		OpenElement  = 0, ///< The dialog should open an element
+		SaveElement  = 1, ///< The dialog should select an element for saving
+		OpenCategory = 2, ///< The dialog should open a category
+		SaveCategory = 3  ///< The dialog should select a category for saving
+	};
+	
+	// constructors, destructor
+	public:
+	ElementDialog(uint = ElementDialog::OpenElement, QWidget * = 0, QObject * = 0);
+	virtual ~ElementDialog();
+	private:
+	ElementDialog(const ElementDialog &);
+	
+	// methods
+	public:
+	int exec();
+	ElementsLocation location() const;
+	static ElementsLocation getExistingCategoryLocation(QWidget * = 0);
+	static ElementsLocation getNewCategoryLocation(QWidget * = 0);
+	static ElementsLocation getOpenElementLocation(QWidget * = 0);
+	static ElementsLocation getSaveElementLocation(QWidget * = 0);
+	
+	private:
+	static ElementsLocation execConfiguredDialog(int, QWidget * = 0);
+	
+	private slots:
+	void locationChanged(const ElementsLocation &);
+	void textFieldChanged(const QString &);
+	void dialogAccepted();
+	void dialogRejected();
+	void checkDialog();
+	
+	private:
+	void makeInterface();
+	
+	// attributes
+	private:
+	uint mode_;
+	ElementsLocation location_;
+	QString title_;
+	QString label_;
+	QDialog *dialog_;
+	QDialogButtonBox *buttons_;
+	ElementsCategoriesList *list_;
+	QFileNameEdit *textfield_;
+};
+#endif

Added: trunk/sources/elementscategorieslist.cpp
===================================================================
--- trunk/sources/elementscategorieslist.cpp	                        (rev 0)
+++ trunk/sources/elementscategorieslist.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,141 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscategorieslist.h"
+#include "qetapp.h"
+#include "qetgraphicsitem/customelement.h"
+#include "elementscollection.h"
+#include "elementscategory.h"
+#include "elementdefinition.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param display_elements true pour afficher les elements, false sinon
+	@param selectables Types selectionnables
+	@see QET::ItemType
+	@param parent QWidget parent de ce widget
+*/
+ElementsCategoriesList::ElementsCategoriesList(bool display_elements, uint selectables, QWidget *parent) :
+	GenericPanel(parent),
+	display_elements_(display_elements),
+	selectables_(selectables),
+	first_load(true)
+{
+	// selection unique
+	setSelectionMode(QAbstractItemView::SingleSelection);
+	setColumnCount(1);
+	
+	// charge les categories
+	setElementsCache(QETApp::collectionCache());
+	reload();
+	
+	connect(
+		this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
+		this, SLOT(selectionChanged(QTreeWidgetItem *, QTreeWidgetItem *))
+	);
+}
+
+/**
+	Destructeur
+*/
+ElementsCategoriesList::~ElementsCategoriesList() {
+}
+
+/**
+	Recharge l'arbre des categories
+*/
+void ElementsCategoriesList::reload() {
+	GenericPanel::PanelOptions  options = display_elements_ ? GenericPanel::AddAllChildElements : GenericPanel::AddChildElementsContainers;
+	options |= GenericPanel::DisplayElementsPreview;
+	
+	foreach(ElementsCollection *collection, QETApp::availableCollections()) {
+		if (collection == QETApp::commonElementsCollection()) continue;
+		if (collection == QETApp::customElementsCollection()) continue;
+		addElementsCollection(collection, invisibleRootItem(), options) -> setExpanded(true);
+	}
+	
+	// chargement des elements de la collection commune si droits d'ecriture
+	if (QETApp::commonElementsCollection() -> isWritable()) {
+		addElementsCollection(
+			QETApp::commonElementsCollection(),
+			invisibleRootItem(),
+			options
+		) -> setExpanded(true);
+	}
+	
+	// chargement des elements de la collection utilisateur
+	addElementsCollection(
+		QETApp::customElementsCollection(),
+		invisibleRootItem(),
+		options
+	) -> setExpanded(true);
+	
+	if (first_load) first_load = false;
+}
+
+/**
+	Create a QTreeWidgetItem
+	@param type Item type (e.g QET::Diagram, QET::Project, ...)
+	@param parent Parent for the created item
+	@param label Label for the created item
+	@param icon Icon for the created item
+	@return the create QTreeWidgetItem
+*/
+QTreeWidgetItem *ElementsCategoriesList::makeItem(QET::ItemType type, QTreeWidgetItem *parent, const QString &label, const QIcon &icon) {
+	QTreeWidgetItem *item = GenericPanel::makeItem(type, parent, label, icon);
+	Qt::ItemFlags flags = Qt::ItemIsEnabled;
+	if (selectables_ & item -> type()) flags |= Qt::ItemIsSelectable;
+	item -> setFlags(flags);
+	return(item);
+}
+
+/**
+	@return l'emplacement correspondant au QTreeWidgetItem selectionne
+*/
+ElementsLocation ElementsCategoriesList::selectedLocation() const {
+	QTreeWidgetItem *current_qtwi = currentItem();
+	if (!current_qtwi) return(ElementsLocation());
+	return(valueForItem<ElementsLocation>(current_qtwi));
+}
+
+/**
+	Selectionne un element dans la liste a partir de son emplacement
+	@see ElementsLocation
+	@param location Emplacement a selectionner
+	@return true si la selection a pu etre effectuee, false sinon
+*/
+bool ElementsCategoriesList::selectLocation(const ElementsLocation &location) {
+	QTreeWidgetItem *qtwi = itemForElementsLocation(location);
+	if (qtwi) setCurrentItem(qtwi);
+	return(qtwi);
+}
+
+/**
+	Recupere le chemin virtuel de l'element selectionne et emet le signal
+	virtualPathChanged.
+	@param current  QTreeWidgetItem selectionne
+	@param previous QTreeWidgetItem precedemment selectionne
+*/
+void ElementsCategoriesList::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) {
+	Q_UNUSED(previous);
+	ElementsLocation emited_location;
+	if (current) {
+		emited_location = valueForItem<ElementsLocation>(current);
+	}
+	emit(locationChanged(emited_location));
+}

Added: trunk/sources/elementscategorieslist.h
===================================================================
--- trunk/sources/elementscategorieslist.h	                        (rev 0)
+++ trunk/sources/elementscategorieslist.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,65 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_CATEGORIES_LIST_H
+#define ELEMENTS_CATEGORIES_LIST_H
+#include <QtGui>
+#include "qet.h"
+#include "elementslocation.h"
+#include "genericpanel.h"
+class ElementsCollection;
+class ElementsCategory;
+class ElementDefinition;
+/**
+	This class provides a visual listing of available elements categories.
+*/
+class ElementsCategoriesList : public GenericPanel {
+	Q_OBJECT
+	
+	// Constructors, destructor
+	public:
+	ElementsCategoriesList(bool = false, uint = QET::All, QWidget * = 0);
+	virtual ~ElementsCategoriesList();
+	
+	private:
+	ElementsCategoriesList(const ElementsCategoriesList &);
+	
+	// methods
+	public:
+	ElementsLocation selectedLocation() const;
+	bool selectLocation(const ElementsLocation &);
+	
+	private:
+	QString categoryName(QDir &);
+	QTreeWidgetItem *makeItem(QET::ItemType, QTreeWidgetItem *, const QString &, const QIcon &);
+	
+	public slots:
+	void reload();
+	
+	private slots:
+	void selectionChanged(QTreeWidgetItem *, QTreeWidgetItem *);
+	
+	signals:
+	void locationChanged(const ElementsLocation &);
+	
+	// attributes
+	private:
+	bool display_elements_;
+	int selectables_;
+	bool first_load;
+};
+#endif

Added: trunk/sources/elementscategorieswidget.cpp
===================================================================
--- trunk/sources/elementscategorieswidget.cpp	                        (rev 0)
+++ trunk/sources/elementscategorieswidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,126 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscategorieswidget.h"
+#include "elementscategorieslist.h"
+#include "elementscategoryeditor.h"
+#include "elementscategorydeleter.h"
+#include "elementscategory.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param parent Le QWidget parent
+*/
+ElementsCategoriesWidget::ElementsCategoriesWidget(QWidget *parent) : QWidget(parent) {
+	// initialise la liste des categories
+	elementscategorieslist = new ElementsCategoriesList(false, QET::All, this);
+	
+	// actions
+	action_reload = new QAction(QET::Icons::ViewRefresh,    tr("Recharger les cat\351gories"), this);
+	action_new    = new QAction(QET::Icons::FolderNew,    tr("Nouvelle cat\351gorie"),       this);
+	action_open   = new QAction(QET::Icons::FolderEdit,   tr("\311diter la cat\351gorie"),   this);
+	action_delete = new QAction(QET::Icons::FolderDelete, tr("Supprimer la cat\351gorie"),   this);
+	
+	// initialise la barre d'outils
+	toolbar = new QToolBar(this);
+	toolbar -> setMovable(false);
+	toolbar -> addAction(action_reload);
+	toolbar -> addAction(action_new);
+	toolbar -> addAction(action_open);
+	toolbar -> addAction(action_delete);
+	
+	connect(action_reload,          SIGNAL(triggered()),              elementscategorieslist, SLOT(reload())        );
+	connect(action_new,             SIGNAL(triggered()),              this,                   SLOT(newCategory())   );
+	connect(action_open,            SIGNAL(triggered()),              this,                   SLOT(editCategory())  );
+	connect(action_delete,          SIGNAL(triggered()),              this,                   SLOT(removeCategory()));
+	connect(elementscategorieslist, SIGNAL(itemSelectionChanged()),   this,                   SLOT(updateButtons()) );
+	
+	updateButtons();
+	
+	// disposition verticale
+	QVBoxLayout *vlayout = new QVBoxLayout(this);
+	vlayout -> setMargin(0);
+	vlayout -> setSpacing(0);
+	vlayout -> addWidget(toolbar);
+	vlayout -> addWidget(elementscategorieslist);
+	vlayout -> setStretchFactor(elementscategorieslist, 75000);
+	setLayout(vlayout);
+}
+
+/**
+	Destructeur
+*/
+ElementsCategoriesWidget::~ElementsCategoriesWidget() {
+}
+
+/**
+	Lance un editeur de categorie en mode "creation de categorie"
+*/
+void ElementsCategoriesWidget::newCategory() {
+	// recupere le chemin virtuel de la categorie selectionnee
+	ElementsLocation s_c_path = elementscategorieslist -> selectedLocation();
+	if (s_c_path.isNull()) return;
+	
+	// lance un editeur de categorie
+	ElementsCategoryEditor *editor = new ElementsCategoryEditor(s_c_path, false, this);
+	int result = editor -> exec();
+	
+	// recharge la collection si besoin
+	if (result == QDialog::Accepted) {
+		elementscategorieslist -> reload();
+	}
+}
+
+/**
+	Lance un editeur de categorie en mode "edition de categorie"
+*/
+void ElementsCategoriesWidget::editCategory() {
+	ElementsLocation s_c_path = elementscategorieslist -> selectedLocation();
+	if (s_c_path.isNull()) return;
+	(new ElementsCategoryEditor(s_c_path, true, this)) -> exec();
+	elementscategorieslist -> reload();
+}
+
+/**
+	Supprime la categorie selectionnee
+*/
+void ElementsCategoriesWidget::removeCategory() {
+	// recupere le chemin de la categorie
+	ElementsLocation s_c_path = elementscategorieslist -> selectedLocation();
+	if (s_c_path.isNull()) return;
+	
+	// supprime la categorie
+	ElementsCategoryDeleter cat_deleter(s_c_path, this);
+	cat_deleter.exec();
+	
+	// recharge la liste des categories
+	elementscategorieslist -> reload();
+}
+
+/**
+	Met a jour l'etat (active / desactive) des boutons en fonction de ce qui
+	est selectionne.
+*/
+void ElementsCategoriesWidget::updateButtons() {
+	QList<QTreeWidgetItem *> sel_items = elementscategorieslist -> selectedItems();
+	bool sel_items_empty = !sel_items.isEmpty();
+	bool is_top_lvl_item = sel_items_empty && (elementscategorieslist -> indexOfTopLevelItem(sel_items.at(0)) != -1);
+	action_new    -> setEnabled(sel_items_empty);
+	action_open   -> setEnabled(sel_items_empty && !is_top_lvl_item);
+	action_delete -> setEnabled(sel_items_empty && !is_top_lvl_item);
+}

Added: trunk/sources/elementscategorieswidget.h
===================================================================
--- trunk/sources/elementscategorieswidget.h	                        (rev 0)
+++ trunk/sources/elementscategorieswidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,64 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_CATEGORIES_WIDGET_H
+#define ELEMENTS_CATEGORIES_WIDGET_H
+#include <QtGui>
+class ElementsCategoriesList;
+/**
+	This class provides a widget listing available elements categories along
+	with buttons to add, change or create categories.
+*/
+class ElementsCategoriesWidget : public QWidget {
+	Q_OBJECT
+	
+	// Constructors, destructor
+	public:
+	ElementsCategoriesWidget(QWidget * = 0);
+	virtual ~ElementsCategoriesWidget();
+	
+	private:
+	ElementsCategoriesWidget(const ElementsCategoriesWidget &);
+	
+	// attributes
+	private:
+	ElementsCategoriesList *elementscategorieslist;
+	QToolBar *toolbar;
+	QAction *action_reload;
+	QAction *action_new;
+	QAction *action_open;
+	QAction *action_delete;
+	
+	// methods
+	public:
+	ElementsCategoriesList &elementsCategoriesList() const;
+	
+	public slots:
+	void newCategory();
+	void editCategory();
+	void removeCategory();
+	void updateButtons();
+};
+
+/**
+	@return The ElementsCategoriesList embedded within this widget.
+*/
+inline ElementsCategoriesList &ElementsCategoriesWidget::elementsCategoriesList() const {
+	return(*elementscategorieslist);
+}
+
+#endif

Added: trunk/sources/elementscategory.cpp
===================================================================
--- trunk/sources/elementscategory.cpp	                        (rev 0)
+++ trunk/sources/elementscategory.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,667 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscategory.h"
+#include "elementscollection.h"
+#include "elementdefinition.h"
+#include "moveelementshandler.h"
+#include "moveelementsdescription.h"
+#include "qet.h"
+#include "qetproject.h"
+
+/**
+	Constructeur
+*/
+ElementsCategory::ElementsCategory(ElementsCategory *parent, ElementsCollection *collection) :
+	ElementsCollectionItem(parent),
+	parent_collection_(collection),
+	parent_category_(parent)
+{
+}
+
+/**
+	Destructeur
+*/
+ElementsCategory::~ElementsCategory() {
+}
+
+/**
+	@return le projet auquel appartient cette categorie, si celle-ci
+	appartient a une collection.
+*/
+QETProject *ElementsCategory::project() {
+	if (parent_collection_) {
+		return(parent_collection_ -> project());
+	}
+	return(0);
+}
+
+/**
+	Ne fait rien ; le projet doit etre defini au niveau d'une collection
+*/
+void ElementsCategory::setProject(QETProject *) {
+}
+
+/**
+	@return le protocole utilise pour designer la collection a laquelle
+	appartient cette categorie
+*/
+QString ElementsCategory::protocol() {
+	// il n'est pas possible d'avoir un protocole sans appartenir a une collection
+	if (!hasParentCollection()) return(QString());
+	
+	return(parentCollection() -> protocol());
+}
+
+/**
+	Ne fait rien
+*/
+void ElementsCategory::setProtocol(const QString &) {
+}
+
+/**
+	@return la categorie parente de cette categorie, ou 0 si celle-ci n'en possede
+*/
+ElementsCategory *ElementsCategory::parentCategory() {
+	return(parent_category_);
+}
+
+/**
+	@return la liste des categories parentes de cet item.
+	Cette liste peut etre vide s'il s'agit d'une categorie racine
+*/
+QList<ElementsCategory *> ElementsCategory::parentCategories() {
+	QList<ElementsCategory *> cat_list;
+	if (ElementsCategory *par_cat = parentCategory()) {
+		cat_list << par_cat -> parentCategories() << par_cat;
+	}
+	return(cat_list);
+}
+
+/**
+	@return true si cette categorie possede une categorie parente
+*/
+bool ElementsCategory::hasParentCategory() {
+	return(parent_category_);
+}
+
+/**
+	@return la collection a laquelle appartient la categorie
+*/
+ElementsCollection *ElementsCategory::parentCollection() {
+	return(parent_collection_);
+}
+
+/**
+	@return true si la categorie appartient a une collection
+*/
+bool ElementsCategory::hasParentCollection() {
+	return(parent_collection_);
+}
+
+/**
+	@param other_item Un autre item
+	@return true si other_item est parent (direct ou indirect) de cet item, false sinon
+*/
+bool ElementsCategory::isChildOf(ElementsCollectionItem *other_item) {
+	// verifie si l'autre item n'est pas la collection a laquelle cette categorie appartient
+	if (ElementsCollection *other_item_coll = other_item -> toCollection()) {
+		if (other_item_coll == parentCollection()) {
+			return(true);
+		}
+	}
+	
+	// remonte les categories parentes pour voir si l'une d'entre elles correspond a cet autre item
+	ElementsCategory *parent_cat = this;
+	while ((parent_cat = parent_cat -> parentCategory())) {
+		if (parent_cat == other_item) {
+			return(true);
+		}
+	}
+	
+	// arrive ici, l'autre item n'est pas parent de cet item
+	return(false);
+}
+
+/**
+	@return le chemin virtuel de la categorie, avec le protocole
+*/
+QString ElementsCategory::fullVirtualPath() {
+	// il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection
+	if (!hasParentCollection()) return(QString());
+	
+	return(protocol() + "://" + virtualPath());
+}
+
+/**
+	@return l'emplacement de la categorie
+*/
+ElementsLocation ElementsCategory::location() {
+	return(ElementsLocation(fullVirtualPath(), project()));
+}
+
+/**
+	@return true si cette categorie est la categorie racine de la collection
+	a laquelle elle appartient, false sinon
+*/
+bool ElementsCategory::isRootCategory() const {
+	// il faut appartenir a une collection pour etre categorie racine
+	if (!parent_collection_) return(false);
+	
+	return(this == parent_collection_ -> rootCategory());
+}
+
+/**
+	@return toujours false
+*/
+bool ElementsCategory::isCollection() const {
+	return(false);
+}
+
+/**
+	@return toujours true
+*/
+bool ElementsCategory::isCategory() const {
+	return(true);
+}
+
+/**
+	@return toujours false
+*/
+bool ElementsCategory::isElement() const {
+	return(false);
+}
+
+/**
+	@return toujours 0 - une categorie n'est pas une collection
+*/
+ElementsCollection *ElementsCategory::toCollection() {
+	return(0);
+}
+
+/**
+	@return un pointeur ElementsCategory * sur cette categorie
+*/
+ElementsCategory *ElementsCategory::toCategory() {
+	return(this);
+}
+
+/**
+	@return un pointeur ElementsCategory * sur cette categorie
+*/
+ElementsCategory *ElementsCategory::toPureCategory() {
+	return(this);
+}
+
+/**
+	@return toujours 0 - une categorie n'est pas un element
+*/
+ElementDefinition *ElementsCategory::toElement() {
+	return(0);
+}
+
+/**
+	@param target_category Categorie cible pour la copie ; cette categorie
+	sera copiee en tant que sous-categorie de la categorie cible
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer la copie
+	Si handler vaut 0, les erreurs, problemes et questions sont purement et
+	simplement ignores.
+	@param deep_copy true pour copier recursivement le contenu (elements et
+	sous-categories) de cette categorie, false sinon
+	@return La copie de la categorie, ou 0 si le processus a echoue.
+*/
+ElementsCollectionItem *ElementsCategory::copy(ElementsCategory *target_category, MoveElementsHandler *handler, bool deep_copy) {
+	if (!target_category) return(0);
+	
+	// echec si le path name de cette categorie est vide
+	QString cat_name(pathName());
+	if (cat_name.isEmpty()) return(0);
+	
+	// cree une description du mouvement a effectuer
+	MoveElementsDescription mvt_desc;
+	mvt_desc.setDestinationParentCategory(target_category);
+	// on tente une copie avec le meme nom interne
+	mvt_desc.setOriginalDestinationInternalName(cat_name);
+	mvt_desc.setFinalDestinationInternalName(cat_name);
+	mvt_desc.setHandler(handler);
+	mvt_desc.setRecursive(deep_copy);
+	
+	// effectue le mouvement
+	copy(&mvt_desc);
+	return(mvt_desc.createdItem());
+}
+
+/**
+	Methode privee effectuant une copie de cette categorie a partir d'une
+	description du mouvement
+*/
+void ElementsCategory::copy(MoveElementsDescription *mvt_desc) {
+	// quelques pointeurs pour simplifier l'ecriture de la methode
+	MoveElementsHandler *handler = mvt_desc -> handler();
+	ElementsCategory *target_category = mvt_desc -> destinationParentCategory();
+	
+	// verifie que la categorie parente cible est accessible en lecture
+	if (!target_category -> isReadable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				QET::Action todo = handler -> categoryIsNotReadable(target_category);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!target_category -> isReadable());
+		}
+	}
+	
+	// verifie que la source et la destination ne sont pas identiques
+	if (target_category == this || target_category -> isChildOf(this)) {
+		if (handler) {
+			handler -> errorWithACategory(this, tr("La copie d'une cat\351gorie vers elle-m\352me ou vers l'une de ses sous-cat\351gories n\'est pas g\351r\351e."));
+		}
+		return;
+	}
+	
+	// verifie que la categorie parente cible est accessible en ecriture
+	if (!target_category -> isWritable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				QET::Action todo = handler -> categoryIsNotWritable(target_category);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!target_category -> isWritable());
+		}
+	}
+	
+	ElementsCategory *category_copy = 0;
+	
+	// verifie que la cible n'existe pas deja
+	if ((category_copy = target_category -> category(mvt_desc -> finalDestinationInternalName()))) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				// la cible existe deja : on demande au Handler ce qu'on doit faire
+				QET::Action todo = handler -> categoryAlreadyExists(this, category_copy);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Erase) {
+					break;
+				} else if (todo == QET::Rename) {
+					mvt_desc -> setFinalDestinationInternalName(handler -> nameForRenamingOperation());
+				}
+			} while ((category_copy = target_category -> category(mvt_desc -> finalDestinationInternalName())));
+		}
+	}
+	
+	/*
+		A ce stade, on peut creer la categorie cible : soit elle n'existe pas,
+		soit on a l'autorisation de l'ecraser
+	*/
+	
+	// si la cible existe deja, verifie qu'elle est accessible en ecriture
+	category_copy = target_category -> category(mvt_desc -> finalDestinationInternalName());
+	if (category_copy && !category_copy -> isWritable()) {
+		if (!handler) {
+			return;
+		} else {
+			do {
+				// la cible n'est pas accessible en ecriture : on demande au Handler ce qu'on doit faire
+				QET::Action todo = handler -> categoryIsNotWritable(category_copy);
+				
+				// on agit en fonction de la reponse du handler
+				if (todo == QET::Abort) {
+					mvt_desc -> abort();
+					return;
+				} else if (todo == QET::Ignore || todo == QET::Managed) {
+					return;
+				} else if (todo == QET::Retry || todo == QET::Erase) {
+					// reessayer = repasser dans la boucle
+				} else if (todo == QET::Rename) {
+					// cas non gere
+				}
+			} while (!category_copy -> isWritable());
+		}
+	}
+	
+	// memorise la liste des sous-categories et elements directs
+	QList<ElementsCategory  *> categories_list = categories();
+	QList<ElementDefinition *> elements_list   = elements();
+	
+	// cree la categorie cible
+	category_copy = target_category -> createCategory(mvt_desc -> finalDestinationInternalName());
+	if (!category_copy) {
+		/// @todo la creation a echoue : gerer ce cas
+		return;
+	}
+	
+	// recopie les noms de la categorie
+	category_copy -> category_names = category_names;
+	category_copy -> write();
+	mvt_desc -> setCreatedItem(category_copy);
+	
+	// copie recursive
+	if (mvt_desc -> isRecursive()) {
+		// copie les sous-categories
+		foreach(ElementsCategory *sub_category, categories_list) {
+			// cree une description du mouvement a effectuer
+			MoveElementsDescription sub_category_mvt_desc;
+			sub_category_mvt_desc.setDestinationParentCategory(category_copy);
+			// on tente une copie avec le meme nom interne
+			sub_category_mvt_desc.setOriginalDestinationInternalName(sub_category -> pathName());
+			sub_category_mvt_desc.setFinalDestinationInternalName(sub_category -> pathName());
+			sub_category_mvt_desc.setHandler(handler);
+			sub_category_mvt_desc.setRecursive(true);
+			
+			// effectue la copie
+			sub_category -> copy(&sub_category_mvt_desc);
+			
+			// abort si besoin
+			if (sub_category_mvt_desc.mustAbort()) {
+				mvt_desc -> abort();
+				return;
+			}
+		}
+		
+		// copie les elements
+		foreach(ElementDefinition *element, elements_list) {
+			// cree une description du mouvement a effectuer
+			MoveElementsDescription element_mvt_desc;
+			element_mvt_desc.setDestinationParentCategory(category_copy);
+			// on tente une copie avec le meme nom interne
+			element_mvt_desc.setOriginalDestinationInternalName(element -> pathName());
+			element_mvt_desc.setFinalDestinationInternalName(element -> pathName());
+			element_mvt_desc.setHandler(handler);
+			
+			element -> copy(&element_mvt_desc);
+			
+			// abort si besoin
+			if (element_mvt_desc.mustAbort()) {
+				mvt_desc -> abort();
+				return;
+			}
+		}
+	}
+}
+
+/**
+	Cette methode copie la categorie recursivement puis la supprime, ce qui
+	equivaut a un deplacement. Elle cree donc un nouvel objet representant la
+	categorie, qu'elle retourne ensuite.
+	@param target_category Categorie cible pour le deplacement ; cette categorie
+	sera deplacee de faon a devenir une sous-categorie de la categorie cible
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer le
+	deplacement. Si handler vaut 0, les erreurs, problemes et questions sont
+	purement et simplement ignores.
+	@return Un pointeur vers la categorie apres le deplacement, ou 0 si le
+	processus a echoue.
+*/
+ElementsCollectionItem *ElementsCategory::move(ElementsCategory *target_category, MoveElementsHandler *handler) {
+	if (!target_category) return(0);
+	
+	// echec si le path name de cette categorie est vide
+	QString cat_name(pathName());
+	if (cat_name.isEmpty()) return(0);
+	
+	// cree une description du mouvement a effectuer
+	MoveElementsDescription mvt_desc;
+	mvt_desc.setDestinationParentCategory(target_category);
+	// on tente une copie avec le meme nom interne
+	mvt_desc.setOriginalDestinationInternalName(cat_name);
+	mvt_desc.setFinalDestinationInternalName(cat_name);
+	mvt_desc.setHandler(handler);
+	mvt_desc.setRecursive(true);  // un deplacement est forcement recursif
+	
+	// effectue le mouvement
+	move(&mvt_desc);
+	return(mvt_desc.createdItem());
+}
+
+/**
+	Methode privee effectuant le deplacement de cette categorie a partir d'une
+	description du mouvement
+	@param mvt_desc Description du mouvement
+*/
+void ElementsCategory::move(MoveElementsDescription *mvt_desc) {
+	// quelques pointeurs pour simplifier l'ecriture de la methode
+	MoveElementsHandler *handler = mvt_desc -> handler();
+	ElementsCategory *target_category = mvt_desc -> destinationParentCategory();
+	
+	// empeche le deplacement s'il s'agit d'une categorie racine
+	if (isRootCategory()) {
+		if (handler) handler -> errorWithACategory(this, tr("Il n'est pas possible de d\351placer une collection."));
+		return;
+	}
+	
+	// empeche le deplacement de la categorie dans une sous-categorie
+	if (target_category == this || target_category -> isChildOf(this)) {
+		if (handler) handler -> errorWithACategory(this, tr("Le d\351placement d'une cat\351gorie dans une de ses sous-cat\351gories n'est pas possible."));
+		return;
+	}
+	
+	// effectue une copie non recursive de cette categorie
+	ElementsCollectionItem *item_copy = copy(target_category, handler, false);
+	if (!item_copy) return;
+	ElementsCategory *category_copy = item_copy -> toCategory();
+	if (!category_copy) return;
+	
+	// memorise la liste des sous-categories et elements directs
+	QList<ElementsCategory  *> categories_list = categories();
+	QList<ElementDefinition *> elements_list   = elements();
+	
+	// booleen indiquant si on pourra tenter de supprimer la categorie apres la copie
+	bool remove_category = true;
+	
+	// tente de deplacer les sous-categories
+	foreach(ElementsCategory *sub_category, categories_list) {
+		// cree une description du mouvement a effectuer
+		MoveElementsDescription sub_category_mvt_desc;
+		sub_category_mvt_desc.setDestinationParentCategory(category_copy);
+		// on tente un deplacement avec le meme nom interne
+		sub_category_mvt_desc.setOriginalDestinationInternalName(sub_category -> pathName());
+		sub_category_mvt_desc.setFinalDestinationInternalName(sub_category -> pathName());
+		sub_category_mvt_desc.setHandler(handler);
+		sub_category_mvt_desc.setRecursive(true);
+		
+		// effectue le deplacement
+		sub_category -> move(&sub_category_mvt_desc);
+		
+		// abort si besoin
+		if (sub_category_mvt_desc.mustAbort()) {
+			mvt_desc -> abort();
+			return;
+		}
+		
+		// si la sous-categorie n'a pas ete supprimee, on ne supprimera pas cette categorie
+		if (remove_category) remove_category = sub_category_mvt_desc.sourceItemWasDeleted();
+		
+		// si la sous-categorie n'a pas ete supprimee, ...
+		if (!sub_category_mvt_desc.sourceItemWasDeleted()) {
+			// on ne supprimera pas cette categorie
+			if (remove_category) remove_category = false;
+		}
+	}
+	
+	// tente de deplacer les elements
+	foreach(ElementDefinition *element, elements_list) {
+		// cree une description du mouvement a effectuer
+		MoveElementsDescription element_mvt_desc;
+		element_mvt_desc.setDestinationParentCategory(category_copy);
+		// on tente une copie avec le meme nom interne
+		element_mvt_desc.setOriginalDestinationInternalName(element -> pathName());
+		element_mvt_desc.setFinalDestinationInternalName(element -> pathName());
+		element_mvt_desc.setHandler(handler);
+		
+		element -> move(&element_mvt_desc);
+		
+		// abort si besoin
+		if (element_mvt_desc.mustAbort()) {
+			mvt_desc -> abort();
+			return;
+		}
+		
+		// si l'element n'a pas ete supprime, ...
+		if (!element_mvt_desc.sourceItemWasDeleted()) {
+			// on ne supprimera pas cette categorie
+			if (remove_category) remove_category = false;
+		}
+	}
+	
+	// supprime cette categorie (sinon il ne s'agirait que d'une copie, pas d'un deplacement)
+	if (remove_category) {
+		reload();
+		bool category_deletion = remove();
+		mvt_desc -> setSourceItemDeleted(category_deletion);
+		if (!category_deletion && handler) {
+			handler -> errorWithACategory(this, tr("La suppression de cette cat\351gorie a \351chou\351."));
+		}
+	}
+}
+
+/**
+	Cette methode supprime recursivement les elements inutilises dans le projet.
+	Si cette categorie n'est pas rattachee a un projet, elle ne fait rien
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer le
+	nettoyage. Si handler vaut 0, les erreurs, problemes et questions sont
+	purement et simplement ignores.
+*/
+void ElementsCategory::deleteUnusedElements(MoveElementsHandler *handler) {
+	// si cette categorie n'est pas rattachee a un projet, elle ne fait rien
+	QETProject *parent_project = project();
+	if (!parent_project) return;
+	
+	// supprime les elements inutilises dans les sous-categories
+	foreach(ElementsCategory *sub_category, categories()) {
+		sub_category -> deleteUnusedElements(handler);
+	}
+	
+	// supprime les elements inutilises dans cette categorie
+	foreach(ElementDefinition *element, elements()) {
+		if (!parent_project -> usesElement(element -> location())) {
+			bool element_deletion = element -> remove();
+			if (!element_deletion && handler) {
+				handler -> errorWithAnElement(element, tr("Impossible de supprimer l'\351l\351ment"));
+			}
+		}
+	}
+}
+
+/**
+	Cette methode supprime toutes les sous-categories de cette categories qui
+	ne contiennent pas d'elements ou de categories contenant des elements.
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer le
+	nettoyage. Si handler vaut 0, les erreurs, problemes et questions sont
+	purement et simplement ignores.
+*/
+void ElementsCategory::deleteEmptyCategories(MoveElementsHandler *handler) {
+	// supprime les sous-categories qui ne comportent pas d'elements
+	foreach(ElementsCategory *sub_category, categories()) {
+		sub_category -> deleteEmptyCategories(handler);
+		sub_category -> reload();
+		if (!sub_category -> isEmpty()) {
+			bool category_deletion = sub_category -> remove();
+			if (!category_deletion && handler) {
+				handler -> errorWithACategory(sub_category, tr("Impossible de supprimer la cat\351gorie"));
+			}
+		}
+	}
+}
+
+/**
+	@return true si cette collection est vide (pas de sous-categorie, pas
+	d'element), false sinon.
+*/
+bool ElementsCategory::isEmpty() {
+	return(categories().count() || elements().count());
+}
+
+/**
+	@return the count of categories and elements within this collection
+*/
+int ElementsCategory::count() {
+	int items_count = elements().count();
+	foreach(ElementsCategory *category, categories()) {
+		items_count += category -> count();
+	}
+	return(items_count);
+}
+
+/**
+	Methode permettant d'obtenir le nom affichable de cette categorie.
+	@return Le nom affichable de la categorie
+*/
+QString ElementsCategory::name() const {
+	return(category_names.name(pathName()));
+}
+
+/**
+	@return La liste des differents noms possibles pour la categorie
+*/
+NamesList ElementsCategory::categoryNames() const {
+	return(category_names);
+}
+
+/**
+	Vide la liste des noms de la categorie
+*/
+void ElementsCategory::clearNames() {
+	category_names.clearNames();
+}
+
+/**
+	Ajoute un nom a la categorie.
+	Si la langue existe deja, le nom pour cette langue est remplace.
+	@param lang La langue pour laquelle le nom est utilisable
+	@param value Le nom
+*/
+void ElementsCategory::addName(const QString &lang, const QString &value) {
+	category_names.addName(lang, value);
+}
+
+/**
+	Specifie les noms de la categorie.
+	Tous les noms precedemment connus sont perdus
+*/
+void ElementsCategory::setNames(const NamesList &names_list) {
+	category_names = names_list;
+}

Added: trunk/sources/elementscategory.h
===================================================================
--- trunk/sources/elementscategory.h	                        (rev 0)
+++ trunk/sources/elementscategory.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,90 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_CATEGORY_H
+#define ELEMENTS_CATEGORY_H
+#include "elementscollectionitem.h"
+#include "nameslist.h"
+#include "elementslocation.h"
+class ElementDefinition;
+class ElementsCollection;
+class MoveElementsHandler;
+class MoveElementsDescription;
+/**
+	This abstract class represents an elements category, i.e. a kind of
+	subfolder within elements collections.
+*/
+class ElementsCategory : public ElementsCollectionItem {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	ElementsCategory(ElementsCategory * = 0, ElementsCollection * = 0);
+	virtual ~ElementsCategory();
+	
+	private:
+	ElementsCategory(const ElementsCategory &);
+	
+	// Implementations of pure virtual methods from parent classes
+	public:
+	virtual QETProject *project();
+	virtual void setProject(QETProject *);
+	virtual QString protocol();
+	virtual void setProtocol(const QString &);
+	virtual ElementsCategory *parentCategory();
+	virtual QList<ElementsCategory *> parentCategories();
+	virtual bool hasParentCategory();
+	virtual ElementsCollection *parentCollection();
+	virtual bool hasParentCollection();
+	virtual bool isChildOf(ElementsCollectionItem *);
+	virtual QString fullVirtualPath();
+	virtual ElementsLocation location();
+	virtual bool isRootCategory() const;
+	virtual bool isCollection() const;
+	virtual bool isCategory() const;
+	virtual bool isElement() const;
+	virtual ElementsCollection *toCollection();
+	virtual ElementsCategory *toCategory();
+	virtual ElementsCategory *toPureCategory();
+	virtual ElementDefinition *toElement();
+	virtual ElementsCollectionItem *copy(ElementsCategory *, MoveElementsHandler *, bool = true);
+	virtual ElementsCollectionItem *move(ElementsCategory *, MoveElementsHandler *);
+	virtual void deleteUnusedElements(MoveElementsHandler *handler);
+	virtual void deleteEmptyCategories(MoveElementsHandler *handler);
+	virtual bool isEmpty();
+	virtual int count();
+	
+	// Methods specific to the ElementsCategory class
+	public:
+	virtual QString name() const;
+	virtual NamesList categoryNames() const;
+	virtual void clearNames();
+	virtual void addName(const QString &, const QString &);
+	virtual void setNames(const NamesList &);
+	void copy(MoveElementsDescription *);
+	void move(MoveElementsDescription *);
+	
+	// attributes
+	protected:
+	/// parent collection
+	ElementsCollection *parent_collection_;
+	/// parent category
+	ElementsCategory   *parent_category_;
+	/// names list for this category
+	NamesList           category_names;
+};
+#endif

Added: trunk/sources/elementscategorydeleter.cpp
===================================================================
--- trunk/sources/elementscategorydeleter.cpp	                        (rev 0)
+++ trunk/sources/elementscategorydeleter.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,111 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscategorydeleter.h"
+#include "qetapp.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur
+	@param category_path Chemin virtuel de la categorie a supprimer
+	@param parent QWidget parent
+*/
+ElementsCategoryDeleter::ElementsCategoryDeleter(const ElementsLocation &category_path, QWidget *parent) :
+	QWidget(parent),
+	category(0)
+{
+	// recupere la categorie a supprimer
+	ElementsCollectionItem *category_item = QETApp::collectionItem(category_path);
+	if (!category_item) return;
+	
+	// on exige une collection ou une categorie
+	if (!category_item -> isCollection() && !category_item -> isCategory()) return;
+	
+	category = category_item;
+}
+
+/// Destructeur
+ElementsCategoryDeleter::~ElementsCategoryDeleter() {
+}
+
+/**
+	Supprime la categorie et ses elements : verifie l'existence du dossier,
+	demande deux fois confirmation a l'utilisateur et avertit ce dernier si la
+	suppression a echoue.
+	@return true si la suppression a ete effectuee, false sinon
+*/
+bool ElementsCategoryDeleter::exec() {
+	// verifie l'existence de la categorie
+	if (!category) return(false);
+	
+	// gere le cas ou la suppression d'une collection est demandee
+	if (category -> isCollection()) {
+		QMessageBox::StandardButton answer_0 = QET::MessageBox::question(
+			this,
+			tr("Vider la collection ?", "message box title"),
+			tr("\312tes-vous s\373r de vouloir vider cette collection ?", "message box content"),
+			QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
+		);
+		if (answer_0 != QMessageBox::Yes) return(false);
+	}
+	
+	/**
+		@todo Regression : rafficher le nom de la categorie supprimee
+	*/
+	// QString cat_name(category -> name().replace("<", "&lt;").replace(">", "&gt;"));
+	
+	
+	// avertissement pour la suppression d'une collection
+	// confirmation #1
+	QMessageBox::StandardButton answer_1 = QET::MessageBox::question(
+		this,
+		tr("Supprimer la cat\351gorie ?", "message box title"),
+		tr(
+			"\312tes-vous s\373r de vouloir supprimer la cat\351gorie ?\nTous "
+			"les \351l\351ments et les cat\351gories contenus dans cette "
+			"cat\351gorie seront supprim\351s.",
+			"message box content"
+		),
+		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
+	);
+	if (answer_1 != QMessageBox::Yes) return(false);
+	
+	// confirmation #2
+	QMessageBox::StandardButton answer_2 = QET::MessageBox::question(
+		this,
+		tr("Supprimer la cat\351gorie ?", "message box title"),
+		tr(
+			"\312tes-vous vraiment s\373r de vouloir supprimer cette "
+			"cat\351gorie ?\nLes changements seront d\351finitifs.",
+			"message box content"
+		),
+		QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel
+	);
+	if (answer_2 != QMessageBox::Yes) return(false);
+	
+	// supprime la categorie
+	if (!category -> remove()) {
+		QET::MessageBox::warning(
+			this,
+			tr("Suppression de la cat\351gorie", "message box title"),
+			tr("La suppression de la cat\351gorie a \351chou\351.", "message box content")
+		);
+		return(false);
+	}
+	
+	return(true);
+}

Added: trunk/sources/elementscategorydeleter.h
===================================================================
--- trunk/sources/elementscategorydeleter.h	                        (rev 0)
+++ trunk/sources/elementscategorydeleter.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,47 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_CATEGORY_DELETER_H
+#define ELEMENTS_CATEGORY_DELETER_H
+#include "fileelementscategory.h"
+#include "elementslocation.h"
+#include <QtGui>
+/**
+	This class provide an abstract way to delete an elements category along with
+	its child elements and subcategories. It always requires a double confirmation
+	from users before actually proceeding to the deletion.
+	If the deletion of a whole elements collection is required, this class will
+	require an extra confirmation.
+*/
+class ElementsCategoryDeleter : public QWidget {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ElementsCategoryDeleter(const ElementsLocation &, QWidget * = 0);
+	virtual ~ElementsCategoryDeleter();
+	private:
+	ElementsCategoryDeleter(const ElementsCategory &);
+	
+	// methods
+	public slots:
+	bool exec();
+	
+	// attributes
+	private:
+	ElementsCollectionItem *category;
+};
+#endif

Added: trunk/sources/elementscategoryeditor.cpp
===================================================================
--- trunk/sources/elementscategoryeditor.cpp	                        (rev 0)
+++ trunk/sources/elementscategoryeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,201 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscategoryeditor.h"
+#include "elementscollection.h"
+#include "elementscategory.h"
+#include "nameslistwidget.h"
+#include "qet.h"
+#include "qetapp.h"
+#include "qfilenameedit.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur fournissant un dialogue d'edition de categorie.
+	@param category_path Chemin de la categorie a editer ou de la categorie parente en cas de creation
+	@param edit booleen a true pour le mode edition, a false pour le mode creation
+	@param parent QWidget parent du dialogue
+*/
+ElementsCategoryEditor::ElementsCategoryEditor(const ElementsLocation &category_path, bool edit, QWidget *parent) :
+	QDialog(parent),
+	mode_edit(edit)
+{
+	// dialogue basique
+	buildDialog();
+	
+	// recupere la categorie a editer
+	ElementsCollectionItem *category_item = QETApp::collectionItem(category_path);
+	if (category_item) category_item = category_item -> toCategory();
+	
+	if (!category_item || !category_item -> isCategory()) {
+		QET::MessageBox::warning(
+			this,
+			tr("Cat\351gorie inexistante", "message box title"),
+			tr("La cat\351gorie demand\351e n'existe pas. Abandon.", "message box content")
+		);
+		return;
+	} else {
+		category = category_item -> toPureCategory();
+	}
+	
+	if (mode_edit) {
+		setWindowTitle(tr("\311diter une cat\351gorie", "window title"));
+		connect(buttons, SIGNAL(accepted()), this, SLOT(acceptUpdate()));
+		
+		// edition de categorie = affichage des noms deja existants
+		names_list -> setNames(category -> categoryNames());
+		internal_name_ -> setText(category -> pathName());
+		internal_name_ -> setReadOnly(true);
+	} else {
+		setWindowTitle(tr("Cr\351er une nouvelle cat\351gorie", "window title"));
+		connect(buttons, SIGNAL(accepted()), this, SLOT(acceptCreation()));
+		
+		// nouvelle categorie = une ligne pre-machee
+		NamesList cat_names;
+		cat_names.addName(QLocale::system().name().left(2), tr("Nom de la nouvelle cat\351gorie", "default name when creating a new category"));
+		names_list -> setNames(cat_names);
+	}
+	
+	// gestion de la lecture seule
+	if (!category -> isWritable()) {
+		QET::MessageBox::warning(
+			this,
+			tr("\311dition en lecture seule", "message box title"),
+			tr("Vous n'avez pas les privil\350ges n\351cessaires pour modifier cette cat\351gorie. Elle sera donc ouverte en lecture seule.", "message box content")
+		);
+		names_list -> setReadOnly(true);
+		internal_name_ -> setReadOnly(true);
+	}
+}
+
+/**
+	Destructeur
+*/
+ElementsCategoryEditor::~ElementsCategoryEditor() {
+}
+
+/**
+	Bases du dialogue de creation / edition
+*/
+void ElementsCategoryEditor::buildDialog() {
+	QVBoxLayout *editor_layout = new QVBoxLayout();
+	setLayout(editor_layout);
+	
+	names_list = new NamesListWidget();
+	internal_name_label_ = new QLabel(tr("Nom interne : "));
+	internal_name_ = new QFileNameEdit();
+	
+	buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
+	
+	QHBoxLayout *internal_name_layout = new QHBoxLayout();
+	internal_name_layout -> addWidget(internal_name_label_);
+	internal_name_layout -> addWidget(internal_name_);
+	
+	editor_layout -> addLayout(internal_name_layout);
+	editor_layout -> addWidget(new QLabel(tr("Vous pouvez sp\351cifier un nom par langue pour la cat\351gorie.")));
+	editor_layout -> addWidget(names_list);
+	editor_layout -> addWidget(buttons);
+}
+
+/**
+	Valide les donnees entrees par l'utilisateur lors d'une creation de
+	categorie
+*/
+void ElementsCategoryEditor::acceptCreation() {
+	if (!category -> isWritable()) QDialog::accept();
+	
+	// il doit y avoir au moins un nom
+	if (!names_list -> checkOneName()) return;
+	
+	// exige un nom de dossier de la part de l'utilisateur
+	if (!internal_name_ -> isValid()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Nom interne manquant", "message box title"),
+			tr("Vous devez sp\351cifier un nom interne.", "message box content")
+		);
+		return;
+	}
+	QString dirname = internal_name_ -> text();
+	
+	// verifie que le nom interne n'est pas deja pris
+	if (category -> category(dirname)) {
+		QET::MessageBox::critical(
+			this,
+			tr("Nom interne d\351j\340 utilis\351", "message box title"),
+			tr(
+				"Le nom interne que vous avez choisi est d\351j\340 utilis\351 "
+				"par une cat\351gorie existante. Veuillez en choisir un autre.",
+				"message box content"
+			)
+		);
+		return;
+	}
+	
+	// cree la nouvelle categorie
+	ElementsCategory *new_category = category -> createCategory(dirname);
+	if (!new_category) {
+		QET::MessageBox::critical(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Impossible de cr\351er la cat\351gorie", "message box content")
+		);
+		return;
+	}
+	
+	// chargement des noms
+	NamesList names = names_list -> names();
+	foreach(QString lang, names.langs()) {
+		new_category -> addName(lang, names[lang]);
+	}
+	
+	// ecriture de la 
+	if (!new_category -> write()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Impossible d'enregistrer la cat\351gorie", "message box content")
+		);
+		return;
+	}
+	
+	QDialog::accept();
+}
+
+/**
+	Valide les donnees entrees par l'utilisateur lors d'une modification de
+	categorie
+*/
+void ElementsCategoryEditor::acceptUpdate() {
+	
+	if (!category -> isWritable()) QDialog::accept();
+	
+	// il doit y avoir au moins un nom
+	if (!names_list -> checkOneName()) return;
+	
+	// chargement des noms
+	category -> clearNames();
+	NamesList names = names_list -> names();
+	foreach(QString lang, names.langs()) {
+		category -> addName(lang, names[lang]);
+	}
+	
+	category -> write();
+	
+	QDialog::accept();
+}

Added: trunk/sources/elementscategoryeditor.h
===================================================================
--- trunk/sources/elementscategoryeditor.h	                        (rev 0)
+++ trunk/sources/elementscategoryeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,57 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_CATEGORY_EDITOR_H
+#define ELEMENTS_CATEGORY_EDITOR_H
+#include <QtGui>
+#include "elementslocation.h"
+class ElementsCategory;
+class NamesListWidget;
+class QFileNameEdit;
+/**
+	This class provides a dialog to edit an existing category or create a new
+	one.
+*/
+class ElementsCategoryEditor : public QDialog {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	ElementsCategoryEditor(const ElementsLocation &, bool = true, QWidget * = 0);
+	virtual ~ElementsCategoryEditor();
+	
+	private:
+	ElementsCategoryEditor(const ElementsCategoryEditor &);
+	
+	// attributes
+	private:
+	ElementsCategory *category;
+	QDialogButtonBox *buttons;
+	NamesListWidget *names_list;
+	QLabel *internal_name_label_;
+	QFileNameEdit *internal_name_;
+	bool mode_edit;
+	
+	// methods
+	private:
+	void buildDialog();
+	
+	public slots:
+	void acceptCreation();
+	void acceptUpdate();
+};
+#endif

Added: trunk/sources/elementscollection.cpp
===================================================================
--- trunk/sources/elementscollection.cpp	                        (rev 0)
+++ trunk/sources/elementscollection.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,462 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscollection.h"
+#include "elementscategory.h"
+#include "elementdefinition.h"
+#include "moveelementshandler.h"
+
+/**
+	Constructeur
+	@param parent Item parent
+*/
+ElementsCollection::ElementsCollection(ElementsCollectionItem *parent) :
+	ElementsCollectionItem(parent),
+	cache_(0)
+{
+}
+
+/**
+	Destructeur
+*/
+ElementsCollection::~ElementsCollection() {
+}
+
+/**
+	@return the title for this collection
+*/
+QString ElementsCollection::title() const {
+	return(title_);
+}
+
+/**
+	@param title New title for this collection
+*/
+void ElementsCollection::setTitle(const QString &title) {
+	if (title_ == title) return;
+	title_ = title;
+	emit(elementsCollectionChanged(this));
+}
+
+/**
+	@return the icon for this collection
+*/
+QIcon ElementsCollection::icon() const {
+	return(icon_);
+}
+
+/**
+	@param icon the icon for this collection
+*/
+void ElementsCollection::setIcon(const QIcon &icon) {
+	if (icon_.cacheKey() == icon.cacheKey()) return;
+	icon_ = icon;
+	emit(elementsCollectionChanged(this));
+}
+
+/**
+	@return toujours true
+*/
+bool ElementsCollection::isCollection() const {
+	return(true );
+}
+
+/**
+	@return toujours false
+*/
+bool ElementsCollection::isRootCategory() const {
+	return(false);
+}
+
+/**
+	@return toujours false
+*/
+bool ElementsCollection::isCategory()  const {
+	return(false);
+}
+
+/**
+	@return toujours false
+*/
+bool ElementsCollection::isElement() const {
+	return(false);
+}
+
+/**
+	@return un pointeur ElementsCollection * sur cette collection
+*/
+ElementsCollection *ElementsCollection::toCollection() {
+	return(this);
+}
+
+/**
+	@return un pointeur vers la categorie racine de cette collection
+*/
+ElementsCategory *ElementsCollection::toCategory() {
+	return(rootCategory());
+}
+
+/**
+	@return toujours 0 - une collection n'est pas a proprement parler une
+	categorie
+*/
+ElementsCategory *ElementsCollection::toPureCategory() {
+	return(0);
+}
+
+/**
+	@return toujours 0 - une collection n'est pas un element
+*/
+ElementDefinition *ElementsCollection::toElement() {
+	return(0);
+}
+
+/**
+	@param target_category Categorie cible pour la copie ; la categorie racine
+	de cette collection sera copiee en tant que sous-categorie de la categorie
+	cible
+	@param handler Gestionnaire d'erreurs a utiliser pour effectuer la copie
+	@param deep_copy true pour copier recursivement le contenu (elements et
+	sous-categories) de la categorie racine, false sinon
+	@return La copie de la categorie, ou 0 si le processus a echoue.
+*/
+ElementsCollectionItem *ElementsCollection::copy(ElementsCategory *target_category, MoveElementsHandler *handler, bool deep_copy) {
+	if (ElementsCategory *root = rootCategory()) {
+		return(root -> copy(target_category, handler, deep_copy));
+	}
+	return(0);
+}
+
+/**
+	Il n'est pas possible de deplacer une collection. Cette methode demande
+	simplement au gestionnaire d'erreur handler d'afficher un message.
+*/
+ElementsCollectionItem *ElementsCollection::move(ElementsCategory *, MoveElementsHandler *handler) {
+	if (ElementsCategory *root = rootCategory()) {
+		if (handler) {
+			handler -> errorWithACategory(root, tr("Il n'est pas possible de d\351placer une collection."));
+		}
+	}
+	return(0);
+}
+
+
+/**
+	Vide la collection de son contenu sans la supprimer
+	@return true si l'operation a reussi, false sinon
+*/
+bool ElementsCollection::removeContent() {
+	if (!rootCategory()) return(true);
+	
+	// demande a la categorie racine de supprimer son contenu
+	return(rootCategory() -> removeContent());
+}
+
+/**
+	Vide la collection de son contenu sans la supprimer
+	@return true si l'operation a reussi, false sinon
+*/
+bool ElementsCollection::remove() {
+	return(removeContent());
+}
+
+/**
+	@return le projet auquel est rattachee cette collection ou 0 si
+	celle-ci n'est pas liee a un projet.
+*/
+QETProject *ElementsCollection::project() {
+	return(project_);
+}
+
+/**
+	@param project le nouveau projet auquel est rattachee cette collection
+	Indiquer 0 pour que cette collection ne soit plus liee a un projet.
+*/
+void ElementsCollection::setProject(QETProject *project) {
+	project_ = project;
+}
+
+/**
+	@return le protocole utilise par cette collection ; exemples :
+	"common" pour la collection commune qui utilise des URLs en common://
+	"custom" pour la collection perso qui utilise des URLs en custom://
+	"embed" pour une collection embarquee qui utilise des URLs en embed://
+*/
+QString ElementsCollection::protocol() {
+	return(protocol_);
+}
+
+/**
+	Definit le protocole de cette collection
+	@param p Nouveau protocole de cette collection
+*/
+void ElementsCollection::setProtocol(const QString &p) {
+	if (!p.isEmpty()) protocol_ = p;
+}
+
+/**
+	@return toujours 0 - une collection n'a pas de categorie parente. En
+	revanche, elle peut posseder un projet parent.
+	@see project()
+*/
+ElementsCategory *ElementsCollection::parentCategory() {
+	return(0);
+}
+
+/**
+	@return toujours une liste vide - une collection n'a pas de categorie
+	parente. En revanche, elle peut posseder un projet parent.
+	@see project()
+*/
+QList<ElementsCategory *> ElementsCollection::parentCategories() {
+	return(QList<ElementsCategory *>());
+}
+
+/**
+	@return toujours false0 - une collection n'a pas de categorie parente. En
+	revanche, elle peut posseder un projet parent.
+	@see project()
+*/
+bool ElementsCollection::hasParentCategory() {
+	return(false);
+}
+
+/**
+	@return toujours 0 - une collection n'a pas de collection parente. En
+	revanche, elle peut posseder un projet parent.
+	@see project()
+*/
+ElementsCollection *ElementsCollection::parentCollection() {
+	return(0);
+}
+
+/**
+	@return toujours false - une collection n'a pas de collection parente. En
+	revanche, elle peut posseder un projet parent.
+	@see project()
+*/
+bool ElementsCollection::hasParentCollection() {
+	return(false);
+}
+
+/**
+	@return toujours false - une collection ne peut etre l'enfant de quoi que ce
+	soit.
+*/
+bool ElementsCollection::isChildOf(ElementsCollectionItem *) {
+	return(false);
+}
+
+/**
+	@return toujours une chaine vide
+*/
+QString ElementsCollection::pathName() const {
+	return(QString());
+}
+
+/**
+	@return toujours une chaine vide
+*/
+QString ElementsCollection::virtualPath() {
+	return(QString());
+}
+
+/**
+	@return le protocole suivi de :// ou une chaine vide si cette collection
+	n'a pas de protocole defini.
+*/
+QString ElementsCollection::fullVirtualPath() {
+	if (protocol().isEmpty()) return(QString());
+	return(protocol() + "://");
+}
+
+/**
+	@return l'emplacement de cette collection
+*/
+ElementsLocation ElementsCollection::location() {
+	return(ElementsLocation(fullVirtualPath(), project()));
+}
+
+/**
+	@return une liste ne contenant que la categorie racine de la collection
+*/
+QList<ElementsCategory *> ElementsCollection::categories() {
+	QList<ElementsCategory *> result;
+	if (ElementsCategory *root = rootCategory()) {
+		result << root;
+	}
+	return(result);
+}
+
+/**
+	@param cat_path chemin d'une categorie sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3
+	@return la categorie demandee, ou 0 si celle-ci n'a pas ete trouvee
+*/
+ElementsCategory *ElementsCollection::category(const QString &cat_path) {
+	ElementsCategory *root = rootCategory();
+	// on doit avoir une categorie racine
+	if (!root) return(0);
+	
+	// le protocole de l'adresse virtuelle doit correspondre
+	if (!cat_path.startsWith(protocol_ + "://", Qt::CaseInsensitive)) return(0);
+	
+	// on enleve le protocole
+	QString cat_path_(cat_path);
+	cat_path_.remove(QRegExp("^" + protocol_ + ":\\/\\/", Qt::CaseInsensitive));
+	
+	// on fait appel a la categorie racine pour le reste du traitement
+	return(root -> category(cat_path_));
+}
+
+/**
+	Cree une categorie. La categorie parente doit exister.
+	@param path chemin d'une categorie a creer sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3
+	@return la nouvelle categorie demandee, ou 0 en cas d'echec
+*/
+ElementsCategory *ElementsCollection::createCategory(const QString &path) {
+	ElementsCategory *root = rootCategory();
+	// on doit avoir une categorie racine
+	if (!root) return(0);
+	
+	// on ne doit pas etre en lecture seule
+	if (!isWritable()) return(0);
+	
+	// le protocole de l'adresse virtuelle doit correspondre
+	if (!path.startsWith(protocol_ + "://", Qt::CaseInsensitive)) return(0);
+	
+	// on enleve le protocole
+	QString path_(path);
+	path_.remove(QRegExp("^" + protocol_ + ":\\/\\/", Qt::CaseInsensitive));
+	
+	// on fait appel a la categorie racine pour le reste du traitement
+	return(root -> createCategory(path_));
+}
+
+/**
+	@return une liste vide
+*/
+QList<ElementDefinition *> ElementsCollection::elements() {
+	return(QList<ElementDefinition *>());
+}
+
+/**
+	@param elmt_path chemin d'un element sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3/dog.elmt
+	@return l'element demande, ou 0 si celui-ci n'a pas ete trouve
+*/
+ElementDefinition *ElementsCollection::element(const QString &elmt_path) {
+	ElementsCategory *root = rootCategory();
+	// on doit avoir une categorie racine
+	if (!root) return(0);
+	
+	// le protocole de l'adresse virtuelle doit correspondre
+	if (!elmt_path.startsWith(protocol_ + "://", Qt::CaseInsensitive)) return(0);
+	
+	// on enleve le protocole
+	QString elmt_path_(elmt_path);
+	elmt_path_.remove(QRegExp("^" + protocol_ + ":\\/\\/", Qt::CaseInsensitive));
+	
+	// on fait appel a la categorie racine pour le reste du traitement
+	return(root -> element(elmt_path_));
+}
+
+/**
+	Cree un element. La categorie parente doit exister.
+	@param path chemin d'un element a creer sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3/dog.elmt
+	@return le nouvel element demande, ou 0 en cas d'echec
+*/
+ElementDefinition *ElementsCollection::createElement(const QString &path) {
+	ElementsCategory *root = rootCategory();
+	// on doit avoir une categorie racine
+	if (!rootCategory()) return(0);
+	
+	// on ne doit pas etre en lecture seule
+	if (!isWritable()) return(0);
+	
+	// le protocole de l'adresse virtuelle doit correspondre
+	if (!path.startsWith(protocol_ + "://", Qt::CaseInsensitive)) return(0);
+	
+	// on enleve le protocole
+	QString path_(path);
+	path_.remove(QRegExp("^" + protocol_ + ":\\/\\/", Qt::CaseInsensitive));
+	
+	// on fait appel a la categorie racine pour le reste du traitement
+	return(root -> createElement(path_));
+}
+
+/**
+	@return true si cette collection est vide (pas de sous-categorie, pas
+	d'element), false sinon.
+*/
+bool ElementsCollection::isEmpty() {
+	ElementsCategory *root_category = rootCategory();
+	if (!root_category) return(true);
+	return(root_category -> isEmpty());
+}
+
+/**
+	@return the count of categories and elements within this collection
+*/
+int ElementsCollection::count() {
+	ElementsCategory *root_category = rootCategory();
+	if (!root_category) return(0);
+	return(root_category -> count());
+}
+
+/**
+	@param item_path chemin d'un item sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3
+	@param prefer_collections true pour renvoyer la collection lorsque le
+	chemin correspond aussi bien a une collection qu'a sa categorie racine
+	@return l'item demande, ou 0 si celui-ci n'a pas ete trouve
+*/
+ElementsCollectionItem *ElementsCollection::item(const QString &item_path, bool prefer_collections) {
+	ElementsCollectionItem *result = 0;
+	
+	// essaye de trouver l'item en tant que categorie
+	result = category(item_path);
+	// si la categorie est trouvee, ...
+	if (result) {
+		// ... qu'il s'agit d'une categorie racine et que l'utilisateur prefere les collections
+		if (prefer_collections && result -> isRootCategory()) {
+			// ... alors on renvoie la collection et non la categorie
+			result = this;
+		}
+	}
+	
+	// sinon essaye de trouver l'item en tant qu'element
+	if (!result) result = element(item_path);
+	
+	return(result);
+}
+
+/**
+	@return The cache used by this collection, or 0 if this collection does not have any
+*/
+ElementsCollectionCache *ElementsCollection::cache() const {
+	return(cache_);
+}
+
+/**
+	@param cache The cache to be used by this collection
+*/
+void ElementsCollection::setCache(ElementsCollectionCache *cache) {
+	cache_ = cache;
+}

Added: trunk/sources/elementscollection.h
===================================================================
--- trunk/sources/elementscollection.h	                        (rev 0)
+++ trunk/sources/elementscollection.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,111 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_COLLECTION_H
+#define ELEMENTS_COLLECTION_H
+#include <QtCore>
+#include <QIcon>
+#include "elementscollectionitem.h"
+class QETProject;
+class ElementsCategory;
+class ElementsCollectionCache;
+class ElementDefinition;
+class MoveElementsHandler;
+/**
+	This abstract class represents an elements collection. For instance, it may
+	represent the collection provided along with QElectroTech, users custom
+	collections or collections embedded within QET project files.
+*/
+class ElementsCollection : public ElementsCollectionItem {
+	Q_OBJECT
+	public:
+	// constructors, destructor
+	ElementsCollection(ElementsCollectionItem * = 0);
+	virtual ~ElementsCollection();
+	
+	private:
+	ElementsCollection(const ElementsCollection &);
+	
+	// Implementations of pure virtual methodes from parent classes
+	public:
+	virtual QString title() const;
+	virtual void setTitle(const QString &);
+	virtual QIcon icon() const;
+	virtual void setIcon(const QIcon &);
+	virtual bool isCollection() const;
+	virtual bool isRootCategory() const;
+	virtual bool isCategory()  const;
+	virtual bool isElement() const;
+	virtual ElementsCollection *toCollection();
+	virtual ElementsCategory *toCategory();
+	virtual ElementsCategory *toPureCategory();
+	virtual ElementDefinition *toElement();
+	virtual ElementsCollectionItem *copy(ElementsCategory *, MoveElementsHandler *, bool = true);
+	virtual ElementsCollectionItem *move(ElementsCategory *, MoveElementsHandler *);
+	virtual bool removeContent();
+	virtual bool remove();
+	virtual QETProject *project();
+	virtual void setProject(QETProject *);
+	virtual QString protocol();
+	virtual void setProtocol(const QString &);
+	virtual ElementsCategory *parentCategory();
+	virtual QList<ElementsCategory *> parentCategories();
+	virtual bool hasParentCategory();
+	virtual ElementsCollection *parentCollection();
+	virtual bool hasParentCollection();
+	virtual bool isChildOf(ElementsCollectionItem *);
+	virtual QString pathName() const;
+	virtual QString virtualPath();
+	virtual QString fullVirtualPath();
+	virtual ElementsLocation location();
+	virtual QList<ElementsCategory *> categories();
+	virtual ElementsCategory *category(const QString &);
+	virtual ElementsCategory *createCategory(const QString &);
+	virtual QList<ElementDefinition *> elements();
+	virtual ElementDefinition *element(const QString &);
+	virtual ElementDefinition *createElement(const QString &);
+	virtual bool isEmpty();
+	virtual int count();
+	
+	// Methods specific to the ElementsCollection class
+	public:
+	/**
+		@return the root category of this collection
+	*/
+	virtual ElementsCategory *rootCategory() = 0;
+	virtual ElementsCollectionItem *item(const QString &, bool = true);
+	virtual bool isCacheable() const = 0;
+	virtual ElementsCollectionCache *cache() const;
+	virtual void setCache(ElementsCollectionCache *);
+	
+	signals:
+	void elementsCollectionChanged(ElementsCollection *);
+	
+	// attributes
+	protected:
+	/// Title to be used when referring to this collection
+	QString title_;
+	/// Icon to be displayed when representing this collection
+	QIcon icon_;
+	/// "Protocol" used to access this collection
+	QString protocol_;
+	/// Project this collection belongs to, if any
+	QETProject *project_;
+	/// Optional cache used to improve performance
+	ElementsCollectionCache *cache_;
+};
+#endif

Added: trunk/sources/elementscollectioncache.cpp
===================================================================
--- trunk/sources/elementscollectioncache.cpp	                        (rev 0)
+++ trunk/sources/elementscollectioncache.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,276 @@
+#include "elementscollectioncache.h"
+#include "elementscollection.h"
+#include "elementscategory.h"
+#include "elementdefinition.h"
+#include "qetgraphicsitem/customelement.h"
+
+/**
+	Construct a cache for elements collections.
+	@param database_path Path of the SQLite database to open.
+	@param parent Parent QObject
+*/
+ElementsCollectionCache::ElementsCollectionCache(const QString &database_path, QObject *parent) :
+	QObject(parent),
+	locale_("en"),
+	pixmap_storage_format_("PNG")
+{
+	// initialize the cache SQLite database
+	static int cache_instances = 0;
+	QString connection_name = QString("ElementsCollectionCache-%1").arg(cache_instances++);
+	cache_db_ = QSqlDatabase::addDatabase("QSQLITE", connection_name);
+	cache_db_.setDatabaseName(database_path);
+	if (!cache_db_.open()) {
+		qDebug() << "Unable to open the SQLite database " << database_path << " as " << connection_name << ": " << cache_db_.lastError();
+	} else {
+		cache_db_.exec("PRAGMA temp_store=MEMORY");
+		cache_db_.exec("PRAGMA journal_mode = MEMORY");
+		cache_db_.exec("PRAGMA synchronous=OFF");
+		cache_db_.exec("PRAGMA cache_size=10000");
+		/// @todo the tables could already exist, handle that case.
+		cache_db_.exec("CREATE TABLE names (path VARCHAR(512) NOT NULL, locale VARCHAR(2) NOT NULL, mtime DATETIME NOT NULL, name VARCHAR(128), PRIMARY KEY(path, locale));");
+		cache_db_.exec("CREATE TABLE pixmaps (path VARCHAR(512) NOT NULL UNIQUE, mtime DATETIME NOT NULL, pixmap BLOB, PRIMARY KEY(path), FOREIGN KEY(path) REFERENCES names (path) ON DELETE CASCADE);");
+		
+		// prepare queries
+		select_name_   = new QSqlQuery(cache_db_);
+		select_pixmap_ = new QSqlQuery(cache_db_);
+		insert_name_   = new QSqlQuery(cache_db_);
+		insert_pixmap_ = new QSqlQuery(cache_db_);
+		select_name_   -> prepare("SELECT name FROM names WHERE path = :path AND locale = :locale AND mtime = :file_mtime");
+		select_pixmap_ -> prepare("SELECT pixmap FROM pixmaps WHERE path = :path AND mtime = :file_mtime");
+		insert_name_   -> prepare("REPLACE INTO names (path, locale, mtime, name) VALUES (:path, :locale, :mtime, :name)");
+		insert_pixmap_ -> prepare("REPLACE INTO pixmaps (path, mtime, pixmap) VALUES (:path, :mtime, :pixmap)");
+	}
+}
+
+/**
+	Destructor
+*/
+ElementsCollectionCache::~ElementsCollectionCache() {
+	cache_db_.close();
+}
+
+/**
+	Define the locale to be used when dealing with names.
+	@param locale New locale to be used.
+*/
+void ElementsCollectionCache::setLocale(const QString &locale) {
+	locale_ = locale;
+}
+
+/**
+	@return The locale to be used when dealing with names.
+*/
+QString ElementsCollectionCache::locale() const {
+	return(locale_);
+}
+
+/**
+	Define the storage format for the pixmaps within the SQLite database. See
+	Qt's QPixmap documentation for more information.
+	@param format The new pixmap storage format.
+	@return True if the format change was accepted, false otherwise.
+*/
+bool ElementsCollectionCache::setPixmapStorageFormat(const QString &format) {
+	if (QImageWriter::supportedImageFormats().contains(format.toAscii())) { 
+		pixmap_storage_format_= format;
+		return(true);
+	}
+	return(false);
+}
+
+/**
+	@return the pixmap storage format. Default is "PNG"
+	@see setPixmapStorageFormat()
+*/
+QString ElementsCollectionCache::pixmapStorageFormat() const {
+	return(pixmap_storage_format_);
+}
+
+/**
+	Indicate the cache a new collection is about to be browsed. This is mainly
+	used to delimit database transactions.
+	@param collection The elements collection about to be browsed.
+*/
+void ElementsCollectionCache::beginCollection(ElementsCollection *collection) {
+	bool use_cache = cache_db_.isOpen() && collection -> isCacheable();
+	if (use_cache) {
+		bool transaction_started = cache_db_.transaction();
+		qDebug() << (transaction_started ? "transaction began for " : "transaction not started for ") << collection -> protocol();
+	}
+}
+
+/**
+	Indicate the cache the currently browsed collection end has been reached. This
+	is mainly used to delimit database transactions.
+	@param collection The elements collection being browsed.
+*/
+void ElementsCollectionCache::endCollection(ElementsCollection *collection) {
+	bool use_cache = cache_db_.isOpen() && collection -> isCacheable();
+	if (use_cache) {
+		bool transaction_commited = cache_db_.commit();
+		if (transaction_commited) {
+			qDebug() << "transaction commited for " << collection -> protocol();
+		} else {
+			qDebug() << "transaction not commited for " << collection -> protocol() << ":" << cache_db_.lastError();
+		}
+	}
+}
+
+/**
+	Retrieve the data for a given element, using the cache if available,
+	filling it otherwise. Data are then available through pixmap() and name()
+	methods.
+	@param element The definition of an element.
+	@see pixmap()
+	@see name()
+	@return True if the retrieval succeeded, false otherwise.
+*/
+bool ElementsCollectionCache::fetchElement(ElementDefinition *element) {
+	// can we use the cache with this element?
+	bool use_cache = cache_db_.isOpen() && element -> parentCollection() -> isCacheable();
+	
+	// attempt to fetch the element name from the cache database
+	if (!use_cache) {
+		return(fetchData(element -> location()));
+	} else {
+		QString element_path = element -> location().toString();
+		QDateTime mtime = element -> modificationTime();
+		bool got_name   = fetchNameFromCache(element_path, mtime);
+		bool got_pixmap = fetchPixmapFromCache(element_path, mtime);
+		if (got_name && got_pixmap) {
+			return(true);
+		}
+		if (fetchData(element -> location())) {
+			cacheName(element_path, mtime);
+			cachePixmap(element_path, mtime);
+		}
+		return(true);
+	}
+}
+
+/**
+	@return The last name fetched through fetchElement().
+*/
+QString ElementsCollectionCache::name() const {
+	return(current_name_);
+}
+
+/**
+	@return The last pixmap fetched through fetchElement().
+*/
+QPixmap ElementsCollectionCache::pixmap() const {
+	return(current_pixmap_);
+}
+
+/**
+	Retrieve the data by building the full CustomElement object matching the
+	given location, without using the cache. Data are then available through
+	pixmap() and name() methods.
+	@param Location Location of a given Element.
+	@return True if the retrieval succeeded, false otherwise.
+*/
+bool ElementsCollectionCache::fetchData(const ElementsLocation &location) {
+	int state;
+	CustomElement *custom_elmt = new CustomElement(location, 0, 0, &state);
+	if (state) {
+		qDebug() << "ElementsCollectionCache::fetchData() : Le chargement du composant" << qPrintable(location.toString()) << "a echoue avec le code d'erreur" << state;
+	} else {
+		current_name_   = custom_elmt -> name();
+		current_pixmap_ = custom_elmt -> pixmap();
+	}
+	delete custom_elmt;
+	return(!state);
+}
+
+/**
+	Retrieve the name for an element, given its path and last modification
+	time. The value is then available through the name() method.
+	@param path Element path (as obtained using ElementsLocation::toString())
+	@param file_mtime Date and time of last modification of this element. Any
+	older cached value will be ignored.
+	@return True if the retrieval succeeded, false otherwise.
+*/
+bool ElementsCollectionCache::fetchNameFromCache(const QString &path, const QDateTime &file_mtime) {
+	select_name_ -> bindValue(":path", path);
+	select_name_ -> bindValue(":locale", locale_);
+	select_name_ -> bindValue(":file_mtime", file_mtime);
+	if (select_name_ -> exec()) {
+		if (select_name_ -> first()) {
+			current_name_ = select_name_ -> value(0).toString();
+			select_name_ -> finish();
+			return(true);
+		}
+	} else {
+		qDebug() << "select_name_->exec() failed";
+	}
+	return(false);
+}
+
+/**
+	Retrieve the pixmap for an element, given its path and last modification
+	time. It is then available through the pixmap() method.
+	@param path Element path (as obtained using ElementsLocation::toString())
+	@param file_mtime Date and time of last modification of this element. Any
+	older cached pixmap will be ignored.
+	@return True if the retrieval succeeded, false otherwise.
+*/
+bool ElementsCollectionCache::fetchPixmapFromCache(const QString &path, const QDateTime &file_mtime) {
+	select_pixmap_ -> bindValue(":path", path);
+	select_pixmap_ -> bindValue(":file_mtime", file_mtime);
+	if (select_pixmap_ -> exec()) {
+		if (select_pixmap_ -> first()) {
+			QByteArray ba = select_pixmap_ -> value(0).toByteArray();
+			// avoid returning always the same pixmap (i.e. same cacheKey())
+			current_pixmap_.detach();
+			current_pixmap_.loadFromData(ba, qPrintable(pixmap_storage_format_));
+			select_pixmap_ -> finish();
+		}
+		return(true);
+	} else {
+		qDebug() << "select_pixmap_->exec() failed";
+	}
+	return(false);
+}
+
+/**
+	Cache the current (i.e. last retrieved) name. The cache entry will use
+	the current date and time and the locale set via setLocale().
+	@param path Element path (as obtained using ElementsLocation::toString())
+	@param mtime Modification time associated with the cache entry -- defaults to current datetime
+	@return True if the caching succeeded, false otherwise.
+	@see name()
+*/
+bool ElementsCollectionCache::cacheName(const QString &path, const QDateTime &mtime) {
+	insert_name_ -> bindValue(":path",   path);
+	insert_name_ -> bindValue(":locale", locale_);
+	insert_name_ -> bindValue(":mtime",  QVariant(mtime));
+	insert_name_ -> bindValue(":name",   current_name_);
+	if (!insert_name_ -> exec()) {
+		qDebug() << cache_db_.lastError();
+		return(false);
+	}
+	return(true);
+}
+
+/**
+	Cache the current (i.e. last retrieved) pixmap. The cache entry will use
+	the current date and time.
+	@param path Element path (as obtained using ElementsLocation::toString())
+	@param mtime Modification time associated with the cache entry -- defaults to current datetime
+	@return True if the caching succeeded, false otherwise.
+	@see pixmap()
+*/
+bool ElementsCollectionCache::cachePixmap(const QString &path, const QDateTime &mtime) {
+	QByteArray ba;
+	QBuffer buffer(&ba);
+	buffer.open(QIODevice::WriteOnly);
+	current_pixmap_.save(&buffer, qPrintable(pixmap_storage_format_));
+	insert_pixmap_ -> bindValue(":path", path);
+	insert_pixmap_ -> bindValue(":mtime", QVariant(mtime));
+	insert_pixmap_ -> bindValue(":pixmap", QVariant(ba));
+	if (!insert_pixmap_->exec()) {
+		qDebug() << cache_db_.lastError();
+		return(false);
+	}
+	return(true);
+}

Added: trunk/sources/elementscollectioncache.h
===================================================================
--- trunk/sources/elementscollectioncache.h	                        (rev 0)
+++ trunk/sources/elementscollectioncache.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,67 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_COLLECTION_CACHE_H
+#define ELEMENTS_COLLECTION_CACHE_H
+#include <QtCore>
+#include <QtSql>
+#include "elementslocation.h"
+class ElementsCollection;
+class ElementsCategory;
+class ElementDefinition;
+/**
+	This class implements a SQLite cache for data related to elements
+	collections, mainly names and pixmaps. This avoids the cost of parsing XML
+	definitions of elements and building full CustomElement objects when
+	(re)loading the elements panel.
+*/
+class ElementsCollectionCache : public QObject {
+	public:
+	// constructor, destructor
+	ElementsCollectionCache(const QString &database_path, QObject * = 0);
+	virtual ~ElementsCollectionCache();
+	
+	// methods
+	public:
+	void setLocale(const QString &);
+	QString locale() const;
+	bool setPixmapStorageFormat(const QString &);
+	QString pixmapStorageFormat() const;
+	void beginCollection(ElementsCollection *);
+	void endCollection(ElementsCollection *);
+	bool fetchElement(ElementDefinition *);
+	QString name() const;
+	QPixmap pixmap() const;
+	bool fetchData(const ElementsLocation &);
+	bool fetchNameFromCache(const QString &, const QDateTime &);
+	bool fetchPixmapFromCache(const QString &, const QDateTime &);
+	bool cacheName(const QString &, const QDateTime & = QDateTime::currentDateTime());
+	bool cachePixmap(const QString &, const QDateTime & = QDateTime::currentDateTime());
+	
+	// attributes
+	private:
+	QSqlDatabase cache_db_;         ///< Object providing access to the SQLite database this cache relies on
+	QSqlQuery *select_name_;        ///< Prepared statement to fetch names from the cache
+	QSqlQuery *select_pixmap_;      ///< Prepared statement to fetch pixmaps from the cache
+	QSqlQuery *insert_name_;        ///< Prepared statement to insert names into the cache
+	QSqlQuery *insert_pixmap_;      ///< Prepared statement to insert pixmaps into the cache
+	QString locale_;                ///< Locale to be used when dealing with names
+	QString pixmap_storage_format_; ///< Storage format for cached pixmaps
+	QString current_name_;          ///< Last name fetched
+	QPixmap current_pixmap_;        ///< Last pixmap fetched
+};
+#endif

Added: trunk/sources/elementscollectionitem.h
===================================================================
--- trunk/sources/elementscollectionitem.h	                        (rev 0)
+++ trunk/sources/elementscollectionitem.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,212 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_COLLECTION_ITEM_H
+#define ELEMENTS_COLLECTION_ITEM_H
+#include <QtCore>
+#include "elementslocation.h"
+class ElementsCollection;
+class ElementsCategory;
+class ElementDefinition;
+class MoveElementsHandler;
+/**
+	This interface is the base class for all classes representing a part of an
+	elements collection.
+*/
+class ElementsCollectionItem : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	ElementsCollectionItem(ElementsCollectionItem *parent = 0) : QObject(parent) {};
+	virtual ~ElementsCollectionItem() {};
+	
+	private:
+	ElementsCollectionItem(const ElementsCollectionItem &);
+	
+	// methods
+	public:
+	/// @return whether the item is an elements collection
+	virtual bool isCollection() const = 0;
+	/// @return whether the item is an elements category
+	virtual bool isCategory() const = 0;
+	/// @return whether the item is the root category of a collection
+	virtual bool isRootCategory() const = 0;
+	/// @return whether the item is an element
+	virtual bool isElement() const = 0;
+	
+	/**
+		@return a pointer to this item as a collection object, or 0 if this item is not a collection.
+	*/
+	virtual ElementsCollection *toCollection() = 0;
+	/**
+		@return a pointer to this item as a category object.
+		If this item is a collection, return a valid pointer to its root category.
+		If this item is a category, return a valid pointer.
+		If this item is an element, return a valid pointer to its parent category.
+	*/
+	virtual ElementsCategory *toCategory() = 0;
+	/**
+		@return a pointer to this item as a category if and only if this object is a non-root elements category.
+		If this item is a collection, return 0.
+		If this item is a category, return a valid pointer.
+		If this item is an element, return 0.
+	*/
+	virtual ElementsCategory *toPureCategory() = 0;
+	/**
+		@return a pointer to this item as an element object.
+	*/
+	virtual ElementDefinition *toElement() = 0;
+	
+	/**
+		Copy this item to the specified target, using a specific strategy to handle
+		corner cases (errors, conflicts, confirmations, ...), recursively or not.
+	*/
+	virtual ElementsCollectionItem *copy(ElementsCategory *, MoveElementsHandler *, bool = true) = 0;
+	
+	/**
+		Move this item to the specified destination, using a specific strategy to
+		handle corner cases (errors, conflicts, confirmations, ...).
+	*/
+	virtual ElementsCollectionItem *move(ElementsCategory *, MoveElementsHandler *) = 0;
+	
+	/// Reload this item
+	virtual void reload() = 0;
+	/// @return whether the item really exists
+	virtual bool exists() = 0;
+	/// @return whether the item is readable
+	virtual bool isReadable() = 0;
+	/// @return whether the item is writable
+	virtual bool isWritable() = 0;
+	/**
+		Delete the item content (elements and subcategories) without deleting the item itself.
+		@return true if the operation succeeded, false otherwise
+	*/
+	virtual bool removeContent() = 0;
+	/**
+		Delete the item content (elements and categories) before deleting the item itself.
+		@return true if the operation succeeded, false otherwise
+	*/
+	virtual bool remove() = 0;
+	/**
+		Save the item.
+		@return true if the operation succeeded, false otherwise
+	*/
+	virtual bool write() = 0;
+	/**
+		@return the project this item belongs to
+	*/
+	virtual QETProject *project() = 0;
+	/**
+		Set the project this item belongs to.
+	*/
+	virtual void setProject(QETProject *) = 0;
+	/**
+		@return the protocol used to access the collection this item belongs to.
+	*/
+	virtual QString protocol() = 0;
+	/**
+		Set the protocol used to access the collection this item belongs to.
+	*/
+	virtual void setProtocol(const QString &) = 0;
+	/**
+		@return the parent category of this item, or 0 if it does not have any
+	*/
+	virtual ElementsCategory *parentCategory() = 0;
+	/**
+		@return the list of parent categories of this item
+	*/
+	virtual QList<ElementsCategory *> parentCategories() = 0;
+	/**
+		@return whether this item has a parent category
+	*/
+	virtual bool hasParentCategory() = 0;
+	/**
+		@return the parent collection of this item, or 0 if it does not have any
+	*/
+	virtual ElementsCollection *parentCollection() = 0;
+	/**
+		@return whether this item belongs to an elements collection
+	*/
+	virtual bool hasParentCollection() = 0;
+	/**
+		@param other_item other item
+		@return whether \a other_item is a direct or indirect parent of this item.
+	*/
+	virtual bool isChildOf(ElementsCollectionItem *other_item) = 0;
+	/**
+		@return the name of this item within the elements tree.
+	*/
+	virtual QString pathName() const = 0;
+	/**
+		@return the virtual path to this item within the elements tree, protocol excluded.
+	*/
+	virtual QString virtualPath() = 0;
+	/**
+		@return the virtual path to this item within the elements tree, protocol included.
+	*/
+	virtual QString fullVirtualPath() = 0;
+	/**
+		@return the location of this item
+	*/
+	virtual ElementsLocation location() = 0;
+	/**
+		@return whether this item is stored somewhere on the filesystem.
+	*/
+	virtual bool hasFilePath() = 0;
+	/**
+		@return the path of this item within the filesystem
+	*/
+	virtual QString filePath() = 0;
+	/**
+		Set the path to this item within the filesystem.
+	*/
+	virtual void setFilePath(const QString &) = 0;
+	/**
+		@return the list of direct child elements category within this item
+	*/
+	virtual QList<ElementsCategory *> categories() = 0;
+	/**
+		@return the specified category, provided its virtual path
+	*/
+	virtual ElementsCategory *category(const QString &) = 0;
+	/**
+		@return a new category, created from the spcified virtual path
+	*/
+	virtual ElementsCategory *createCategory(const QString &) = 0;
+	/**
+		@return the list of child elements within this item
+	*/
+	virtual QList<ElementDefinition *> elements() = 0;
+	/**
+		@return an element, provided its virtual path
+	*/
+	virtual ElementDefinition *element(const QString &) = 0;
+	/**
+		@return a new element, created from the specified virtual path
+	*/
+	virtual ElementDefinition *createElement(const QString &) = 0;
+	/**
+		@return whether the item is empty
+	*/
+	virtual bool isEmpty() = 0;
+	/**
+		@return the count of categories and elements within this item
+	*/
+	virtual int count() = 0;
+};
+#endif

Added: trunk/sources/elementslocation.cpp
===================================================================
--- trunk/sources/elementslocation.cpp	                        (rev 0)
+++ trunk/sources/elementslocation.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,234 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementslocation.h"
+#include "qetapp.h"
+
+// make this class usable with QVariant
+int ElementsLocation::MetaTypeId = qRegisterMetaType<ElementsLocation>("ElementsLocation");
+
+/**
+	Constructeur par defaut
+*/
+ElementsLocation::ElementsLocation() : project_(0) {
+}
+
+/**
+	Constructeur
+	@param p Chemin de l'emplacement de l'element
+	@param pr Projet de l'emplacement de l'element
+*/
+ElementsLocation::ElementsLocation(const QString &p, QETProject *pr) :
+	project_(pr)
+{
+	setPath(p);
+}
+
+/**
+	Destructeur
+*/
+ElementsLocation::~ElementsLocation() {
+}
+
+/**
+	Constructeur de copie
+	@param other Autre emplacement d'element a copier
+*/
+ElementsLocation::ElementsLocation(const ElementsLocation &other) :
+	path_(other.path_),
+	project_(other.project_)
+{
+}
+
+/**
+	Operateur d'affectation
+	@param other Autre emplacement d'element a affecter
+*/
+ElementsLocation &ElementsLocation::operator=(const ElementsLocation &other) {
+	path_ = other.path_;
+	project_ = other.project_;
+	return(*this);
+}
+
+/**
+	Operateur de comparaison
+	@param other Autre emplacement d'element a comparer
+	@return true si other et cet ElementsLocation sont identiques, false sinon
+*/
+bool ElementsLocation::operator==(const ElementsLocation &other) const {
+	return(
+		path_ == other.path_ &&\
+		project_ == other.project_
+	);
+}
+
+/**
+	Operateur de comparaison
+	@param other Autre emplacement d'element a comparer
+	@return true si other et cet ElementsLocation sont differents, false sinon
+*/
+bool ElementsLocation::operator!=(const ElementsLocation &other) const {
+	return(
+		path_ != other.path_ ||\
+		project_ != other.project_
+	);
+}
+
+/**
+	@return le nom de base de l'element
+*/
+QString ElementsLocation::baseName() const {
+	QRegExp regexp("^.*([^/]+)\\.elmt$");
+	if (regexp.exactMatch(path_)) {
+		return(regexp.capturedTexts().at(1));
+	}
+	return(QString());
+}
+
+/**
+	@return Le chemin virtuel de cet emplacement
+*/
+QString ElementsLocation::path() const {
+	return(path_);
+}
+
+/**
+	Change le chemin virtuel de cet emplacement
+	@param p Nouveau chemin virtuel
+*/
+void ElementsLocation::setPath(const QString &p) {
+#ifdef Q_OS_WIN32
+	// sous Windows : on convertit les backslashs en slashs
+	path_ = QDir::fromNativeSeparators(p);
+#else
+	// ailleurs : si on detecte des backslashs, on tente d'etre "compatible"
+	path_ = p;
+	path_.replace("\\", "/");
+#endif
+}
+
+/**
+	Ajoute une chaine au chemin
+	@param string Chaine a ajouter
+	@return true si l'operation a reussi, false si l'operation n'a pas de sens.
+	Par exemple, il n'y a pas de sens a vouloir ajouter quelque chose apres le
+	chemin d'un element.
+*/
+bool ElementsLocation::addToPath(const QString &string) {
+	if (path_.endsWith(".elmt", Qt::CaseInsensitive)) return(false);
+	if (!path_.endsWith("/") && !string.startsWith("/")) path_ += "/";
+	path_ += string;
+	return(true);
+}
+
+/**
+	@return the location of the parent category, or a copy of this location
+	when it represents a root category.
+*/
+ElementsLocation ElementsLocation::parent() const {
+	ElementsLocation copy(*this);
+	QRegExp re1("^([a-z]+://)(.*)/*$");
+	if (re1.exactMatch(path_)) {
+		QString path_proto = re1.capturedTexts().at(1);
+		QString path_path = re1.capturedTexts().at(2);
+		QString parent_path = path_path.remove(QRegExp("/*[^/]+$"));
+		copy.setPath(path_proto + parent_path);
+	}
+	return(copy);
+}
+
+/**
+	@return le projet de cet emplacement ou 0 si celui-ci n'est pas lie a
+	un projet.
+*/
+QETProject *ElementsLocation::project() const {
+	return(project_);
+}
+
+/**
+	@param project le nouveau projet pointe par cet emplacement
+	Indiquer 0 pour que cet emplacement ne soit plus lie a un projet.
+*/
+void ElementsLocation::setProject(QETProject *project) {
+	project_ = project;
+}
+
+/**
+	@return true si l'emplacement semble utilisable (chemin virtuel non vide).
+*/
+bool ElementsLocation::isNull() const {
+	return(path_.isEmpty());
+}
+
+/**
+	@return Une chaine de caracteres representant l'emplacement
+*/
+QString ElementsLocation::toString() const {
+	QString result;
+	if (project_) {
+		int project_id = QETApp::projectId(project_);
+		if (project_id != -1) {
+			result += "project" + QString().setNum(project_id) + "+";
+		}
+	}
+	result += path_;
+	return(result);
+}
+
+/**
+	Charge l'emplacemant a partir d'une chaine de caractere du type
+	project42+embed://foo/bar/thing.elmt
+	@param string Une chaine de caracteres representant l'emplacement
+*/
+void ElementsLocation::fromString(const QString &string) {
+	QRegExp embedded("^project([0-9]+)\\+(embed:\\/\\/.*)$", Qt::CaseInsensitive);
+	if (embedded.exactMatch(string)) {
+		bool conv_ok = false;
+		uint project_id = embedded.capturedTexts().at(1).toUInt(&conv_ok);
+		if (conv_ok) {
+			QETProject *the_project = QETApp::project(project_id);
+			if (the_project) {
+				path_ = embedded.capturedTexts().at(2);
+				project_ = the_project;
+				return;
+			}
+		}
+	}
+	
+	// fallback : le chemin devient la chaine complete et aucun projet n'est utilise
+	path_ = string;
+	project_ = 0;
+}
+
+/**
+	@param string Une chaine de caracteres representant l'emplacement
+	@return un emplacemant a partir d'une chaine de caractere du type
+	project42+embed://foo/bar/thing.elmt
+*/
+ElementsLocation ElementsLocation::locationFromString(const QString &string) {
+	ElementsLocation location;
+	location.fromString(string);
+	return(location);
+}
+
+/**
+	@param location A standard element location
+	@return a hash identifying this location
+*/
+uint qHash(const ElementsLocation &location) {
+	return(qHash(location.toString()));
+}

Added: trunk/sources/elementslocation.h
===================================================================
--- trunk/sources/elementslocation.h	                        (rev 0)
+++ trunk/sources/elementslocation.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,63 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_LOCATION_H
+#define ELEMENTS_LOCATION_H
+#include <QtCore>
+#include <QString>
+class QETProject;
+/**
+	Cette classe represente la localisation, l'emplacement d'un element ou
+	d'une categorie, voire d'une collection... dans une collection. Elle
+	encapsule un chemin virtuel.
+*/
+class ElementsLocation {
+	// constructors, destructor et operateur d'affectation
+	public:
+	ElementsLocation();
+	explicit ElementsLocation(const QString &, QETProject * = 0);
+	ElementsLocation(const ElementsLocation &);
+	virtual ~ElementsLocation();
+	ElementsLocation &operator=(const ElementsLocation &);
+	bool operator==(const ElementsLocation &) const;
+	bool operator!=(const ElementsLocation &) const;
+	
+	// methods
+	public:
+	QString baseName() const;
+	QString path() const;
+	void setPath(const QString &);
+	bool addToPath(const QString &);
+	ElementsLocation parent() const;
+	QETProject *project() const;
+	void setProject(QETProject *);
+	bool isNull() const;
+	QString toString() const;
+	void fromString(const QString &);
+	static ElementsLocation locationFromString(const QString &);
+	
+	// attributes
+	private:
+	QString path_;
+	QETProject *project_;
+	
+	public:
+	static int MetaTypeId; ///< Id of the corresponding Qt meta type
+};
+Q_DECLARE_METATYPE(ElementsLocation)
+uint qHash(const ElementsLocation &);
+#endif

Added: trunk/sources/elementsmover.cpp
===================================================================
--- trunk/sources/elementsmover.cpp	                        (rev 0)
+++ trunk/sources/elementsmover.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,186 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementsmover.h"
+#include "qetgraphicsitem/conductor.h"
+#include "qetgraphicsitem/conductortextitem.h"
+#include "diagram.h"
+#include "diagramcommands.h"
+#include "qetgraphicsitem/element.h"
+#include "qetgraphicsitem/independenttextitem.h"
+#include "qetgraphicsitem/diagramimageitem.h"
+
+/**
+	Constructeur
+*/
+ElementsMover::ElementsMover() :
+	movement_running_(false),
+	current_movement_(),
+	diagram_(0),
+	movement_driver_(0),
+	moved_content_()
+{
+	
+}
+
+/**
+	Destructeur
+*/
+ElementsMover::~ElementsMover() {
+}
+
+/**
+	@return true si ce gestionnaire de deplacement est pret a etre utilise,
+	false sinon. Un gestionnaire de deplacement est pret a etre utilise a partir
+	du moment ou le mouvement precedemment gere n'est plus en cours.
+*/
+bool ElementsMover::isReady() const {
+	return(!movement_running_);
+}
+
+/**
+	Demarre un nouveau mouvement d'element
+	@param diagram Schema sur lequel se deroule le deplacement
+	@param driver_item Item deplace par la souris et ne necessitant donc pas
+	d'etre deplace lors des appels a continueMovement.
+	@return le nombre d'items concernes par le deplacement, ou -1 si le
+	mouvement n'a pas ete initie
+*/
+int ElementsMover::beginMovement(Diagram *diagram, QGraphicsItem *driver_item) {
+	// il ne doit pas y avoir de mouvement en cours
+	if (movement_running_) return(-1);
+	
+	// on s'assure que l'on dispose d'un schema pour travailler
+	if (!diagram) return(-1);
+	diagram_ = diagram;
+	
+	// on prend en compte le driver_item
+	movement_driver_ = driver_item;
+	
+	// au debut du mouvement, le deplacement est nul
+	current_movement_ = QPointF(0.0, 0.0);
+	
+	// on stocke dans cet objet les items concernes par le deplacement
+	moved_content_ = diagram -> selectedContent();
+	
+	// on a egalement besoin de retenir la position des champs de textes
+	// rattaches a un conducteur (ConductorTextItem) si cette position a ete
+	// personnalisee.
+	// ceci n'est necessaire que pour les conducteurs dont le trajet sera
+	// recalcule a cause du mouvement
+	foreach(Conductor *conductor, moved_content_.conductorsToUpdate) {
+		if (ConductorTextItem *text_item = conductor -> textItem()) {
+			if (text_item -> wasMovedByUser()) {
+				updated_conductors_text_pos_.insert(
+					text_item,
+					text_item -> pos()
+				);
+			}
+		}
+	}
+	// on s'assure qu'il y a quelque chose a deplacer
+	if (!moved_content_.count()) return(-1);
+	
+	// a ce stade, on dispose de toutes les informations necessaires pour
+	// prendre en compte les mouvements
+	
+	// il y a desormais un mouvement en cours
+	movement_running_ = true;
+	
+	return(moved_content_.count());
+}
+
+/**
+	Ajoute un mouvement au deplacement en cours. Cette methode
+	@param movement mouvement a ajouter au deplacement en cours
+*/
+void ElementsMover::continueMovement(const QPointF &movement) {
+	// un mouvement doit avoir ete initie
+	if (!movement_running_) return;
+	
+	// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
+	if (movement.isNull()) return;
+	
+	// prise en compte du mouvement
+	current_movement_ += movement;
+	
+	// deplace les elements selectionnes
+	foreach(Element *element, moved_content_.elements) {
+		if (movement_driver_ && element == movement_driver_) continue;
+		element -> setPos(element -> pos() + movement);
+	}
+	
+	// deplace certains conducteurs
+	foreach(Conductor *conductor, moved_content_.conductorsToMove) {
+		conductor -> setPos(conductor -> pos() + movement);
+	}
+	
+	// recalcule les autres conducteurs
+	foreach(Conductor *conductor, moved_content_.conductorsToUpdate) {
+		conductor -> updatePath();
+	}
+	
+	// deplace les champs de texte
+	foreach(IndependentTextItem *text_field, moved_content_.textFields) {
+		if (movement_driver_ && text_field == movement_driver_) continue;
+		text_field -> setPos(text_field -> pos() + movement);
+	}
+
+	//deplace les images
+	foreach(DiagramImageItem *dii, moved_content_.images) {
+		if (movement_driver_ && dii == movement_driver_) continue;
+		dii -> setPos(dii -> pos() + movement);
+	}
+
+}
+
+/**
+	Termine le deplacement en creant un objet d'annulation et en l'ajoutant a
+	la QUndoStack du schema concerne.
+	@see Diagram::undoStack()
+*/
+void ElementsMover::endMovement() {
+	// un mouvement doit avoir ete initie
+	if (!movement_running_) return;
+	
+	// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
+	if (!current_movement_.isNull()) {
+		// cree un objet d'annulation pour le mouvement ainsi realise
+		MoveElementsCommand *undo_object = new MoveElementsCommand(
+			diagram_,
+			moved_content_,
+			current_movement_
+		);
+		
+		// ajoute les informations necessaires au repositionnement des champs
+		// de textes des conducteurs
+		foreach(ConductorTextItem *text_item, updated_conductors_text_pos_.keys()) {
+			if (text_item -> pos() != updated_conductors_text_pos_[text_item]) {
+				undo_object -> addConductorTextItemMovement(
+					text_item,
+					updated_conductors_text_pos_[text_item],
+					text_item -> pos()
+				);
+			}
+		}
+		
+		diagram_ -> undoStack().push(undo_object);
+	}
+	
+	// il n'y a plus de mouvement en cours
+	movement_running_ = false;
+}

Added: trunk/sources/elementsmover.h
===================================================================
--- trunk/sources/elementsmover.h	                        (rev 0)
+++ trunk/sources/elementsmover.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,52 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_MOVER_H
+#define ELEMENTS_MOVER_H
+#include <QtGui>
+#include "diagramcontent.h"
+class ConductorTextItem;
+class Diagram;
+/**
+	This class manages the interactive movement of different items (elements,
+	conductors, text items) on a particular diagram.
+*/
+class ElementsMover {
+	// constructors, destructor
+	public:
+	ElementsMover();
+	virtual ~ElementsMover();
+	private:
+	ElementsMover(const ElementsMover &);
+	
+	// methods
+	public:
+	bool isReady() const;
+	int  beginMovement(Diagram *, QGraphicsItem * = 0);
+	void continueMovement(const QPointF &);
+	void endMovement();
+	
+	// attributes
+	private:
+	bool movement_running_;
+	QPointF current_movement_;
+	Diagram *diagram_;
+	QGraphicsItem *movement_driver_;
+	DiagramContent moved_content_;
+	QHash<ConductorTextItem *, QPointF> updated_conductors_text_pos_;
+};
+#endif

Added: trunk/sources/elementspanel.cpp
===================================================================
--- trunk/sources/elementspanel.cpp	                        (rev 0)
+++ trunk/sources/elementspanel.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,884 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementspanel.h"
+#include "qetapp.h"
+#include "qetproject.h"
+#include "diagram.h"
+#include "elementscategory.h"
+#include "elementscollectioncache.h"
+#include "qetgraphicsitem/customelement.h"
+#include "fileelementscollection.h"
+#include "fileelementdefinition.h"
+#include "qeticons.h"
+#include "templatescollection.h"
+#include "treecoloranimation.h"
+
+/*
+	Lorsque le flag ENABLE_PANEL_DND_CHECKS est defini, le panel d'elements
+	effectue des verifications lors des drag'n drop d'elements et categories.
+	Par exemple, il verifie qu'une categorie cible est accessible en ecriture
+	avant d'y autoriser le drop d'un element.
+	Supprimer ce flag permet de tester le comportement des fonctions de gestion
+	des items (copy, move, etc.).
+*/
+#define ENABLE_PANEL_DND_CHECKS
+
+/*
+	Largeur maximale, en pixels, de la pixmap accrochee au pointeur de la
+	souris
+*/
+#define QET_MAX_DND_PIXMAP_WIDTH 500
+
+/*
+	Hauteur maximale, en pixels, de la pixmap accrochee au pointeur de la
+	souris
+*/
+#define QET_MAX_DND_PIXMAP_HEIGHT 375
+
+/**
+	Constructeur
+	@param parent Le QWidget parent du panel d'appareils
+*/
+ElementsPanel::ElementsPanel(QWidget *parent) :
+	GenericPanel(parent),
+	common_collection_item_(0),
+	custom_collection_item_(0),
+	first_reload_(true)
+{
+	// selection unique
+	setSelectionMode(QAbstractItemView::SingleSelection);
+	setColumnCount(1);
+	setExpandsOnDoubleClick(true);
+	setMouseTracking(true);
+	
+	// drag'n drop autorise
+	setDragEnabled(true);
+	setAcceptDrops(true);
+	setDropIndicatorShown(true);
+	setAutoExpandDelay(1000);
+	
+	// force du noir sur une alternance de blanc (comme le schema) et de gris
+	// clair, avec du blanc sur bleu pas trop fonce pour la selection
+	QPalette qp = palette();
+	qp.setColor(QPalette::Text,            Qt::black);
+	qp.setColor(QPalette::Base,            Qt::white);
+	qp.setColor(QPalette::AlternateBase,   QColor("#e8e8e8"));
+	qp.setColor(QPalette::Highlight,       QColor("#678db2"));
+	qp.setColor(QPalette::HighlightedText, Qt::white);
+	setPalette(qp);
+	
+	// we handle double click on items ourselves
+	connect(
+		this,
+		SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
+		this,
+		SLOT(slot_doubleClick(QTreeWidgetItem *, int))
+	);
+	
+	connect(this, SIGNAL(firstActivated()), this, SLOT(firstActivation()));
+	connect(this, SIGNAL(panelContentChanged()), this, SLOT(panelContentChange()));
+	
+	// emet un signal au lieu de gerer son menu contextuel
+	setContextMenuPolicy(Qt::CustomContextMenu);
+	
+	setElementsCache(QETApp::collectionCache());
+}
+
+/**
+	Destructeur
+*/
+ElementsPanel::~ElementsPanel() {
+}
+
+/**
+	@param qtwi Un QTreeWidgetItem
+	@return true si qtwi represente un element, false sinon
+*/
+bool ElementsPanel::itemIsWritable(QTreeWidgetItem *qtwi) const {
+	if (ElementsCollectionItem *qtwi_item = collectionItemForItem(qtwi)) {
+		return(qtwi_item -> isWritable());
+	}
+	return(false);
+}
+
+
+/**
+	@return true si l'item selectionne est accessible en ecriture, false sinon
+*/
+bool ElementsPanel::selectedItemIsWritable() const {
+	if (ElementsCollectionItem *selected_item = selectedItem()) {
+		return(selected_item -> isWritable());
+	}
+	return(false);
+}
+
+/**
+	@return la collection, la categorie ou l'element selectionne(e)
+*/
+ElementsCollectionItem *ElementsPanel::selectedItem() const {
+	ElementsLocation selected_location(selectedElementLocation());
+	if (!selected_location.isNull()) {
+		return(QETApp::collectionItem(selected_location));
+	}
+	return(0);
+}
+
+/**
+	Gere l'entree d'un drag'n drop. L'evenement est accepte si les donnees
+	fournies contiennent un type MIME representant une categorie ou un element
+	QET.
+	@param e QDragEnterEvent decrivant l'entree du drag'n drop
+*/
+void ElementsPanel::dragEnterEvent(QDragEnterEvent *e) {
+	if (e -> mimeData() -> hasFormat("application/x-qet-category-uri")) {
+		e -> acceptProposedAction();
+	} else if (e -> mimeData() -> hasFormat("application/x-qet-element-uri")) {
+		e -> acceptProposedAction();
+	}
+}
+
+/**
+	Gere le mouvement lors d'un drag'n drop
+*/
+void ElementsPanel::dragMoveEvent(QDragMoveEvent *e) {
+	// scrolle lorsque le curseur est pres des bords
+	int limit = 40;
+	QScrollBar *scroll_bar = verticalScrollBar();
+	if (e -> pos().y() < limit) {
+		scroll_bar -> setValue(scroll_bar -> value() - 1);
+	} else if (e -> pos().y() > height() - limit) {
+		scroll_bar -> setValue(scroll_bar -> value() + 1);
+	}
+	
+	QTreeWidget::dragMoveEvent(e);
+	
+	// recupere la categorie cible pour le deplacement / la copie
+	ElementsCategory *target_category = categoryForPos(e -> pos());
+	if (!target_category) {
+		e -> ignore();
+		return;
+	}
+	
+	// recupere la source (categorie ou element) pour le deplacement / la copie
+	ElementsLocation dropped_location = ElementsLocation::locationFromString(e -> mimeData() -> text());
+	ElementsCollectionItem *source_item = QETApp::collectionItem(dropped_location, false);
+	if (!source_item) {
+		e -> ignore();
+		return;
+	}
+	
+#ifdef ENABLE_PANEL_DND_CHECKS
+	// ne prend pas en consideration le drop d'un item sur lui-meme ou une categorie imbriquee
+	if (
+		source_item -> location() == target_category -> location() ||\
+		target_category -> isChildOf(source_item)
+	) {
+		e -> ignore();
+		return;
+	}
+	
+	// s'assure que la categorie cible est accessible en ecriture
+	if (!target_category -> isWritable()) {
+		e -> ignore();
+		return;
+	}
+#endif
+	
+	e -> accept();
+	/// @todo mettre en valeur le lieu de depot 
+}
+
+/**
+	Gere le depot lors d'un drag'n drop
+	@param e QDropEvent decrivant le depot
+*/
+void ElementsPanel::dropEvent(QDropEvent *e) {
+	// recupere la categorie cible pour le deplacement / la copie
+	ElementsCategory *target_category = categoryForPos(e -> pos());
+	if (!target_category) {
+		e -> ignore();
+		return;
+	}
+	
+	// recupere la source (categorie ou element) pour le deplacement / la copie
+	ElementsLocation dropped_location = ElementsLocation::locationFromString(e -> mimeData() -> text());
+	ElementsCollectionItem *source_item = QETApp::collectionItem(dropped_location, false);
+	if (!source_item) {
+		e -> ignore();
+		return;
+	}
+	
+#ifdef ENABLE_PANEL_DND_CHECKS
+	// ne prend pas en consideration le drop d'un item sur lui-meme ou une categorie imbriquee
+	if (
+		source_item -> location() == target_category -> location() ||\
+		target_category -> isChildOf(source_item)
+	) {
+		e -> ignore();
+		return;
+	}
+	
+	// s'assure que la categorie cible est accessible en ecriture
+	if (!target_category -> isWritable()) {
+		e -> ignore();
+		return;
+	}
+#endif
+	
+	e -> accept();
+	emit(requestForMoveElements(source_item, target_category, e -> pos()));
+}
+
+/**
+	Gere le debut des drag'n drop
+	@param supportedActions Les actions supportees
+*/
+void ElementsPanel::startDrag(Qt::DropActions supportedActions) {
+	Q_UNUSED(supportedActions);
+	// recupere l'emplacement selectionne
+	ElementsLocation element_location = selectedElementLocation();
+	if (!element_location.isNull()) {
+		startElementDrag(element_location);
+		return;
+	}
+	
+	TitleBlockTemplateLocation tbt_location = selectedTemplateLocation();
+	if (tbt_location.isValid()) {
+		startTitleBlockTemplateDrag(tbt_location);
+		return;
+	}
+}
+
+/**
+	Handle the dragging of an element.
+	@param location Location of the dragged element
+*/
+void ElementsPanel::startElementDrag(const ElementsLocation &location) {
+	// recupere la selection
+	ElementsCollectionItem *selected_item = QETApp::collectionItem(location);
+	if (!selected_item) return;
+	
+	// objet QDrag pour realiser le drag'n drop
+	QDrag *drag = new QDrag(this);
+	
+	// donnees qui seront transmises par le drag'n drop
+	QString location_string(location.toString());
+	QMimeData *mimeData = new QMimeData();
+	mimeData -> setText(location_string);
+	
+	if (selected_item -> isCategory() || selected_item -> isCollection()) {
+		mimeData -> setData("application/x-qet-category-uri", location_string.toAscii());
+		drag -> setPixmap(QET::Icons::Folder.pixmap(22, 22));
+	} else if (selected_item -> isElement()) {
+		mimeData -> setData("application/x-qet-element-uri", location_string.toAscii());
+		
+		// element temporaire pour fournir un apercu
+		int elmt_creation_state;
+		Element *temp_elmt = new CustomElement(location, 0, 0, &elmt_creation_state);
+		if (elmt_creation_state) {
+			delete temp_elmt;
+			return;
+		}
+		
+		// accrochage d'une pixmap representant l'appareil au pointeur
+		QPixmap elmt_pixmap(temp_elmt -> pixmap());
+		QPoint elmt_hotspot(temp_elmt -> hotspot());
+		
+		// ajuste la pixmap si celle-ci est trop grande
+		QPoint elmt_pixmap_size(elmt_pixmap.width(), elmt_pixmap.height());
+		if (elmt_pixmap.width() > QET_MAX_DND_PIXMAP_WIDTH || elmt_pixmap.height() > QET_MAX_DND_PIXMAP_HEIGHT) {
+			elmt_pixmap = elmt_pixmap.scaled(QET_MAX_DND_PIXMAP_WIDTH, QET_MAX_DND_PIXMAP_HEIGHT, Qt::KeepAspectRatio);
+			elmt_hotspot = QPoint(
+				elmt_hotspot.x() * elmt_pixmap.width() / elmt_pixmap_size.x(),
+				elmt_hotspot.y() * elmt_pixmap.height() / elmt_pixmap_size.y()
+			);
+		}
+		
+		drag -> setPixmap(elmt_pixmap);
+		drag -> setHotSpot(elmt_hotspot);
+		
+		// suppression de l'appareil temporaire
+		delete temp_elmt;
+	}
+	
+	// realisation du drag'n drop
+	drag -> setMimeData(mimeData);
+	drag -> start(Qt::MoveAction | Qt::CopyAction);
+}
+
+/**
+	Handle the dragging of a title block template
+	@param location Location of the dragged template.
+*/
+void ElementsPanel::startTitleBlockTemplateDrag(const TitleBlockTemplateLocation &location) {
+	QString location_string = location.toString();
+	
+	QMimeData *mime_data = new QMimeData();
+	mime_data -> setText(location_string);
+	mime_data -> setData("application/x-qet-titleblock-uri", location_string.toAscii());
+	
+	QDrag *drag = new QDrag(this);
+	drag -> setMimeData(mime_data);
+	drag -> setPixmap(QET::Icons::TitleBlock.pixmap(22, 16));
+	drag -> start(Qt::CopyAction);
+}
+
+void ElementsPanel::firstActivation() {
+	QTimer::singleShot(250, this, SLOT(reload()));
+}
+
+/**
+	Ensure the filter is applied again after the panel content has changed.
+*/
+void ElementsPanel::panelContentChange() {
+	if (!filter_.isEmpty()) {
+		filter(filter_);
+	}
+}
+
+/**
+	Inform this panel the project \a project has integrated the element at \a location
+*/
+QList<ElementsLocation> ElementsPanel::elementIntegrated(QETProject *project, const ElementsLocation &location) {
+	// the base implementation simply refreshes the adequate category and returns the list of added locations
+	QList<ElementsLocation> added_locations = GenericPanel::elementIntegrated(project, location);
+	if (!added_locations.count() || !mustHighlightIntegratedElements()) {
+		return(added_locations);
+	}
+	
+	// the additional job of this method consists in displaying the integrated elements...
+	if (QTreeWidgetItem *integrated_element_qtwi = itemForElementsLocation(location)) {
+		ensureHierarchyIsVisible(QList<QTreeWidgetItem *>() << integrated_element_qtwi);
+		scrollToItem(integrated_element_qtwi, QAbstractItemView::PositionAtCenter);
+	}
+	
+	// and make them "flash" (not too obviously though) so the user notices they have been integrated.
+	QList<QTreeWidgetItem *> items;
+	foreach (ElementsLocation loc, added_locations) {
+		if (QTreeWidgetItem *added_item = itemForElementsLocation(loc)) {
+			items << added_item;
+		}
+	}
+	highlightItems(items, this, SLOT(scrollToSelectedItem()));
+	
+	return(added_locations);
+}
+
+/**
+	Methode permettant d'ajouter un projet au panel d'elements.
+	@param qtwi_parent QTreeWidgetItem parent sous lequel sera insere le projet
+	@param project Projet a inserer dans le panel d'elements
+	@return Le QTreeWidgetItem insere le plus haut
+*/
+QTreeWidgetItem *ElementsPanel::addProject(QETProject *project) {
+	bool first_add = (first_reload_ || !projects_to_display_.contains(project));
+	
+	// create the QTreeWidgetItem representing the project
+	QTreeWidgetItem *qtwi_project = GenericPanel::addProject(project, 0, GenericPanel::All);
+	// the project will be inserted right before the common tb templates collection
+	invisibleRootItem() -> insertChild(
+		indexOfTopLevelItem(common_tbt_collection_item_),
+		qtwi_project
+	);
+	if (first_add) qtwi_project -> setExpanded(true);
+	
+	if (TitleBlockTemplatesCollection *tbt_collection = project -> embeddedTitleBlockTemplatesCollection()) {
+		if (QTreeWidgetItem *tbt_collection_qtwi = itemForTemplatesCollection(tbt_collection)) {
+			if (first_add) tbt_collection_qtwi -> setExpanded(true);
+		}
+	}
+	
+	if (ElementsCollection *elmt_collection = project -> embeddedCollection()) {
+		if (QTreeWidgetItem *elmt_collection_qtwi = itemForElementsCollection(elmt_collection)) {
+
+			if (first_add) elmt_collection_qtwi -> setExpanded(true);
+		}
+	}
+	
+	qtwi_project -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper ce projet", "Status tip"));
+	
+	return(qtwi_project);
+}
+
+/**
+	Methode privee permettant d'ajouter une collection d'elements au panel d'elements
+	@param qtwi_parent QTreeWidgetItem parent sous lequel sera insere la collection d'elements
+	@param collection Collection a inserer dans le panel d'elements - si
+	collection vaut 0, cette methode retourne 0.
+	@param coll_name Nom a utiliser pour la collection
+	@param icon Icone a utiliser pour l'affichage de la collection
+	@return Le QTreeWidgetItem insere le plus haut
+*/
+QTreeWidgetItem *ElementsPanel::addCollection(ElementsCollection *collection) {
+	PanelOptions options = GenericPanel::AddAllChild;
+	options |= GenericPanel::DisplayElementsPreview;
+	return(addElementsCollection(collection, invisibleRootItem(), options));
+}
+
+QTreeWidgetItem *ElementsPanel::updateTemplatesCollectionItem(QTreeWidgetItem *tbt_collection_qtwi, TitleBlockTemplatesCollection *tbt_collection, PanelOptions options, bool freshly_created) {
+	QTreeWidgetItem *tbtc_qtwi = GenericPanel::updateTemplatesCollectionItem(tbt_collection_qtwi, tbt_collection, options, freshly_created);
+	if (tbt_collection && tbt_collection -> parentProject()) {
+		tbtc_qtwi -> setText(0, tr("Cartouches embarqu\351s"));
+		tbtc_qtwi -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper cette collection de cartouches embarqu\351e", "Status tip"));
+	}
+	return(tbtc_qtwi);
+}
+
+QTreeWidgetItem *ElementsPanel::updateTemplateItem(QTreeWidgetItem *tb_template_qtwi, const TitleBlockTemplateLocation &tb_template, PanelOptions options, bool freshly_created) {
+	QTreeWidgetItem *item = GenericPanel::updateTemplateItem(tb_template_qtwi, tb_template, options, freshly_created);
+	item -> setStatusTip(
+		0,
+		tr(
+			"Cliquer-d\351posez ce mod\350le de cartouche sur un sch\351ma pour l'y appliquer.",
+			"Status tip displayed when selecting a title block template"
+		)
+	);
+	return(item);
+}
+
+QTreeWidgetItem *ElementsPanel::updateElementsCategoryItem(QTreeWidgetItem *category_qtwi, ElementsCategory *category, PanelOptions options, bool freshly_created) {
+	QTreeWidgetItem *item = GenericPanel::updateElementsCategoryItem(category_qtwi, category, options, freshly_created);
+	item -> setStatusTip(
+		0,
+		tr(
+			"Double-cliquez pour r\351duire ou d\351velopper cette cat\351gorie d'\351l\351ments",
+			"Status tip displayed by elements category"
+		)
+	);
+	emit(loadingProgressed(++ loading_progress_, -1));
+	return(item);
+}
+
+QTreeWidgetItem *ElementsPanel::updateElementsCollectionItem(QTreeWidgetItem *collection_qtwi, ElementsCollection *collection, PanelOptions options, bool freshly_created) {
+	QTreeWidgetItem *c_qtwi = GenericPanel::updateElementsCollectionItem(collection_qtwi, collection, options, freshly_created);
+	if (collection && collection -> project()) {
+		c_qtwi -> setText(0, tr("Collection embarqu\351e"));
+		c_qtwi -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper cette collection d'\351l\351ments embarqu\351e", "Status tip"));
+	}
+	return(c_qtwi);
+}
+
+QTreeWidgetItem *ElementsPanel::updateElementItem(QTreeWidgetItem *element_qtwi, ElementDefinition *element, PanelOptions options, bool freshly_created) {
+	QTreeWidgetItem *item = GenericPanel::updateElementItem(element_qtwi, element, options, freshly_created);
+	
+	QString status_tip = tr(
+		"Cliquer-d\351posez cet \351l\351ment sur un sch\351ma pour y ins\351rer un \351l\351ment \253 %1 \273, double-cliquez dessus pour l'\351diter",
+		"Status tip displayed in the status bar when selecting an element"
+	);
+	item -> setStatusTip(0, status_tip.arg(item -> text(0)));
+	
+	item -> setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled);
+	
+	emit(loadingProgressed(++ loading_progress_, -1));
+	return(item);
+}
+
+/**
+	@return true if \a item matches the current filter, false otherwise
+*/
+bool ElementsPanel::matchesCurrentFilter(const QTreeWidgetItem *item) const {
+	if (!item) return(false);
+	
+	// no filter => we consider the item matches
+	if (filter_.isEmpty()) return(true);
+	
+	bool item_matches = item -> text(0).contains(filter_, Qt::CaseInsensitive);
+	
+	return(item_matches);
+}
+
+/**
+	Reloads the following collections:
+	  * common collection
+	  * custom collection
+	  * collection of every project displayed in this panel
+*/
+void ElementsPanel::reloadCollections() {
+	QETApp::commonElementsCollection() -> reload();
+	QETApp::customElementsCollection() -> reload();
+	
+	// reloads collection of every project displayed in this panel
+	foreach(QETProject *project, projects_to_display_) {
+		if (ElementsCollection *project_collection = project -> embeddedCollection()) {
+			project_collection -> reload();
+		}
+	}
+}
+
+/**
+	@return the count of categories and elements within the following collections:
+	  * common collection
+	  * custom collection
+	  * collection of every project displayed in this panel
+*/
+int ElementsPanel::elementsCollectionItemsCount() {
+	int items_count = 0;
+	items_count += QETApp::commonElementsCollection() -> count();
+	items_count += QETApp::customElementsCollection() -> count();
+	foreach(QETProject *project, projects_to_display_.values()) {
+		if (ElementsCollection *project_collection = project -> embeddedCollection()) {
+			items_count += project_collection -> count();
+		}
+	}
+	return(items_count);
+}
+
+/**
+	@return true if freshly integrated elements should be highlighted, false otherwise.
+*/
+bool ElementsPanel::mustHighlightIntegratedElements() const {
+	return(QETApp::settings().value("diagrameditor/highlight-integrated-elements", true).toBool());
+}
+
+/**
+	Recharge l'arbre des elements
+	@param reload_collections true pour relire les collections depuis leurs sources (fichiers, projets...)
+*/
+void ElementsPanel::reload(bool reload_collections) {
+	if (reload_collections) {
+		emit(readingAboutToBegin());
+		reloadCollections();
+		emit(readingFinished());
+	}
+	
+	QIcon system_icon(":/ico/16x16/qet.png");
+	QIcon user_icon(":/ico/16x16/go-home.png");
+	
+	// estimates the number of categories and elements to load
+	int items_count = elementsCollectionItemsCount();
+	emit(loadingProgressed(loading_progress_ = 0, items_count));
+	
+	// load the common title block templates collection
+	TitleBlockTemplatesCollection *common_tbt_collection = QETApp::commonTitleBlockTemplatesCollection();
+	common_tbt_collection_item_ = addTemplatesCollection(common_tbt_collection, invisibleRootItem());
+	common_tbt_collection_item_ -> setIcon(0, system_icon);
+	common_tbt_collection_item_ -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper la collection de cartouches QElectroTech", "Status tip"));
+	common_tbt_collection_item_ -> setWhatsThis(0, tr("Ceci est la collection de cartouches fournie avec QElectroTech. Install\351e en tant que composant syst\350me, vous ne pouvez normalement pas la personnaliser.", "\"What's this\" tip"));
+	if (first_reload_) common_tbt_collection_item_ -> setExpanded(true);
+	
+	// load the common elements collection
+	if (QETApp::commonElementsCollection() -> rootCategory()) {
+		common_collection_item_ = addCollection(QETApp::commonElementsCollection());
+		common_collection_item_ -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper la collection d'\351l\351ments QElectroTech", "Status tip"));
+		common_collection_item_ -> setWhatsThis(0, tr("Ceci est la collection d'\351l\351ments fournie avec QElectroTech. Install\351e en tant que composant syst\350me, vous ne pouvez normalement pas la personnaliser.", "\"What's this\" tip"));
+		if (first_reload_) common_collection_item_ -> setExpanded(true);
+	}
+	
+	// load the custom title block templates collection
+	TitleBlockTemplatesCollection *custom_tbt_collection = QETApp::customTitleBlockTemplatesCollection();
+	custom_tbt_collection_item_ = addTemplatesCollection(custom_tbt_collection, invisibleRootItem());
+	custom_tbt_collection_item_ -> setIcon(0, user_icon);
+	custom_tbt_collection_item_ -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper votre collection personnelle de cartouches", "Status tip"));
+	custom_tbt_collection_item_ -> setWhatsThis(0, tr("Ceci est votre collection personnelle de cartouches -- utilisez-la pour cr\351er, stocker et \351diter vos propres cartouches.", "\"What's this\" tip"));
+	if (first_reload_) custom_tbt_collection_item_ -> setExpanded(true);
+	
+	// load the custom elements collection
+	if (QETApp::customElementsCollection() -> rootCategory()) {
+		custom_collection_item_ = addCollection(QETApp::customElementsCollection());
+		custom_collection_item_ -> setStatusTip(0, tr("Double-cliquez pour r\351duire ou d\351velopper votre collection personnelle d'\351l\351ments", "Status tip"));
+		custom_collection_item_ -> setWhatsThis(0, tr("Ceci est votre collection personnelle d'\351l\351ments -- utilisez-la pour cr\351er, stocker et \351diter vos propres \351l\351ments.", "\"What's this\" tip"));
+		if (first_reload_) custom_collection_item_ -> setExpanded(true);
+	}
+	
+	// add projects
+	foreach(QETProject *project, projects_to_display_.values()) {
+		addProject(project);
+	}
+	
+	// the first time, expand the first level of collections
+	if (first_reload_) first_reload_ = false;
+	
+	emit(loadingFinished());
+}
+
+/**
+	Gere le double-clic sur un element.
+	Si un double-clic sur un projet est effectue, le signal requestForProject
+	est emis.
+	Si un double-clic sur un schema est effectue, le signal requestForDiagram
+	est emis.
+	Si un double-clic sur une collection, une categorie ou un element est
+	effectue, le signal requestForCollectionItem est emis.
+	@param qtwi
+*/
+void ElementsPanel::slot_doubleClick(QTreeWidgetItem *qtwi, int) {
+	int qtwi_type = qtwi -> type();
+	if (qtwi_type == QET::Project) {
+		QETProject *project = valueForItem<QETProject *>(qtwi);
+		emit(requestForProject(project));
+	} else if (qtwi_type == QET::Diagram) {
+		Diagram *diagram = valueForItem<Diagram *>(qtwi);
+		emit(requestForDiagram(diagram));
+	} else if (qtwi_type & QET::ElementsCollectionItem) {
+		ElementsLocation element = valueForItem<ElementsLocation>(qtwi);
+		emit(requestForCollectionItem(element));
+	} else if (qtwi_type == QET::TitleBlockTemplate) {
+		TitleBlockTemplateLocation tbt = valueForItem<TitleBlockTemplateLocation>(qtwi);
+		emit(requestForTitleBlockTemplate(tbt));
+	}
+}
+
+/**
+	@param qtwi Un QTreeWidgetItem
+	@return L'ElementsCollectionItem represente par qtwi, ou 0 si qtwi ne
+	represente pas un ElementsCollectionItem
+*/
+ElementsCollectionItem *ElementsPanel::collectionItemForItem(QTreeWidgetItem *qtwi) const {
+	if (qtwi && qtwi -> type() & QET::ElementsCollectionItem) {
+		ElementsLocation item_location = elementLocationForItem(qtwi);
+		return(QETApp::collectionItem(item_location));
+	}
+	return(0);
+}
+
+/**
+	Cette methode permet d'acceder a la categorie correspondant a un item donne.
+	Si cet item represente une collection, c'est sa categorie racine qui est renvoyee.
+	Si cet item represente une categorie, c'est cette categorie qui est renvoyee.
+	Si cet item represente un element, c'est sa categorie parente qui est renvoyee.
+	@param qtwi un QTreeWidgetItem
+	@return la categorie correspondant au QTreeWidgetItem qtwi, ou 0 s'il n'y a
+	aucune categorie correspondante.
+*/
+ElementsCategory *ElementsPanel::categoryForItem(QTreeWidgetItem *qtwi) {
+	if (!qtwi) return(0);
+	
+	// Recupere le CollectionItem associe a cet item
+	ElementsCollectionItem *collection_item = collectionItemForItem(qtwi);
+	if (!collection_item) return(0);
+	
+	// recupere la categorie cible pour le deplacement
+	return(collection_item -> toCategory());
+}
+
+/**
+	@param pos Position dans l'arborescence
+	@return La categorie situee sous la position pos, ou 0 s'il n'y a aucune
+	categorie correspondante.
+	@see categoryForItem
+*/
+ElementsCategory *ElementsPanel::categoryForPos(const QPoint &pos) {
+	// Accede a l'item sous la position
+	QTreeWidgetItem *pos_qtwi = itemAt(pos);
+	if (!pos_qtwi) {
+		return(0);
+	}
+	
+	return(categoryForItem(pos_qtwi));
+}
+
+/**
+	@param qtwi a QTreeWidgetItem
+	@return the directory path of the object represented by \a qtwi
+*/
+QString ElementsPanel::dirPathForItem(QTreeWidgetItem *item) {
+	QString file_path = filePathForItem(item);
+	if (!file_path.isEmpty()) {
+		QFileInfo path_info(file_path);
+		if (path_info.isDir()) {
+			return(file_path);
+		}
+		else {
+			return(path_info.canonicalPath());
+		}
+	}
+	return(QString());
+}
+
+/**
+	@param qtwi a QTreeWidgetItem
+	@return the filepath of the object represented by \a qtwi
+*/
+QString ElementsPanel::filePathForItem(QTreeWidgetItem *item) {
+	if (!item) return(QString());
+	
+	ElementsCollectionItem *collection_item = collectionItemForItem(item);
+	if (collection_item) {
+		if (collection_item -> hasFilePath()) {
+			return(collection_item -> filePath());
+		}
+	}
+	else {
+		TitleBlockTemplateLocation tbt_location = templateLocationForItem(item);
+		TitleBlockTemplatesCollection *tbt_collection = tbt_location.parentCollection();
+		if (tbt_collection && tbt_collection -> hasFilePath()) {
+			return(tbt_collection -> filePath());
+		}
+		else {
+			QETProject *project = projectForItem(item);
+			if (project) {
+				return(project -> filePath());
+			}
+		}
+	}
+	return(QString());
+}
+
+/**
+	Hide items that do not match the provided string, ensure others are visible
+	along with their parent hierarchy. When ending the filtering, restore the tree
+	as it was before the filtering (except the current item) and scroll to the
+	currently selected item.
+	@param m String to be matched
+	@param filtering whether to begin/apply/end the filtering
+	@see QET::Filtering
+*/
+void ElementsPanel::filter(const QString &m, QET::Filtering filtering) {
+	QList<QTreeWidgetItem *> items = findItems("*", Qt::MatchRecursive | Qt::MatchWildcard);
+	const int expanded_role = 42; // magic number? So you consider Douglas Adams wrote about magic?
+	
+	if (filtering == QET::BeginFilter) {
+		foreach (QTreeWidgetItem *item, items) {
+			item -> setData(0, expanded_role, item -> isExpanded());
+		}
+	}
+	
+	if (filtering != QET::EndFilter) {
+		filter_ = m;
+		applyCurrentFilter(items);
+	} else { // filtering == QET::EndFilter
+		filter_ = QString();
+		QTreeWidgetItem *current_item = currentItem();
+		
+		// restore the tree as it was before the filtering
+		foreach (QTreeWidgetItem *qtwi, items) {
+			qtwi -> setHidden(false);
+			qtwi -> setExpanded(qtwi -> data(0, expanded_role).toBool());
+		}
+		
+		// avoid hiding the currently selected item
+		if (current_item) {
+			ensureHierarchyIsVisible(QList<QTreeWidgetItem *>() << current_item);
+			scrollToItem(current_item);
+		}
+	}
+}
+
+/**
+	Rajoute un projet au panel d'elements
+	@param project Projet ouvert a rajouter au panel
+*/
+void ElementsPanel::projectWasOpened(QETProject *project) {
+	addProject(project);
+	projects_to_display_ << project;
+	emit(panelContentChanged());
+}
+
+/**
+	Enleve un projet du panel d'elements
+	@param project Projet a enlever du panel
+*/
+void ElementsPanel::projectWasClosed(QETProject *project) {
+	if (QTreeWidgetItem *item_to_remove = itemForProject(project)) {
+		GenericPanel::deleteItem(item_to_remove);
+		projects_to_display_.remove(project);
+	}
+	emit(panelContentChanged());
+}
+
+/**
+	Affiche un element etant donne son emplacement
+	@param location Emplacement de l'element a afficher
+*/
+bool ElementsPanel::scrollToElement(const ElementsLocation &location) {
+	// recherche l'element dans le panel
+	QTreeWidgetItem *item = itemForElementsLocation(location);
+	if (!item) return(false);
+	
+	// s'assure que l'item ne soit pas filtre
+	item -> setHidden(false);
+	setCurrentItem(item);
+	ensureHierarchyIsVisible(QList<QTreeWidgetItem *>() << item);
+	scrollToItem(item);
+	return(true);
+}
+
+/**
+	Apply the current filter to a given item.
+*/
+void ElementsPanel::applyCurrentFilter(const QList<QTreeWidgetItem *> &items) {
+	if (filter_.isEmpty()) return;
+	QList<QTreeWidgetItem *> matching_items;
+	foreach (QTreeWidgetItem *item, items) {
+		bool item_matches = matchesCurrentFilter(item);
+		if (item_matches) matching_items << item;
+		item -> setHidden(!item_matches);
+	}
+	ensureHierarchyIsVisible(matching_items);
+}
+
+/**
+	@param items une liste de QTreeWidgetItem pour lesquels il faut s'assurer
+	que eux et leurs parents sont visibles
+*/
+void ElementsPanel::ensureHierarchyIsVisible(const QList<QTreeWidgetItem *> &items) {
+	// remonte l'arborescence pour lister les categories contenant les elements filtres
+	QSet<QTreeWidgetItem *> parent_items;
+	foreach(QTreeWidgetItem *item, items) {
+		for (QTreeWidgetItem *parent_qtwi = item -> parent() ; parent_qtwi ; parent_qtwi = parent_qtwi -> parent()) {
+			parent_items << parent_qtwi;
+		}
+	}
+	
+	// etend les parents
+	foreach(QTreeWidgetItem *parent_qtwi, parent_items) {
+		if (!parent_qtwi -> isExpanded()) parent_qtwi -> setExpanded(true);
+	}
+	
+	// affiche les parents
+	foreach(QTreeWidgetItem *parent_qtwi, parent_items) {
+		if (parent_qtwi -> isHidden()) parent_qtwi -> setHidden(false);
+	}
+}
+
+/**
+	Scroll to the currently selected item.
+*/
+void ElementsPanel::scrollToSelectedItem() {
+	QList<QTreeWidgetItem *> selected_items = selectedItems();
+	if (selected_items.count()) {
+		scrollToItem(selected_items.first(), QAbstractItemView::PositionAtCenter);
+	}
+}
+
+/**
+	Scroll to and highlight \a items. Once the animation is finished, the slot
+	\a method is called on the object \a receiver.
+*/
+void ElementsPanel::highlightItems(const QList<QTreeWidgetItem *> &items, const QObject *receiver, const char *method) {
+	TreeColorAnimation *animation1 = new TreeColorAnimation(items);
+	animation1 -> setStartValue(QColor(Qt::white));
+	animation1 -> setEndValue(QColor(Qt::yellow));
+	animation1 -> setDuration(400);
+	animation1 -> setEasingCurve(QEasingCurve::InQuad);
+	
+	TreeColorAnimation *animation2 = new TreeColorAnimation(items);
+	animation2 -> setStartValue(QColor(Qt::yellow));
+	animation2 -> setEndValue(QColor(Qt::white));
+	animation2 -> setDuration(500);
+	animation2 -> setEasingCurve(QEasingCurve::OutInQuint);
+	
+	QSequentialAnimationGroup *animation = new QSequentialAnimationGroup(this);
+	animation -> addAnimation(animation1);
+	animation -> addAnimation(new QPauseAnimation(700));
+	animation -> addAnimation(animation2);
+	if (receiver) {
+		connect(animation, SIGNAL(finished()), receiver, method);
+	}
+	animation -> start(QAbstractAnimation::DeleteWhenStopped);
+}

Added: trunk/sources/elementspanel.h
===================================================================
--- trunk/sources/elementspanel.h	                        (rev 0)
+++ trunk/sources/elementspanel.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,125 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PANELAPPAREILS_H
+#define PANELAPPAREILS_H
+#include <QtGui>
+#include "genericpanel.h"
+#include "elementslocation.h"
+#include "templatelocation.h"
+class QETProject;
+class Diagram;
+class ElementsCollection;
+class ElementsCollectionItem;
+class ElementsCategory;
+class ElementDefinition;
+class ElementsCollectionCache;
+class TitleBlockTemplatesFilesCollection;
+
+/**
+	This class provides a tree widget listing known filesystem-based elements
+	collections and opened project files along with their content (diagrams,
+	embedded collections, ...). It enables users to perform various actions on
+	the displayed content
+*/
+class ElementsPanel : public GenericPanel {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	ElementsPanel(QWidget * = 0);
+	virtual ~ElementsPanel();
+	
+	private:
+	ElementsPanel(const ElementsPanel &);
+	
+	// methods
+	public:
+	// methods used to determine what is represented by a particular visual item
+	bool itemIsWritable(QTreeWidgetItem *) const;
+	bool selectedItemIsWritable() const;
+	
+	// methods used to get what is represented by a particular visual item
+	ElementsCollectionItem *collectionItemForItem(QTreeWidgetItem *) const;
+	ElementsCollectionItem *selectedItem() const;
+	ElementsCategory *categoryForItem(QTreeWidgetItem *);
+	ElementsCategory *categoryForPos(const QPoint &);
+	QString dirPathForItem(QTreeWidgetItem *);
+	QString filePathForItem(QTreeWidgetItem *);
+	
+	void reloadCollections();
+	int elementsCollectionItemsCount();
+	bool mustHighlightIntegratedElements() const;
+	
+	signals:
+	void requestForProject(QETProject *);
+	void requestForDiagram(Diagram *);
+	void requestForCollectionItem(const ElementsLocation &);
+	void requestForMoveElements(ElementsCollectionItem *, ElementsCollectionItem *, QPoint);
+	void requestForTitleBlockTemplate(const TitleBlockTemplateLocation &);
+	void readingAboutToBegin();
+	void readingFinished();
+	void loadingProgressed(int, int);
+	void loadingFinished();
+	
+	public slots:
+	void slot_doubleClick(QTreeWidgetItem *, int);
+	void reload(bool = false);
+	void filter(const QString &, QET::Filtering = QET::RegularFilter);
+	void projectWasOpened(QETProject *);
+	void projectWasClosed(QETProject *);
+	bool scrollToElement(const ElementsLocation &);
+	void applyCurrentFilter(const QList<QTreeWidgetItem *> &);
+	void ensureHierarchyIsVisible(const QList<QTreeWidgetItem *> &);
+	void scrollToSelectedItem();
+	void highlightItems(const QList<QTreeWidgetItem *> &, const QObject * = 0, const char * = 0);
+	
+	protected:
+	void dragEnterEvent(QDragEnterEvent *);
+	void dragMoveEvent(QDragMoveEvent *);
+	void dropEvent(QDropEvent *);
+	void startDrag(Qt::DropActions);
+	void startElementDrag(const ElementsLocation &);
+	void startTitleBlockTemplateDrag(const TitleBlockTemplateLocation &);
+	bool matchesCurrentFilter(const QTreeWidgetItem *) const;
+	
+	protected slots:
+	void firstActivation();
+	void panelContentChange();
+	virtual QList<ElementsLocation> elementIntegrated(QETProject *, const ElementsLocation &);
+	
+	private:
+	virtual QTreeWidgetItem *addProject   (QETProject *);
+	virtual QTreeWidgetItem *addCollection(ElementsCollection *);
+	virtual QTreeWidgetItem *updateTemplatesCollectionItem(QTreeWidgetItem *, TitleBlockTemplatesCollection *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *updateTemplateItem        (QTreeWidgetItem *, const TitleBlockTemplateLocation &,  PanelOptions, bool = false);
+	virtual QTreeWidgetItem *updateElementsCategoryItem(QTreeWidgetItem *, ElementsCategory *,  PanelOptions, bool = false);
+	virtual QTreeWidgetItem *updateElementsCollectionItem(QTreeWidgetItem *, ElementsCollection *,  PanelOptions, bool = false);
+	virtual QTreeWidgetItem *updateElementItem         (QTreeWidgetItem *, ElementDefinition *, PanelOptions, bool = false);
+	
+	// attributes
+	private:
+	QSet<QETProject *> projects_to_display_;       ///< list of projects that have been added to this panel
+	QTreeWidgetItem *common_collection_item_;      ///< pointer to the item representing the common elements collection
+	QTreeWidgetItem *common_tbt_collection_item_;  ///< pointer to the item representing the common templates collection
+	QTreeWidgetItem *custom_collection_item_;      ///< pointer to the item representing the user elements collection
+	QTreeWidgetItem *custom_tbt_collection_item_;  ///< pointer to the item representing the user templates collection
+	int loading_progress_;                         ///< used to track the loading progress of elements collections
+	bool first_reload_;                            ///< used to distinguish the first time this panel is reloaded
+	QString filter_;                               ///< Currently applied filter
+};
+#endif

Added: trunk/sources/elementspanelwidget.cpp
===================================================================
--- trunk/sources/elementspanelwidget.cpp	                        (rev 0)
+++ trunk/sources/elementspanelwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,791 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementspanelwidget.h"
+#include "newelementwizard.h"
+#include "elementscategorieswidget.h"
+#include "elementscollectionitem.h"
+#include "qetelementeditor.h"
+#include "elementdeleter.h"
+#include "elementscategoryeditor.h"
+#include "elementscategorydeleter.h"
+#include "qetapp.h"
+#include "interactivemoveelementshandler.h"
+#include "qetproject.h"
+#include "diagram.h"
+#include "qeticons.h"
+#include "templatedeleter.h"
+
+/*
+	Lorsque le flag ENABLE_PANEL_WIDGET_DND_CHECKS est defini, le panel
+	effectue des verifications lors des drag'n drop d'elements et categories.
+	Par exemple, il verifie qu'une categorie cible est accessible en ecriture
+	avant d'y autoriser le drop d'un element.
+	Supprimer ce flag permet de tester le comportement des fonctions de gestion
+	des items (copy, move, etc.).
+*/
+#define ENABLE_PANEL_WIDGET_DND_CHECKS
+
+/**
+	Constructeur
+	@param parent Le QWidget parent de ce widget
+*/
+ElementsPanelWidget::ElementsPanelWidget(QWidget *parent) : QWidget(parent) {
+	// initialize the progress bar (hidden by default)
+	progress_bar_ = new QProgressBar(this);
+	progress_bar_ -> setVisible(false);
+	progress_bar_ -> setTextVisible(true);
+	// initalise le panel d'elements
+	elements_panel = new ElementsPanel(this);
+	
+	// initialise les actions
+	open_directory        = new QAction(QET::Icons::DocumentOpen,              tr("Ouvrir le dossier correspondant"),     this);
+	copy_path             = new QAction(QET::Icons::CopyFile,                  tr("Copier le chemin"),                    this);
+	reload                = new QAction(QET::Icons::ViewRefresh,               tr("Recharger les collections"),           this);
+	new_category          = new QAction(QET::Icons::FolderNew,                 tr("Nouvelle cat\351gorie"),               this);
+	edit_category         = new QAction(QET::Icons::FolderEdit,                tr("\311diter la cat\351gorie"),           this);
+	delete_category       = new QAction(QET::Icons::FolderDelete,              tr("Supprimer la cat\351gorie"),           this);
+	delete_collection     = new QAction(QET::Icons::FolderDelete,              tr("Vider la collection"),                 this);
+	new_element           = new QAction(QET::Icons::ElementNew,                tr("Nouvel \351l\351ment"),                this);
+	edit_element          = new QAction(QET::Icons::ElementEdit,               tr("\311diter l'\351l\351ment"),           this);
+	delete_element        = new QAction(QET::Icons::ElementDelete,             tr("Supprimer l'\351l\351ment"),           this);
+	open_element          = new QAction(QET::Icons::DocumentImport,            tr("Ouvrir un fichier \351l\351ment"),     this);
+	prj_activate          = new QAction(QET::Icons::ProjectFile,               tr("Basculer vers ce projet"),             this);
+	prj_close             = new QAction(QET::Icons::DocumentClose,             tr("Fermer ce projet"),                    this);
+	prj_edit_prop         = new QAction(QET::Icons::DialogInformation,         tr("Propri\351t\351s du projet"),          this);
+	prj_prop_diagram      = new QAction(QET::Icons::DialogInformation,         tr("Propri\351t\351s du sch\351ma"),       this);
+	prj_add_diagram       = new QAction(QET::Icons::DiagramAdd,                tr("Ajouter un sch\351ma"),                this);
+	prj_del_diagram       = new QAction(QET::Icons::DiagramDelete,             tr("Supprimer ce sch\351ma"),              this);
+	prj_move_diagram_up   = new QAction(QET::Icons::GoUp,                      tr("Remonter ce sch\351ma"),               this);
+	prj_move_diagram_down = new QAction(QET::Icons::GoDown,                    tr("Abaisser ce sch\351ma"),               this);
+	tbt_add               = new QAction(QET::Icons::TitleBlock,                tr("Nouveau mod\350le"),                   this);
+	tbt_edit              = new QAction(QET::Icons::TitleBlock,                tr("\311diter ce mod\350le"),              this);
+	tbt_remove            = new QAction(QET::Icons::TitleBlock,                tr("Supprimer ce mod\350le"),              this);
+	move_elements_        = new QAction(QET::Icons::MoveFile,                  tr("D\351placer dans cette cat\351gorie"), this);
+	copy_elements_        = new QAction(QET::Icons::CopyFile,                  tr("Copier dans cette cat\351gorie"),      this);
+	cancel_elements_      = new QAction(QET::Icons::Cancel,                    tr("Annuler"),                             this);
+	erase_textfield       = new QAction(QET::Icons::EditClearLocationBar,      tr("Effacer le filtre"),                   this);
+
+	reload            -> setShortcut(Qt::Key_F5);
+
+	// initialise le champ de texte pour filtrer avec une disposition horizontale
+	QLabel *filter_label = new QLabel(tr("Filtrer : "), this);
+	filter_textfield = new QLineEdit(this);
+	filter_toolbar = new QToolBar("filter");
+	filter_toolbar -> addAction(erase_textfield);
+	filter_toolbar -> addWidget(filter_label);
+	filter_toolbar -> addWidget(filter_textfield);
+	
+	// ajoute une petite marge a la droite du champ pour filtrer lorsque le style CleanLooks est utilise
+	if (qobject_cast<QCleanlooksStyle *>(QApplication::style())) {
+		int l, t, r, b;
+		filter_toolbar -> getContentsMargins(&l, &t, &r, &b);
+		filter_toolbar -> setContentsMargins (l, t, r + 4, b);
+	}
+	
+	context_menu = new QMenu(this);
+	
+	connect(open_directory,        SIGNAL(triggered()), this,           SLOT(openDirectoryForSelectedItem()));
+	connect(copy_path,             SIGNAL(triggered()), this,           SLOT(copyPathForSelectedItem()));
+	connect(reload,                SIGNAL(triggered()), this,           SLOT(reloadAndFilter()));
+	connect(new_category,          SIGNAL(triggered()), this,           SLOT(newCategory()));
+	connect(edit_category,         SIGNAL(triggered()), this,           SLOT(editCategory()));
+	connect(delete_category,       SIGNAL(triggered()), this,           SLOT(deleteCategory()));
+	connect(delete_collection,     SIGNAL(triggered()), this,           SLOT(deleteCategory()));
+	connect(new_element,           SIGNAL(triggered()), this,           SLOT(newElement()));
+	connect(edit_element,          SIGNAL(triggered()), this,           SLOT(editElement()));
+	connect(delete_element,        SIGNAL(triggered()), this,           SLOT(deleteElement()));
+	connect(open_element,          SIGNAL(triggered()), this,           SLOT(openElementFromFile()));
+	connect(prj_activate,          SIGNAL(triggered()), this,           SLOT(activateProject()));
+	connect(prj_close,             SIGNAL(triggered()), this,           SLOT(closeProject()));
+	connect(prj_edit_prop,         SIGNAL(triggered()), this,           SLOT(editProjectProperties()));
+	connect(prj_prop_diagram,      SIGNAL(triggered()), this,           SLOT(editDiagramProperties()));
+	connect(prj_add_diagram,       SIGNAL(triggered()), this,           SLOT(newDiagram()));
+	connect(prj_del_diagram,       SIGNAL(triggered()), this,           SLOT(deleteDiagram()));
+	connect(prj_move_diagram_up,   SIGNAL(triggered()), this,           SLOT(moveDiagramUp()));
+	connect(prj_move_diagram_down, SIGNAL(triggered()), this,           SLOT(moveDiagramDown()));
+	connect(tbt_add,               SIGNAL(triggered()), this,           SLOT(addTitleBlockTemplate()));
+	connect(tbt_edit,              SIGNAL(triggered()), this,           SLOT(editTitleBlockTemplate()));
+	connect(tbt_remove,            SIGNAL(triggered()), this,           SLOT(removeTitleBlockTemplate()));
+	connect(move_elements_,        SIGNAL(triggered()), this,           SLOT(moveElements()));
+	connect(copy_elements_,        SIGNAL(triggered()), this,           SLOT(copyElements()));
+	
+	connect(erase_textfield,       SIGNAL(triggered()),                 this,             SLOT(clearFilterTextField()));
+	connect(filter_textfield,      SIGNAL(textEdited(const QString &)), this,             SLOT(filterEdited(const QString &)));
+	
+	connect(elements_panel,        SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(updateButtons()));
+	connect(elements_panel,        SIGNAL(customContextMenuRequested(const QPoint &)),               this, SLOT(handleContextMenu(const QPoint &)));
+	connect(elements_panel,        SIGNAL(requestForDiagram(Diagram*)),                              this, SIGNAL(requestForDiagram(Diagram*)));
+	connect(elements_panel,        SIGNAL(requestForCollectionItem(const ElementsLocation &)),       this, SLOT(handleCollectionRequest(const ElementsLocation &)));
+	connect(
+		elements_panel,
+		SIGNAL(requestForMoveElements(ElementsCollectionItem *, ElementsCollectionItem *, QPoint)),
+		this,
+		SLOT(handleMoveElementsRequest(ElementsCollectionItem *, ElementsCollectionItem *, const QPoint &)),
+		Qt::QueuedConnection
+	);
+	connect(
+		elements_panel,
+		SIGNAL(requestForTitleBlockTemplate(const TitleBlockTemplateLocation &)),
+		QETApp::instance(),
+		SLOT(openTitleBlockTemplate(const TitleBlockTemplateLocation &))
+	);
+	connect(elements_panel, SIGNAL(loadingProgressed(int, int)),  this, SLOT(updateProgressBar(int, int)));
+	connect(elements_panel, SIGNAL(readingAboutToBegin()),        this, SLOT(collectionsRead()));
+	connect(elements_panel, SIGNAL(readingFinished()),            this, SLOT(collectionsReadFinished()));
+	connect(elements_panel, SIGNAL(loadingFinished()),            this, SLOT(loadingFinished()));
+	
+	// initialise la barre d'outils
+	toolbar = new QToolBar(this);
+	toolbar -> setMovable(false);
+	toolbar -> addAction(reload);
+	toolbar -> addSeparator();
+	toolbar -> addAction(new_category);
+	toolbar -> addAction(edit_category);
+	toolbar -> addAction(delete_category);
+	toolbar -> addSeparator();
+	toolbar -> addAction(new_element);
+	toolbar -> addAction(edit_element);
+	toolbar -> addAction(delete_element);
+	toolbar -> addSeparator();
+	toolbar -> addAction(open_element);
+	
+	// disposition verticale
+	QVBoxLayout *vlayout = new QVBoxLayout(this);
+	vlayout -> setMargin(0);
+	vlayout -> setSpacing(0);
+	vlayout -> addWidget(toolbar);
+	vlayout -> addWidget(filter_toolbar);
+	vlayout -> addWidget(elements_panel);
+	vlayout -> addWidget(progress_bar_);
+	vlayout -> setStretchFactor(elements_panel, 75000);
+	setLayout(vlayout);
+	
+	// by default, the reload button is disabled
+	reload -> setEnabled(false);
+}
+
+/**
+	Destructeur
+*/
+ElementsPanelWidget::~ElementsPanelWidget() {
+}
+
+/**
+	Vide le champ de texte permettant a l'utilisateur de filtrer, donne le
+	focus a ce champ et annule le filtrage.
+*/
+void ElementsPanelWidget::clearFilterTextField() {
+	filter_textfield -> clear();
+	filter_textfield -> setFocus();
+	filterEdited(QString());
+}
+
+/**
+	Require the desktop environment to open the directory containing the file
+	represented by the selected item, if any.
+*/
+void ElementsPanelWidget::openDirectoryForSelectedItem() {
+	if (QTreeWidgetItem *qtwi = elements_panel -> currentItem()) {
+		QString dir_path = elements_panel -> dirPathForItem(qtwi);
+		if (!dir_path.isEmpty()) {
+			QDesktopServices::openUrl(QUrl::fromLocalFile(dir_path));
+		}
+	}
+}
+
+/**
+	Copy the full path to the file represented by the selected item to the
+	clipboard.
+*/
+void ElementsPanelWidget::copyPathForSelectedItem() {
+	if (QTreeWidgetItem *qtwi = elements_panel -> currentItem()) {
+		QString file_path = elements_panel -> filePathForItem(qtwi);
+		file_path = QDir::toNativeSeparators(file_path);
+		if (!file_path.isEmpty()) {
+			QApplication::clipboard() -> setText(file_path);
+		}
+	}
+}
+
+/**
+	Recharge le panel d'elements
+*/
+void ElementsPanelWidget::reloadAndFilter() {
+	// recharge tous les elements
+	reload -> setEnabled(false);
+	elements_panel -> reload(true);
+	
+	// the reload button was enabled again through loadingFinished()
+	reload -> setEnabled(false);
+	// reapplique le filtre
+	if (!filter_textfield -> text().isEmpty()) {
+		elements_panel -> filter(filter_textfield -> text());
+	}
+	reload -> setEnabled(true);
+}
+
+/**
+	* Emit the requestForProject signal with te selected project
+*/
+void ElementsPanelWidget::activateProject() {
+	if (QETProject *selected_project = elements_panel -> selectedProject()) {
+		emit(requestForProject(selected_project));
+	}
+}
+
+/**
+	Emet le signal requestForProjectClosing avec le projet selectionne
+*/
+void ElementsPanelWidget::closeProject() {
+	if (QETProject *selected_project = elements_panel -> selectedProject()) {
+		emit(requestForProjectClosing(selected_project));
+	}
+}
+
+/**
+	Emet le signal requestForProjectPropertiesEdition avec le projet selectionne
+*/
+void ElementsPanelWidget::editProjectProperties() {
+	if (QETProject *selected_project = elements_panel -> selectedProject()) {
+		emit(requestForProjectPropertiesEdition(selected_project));
+	}
+}
+
+/**
+	Emet le signal requestForDiagramPropertiesEdition avec le schema selectionne
+*/
+void ElementsPanelWidget::editDiagramProperties() {
+	if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
+		emit(requestForDiagramPropertiesEdition(selected_diagram));
+	}
+}
+
+/**
+	Emet le signal requestForNewDiagram avec le projet selectionne
+*/
+void ElementsPanelWidget::newDiagram() {
+	if (QETProject *selected_project = elements_panel -> selectedProject()) {
+		emit(requestForNewDiagram(selected_project));
+	}
+}
+
+/**
+	Emet le signal requestForDiagramDeletion avec le schema selectionne
+*/
+void ElementsPanelWidget::deleteDiagram() {
+	if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
+		emit(requestForDiagramDeletion(selected_diagram));
+	}
+}
+
+/**
+	Emet le signal requestForDiagramMoveUp avec le schema selectionne
+*/
+void ElementsPanelWidget::moveDiagramUp() {
+	if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
+		emit(requestForDiagramMoveUp(selected_diagram));
+	}
+}
+
+/**
+	Emet le signal requestForDiagramMoveDown avec le schema selectionne
+*/
+void ElementsPanelWidget::moveDiagramDown() {
+	if (Diagram *selected_diagram = elements_panel -> selectedDiagram()) {
+		emit(requestForDiagramMoveDown(selected_diagram));
+	}
+}
+
+/**
+	Opens a template editor to create a new title block template.
+*/
+void ElementsPanelWidget::addTitleBlockTemplate() {
+	QTreeWidgetItem *current_item = elements_panel -> currentItem();
+	if (!current_item) return;
+	
+	if (current_item -> type() == QET::TitleBlockTemplatesCollection) {
+		QETApp::instance() -> openTitleBlockTemplate(
+			elements_panel -> templateLocationForItem(current_item)
+		);
+	}
+}
+
+/**
+	Opens an editor to edit the currently selected title block template, if any.
+*/
+void ElementsPanelWidget::editTitleBlockTemplate() {
+	QTreeWidgetItem *current_item = elements_panel -> currentItem();
+	if (current_item && current_item -> type() == QET::TitleBlockTemplate) {
+		QETApp::instance() -> openTitleBlockTemplate(
+			elements_panel -> templateLocationForItem(current_item)
+		);
+	}
+}
+
+/**
+	Delete the currently selected title block template, if any.
+*/
+void ElementsPanelWidget::removeTitleBlockTemplate() {
+	QTreeWidgetItem *current_item = elements_panel -> currentItem();
+	if (current_item && current_item -> type() == QET::TitleBlockTemplate) {
+		TitleBlockTemplateDeleter(
+			elements_panel -> templateLocationForItem(current_item),
+			this
+		).exec();
+	}
+}
+
+/**
+	Appelle l'assistant de creation de nouvel element
+*/
+void ElementsPanelWidget::newElement() {
+	ElementsCategory *selected_category = writableSelectedCategory();
+	
+	NewElementWizard new_element_wizard(this);
+	if (selected_category) {
+		new_element_wizard.preselectCategory(selected_category);
+	}
+	new_element_wizard.exec();
+}
+
+/**
+	Open an element from a file freely chosen by the user.
+*/
+void ElementsPanelWidget::openElementFromFile() {
+	QString fileName = QETElementEditor::getOpenElementFileName(this);
+	
+	// Ouverture de l'element dans l'editeur pour pouvoir ensuite l'enregistrer dans la categorie voulue
+	if (!fileName.isEmpty()) {
+		QETApp::instance() -> openElementFiles(QStringList() << fileName);
+	}
+}
+
+/**
+	Si une categorie accessible en ecriture est selectionnee, cette methode
+	affiche directement un formulaire de creation de categorie en utilisant la
+	selection comme categorie parente.
+	Sinon, elle affiche un gestionnaire de categories, permettant ainsi a
+	l'utilisateur de choisir une categorie parente.
+*/
+void ElementsPanelWidget::newCategory() {
+	ElementsCategory *selected_category = writableSelectedCategory();
+	
+	if (selected_category) {
+		ElementsCategoryEditor new_category_dialog(selected_category -> location(), false, this);
+		if (new_category_dialog.exec() == QDialog::Accepted) {
+			elements_panel -> reload();
+		}
+	} else {
+		launchCategoriesManager();
+		elements_panel -> reload();
+	}
+}
+
+/**
+	Met a jour les boutons afin d'assurer la coherence de l'interface
+*/
+void ElementsPanelWidget::updateButtons() {
+	QTreeWidgetItem *current_item = elements_panel -> currentItem();
+	int current_type = elements_panel -> currentItemType();
+	
+	bool collection_selected = current_type == QET::ElementsCollection;
+	bool category_selected   = current_type & QET::ElementsContainer;
+	bool element_selected    = current_type == QET::Element;
+	
+	if (collection_selected || category_selected || element_selected) {
+		bool element_writable = elements_panel -> selectedItemIsWritable();
+		delete_collection -> setEnabled(collection_selected && element_writable);
+		new_category      -> setEnabled(category_selected && element_writable);
+		edit_category     -> setEnabled(category_selected && !collection_selected);
+		delete_category   -> setEnabled(category_selected && element_writable);
+		new_element       -> setEnabled(category_selected && element_writable);
+		edit_element      -> setEnabled(element_selected);
+		delete_element    -> setEnabled(element_selected && element_writable);
+	} else if (current_type == QET::Project) {
+		bool is_writable = !(elements_panel -> selectedProject() -> isReadOnly());
+		prj_add_diagram -> setEnabled(is_writable);
+		setElementsActionEnabled(false);
+	} else if (current_type == QET::Diagram) {
+		Diagram    *selected_diagram         = elements_panel -> selectedDiagram();
+		QETProject *selected_diagram_project = selected_diagram -> project();
+		
+		bool is_writable           = !(selected_diagram_project -> isReadOnly());
+		int project_diagrams_count = selected_diagram_project -> diagrams().count();
+		int diagram_position       = selected_diagram_project -> diagrams().indexOf(selected_diagram);
+		
+		prj_del_diagram       -> setEnabled(is_writable);
+		prj_move_diagram_up   -> setEnabled(is_writable && diagram_position > 0);
+		prj_move_diagram_down -> setEnabled(is_writable && diagram_position < project_diagrams_count - 1);
+		setElementsActionEnabled(false);
+	} else if (current_type == QET::TitleBlockTemplatesCollection) {
+		TitleBlockTemplateLocation location = elements_panel -> templateLocationForItem(current_item);
+		tbt_add    -> setEnabled(!location.isReadOnly());
+		tbt_edit   -> setEnabled(false); // would not make sense
+		tbt_remove -> setEnabled(false); // would not make sense
+		setElementsActionEnabled(false);
+	} else if (current_type == QET::TitleBlockTemplate) {
+		QTreeWidgetItem *item = elements_panel -> currentItem();
+		TitleBlockTemplateLocation location = elements_panel -> templateLocationForItem(item);
+		tbt_add    -> setEnabled(false); // would not make sense
+		tbt_edit   -> setEnabled(true); // the tbt editor has a read-only mode
+		// deleting a tbt requires its parent collection to be writable
+		tbt_remove -> setEnabled(location.parentCollection() && !(location.parentCollection() -> isReadOnly()));
+		setElementsActionEnabled(false);
+	}
+}
+
+/**
+	Enable or disable elements-related actions (i.e. new/edit/delete
+	categories/elements).
+	@param bool true to enable actions, false to disable them
+*/
+void ElementsPanelWidget::setElementsActionEnabled(bool enable) {
+	delete_collection -> setEnabled(enable);
+	new_category      -> setEnabled(enable);
+	edit_category     -> setEnabled(enable);
+	delete_category   -> setEnabled(enable);
+	new_element       -> setEnabled(enable);
+	edit_element      -> setEnabled(enable);
+	delete_element    -> setEnabled(enable);
+}
+
+/**
+	Lance le gestionnaire de categories. Il s'agit d'un petit dialogue listant
+	les categories accessibles en ecriture et permettant de les editer, de les
+	supprimer et d'en creer de nouvelles.
+*/
+int ElementsPanelWidget::launchCategoriesManager() {
+	QDialog new_category_dialog(this);
+	new_category_dialog.setMinimumSize(480, 280);
+	new_category_dialog.setWindowTitle(tr("Gestionnaire de cat\351gories", "window title"));
+	
+	QVBoxLayout *layout = new QVBoxLayout(&new_category_dialog);
+	QLabel *explication = new QLabel(tr("Vous pouvez utiliser ce gestionnaire pour ajouter, supprimer ou modifier les cat\351gories."));
+	explication -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
+	explication -> setWordWrap(true);
+	layout -> addWidget(explication);
+	
+	layout -> addWidget(new ElementsCategoriesWidget());
+	
+	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close);
+	connect(buttons, SIGNAL(rejected()), &new_category_dialog, SLOT(accept()));
+	layout -> addWidget(buttons);
+	
+	return(new_category_dialog.exec());
+}
+
+/**
+	Gere le menu contextuel du panel d'elements
+	@param pos Position ou le menu contextuel a ete demande
+*/
+void ElementsPanelWidget::handleContextMenu(const QPoint &pos) {
+	// recupere l'item concerne par l'evenement ainsi que son chemin
+	QTreeWidgetItem *item = elements_panel -> itemAt(pos);
+	if (!item) return;
+	
+	updateButtons();
+	context_menu -> clear();
+	
+	QString dir_path = elements_panel -> dirPathForItem(item);
+	if (!dir_path.isEmpty()) {
+		context_menu -> addAction(open_directory);
+		context_menu -> addAction(copy_path);
+		context_menu -> addSeparator();
+	}
+	
+	switch(item -> type()) {
+		case QET::ElementsCategory:
+			context_menu -> addAction(new_category);
+			context_menu -> addAction(edit_category);
+			context_menu -> addAction(delete_category);
+			context_menu -> addAction(new_element);
+			break;
+		case QET::Element:
+			context_menu -> addAction(edit_element);
+			context_menu -> addAction(delete_element);
+			break;
+		case QET::ElementsCollection:
+			context_menu -> addAction(new_category);
+			context_menu -> addAction(delete_collection);
+			context_menu -> addAction(new_element);
+			break;
+		case QET::Project:
+			context_menu -> addAction(prj_activate);
+			context_menu -> addAction(prj_edit_prop);
+			context_menu -> addAction(prj_add_diagram);
+			context_menu -> addAction(prj_close);
+			break;
+		case QET::Diagram:
+			context_menu -> addAction(prj_prop_diagram);
+			context_menu -> addAction(prj_del_diagram);
+			context_menu -> addAction(prj_move_diagram_up);
+			context_menu -> addAction(prj_move_diagram_down);
+			break;
+		case QET::TitleBlockTemplatesCollection:
+			context_menu -> addAction(tbt_add);
+			break;
+		case QET::TitleBlockTemplate:
+			context_menu -> addAction(tbt_edit);
+			context_menu -> addAction(tbt_remove);
+			break;
+	}
+	
+	// affiche le menu
+	if (!context_menu -> isEmpty()) {
+		context_menu -> popup(mapToGlobal(elements_panel -> mapTo(this, pos + QPoint(2, 2))));
+	}
+}
+
+/**
+	Gere les demandes d'edition de categories ou d'elements
+	@param item Item de la collection a editer
+*/
+void ElementsPanelWidget::handleCollectionRequest(const ElementsLocation &item_location) {
+	if (item_location.isNull()) return;
+	ElementsCollectionItem *item = QETApp::collectionItem(item_location);
+	if (!item) return;
+	if (item -> isElement()) {
+		// il s'agit d'un element
+		launchElementEditor(item -> location());
+	}
+	// we could edit it categories, but instead people prefer the double-clic to
+	// expand/collapse them
+}
+
+/**
+	Gere le drop d'un collectionItem sur un autre.
+	Elle memorise dans les attributs de cette classe l'item source et l'item
+	destination du drag'n drop.
+	Un menu est ensuite affiche pour demander a l'utilisateur ce qu'il
+	souhaite faire (deplacer, copier ou annuler).
+	@param src Item source
+	@param dst Item cible
+	@param pos Position ou le menu contextuel a ete demande
+*/
+void ElementsPanelWidget::handleMoveElementsRequest(ElementsCollectionItem *src, ElementsCollectionItem *dst, const QPoint &pos) {
+	if (!src || !dst || !dst -> isCategory()) return;
+	
+	// memorise les items source et cible du drag'n drop
+	dnd_item_src_ = src;
+	dnd_item_dst_ = dst;
+	
+#ifdef ENABLE_PANEL_WIDGET_DND_CHECKS
+	// active ou desactive les actions selon la source et la cible
+	copy_elements_ -> setEnabled(src -> isReadable() && dst -> isWritable());
+	move_elements_ -> setEnabled(!src -> isRootCategory() && src -> isWritable() && dst -> isWritable());
+#endif
+	
+	// affiche un menu contextuel pour que l'utilisateur indique s'il souhaite
+	// effectuer un deplacement ou une copie
+	context_menu -> clear();
+	context_menu -> addAction(copy_elements_);
+	context_menu -> addAction(move_elements_);
+	context_menu -> addSeparator();
+	context_menu -> addAction(cancel_elements_);
+	
+	context_menu -> popup(mapToGlobal(elements_panel -> mapTo(this, pos + QPoint(2, 2))));
+}
+
+/**
+	Cette classe memorise l'item source et l'item destination du dernier drag'n
+	drop. Cette methode effectue le deplacement de l'item source memorise dans
+	l'item destination memorise.
+	@see handleMoveElementsRequest
+*/
+void ElementsPanelWidget::moveElements() {
+	moveElements(dnd_item_src_, dnd_item_dst_);
+}
+
+/**
+	Deplace l'item src dans l'item dst
+*/
+void ElementsPanelWidget::moveElements(ElementsCollectionItem *src, ElementsCollectionItem *dst) {
+	InteractiveMoveElementsHandler *interactive_handler = new InteractiveMoveElementsHandler();
+	src -> move(dst -> toCategory(), interactive_handler);
+	delete interactive_handler;
+	elements_panel -> reload(true);
+}
+
+/**
+	Cette classe memorise l'item source et l'item destination du dernier drag'n
+	drop. Cette methode effectue la copie de l'item source memorise dans l'item
+	destination memorise.
+	@see handleMoveElementsRequest
+*/
+void ElementsPanelWidget::copyElements() {
+	copyElements(dnd_item_src_, dnd_item_dst_);
+	elements_panel -> reload(true);
+}
+
+/**
+	Reflects the fact that collections are being read (i.e from filesystem) in
+	the progress bar.
+*/
+void ElementsPanelWidget::collectionsRead() {
+	progress_bar_ -> setMinimum(0);
+	progress_bar_ -> setMaximum(1);
+	progress_bar_ -> setValue(0);
+	progress_bar_ -> setFormat(tr("Lecture...", "Reading of elements/categories files"));
+	progress_bar_ -> setVisible(true);
+}
+
+/**
+	Reflects the fact that collections have been read (i.e from filesystem) in
+	the progress bar.
+*/
+void ElementsPanelWidget::collectionsReadFinished() {
+	// we do not hide the progress bar because it will be used by updateProgressBar
+}
+
+/**
+	Updates the progress bar
+	@param current value that should be displayed
+	@param maximum maximum expected value; -1 means "use the previously known one"
+*/
+void ElementsPanelWidget::updateProgressBar(int current, int maximum) {
+	int provided_maximum = maximum == -1 ? progress_bar_ -> maximum() : maximum;
+	if (provided_maximum != progress_bar_ -> maximum()) {
+		progress_bar_ -> setMaximum(maximum);
+	}
+	if (!current) {
+		progress_bar_ -> setFormat(tr("Chargement : %p%", "Visual rendering of elements/categories files - %p is the progress percentage"));
+		progress_bar_ -> setVisible(true);
+	}
+	progress_bar_ -> setValue(current);
+}
+
+/**
+	Reflects the fact the whole panel content was loaded by hiding the progress
+	bar and enabling again the reload button.
+*/
+void ElementsPanelWidget::loadingFinished() {
+	QTimer::singleShot(500, progress_bar_, SLOT(hide()));
+	reload -> setEnabled(true);
+	
+}
+
+void ElementsPanelWidget::filterEdited(const QString &next_text) {
+	if (previous_filter_.isEmpty() && next_text.length() == 1) {
+		// the field is not empty anymore: begin filtering
+		elements_panel -> filter(next_text, QET::BeginFilter);
+	} else if (!previous_filter_.isEmpty() && next_text.isEmpty()) {
+		// the field is now empty again: end of filtering
+		elements_panel -> filter(QString(), QET::EndFilter);
+	} else {
+		// regular filtering
+		elements_panel -> filter(next_text, QET::RegularFilter);
+	}
+	previous_filter_ = next_text;
+}
+
+/**
+	Copie l'item src dans l'item dst
+*/
+void ElementsPanelWidget::copyElements(ElementsCollectionItem *src, ElementsCollectionItem *dst) {
+	InteractiveMoveElementsHandler *interactive_handler = new InteractiveMoveElementsHandler();
+	src -> copy(dst -> toCategory(), interactive_handler, true);
+	delete interactive_handler;
+}
+
+/**
+	Edite la categorie selectionnee
+*/
+void ElementsPanelWidget::editCategory() {
+	if (ElementsCollectionItem *selected_item = elements_panel -> selectedItem()) {
+		if (selected_item -> isCategory()) {
+			launchCategoryEditor(selected_item -> location());
+		}
+	}
+}
+
+/**
+	Edite l'element selectionne
+*/
+void ElementsPanelWidget::editElement() {
+	if (ElementsCollectionItem *selected_item = elements_panel -> selectedItem()) {
+		if (selected_item -> isElement()) {
+			launchElementEditor(selected_item -> location());
+		}
+	}
+}
+
+/**
+	Supprime la categorie selectionnee
+*/
+void ElementsPanelWidget::deleteCategory() {
+	if (ElementsCollectionItem *selected_item = elements_panel -> selectedItem()) {
+		if (selected_item -> isCategory() || selected_item -> isCollection()) {
+			ElementsCategoryDeleter cat_deleter(selected_item -> location(), this);
+			if (cat_deleter.exec()) elements_panel -> reload(true);
+		}
+	}
+}
+
+/**
+	Supprime l'element selectionne
+*/
+void ElementsPanelWidget::deleteElement() {
+	if (ElementsCollectionItem *selected_item = elements_panel -> selectedItem()) {
+		if (selected_item -> isElement()) {
+			ElementDeleter elmt_deleter(selected_item -> location(), this);
+			if (elmt_deleter.exec()) elements_panel -> reload(true);
+		}
+	}
+}
+
+/**
+	Lance l'editeur d'element pour l'element filename
+	@param location Emplacement de l'element a editer
+*/
+void ElementsPanelWidget::launchElementEditor(const ElementsLocation &location) {
+	QETApp::instance() -> openElementLocations(QList<ElementsLocation>() << location);
+}
+
+/**
+	Lance l'editeur de categorie pour la categorie path
+	@param location Emplacement de la categorie a editer
+*/
+void ElementsPanelWidget::launchCategoryEditor(const ElementsLocation &location) {
+	ElementsCategoryEditor ece(location, true);
+	if (ece.exec() == QDialog::Accepted) {
+		elements_panel -> reload();
+	}
+}
+
+/**
+	@return la categorie selectionnee s'il y en a une et que celle-ci est
+	accessible en ecriture ; sinon retourne 0
+	@see ElementsPanel::categoryForItem(QTreeWidgetItem *)
+*/
+ElementsCategory *ElementsPanelWidget::writableSelectedCategory() {
+	// recupere l'element selectionne
+	QTreeWidgetItem *selected_qtwi = elements_panel -> currentItem();
+	if (!selected_qtwi) return(0);
+	
+	// l'element selectionne doit pouvoir correspondre a une categorie
+	if (!(selected_qtwi -> type() & QET::ElementsContainer)) return(0);
+	ElementsLocation category_location = elements_panel -> elementLocationForItem(selected_qtwi);
+	ElementsCollectionItem *category = QETApp::collectionItem(category_location, false);
+	ElementsCategory *selected_category = category -> toCategory();
+	if (!selected_category) return(0);
+	
+	// la categorie doit etre accessible en ecriture
+	if (!selected_category -> isWritable()) return(0);
+	
+	return(selected_category);
+}

Added: trunk/sources/elementspanelwidget.h
===================================================================
--- trunk/sources/elementspanelwidget.h	                        (rev 0)
+++ trunk/sources/elementspanelwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,124 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTS_PANEL_WIDGET_H
+#define ELEMENTS_PANEL_WIDGET_H
+#include <QtGui>
+#include "elementspanel.h"
+/**
+	This class embeds an elements panel under a toolbar providing various actions
+	to manage elements.
+	@see ElementsPanel
+*/
+class ElementsPanelWidget : public QWidget {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	ElementsPanelWidget(QWidget * = 0);
+	virtual ~ElementsPanelWidget();
+	
+	private:
+	ElementsPanelWidget(const ElementsPanelWidget &);
+	
+	// attributes
+	private:
+	ElementsPanel *elements_panel;
+	QToolBar *toolbar, *filter_toolbar;
+	QAction *open_directory, *copy_path;
+	QAction *reload;
+	QAction *new_category, *edit_category, *delete_category;
+	QAction *delete_collection;
+	QAction *new_element, *edit_element, *delete_element, *open_element;
+	QAction *prj_activate, *prj_close, *prj_edit_prop, *prj_prop_diagram, *prj_add_diagram, *prj_del_diagram, *prj_move_diagram_up, *prj_move_diagram_down;
+	QAction *tbt_add, *tbt_edit, *tbt_remove;
+	QAction *copy_elements_, *move_elements_, *cancel_elements_;
+	QMenu *context_menu;
+	QAction *erase_textfield;
+	QLineEdit *filter_textfield;
+	ElementsCollectionItem *dnd_item_src_, *dnd_item_dst_;
+	QProgressBar *progress_bar_;
+	
+	// methods
+	public:
+	inline ElementsPanel &elementsPanel() const;
+	
+	signals:
+	void requestForDiagram(Diagram *);
+	void requestForProject(QETProject *);
+	void requestForNewDiagram(QETProject *);
+	void requestForProjectClosing(QETProject *);
+	void requestForProjectPropertiesEdition(QETProject *);
+	void requestForDiagramPropertiesEdition(Diagram *);
+	void requestForDiagramDeletion(Diagram *);
+	void requestForDiagramMoveUp(Diagram *);
+	void requestForDiagramMoveDown(Diagram *);
+	
+	public slots:
+	void clearFilterTextField();
+	void openDirectoryForSelectedItem();
+	void copyPathForSelectedItem();
+	void reloadAndFilter();
+	void activateProject();
+	void closeProject();
+	void editProjectProperties();
+	void editDiagramProperties();
+	void newDiagram();
+	void deleteDiagram();
+	void moveDiagramUp();
+	void moveDiagramDown();
+	void addTitleBlockTemplate();
+	void editTitleBlockTemplate();
+	void removeTitleBlockTemplate();
+	void newCategory();
+	void newElement();
+	void openElementFromFile();
+	void editCategory();
+	void editElement();
+	void deleteCategory();
+	void deleteElement();
+	void updateButtons();
+	void setElementsActionEnabled(bool);
+	int  launchCategoriesManager();
+	void handleContextMenu(const QPoint &);
+	void handleCollectionRequest(const ElementsLocation &);
+	void handleMoveElementsRequest(ElementsCollectionItem *, ElementsCollectionItem *, const QPoint & = QPoint());
+	void moveElements();
+	void moveElements(ElementsCollectionItem *, ElementsCollectionItem *);
+	void copyElements();
+	void copyElements(ElementsCollectionItem *, ElementsCollectionItem *);
+	void collectionsRead();
+	void collectionsReadFinished();
+	void updateProgressBar(int, int);
+	void loadingFinished();
+	void filterEdited(const QString &);
+	
+	private:
+	void launchElementEditor(const ElementsLocation &);
+	void launchCategoryEditor(const ElementsLocation &);
+	ElementsCategory *writableSelectedCategory();
+	QString previous_filter_;
+};
+
+/**
+	@return The elements panel embedded within this widget.
+*/
+inline ElementsPanel &ElementsPanelWidget::elementsPanel() const {
+	return(*elements_panel);
+}
+
+#endif

Added: trunk/sources/elementtextsmover.cpp
===================================================================
--- trunk/sources/elementtextsmover.cpp	                        (rev 0)
+++ trunk/sources/elementtextsmover.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,141 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementtextsmover.h"
+#include "qetgraphicsitem/conductor.h"
+#include "qetgraphicsitem/elementtextitem.h"
+#include "diagram.h"
+#include "diagramcommands.h"
+#include "qetgraphicsitem/element.h"
+#include "qetgraphicsitem/independenttextitem.h"
+
+/**
+	Constructeur
+*/
+ElementTextsMover::ElementTextsMover() :
+	movement_running_(false),
+	current_movement_(),
+	diagram_(0),
+	movement_driver_(0),
+	moved_texts_()
+{
+	
+}
+
+/**
+	Destructeur
+*/
+ElementTextsMover::~ElementTextsMover() {
+}
+
+/**
+	@return true si ce gestionnaire de deplacement est pret a etre utilise,
+	false sinon. Un gestionnaire de deplacement est pret a etre utilise a partir
+	du moment ou le mouvement precedemment gere n'est plus en cours.
+*/
+bool ElementTextsMover::isReady() const {
+	return(!movement_running_);
+}
+
+/**
+	Demarre un nouveau mouvement d'ElementTextItems
+	@param diagram Schema sur lequel se deroule le deplacement
+	@param driver_item Item deplace par la souris et ne necessitant donc pas
+	d'etre deplace lors des appels a continueMovement.
+	@return le nombre d'items concernes par le deplacement, ou -1 si le
+	mouvement n'a pas ete initie
+*/
+int ElementTextsMover::beginMovement(Diagram *diagram, QGraphicsItem *driver_item) {
+	// il ne doit pas y avoir de mouvement en cours
+	if (movement_running_) return(-1);
+	
+	// on s'assure que l'on dispose d'un schema pour travailler
+	if (!diagram) return(-1);
+	diagram_ = diagram;
+	
+	// on prend en compte le driver_item
+	movement_driver_ = driver_item;
+	
+	// au debut du mouvement, le deplacement est nul
+	current_movement_ = QPointF(0.0, 0.0);
+	
+	// on stocke dans cet objet les items concernes par le deplacement
+	moved_texts_.clear();
+	foreach(QGraphicsItem *item, diagram -> selectedItems()) {
+		if (ElementTextItem *text_item = qgraphicsitem_cast<ElementTextItem *>(item)) {
+			moved_texts_ << text_item;
+		}
+	}
+	
+	// on s'assure qu'il y a quelque chose a deplacer
+	if (!moved_texts_.count()) return(-1);
+	
+	// a ce stade, on dispose de toutes les informations necessaires pour
+	// prendre en compte les mouvements
+	
+	// il y a desormais un mouvement en cours
+	movement_running_ = true;
+	
+	return(moved_texts_.count());
+}
+
+/**
+	Ajoute un mouvement au deplacement en cours. Cette methode
+	@param movement mouvement a ajouter au deplacement en cours
+*/
+void ElementTextsMover::continueMovement(const QPointF &movement) {
+	// un mouvement doit avoir ete initie
+	if (!movement_running_) return;
+	
+	// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
+	if (movement.isNull()) return;
+	
+	// prise en compte du mouvement
+	current_movement_ += movement;
+	
+	// deplace les elements selectionnes
+	foreach(ElementTextItem *text_item, moved_texts_) {
+		if (movement_driver_ && text_item == movement_driver_) continue;
+		QPointF applied_movement = text_item -> mapMovementToParent(text_item-> mapMovementFromScene(movement));
+		text_item -> setPos(text_item -> pos() + applied_movement);
+	}
+}
+
+/**
+	Termine le deplacement en creant un objet d'annulation et en l'ajoutant a
+	la QUndoStack du schema concerne.
+	@see Diagram::undoStack()
+*/
+void ElementTextsMover::endMovement() {
+	// un mouvement doit avoir ete initie
+	if (!movement_running_) return;
+	
+	// inutile de faire quoi que ce soit s'il n'y a pas eu de mouvement concret
+	if (!current_movement_.isNull()) {
+		// cree un objet d'annulation pour le mouvement ainsi realise
+		MoveElementsTextsCommand*undo_object = new MoveElementsTextsCommand(
+			diagram_,
+			moved_texts_,
+			current_movement_
+		);
+		
+		diagram_ -> undoStack().push(undo_object);
+	}
+	
+	// il n'y a plus de mouvement en cours
+	movement_running_ = false;
+}

Added: trunk/sources/elementtextsmover.h
===================================================================
--- trunk/sources/elementtextsmover.h	                        (rev 0)
+++ trunk/sources/elementtextsmover.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,51 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_TEXTS_MOVER_H
+#define ELEMENT_TEXTS_MOVER_H
+#include <QtGui>
+#include "diagramcontent.h"
+class ElementTextItem;
+class Diagram;
+/**
+	This class manages the interactive movement of element text items on a
+	particular diagram.
+*/
+class ElementTextsMover {
+	// constructors, destructor
+	public:
+	ElementTextsMover();
+	virtual ~ElementTextsMover();
+	private:
+	ElementTextsMover(const ElementTextsMover &);
+	
+	// methods
+	public:
+	bool isReady() const;
+	int  beginMovement(Diagram *, QGraphicsItem * = 0);
+	void continueMovement(const QPointF &);
+	void endMovement();
+	
+	// attributes
+	private:
+	bool movement_running_;
+	QPointF current_movement_;
+	Diagram *diagram_;
+	QGraphicsItem *movement_driver_;
+	QSet<ElementTextItem *> moved_texts_;
+};
+#endif

Added: trunk/sources/exportdialog.cpp
===================================================================
--- trunk/sources/exportdialog.cpp	                        (rev 0)
+++ trunk/sources/exportdialog.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,695 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "exportdialog.h"
+#include <QSvgGenerator>
+#include <QtXml>
+#include "qeticons.h"
+#include "qetmessagebox.h"
+#include "exportpropertieswidget.h"
+#include "qetdiagrameditor.h"
+
+/**
+	Constructeur
+	@param project Le projet a exporter
+	@param parent Le Widget parent de ce dialogue
+*/
+ExportDialog::ExportDialog(QETProject *project, QWidget *parent) : QDialog(parent) {
+	if (!project) return;
+	
+	// recupere le projet a exporter
+	project_ = project;
+	
+	// recupere les parametres d'export definis dans la configuration de l'application
+	ExportProperties default_export_properties = QETDiagramEditor::defaultExportProperties();
+	
+	// on utilise le repertoire du projet a exporter si possible
+	if (!project_ -> filePath().isEmpty()) {
+		default_export_properties.destination_directory = project_ -> currentDir();
+	}
+	
+	// la taille minimale du dialogue est fixee
+	setMinimumSize(800, 390);
+	resize(minimumSize());
+	setWindowTitle(tr("Exporter les sch\351mas du projet", "window title"));
+
+	// options d'export, dans le widget epw
+	epw = new ExportPropertiesWidget(default_export_properties);
+	
+	// le dialogue comporte deux boutons
+	buttons = new QDialogButtonBox(this);
+	buttons -> setOrientation(Qt::Horizontal);
+	buttons -> setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Save);
+	QPushButton *export_button = buttons -> button(QDialogButtonBox::Save);
+	export_button -> setText(tr("Exporter"));
+	
+	// disposition des elements
+	QVBoxLayout *layout = new QVBoxLayout(this);
+	layout -> addWidget(new QLabel(tr("Choisissez les sch\351mas que vous d\351sirez exporter ainsi que leurs dimensions :")));
+	layout -> addWidget(initDiagramsListPart(), 1);
+	layout -> addWidget(epw);
+	layout -> addWidget(buttons);
+	
+	// connexions signaux/slots
+	connect(epw,     SIGNAL(formatChanged()),       this, SLOT(slot_changeFilesExtension()));
+	connect(epw,     SIGNAL(exportedAreaChanged()), this, SLOT(slot_changeUseBorder()));
+	connect(buttons, SIGNAL(accepted()),            this, SLOT(slot_export()));
+	connect(buttons, SIGNAL(rejected()),            this, SLOT(reject()));
+	
+	// ajustement des extensions des fichiers
+	slot_changeFilesExtension(true);
+}
+
+/**
+	Destructeur - ne fait rien
+*/
+ExportDialog::~ExportDialog() {
+}
+
+/**
+	@return le nombre de schemas coches (donc a exporter)
+*/
+int ExportDialog::diagramsToExportCount() const {
+	int checked_diagrams_count = 0;
+	foreach(ExportDiagramLine *diagram_line, diagram_lines_.values()) {
+		if (diagram_line -> must_export -> isChecked()) ++ checked_diagrams_count;
+	}
+	return(checked_diagrams_count);
+}
+
+/**
+	Met en place la liste des schemas
+	@return Le widget representant la liste des schemas
+*/
+QWidget *ExportDialog::initDiagramsListPart() {
+	preview_mapper_   = new QSignalMapper(this);
+	width_mapper_     = new QSignalMapper(this);
+	height_mapper_    = new QSignalMapper(this);
+	ratio_mapper_     = new QSignalMapper(this);
+	reset_mapper_     = new QSignalMapper(this);
+	clipboard_mapper_ = new QSignalMapper(this);
+	
+	connect(preview_mapper_,   SIGNAL(mapped(int)), this, SLOT(slot_previewDiagram(int)));
+	connect(width_mapper_,     SIGNAL(mapped(int)), this, SLOT(slot_correctHeight(int)));
+	connect(height_mapper_,    SIGNAL(mapped(int)), this, SLOT(slot_correctWidth(int)));
+	connect(ratio_mapper_,     SIGNAL(mapped(int)), this, SLOT(slot_keepRatioChanged(int)));
+	connect(reset_mapper_,     SIGNAL(mapped(int)), this, SLOT(slot_resetSize(int)));
+	connect(clipboard_mapper_, SIGNAL(mapped(int)), this, SLOT(slot_exportToClipBoard(int)));
+	
+	diagrams_list_layout_ = new QGridLayout();
+	
+	int line_count = 0;
+	diagrams_list_layout_ -> addWidget(new QLabel(tr("Sch\351ma")),        line_count, 1, Qt::AlignHCenter | Qt::AlignVCenter);
+	diagrams_list_layout_ -> addWidget(new QLabel(tr("Nom de fichier")),   line_count, 2, Qt::AlignHCenter | Qt::AlignVCenter);
+	diagrams_list_layout_ -> addWidget(new QLabel(tr("Dimensions")),       line_count, 3, Qt::AlignHCenter | Qt::AlignVCenter);
+	
+	// remplit la liste
+	foreach (Diagram *diagram, project_ -> diagrams()) {
+		++ line_count;
+		ExportDiagramLine *diagram_line = new ExportDiagramLine(diagram, diagramSize(diagram));
+		diagram_lines_.insert(line_count, diagram_line);
+		diagrams_list_layout_ -> addWidget(diagram_line -> must_export,    line_count, 0);
+		diagrams_list_layout_ -> addWidget(diagram_line -> title_label,    line_count, 1);
+		diagrams_list_layout_ -> addWidget(diagram_line -> file_name,      line_count, 2);
+		diagrams_list_layout_ -> addLayout(diagram_line -> sizeLayout(),   line_count, 3);
+		
+		// si on decoche tous les schemas, on desactive le bouton "Exporter"
+		connect(diagram_line -> must_export, SIGNAL(toggled(bool)), this, SLOT(slot_checkDiagramsCount()));
+		
+		// mappings et signaux pour la gestion des dimensions du schema
+		width_mapper_  -> setMapping(diagram_line -> width,      line_count);
+		height_mapper_ -> setMapping(diagram_line -> height,     line_count);
+		ratio_mapper_  -> setMapping(diagram_line -> keep_ratio, line_count);
+		reset_mapper_  -> setMapping(diagram_line -> reset_size, line_count);
+		connect(diagram_line -> width,      SIGNAL(valueChanged(int)), width_mapper_,  SLOT(map()));
+		connect(diagram_line -> height,     SIGNAL(valueChanged(int)), height_mapper_, SLOT(map()));
+		connect(diagram_line -> keep_ratio, SIGNAL(toggled(bool)),     ratio_mapper_,  SLOT(map()));
+		connect(diagram_line -> reset_size, SIGNAL(clicked(bool)),     reset_mapper_,  SLOT(map()));
+		
+		// mappings et signaux pour l'apercu du schema
+		preview_mapper_ -> setMapping(diagram_line -> preview, line_count);
+		connect(diagram_line -> preview, SIGNAL(clicked(bool)), preview_mapper_, SLOT(map()));
+		
+		// mappings et signaux pour l'export du schema vers le presse-papier
+		clipboard_mapper_ -> setMapping(diagram_line -> clipboard, line_count);
+		connect(diagram_line -> clipboard, SIGNAL(clicked(bool)), clipboard_mapper_, SLOT(map()));
+	}
+	
+	QWidget *widget_diagrams_list = new QWidget();
+	widget_diagrams_list -> setLayout(diagrams_list_layout_);
+	
+	QScrollArea *scroll_diagrams_list = new QScrollArea();
+	scroll_diagrams_list -> setWidget(widget_diagrams_list);
+	
+	return(scroll_diagrams_list);
+}
+
+/**
+	@param diagram Un schema
+	@return le rapport largeur / hauteur du schema
+*/
+qreal ExportDialog::diagramRatio(Diagram *diagram) {
+	QSize diagram_size = diagramSize(diagram);
+	qreal diagram_ratio = (qreal)diagram_size.width() / (qreal)diagram_size.height();
+	return(diagram_ratio);
+}
+
+/**
+	@param diagram Un schema
+	@return les dimensions du schema, en tenant compte du type d'export : cadre
+	ou elements
+*/
+QSize ExportDialog::diagramSize(Diagram *diagram) {
+	// sauvegarde le parametre useBorder du schema
+	bool state_useBorder = diagram -> useBorder();
+	
+	// applique le useBorder adequat et calcule le ratio
+	diagram -> setUseBorder(epw -> exportProperties().exported_area == QET::BorderArea);
+	QSize diagram_size = diagram -> imageSize();
+	
+	// restaure le parametre useBorder du schema
+	diagram -> setUseBorder(state_useBorder);
+	
+	return(diagram_size);
+}
+
+/**
+	Cette methode ajuste la largeur d'un des schemas a exporter en fonction de
+	sa hauteur si et seulement si l'option "Conserver les proportions" est
+	activee pour ce schema.
+	@param diagram_id numero du schema concerne
+*/
+void ExportDialog::slot_correctWidth(int diagram_id) {
+	// recupere l'ExportDiagramLine concernee
+	ExportDialog::ExportDiagramLine *current_diagram = diagram_lines_[diagram_id];
+	if (!current_diagram) return;
+	
+	// ne fait rien si l'option "Conserver les proportions" n'est pas activee
+	if (!(current_diagram -> keep_ratio -> isChecked())) return;
+	
+	// recupere les proportions du schema
+	qreal diagram_ratio = diagramRatio(current_diagram -> diagram);
+	
+	// ajuste la largeur
+	current_diagram -> width -> blockSignals(true);
+	current_diagram -> width -> setValue(qRound(current_diagram -> height -> value() * diagram_ratio));
+	current_diagram -> width -> blockSignals(false);
+}
+
+/**
+	Cette methode ajuste la hauteur d'un des schemas a exporter en fonction de
+	sa largeur si et seulement si l'option "Conserver les proportions" est
+	activee pour ce schema.
+	@param diagram_id numero du schema concerne
+*/
+void ExportDialog::slot_correctHeight(int diagram_id) {
+	// recupere l'ExportDiagramLine concernee
+	ExportDialog::ExportDiagramLine *current_diagram = diagram_lines_[diagram_id];
+	if (!current_diagram) return;
+	
+	// ne fait rien si l'option "Conserver les proportions" n'est pas activee
+	if (!(current_diagram -> keep_ratio -> isChecked())) return;
+	
+	// recupere les proportions du schema
+	qreal diagram_ratio = diagramRatio(current_diagram -> diagram);
+	
+	// ajuste la hauteur
+	current_diagram -> height -> blockSignals(true);
+	current_diagram -> height -> setValue(qRound(current_diagram -> width -> value() / diagram_ratio));
+	current_diagram -> height -> blockSignals(false);
+}
+
+/**
+	Prend en compte le fait qu'il faut desormais conserver ou non les
+	proportions d'un des schemas
+	@param diagram_id numero du schema concerne
+*/
+void ExportDialog::slot_keepRatioChanged(int diagram_id) {
+	// recupere l'ExportDiagramLine concernee
+	ExportDialog::ExportDiagramLine *current_diagram = diagram_lines_[diagram_id];
+	if (!current_diagram) return;
+	
+	// gere l'icone du bouton "Conserver les proportions"
+	if (current_diagram -> keep_ratio -> isChecked()) {
+		current_diagram -> keep_ratio -> setIcon(QET::Icons::ObjectLocked);
+	} else {
+		current_diagram -> keep_ratio -> setIcon(QET::Icons::ObjectUnlocked);
+	}
+	
+	// ne fait rien si l'option "Conserver les proportions" n'est pas activee
+	if (!(current_diagram -> keep_ratio -> isChecked())) return;
+	
+	// au contraire, si elle est activee, ajuste la hauteur en fonction de la largeur
+	slot_correctHeight(diagram_id);
+}
+
+/**
+	Reinitialise les dimensions d'un des schemas
+	@param diagram_id numero du schema concerne
+*/
+void ExportDialog::slot_resetSize(int diagram_id) {
+	// recupere l'ExportDiagramLine concernee
+	ExportDialog::ExportDiagramLine *current_diagram = diagram_lines_[diagram_id];
+	if (!current_diagram) return;
+	
+	// recupere la taille du schema
+	QSize diagram_size = diagramSize(current_diagram -> diagram);
+	
+	// reinitialise les champs largeur et hauteur
+	current_diagram -> width  -> blockSignals(true);
+	current_diagram -> height -> blockSignals(true);
+	current_diagram -> width  -> setValue(diagram_size.width());
+	current_diagram -> height -> setValue(diagram_size.height());
+	current_diagram -> width  -> blockSignals(false);
+	current_diagram -> height -> blockSignals(false);
+}
+
+/**
+	Genere l'image a exporter
+	@param diagram Schema a exporter en SVG
+	@param width  Largeur de l'export
+	@param height Hauteur de l'export
+	@param keep_aspect_ratio True pour conserver le ratio, false sinon
+	@return l'image a exporter
+*/
+QImage ExportDialog::generateImage(Diagram *diagram, int width, int height, bool keep_aspect_ratio) {
+	saveReloadDiagramParameters(diagram, true);
+	
+	QImage image(width, height, QImage::Format_RGB32);
+	diagram -> toPaintDevice(
+		image,
+		width,
+		height,
+		keep_aspect_ratio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio
+	);
+	
+	saveReloadDiagramParameters(diagram, false);
+	
+	return(image);
+}
+
+/**
+	Sauve ou restaure les parametres du schema
+	@param diagram Schema dont on sauve ou restaure les parametres
+	@param save true pour memoriser les parametres du schema et appliquer ceux
+	definis par le formulaire, false pour restaurer les parametres
+*/
+void ExportDialog::saveReloadDiagramParameters(Diagram *diagram, bool save) {
+	static ExportProperties state_exportProperties;
+	
+	if (save) {
+		// memorise les parametres relatifs au schema tout en appliquant les nouveaux
+		state_exportProperties = diagram -> applyProperties(epw -> exportProperties());
+	} else {
+		// restaure les parametres relatifs au schema
+		diagram -> applyProperties(state_exportProperties);
+	}
+}
+
+/**
+	Exporte le schema en SVG
+	@param diagram Schema a exporter en SVG
+	@param width  Largeur de l'export SVG
+	@param height Hauteur de l'export SVG
+	@param keep_aspect_ratio True pour conserver le ratio, false sinon
+	@param io_device Peripherique de sortie pour le code SVG (souvent : un fichier)
+*/
+void ExportDialog::generateSvg(Diagram *diagram, int width, int height, bool keep_aspect_ratio, QIODevice &io_device) {
+	saveReloadDiagramParameters(diagram, true);
+	
+	// genere une QPicture a partir du schema
+	QPicture picture;
+	diagram -> toPaintDevice(
+		picture,
+		width,
+		height,
+		keep_aspect_ratio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio
+	);
+	
+	// "joue" la QPicture sur un QSvgGenerator
+	QSvgGenerator svg_engine;
+	svg_engine.setSize(QSize(width, height));
+	svg_engine.setOutputDevice(&io_device);
+	QPainter svg_painter(&svg_engine);
+	picture.play(&svg_painter);
+	
+	saveReloadDiagramParameters(diagram, false);
+}
+
+/**
+	Slot effectuant les exports apres la validation du dialogue.
+*/
+void ExportDialog::slot_export() {
+	// recupere la liste des schemas a exporter
+	QList<ExportDiagramLine *> diagrams_to_export;
+	foreach(ExportDiagramLine *diagram_line, diagram_lines_.values()) {
+		if (diagram_line -> must_export -> isChecked()) {
+			diagrams_to_export << diagram_line;
+		}
+	}
+	
+	// verification #1 : chaque schema coche doit avoir un nom de fichier distinct
+	QSet<QString> filenames;
+	foreach(ExportDiagramLine *diagram_line, diagrams_to_export) {
+		QString diagram_file = diagram_line -> file_name -> text();
+		if (!diagram_file.isEmpty()) {
+			filenames << diagram_file;
+		}
+	}
+	if (filenames.count() != diagrams_to_export.count()) {
+		QET::MessageBox::warning(
+			this,
+			tr("Noms des fichiers cibles", "message box title"),
+			tr(
+				"Vous devez entrer un nom de fichier non vide et unique pour chaque "
+				"sch\351ma \340 exporter.",
+				"message box content"
+			)
+		);
+		return;
+	}
+	
+	// verification #2 : un chemin vers un dossier doit avoir ete specifie
+	
+	QDir target_dir_path(epw -> exportProperties().destination_directory);
+	if (!target_dir_path.exists()) {
+		QET::MessageBox::warning(
+			this,
+			tr("Dossier non sp\351cifi\351", "message box title"),
+			tr("Vous devez sp\351cifier le chemin du dossier dans lequel seront enregistr\351s les fichiers images.", "message box content"),
+			QMessageBox::Ok
+		);
+		return;
+	}
+	
+	// exporte chaque schema a exporter
+	foreach(ExportDiagramLine *diagram_line, diagrams_to_export) {
+		exportDiagram(diagram_line);
+	}
+	
+	// fermeture du dialogue
+	accept();
+}
+
+/**
+	Exporte un schema
+	@param diagram_line La ligne decrivant le schema a exporter et la maniere
+	de l'exporter
+*/
+void ExportDialog::exportDiagram(ExportDiagramLine *diagram_line) {
+	ExportProperties export_properties(epw -> exportProperties());
+	
+	// recupere le format a utiliser (acronyme et extension)
+	QString format_acronym = export_properties.format;
+	QString format_extension = "." + format_acronym.toLower();
+	
+	// determine le nom de fichier a utiliser
+	QString diagram_path = diagram_line -> file_name -> text();
+	
+	// determine le chemin du fichier
+	QDir target_dir_path(export_properties.destination_directory);
+	diagram_path = target_dir_path.absoluteFilePath(diagram_path);
+	
+	// recupere des informations sur le fichier specifie
+	QFileInfo file_infos(diagram_path);
+	
+	// verifie qu'il est possible d'ecrire dans le fichier en question
+	if (file_infos.exists() && !file_infos.isWritable()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Impossible d'\351crire dans ce fichier", "message box title"),
+			QString(
+				tr(
+					"Il semblerait que vous n'ayez pas les permissions "
+					"n\351cessaires pour \351crire dans le fichier %1.",
+					"message box content"
+				)
+			).arg(diagram_path),
+			QMessageBox::Ok
+		);
+		return;
+	}
+	
+	// ouvre le fichier
+	QFile target_file(diagram_path);
+	
+	// enregistre l'image dans le fichier
+	if (format_acronym == "SVG") {
+		generateSvg(
+			diagram_line -> diagram,
+			diagram_line -> width  -> value(),
+			diagram_line -> height -> value(),
+			diagram_line -> keep_ratio -> isChecked(),
+			target_file
+		);
+	} else {
+		QImage image = generateImage(
+			diagram_line -> diagram,
+			diagram_line -> width  -> value(),
+			diagram_line -> height -> value(),
+			diagram_line -> keep_ratio -> isChecked()
+		);
+		image.save(&target_file, format_acronym.toUtf8().data());
+	}
+	target_file.close();
+}
+
+/**
+	Slot appele lorsque l'utilisateur change la zone du schema qui doit etre
+	exportee. Il faut alors ajuster les dimensions des schemas.
+*/
+void ExportDialog::slot_changeUseBorder() {
+	// parcourt les schemas a exporter
+	foreach(int diagram_id, diagram_lines_.keys()) {
+		ExportDiagramLine *diagram_line = diagram_lines_[diagram_id];
+		
+		// corrige les dimensions des schemas dont il faut preserver le ratio
+		if (diagram_line -> keep_ratio -> isChecked()) {
+			slot_correctHeight(diagram_id);
+		}
+	}
+}
+
+/**
+	Ce slot est appele quand un schema a ete coche ou decoche.
+	Il active ou desactive le bouton "Exporter" en fonction du nombre de
+	schemas coches, et il garde au plus un schema coche si on exporte vers
+	le presse-papier.
+*/
+void ExportDialog::slot_checkDiagramsCount() {
+	QPushButton *export_button = buttons -> button(QDialogButtonBox::Save);
+	export_button -> setEnabled(diagramsToExportCount());
+}
+
+/**
+	Modifie les extensions des fichiers en fonction du format selectionne
+	@param force_extension true pour ajouter l'extension si elle n'est pas
+	presente, false pour se contenter de la modifier si elle est incorrecte.
+*/
+void ExportDialog::slot_changeFilesExtension(bool force_extension) {
+	// recupere le format a utiliser (acronyme et extension)
+	QString format_acronym = epw -> exportProperties().format;
+	QString format_extension = "." + format_acronym.toLower();
+	
+	// parcourt les schemas a exporter
+	foreach(ExportDiagramLine *diagram_line, diagram_lines_.values()) {
+		QString diagram_filename = diagram_line -> file_name -> text();
+		
+		// cas 1 : l'extension est presente et correcte : on ne fait rien
+		if (diagram_filename.endsWith(format_extension, Qt::CaseInsensitive)) {
+			continue;
+		}
+		
+		QFileInfo diagram_filename_info(diagram_filename);
+		// cas 2 : l'extension est absente
+		if (diagram_filename_info.suffix().isEmpty()) {
+			if (force_extension) {
+				diagram_filename = diagram_filename_info.completeBaseName() + format_extension;
+			}
+		} else {
+			// cas 3 : l'extension est presente mais erronee
+			diagram_filename = diagram_filename_info.completeBaseName() + format_extension;
+		}
+		
+		diagram_line -> file_name -> setText(diagram_filename);
+	}
+}
+
+/**
+	Cette methode fait apparaitre un dialogue permettant de previsualiser un
+	des schemas a exporter
+	@param diagram_id numero du schema a previsualiser
+*/
+void ExportDialog::slot_previewDiagram(int diagram_id) {
+	// recupere l'ExportDiagramLine concernee
+	ExportDialog::ExportDiagramLine *current_diagram = diagram_lines_[diagram_id];
+	if (!current_diagram) return;
+	
+	// initialise un dialogue
+	QDialog preview_dialog;
+	preview_dialog.setWindowTitle(tr("Aper\347u"));
+	preview_dialog.setWindowState(preview_dialog.windowState() | Qt::WindowMaximized);
+	
+	QGraphicsScene *preview_scene = new QGraphicsScene();
+	preview_scene -> setBackgroundBrush(Qt::lightGray);
+	QGraphicsView *preview_view = new QGraphicsView(preview_scene);
+	preview_view -> setDragMode(QGraphicsView::ScrollHandDrag);
+	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
+	connect(buttons, SIGNAL(accepted()), &preview_dialog, SLOT(accept()));
+	
+	QVBoxLayout *vboxlayout1 = new QVBoxLayout();
+	vboxlayout1 -> addWidget(preview_view);
+	vboxlayout1 -> addWidget(buttons);
+	preview_dialog.setLayout(vboxlayout1);
+	
+	// genere le nouvel apercu
+	QImage preview_image = generateImage(
+		current_diagram -> diagram,
+		current_diagram -> width  -> value(),
+		current_diagram -> height -> value(),
+		current_diagram -> keep_ratio -> isChecked()
+	);
+	
+	// nettoie l'apercu
+	foreach (QGraphicsItem *qgi, preview_scene -> items()) {
+		preview_scene -> removeItem(qgi);
+		delete qgi;
+	}
+	
+	// ajoute le nouvel apercu
+	QGraphicsPixmapItem *qgpi = new QGraphicsPixmapItem(QPixmap::fromImage(preview_image));
+	preview_scene -> addItem(qgpi);
+	preview_scene -> setSceneRect(QRectF(0.0, 0.0, preview_image.width(), preview_image.height()));
+	
+	// montre l'apercu
+	preview_dialog.exec();
+}
+
+/**
+	Cette methode exporte un schema vers le presse-papier
+	@param diagram_id numero du schema a previsualiser
+*/
+void ExportDialog::slot_exportToClipBoard(int diagram_id) {
+	// recupere l'ExportDiagramLine concernee
+	ExportDialog::ExportDiagramLine *diagram_line = diagram_lines_[diagram_id];
+	if (!diagram_line) return;
+	
+	// recupere le format a utiliser (acronyme et extension)
+	QString format_acronym = epw -> exportProperties().format;
+	
+	QClipboard *clipboard = QApplication::clipboard();
+	
+	// enregistre l'image dans le fichier
+	if (format_acronym == "SVG") {
+		QByteArray ba;
+		QBuffer buffer(&ba);
+		buffer.open(QIODevice::WriteOnly);
+		generateSvg(
+			diagram_line -> diagram,
+			diagram_line -> width  -> value(),
+			diagram_line -> height -> value(),
+			diagram_line -> keep_ratio -> isChecked(),
+			buffer
+		);
+		buffer.close();
+		clipboard -> setText(ba);
+	} else {
+		QImage image = generateImage(
+			diagram_line -> diagram,
+			diagram_line -> width  -> value(),
+			diagram_line -> height -> value(),
+			diagram_line -> keep_ratio -> isChecked()
+		);
+		clipboard -> setImage(image);
+	}
+}
+
+/**
+	Constructeur
+	@param dia Schema concerne, 
+	@param diagram_size taille du schema tenant compte des parametres d'export
+*/
+ExportDialog::ExportDiagramLine::ExportDiagramLine(Diagram *dia, QSize diagram_size) {
+	diagram = dia;
+	must_export = new QCheckBox();
+	must_export -> setChecked(true);
+	
+	// titre et nom de fichier du schema
+	QString diagram_title = diagram -> title();
+	if (diagram_title.isEmpty()) diagram_title = QObject::tr("Sch\351ma sans titre");
+	QString diagram_filename = diagram -> title();
+	if (diagram_filename.isEmpty()) diagram_filename = QObject::tr("schema");
+	diagram_filename = QET::stringToFileName(diagram_filename);
+	
+	title_label = new QLabel(diagram_title);
+	
+	file_name = new QLineEdit();
+	file_name -> setText(diagram_filename);
+	file_name -> setMinimumWidth(180);
+	
+	width = new QSpinBox();
+	width -> setRange(1, 10000);
+	width -> setSuffix(tr("px"));
+	width -> setValue(diagram_size.width());
+	
+	height = new QSpinBox();
+	height -> setRange(1, 10000);
+	height -> setSuffix(tr("px"));
+	height -> setValue(diagram_size.height());
+	
+	x_label = new QLabel("\327");
+	
+	keep_ratio = new QPushButton();
+	keep_ratio -> setCheckable(true);
+	keep_ratio -> setChecked(true);
+	keep_ratio -> setIcon(QET::Icons::ObjectLocked);
+	keep_ratio -> setToolTip(QObject::tr("Conserver les proportions"));
+	
+	reset_size = new QPushButton();
+	reset_size -> setIcon(QET::Icons::Start);
+	reset_size -> setToolTip(QObject::tr("R\351initialiser les dimensions"));
+	
+	preview = new QPushButton();
+	preview -> setIcon(QET::Icons::ZoomOriginal);
+	preview -> setToolTip(QObject::tr("Aper\347u"));
+	
+	clipboard = new QPushButton();
+	clipboard -> setIcon(QET::Icons::CopyFile);
+	clipboard -> setToolTip(QObject::tr("Exporter vers le presse-papier"));
+}
+
+/**
+	Destructeur
+*/
+ExportDialog::ExportDiagramLine::~ExportDiagramLine() {
+}
+
+/**
+	@return un layout contenant les widgets necessaires a la gestion de la
+	taille d'un schema avant son export.
+*/
+QBoxLayout *ExportDialog::ExportDiagramLine::sizeLayout() {
+	QHBoxLayout *layout = new QHBoxLayout();
+	layout -> addWidget(width);
+	layout -> addWidget(x_label);
+	layout -> addWidget(height);
+	layout -> addWidget(keep_ratio);
+	layout -> addWidget(reset_size);
+	layout -> addWidget(preview);
+	layout -> addWidget(clipboard);
+	return(layout);
+}

Added: trunk/sources/exportdialog.h
===================================================================
--- trunk/sources/exportdialog.h	                        (rev 0)
+++ trunk/sources/exportdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,104 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef EXPORTDIALOG_H
+#define EXPORTDIALOG_H
+#include <QtGui>
+#include "diagram.h"
+#include "qetproject.h"
+class QSvgGenerator;
+class ExportPropertiesWidget;
+/**
+	This class provides a dialog enabling users to export 1 to n diagrams from
+	a project as image files, with features like preview, copy to clipboard,
+	resize, etc.
+*/
+class ExportDialog : public QDialog {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	ExportDialog(QETProject *, QWidget * = 0);
+	virtual ~ExportDialog();
+	
+	private:
+	ExportDialog(const ExportDialog &);
+	
+	// methods
+	public:
+	int diagramsToExportCount() const;
+	
+	private:
+	class ExportDiagramLine {
+		public:
+		ExportDiagramLine(Diagram *, QSize);
+		virtual ~ExportDiagramLine();
+		QBoxLayout *sizeLayout();
+		Diagram *diagram;
+		QCheckBox *must_export;
+		QLabel *title_label;
+		QLineEdit *file_name;
+		QSpinBox *width;
+		QLabel *x_label;
+		QSpinBox *height;
+		QPushButton *keep_ratio;
+		QPushButton *reset_size;
+		QPushButton *preview;
+		QPushButton *clipboard;
+	};
+	
+	// attributes
+	private:
+	QHash<int, ExportDialog::ExportDiagramLine *> diagram_lines_;
+	// visual items
+	QGridLayout *diagrams_list_layout_;
+	ExportPropertiesWidget *epw;
+	QDialogButtonBox *buttons;
+	// mappers
+	QSignalMapper *preview_mapper_;
+	QSignalMapper *width_mapper_;
+	QSignalMapper *height_mapper_;
+	QSignalMapper *ratio_mapper_;
+	QSignalMapper *reset_mapper_;
+	QSignalMapper *clipboard_mapper_;
+	
+	// project whose diagrams are to be exported
+	QETProject *project_;
+	
+	// methods
+	private:
+	QWidget *initDiagramsListPart();
+	void saveReloadDiagramParameters(Diagram *, bool = true);
+	void generateSvg(Diagram *, int, int, bool, QIODevice &);
+	QImage generateImage(Diagram *, int, int, bool);
+	void exportDiagram(ExportDiagramLine *);
+	qreal diagramRatio(Diagram *);
+	QSize diagramSize(Diagram *);
+	
+	public slots:
+	void slot_correctWidth(int);
+	void slot_correctHeight(int);
+	void slot_keepRatioChanged(int);
+	void slot_resetSize(int);
+	void slot_export();
+	void slot_changeUseBorder();
+	void slot_checkDiagramsCount();
+	void slot_changeFilesExtension(bool = false);
+	void slot_previewDiagram(int);
+	void slot_exportToClipBoard(int);
+};
+#endif

Added: trunk/sources/exportproperties.cpp
===================================================================
--- trunk/sources/exportproperties.cpp	                        (rev 0)
+++ trunk/sources/exportproperties.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,82 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "exportproperties.h"
+#include <QDesktopServices>
+
+/**
+	Constructeur par defaut :
+	  * le repertoire de destination est le Bureau de l'utilisateur
+	  * le format d'export est PNG
+	  * la grille et les bornes ne doivent pas etre dessinees
+	  * la bordure et le cartouche doivent etre dessines
+	  * la zone exportee est le schema avec son cadre et son cartouche
+*/
+ExportProperties::ExportProperties() :
+	destination_directory(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)),
+	format("PNG"),
+	draw_grid(false),
+	draw_border(true),
+	draw_titleblock(true),
+	draw_terminals(false),
+	draw_colored_conductors(true),
+	exported_area(QET::BorderArea)
+{
+}
+
+/**
+	Destructeur
+*/
+ExportProperties::~ExportProperties() {
+}
+
+/**
+	Exporte les options dans une configuration.
+	@param settings Parametres a ecrire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void ExportProperties::toSettings(QSettings &settings, const QString &prefix) const {
+	settings.setValue(prefix + "path",                  QDir::toNativeSeparators(destination_directory.absolutePath()));
+	settings.setValue(prefix + "format",                format);
+	settings.setValue(prefix + "drawgrid",              draw_grid);
+	settings.setValue(prefix + "drawborder",            draw_border);
+	settings.setValue(prefix + "drawtitleblock",             draw_titleblock);
+	settings.setValue(prefix + "drawterminals",         draw_terminals);
+	settings.setValue(prefix + "drawcoloredconductors", draw_colored_conductors);
+	settings.setValue(prefix + "area",                  QET::diagramAreaToString(exported_area));
+}
+
+/**
+	Lit les options depuis une configuration.
+	@param settings Parametres a lire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void ExportProperties::fromSettings(QSettings &settings, const QString &prefix) {
+	QString desktop_path = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
+	destination_directory.setPath(settings.value(prefix + "path", desktop_path).toString());
+	if (!destination_directory.exists()) destination_directory.setPath(desktop_path);
+	
+	format = settings.value(prefix + "format").toString();
+	
+	draw_grid               = settings.value(prefix + "drawgrid",              false).toBool();
+	draw_border             = settings.value(prefix + "drawborder",            true ).toBool();
+	draw_titleblock              = settings.value(prefix + "drawtitleblock",             true ).toBool();
+	draw_terminals          = settings.value(prefix + "drawterminals",         false).toBool();
+	draw_colored_conductors = settings.value(prefix + "drawcoloredconductors", true ).toBool();
+	
+	exported_area  = QET::diagramAreaFromString(settings.value(prefix + "area", "border").toString());
+}

Added: trunk/sources/exportproperties.h
===================================================================
--- trunk/sources/exportproperties.h	                        (rev 0)
+++ trunk/sources/exportproperties.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,49 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef EXPORT_PROPERTIES_H
+#define EXPORT_PROPERTIES_H
+#include <QtCore>
+#include "qet.h"
+
+/**
+	This class is a container for various options used when printing or
+	exporting a diagram as an image file.
+*/
+class ExportProperties {
+	// constructeur, destructeur
+	public:
+	ExportProperties();
+	virtual ~ExportProperties();
+	
+	// methods
+	public:
+	void toSettings  (QSettings &, const QString & = QString()) const;
+	void fromSettings(QSettings &, const QString & = QString());
+	
+	// attributes
+	public:
+	QDir destination_directory;     ///< Target directory for generated files
+	QString format;                 ///< Image format of generated files
+	bool draw_grid;                 ///< Whether to render the diagram grid
+	bool draw_border;               ///< Whether to render the border (along with rows/columns headers)
+	bool draw_titleblock;           ///< Whether to render the title block
+	bool draw_terminals;            ///< Whether to render terminals
+	bool draw_colored_conductors;   ///< Whether to render conductors colors
+	QET::DiagramArea exported_area; ///< Area of diagrams to be rendered
+};
+#endif

Added: trunk/sources/exportpropertieswidget.cpp
===================================================================
--- trunk/sources/exportpropertieswidget.cpp	                        (rev 0)
+++ trunk/sources/exportpropertieswidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,208 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "exportpropertieswidget.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+ExportPropertiesWidget::ExportPropertiesWidget(QWidget *parent) : QWidget(parent) {
+	build();
+	setExportProperties(ExportProperties());
+}
+
+/**
+	Constructeur
+	@param export_properties Parametres d'export a afficher / editer
+	@param parent QWidget parent
+*/
+ExportPropertiesWidget::ExportPropertiesWidget(const ExportProperties &export_properties, QWidget *parent) : QWidget(parent) {
+	build();
+	setExportProperties(export_properties);
+}
+
+/// Destructeur
+ExportPropertiesWidget::~ExportPropertiesWidget() {
+}
+
+/**
+	@return les parametres d'export definis via le widget
+*/
+ExportProperties ExportPropertiesWidget::exportProperties() const {
+	ExportProperties export_properties;
+	
+	export_properties.destination_directory   = QDir(dirpath -> text());
+	export_properties.format                  = format -> itemData(format -> currentIndex()).toString();
+	export_properties.draw_grid               = draw_grid      -> isChecked();
+	export_properties.draw_border             = draw_border    -> isChecked();
+	export_properties.draw_titleblock              = draw_titleblock     -> isChecked();
+	export_properties.draw_terminals          = draw_terminals -> isChecked();
+	export_properties.draw_colored_conductors = draw_colored_conductors -> isChecked();
+	export_properties.exported_area           = export_border -> isChecked() ? QET::BorderArea : QET::ElementsArea;
+	
+	return(export_properties);
+}
+
+/**
+	@param export_properties les parametres d'export a afficher / editer via le widget
+*/
+void ExportPropertiesWidget::setExportProperties(const ExportProperties &export_properties) {
+	dirpath -> setText(QDir::toNativeSeparators(export_properties.destination_directory.absolutePath()));
+	
+	int index = format -> findData(export_properties.format);
+	if (index == -1) index = 0;
+	format -> setCurrentIndex(index);
+	
+	draw_grid               -> setChecked(export_properties.draw_grid);
+	draw_border             -> setChecked(export_properties.draw_border);
+	draw_titleblock              -> setChecked(export_properties.draw_titleblock);
+	draw_terminals          -> setChecked(export_properties.draw_terminals);
+	draw_colored_conductors -> setChecked(export_properties.draw_colored_conductors);
+	
+	if (export_properties.exported_area == QET::BorderArea) {
+		export_border -> setChecked(true);
+	} else {
+		export_elements -> setChecked(true);
+	}
+}
+
+/**
+	Passe le widget en mode Impression ou en mode Export. Le mode Impression
+	n'affiche pas autant d'options que le mode Export.
+	@param mode true pour utiliser le widget en mode impression, false pour
+	l'utiliser en mode export
+*/
+void ExportPropertiesWidget::setPrintingMode(bool mode) {
+	dirpath_label   -> setVisible(!mode);
+	dirpath         -> setVisible(!mode);
+	button_browse   -> setVisible(!mode);
+	format_label    -> setVisible(!mode);
+	format          -> setVisible(!mode);
+	export_border   -> setVisible(!mode);
+	export_elements -> setVisible(!mode);
+}
+
+/**
+	Slot demandant a l'utilisateur de choisir un dossier
+*/
+void ExportPropertiesWidget::slot_chooseADirectory() {
+	QString user_dir = QFileDialog::getExistingDirectory(
+		this,
+		tr("Exporter dans le dossier", "dialog title"),
+		dirpath -> text()
+	);
+	if (!user_dir.isEmpty()) {
+		dirpath -> setText(QDir::toNativeSeparators(user_dir));
+	}
+}
+
+/**
+	Cette methode construit le widget en lui-meme
+*/
+void ExportPropertiesWidget::build() {
+	// le dialogue est un empilement vertical d'elements
+	QVBoxLayout *vboxLayout = new QVBoxLayout();
+	vboxLayout -> setContentsMargins(0, 0, 0, 0);
+	
+	/* le dialogue comprend une ligne permettant d'indiquer un chemin de dossier (hboxLayout) */
+	QHBoxLayout *hboxLayout = new QHBoxLayout();
+	dirpath_label = new QLabel(tr("Dossier cible :"), this);
+	dirpath = new QLineEdit(this);
+	QCompleter *completer = new QCompleter(this);
+	completer -> setModel(new QDirModel(completer));
+	dirpath -> setCompleter(completer);
+	button_browse = new QPushButton(tr("Parcourir"), this);
+	hboxLayout -> addWidget(dirpath_label);
+	hboxLayout -> addWidget(dirpath);
+	hboxLayout -> addWidget(button_browse);
+	hboxLayout -> addStretch();
+	
+	vboxLayout -> addLayout(hboxLayout);
+	
+	/* une ligne permettant de choisir le format (hboxLayout1) */
+	QHBoxLayout *hboxLayout1 = new QHBoxLayout();
+	format_label = new QLabel(tr("Format :"), this);
+	hboxLayout1 -> addWidget(format_label);
+	hboxLayout1 -> addWidget(format = new QComboBox(this));
+	format -> addItem(tr("PNG (*.png)"),    "PNG");
+	format -> addItem(tr("JPEG (*.jpg)"),   "JPG");
+	format -> addItem(tr("Bitmap (*.bmp)"), "BMP");
+	format -> addItem(tr("SVG (*.svg)"),    "SVG");
+	hboxLayout1 -> addStretch();
+	
+	vboxLayout -> addLayout(hboxLayout1);
+	
+	/* un cadre permettant de specifier les options de l'image finale */
+	QGroupBox *groupbox_options = new QGroupBox(tr("Options de rendu", "groupbox title"));
+	QGridLayout *optionshlayout = new QGridLayout(groupbox_options);
+	
+	// Choix de la zone du schema a exporter
+	exported_content_choices = new QButtonGroup(groupbox_options);
+	export_border = new QRadioButton(tr("Exporter le cadre"), groupbox_options);
+	optionshlayout -> addWidget(export_border, 0, 0);
+	exported_content_choices -> addButton(export_border);
+	export_elements = new QRadioButton(tr("Exporter les \351l\351ments"), groupbox_options);
+	optionshlayout -> addWidget(export_elements, 0, 1);
+	exported_content_choices -> addButton(export_elements);
+	
+	// dessiner la grille
+	draw_grid = new QCheckBox(tr("Dessiner la grille"), groupbox_options);
+	optionshlayout -> addWidget(draw_grid, 1, 1);
+	
+	// dessiner le cadre
+	draw_border = new QCheckBox(tr("Dessiner le cadre"), groupbox_options);
+	optionshlayout -> addWidget(draw_border, 1, 0);
+	
+	// dessiner le cartouche
+	draw_titleblock = new QCheckBox(tr("Dessiner le cartouche"), groupbox_options);
+	optionshlayout -> addWidget(draw_titleblock, 2, 0);
+	
+	// dessiner les bornes
+	draw_terminals = new QCheckBox(tr("Dessiner les bornes"), groupbox_options);
+	optionshlayout -> addWidget(draw_terminals, 2, 1);
+	
+	// conserver les couleurs des conducteurs
+	draw_colored_conductors = new QCheckBox(tr("Conserver les couleurs des conducteurs"), groupbox_options);
+	optionshlayout -> addWidget(draw_colored_conductors, 3, 0);
+	
+	vboxLayout -> addWidget(groupbox_options);
+	
+	setLayout(vboxLayout);
+
+	// ordre des input selectionnes avec la touche tab
+	setTabOrder(dirpath, button_browse);
+	setTabOrder(button_browse, format);
+	setTabOrder(format, export_border);
+	setTabOrder(export_border, draw_border);
+	setTabOrder(draw_border, draw_grid);
+	setTabOrder(draw_grid, draw_titleblock);
+	setTabOrder(draw_titleblock, draw_terminals);
+	setTabOrder(draw_terminals, draw_colored_conductors);
+	
+	// connexion du bouton permettant le choix du repertoire
+	connect(button_browse, SIGNAL(released()), this, SLOT(slot_chooseADirectory()));
+	
+	// emission de signaux lors du changement de format et lors du changement de zone exportee
+	connect(format,                   SIGNAL(currentIndexChanged(int)),         this, SIGNAL(formatChanged()));
+	connect(exported_content_choices, SIGNAL(buttonClicked(QAbstractButton *)), this, SIGNAL(exportedAreaChanged()));
+	connect(draw_grid,                SIGNAL(stateChanged(int)),                   this, SIGNAL(optionChanged()));
+	connect(draw_border,              SIGNAL(stateChanged(int)),                   this, SIGNAL(optionChanged()));
+	connect(draw_titleblock,               SIGNAL(stateChanged(int)),                   this, SIGNAL(optionChanged()));
+	connect(draw_terminals,           SIGNAL(stateChanged(int)),                   this, SIGNAL(optionChanged()));
+	connect(draw_colored_conductors,  SIGNAL(stateChanged(int)),                   this, SIGNAL(optionChanged()));
+}

Added: trunk/sources/exportpropertieswidget.h
===================================================================
--- trunk/sources/exportpropertieswidget.h	                        (rev 0)
+++ trunk/sources/exportpropertieswidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,70 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef EXPORT_PROPERTIES_WIDGET_H
+#define EXPORT_PROPERTIES_WIDGET_H
+#include <QtGui>
+#include "exportproperties.h"
+
+/**
+	This widget enables users to edit the various options available when
+	exporting a project.
+*/
+class ExportPropertiesWidget : public QWidget {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ExportPropertiesWidget(QWidget * = 0);
+	ExportPropertiesWidget(const ExportProperties &, QWidget * = 0);
+	virtual ~ExportPropertiesWidget();
+	private:
+	ExportPropertiesWidget(const ExportPropertiesWidget &);
+	
+	// methods
+	public:
+	void setExportProperties(const ExportProperties &);
+	ExportProperties exportProperties() const;
+	void setPrintingMode(bool);
+	
+	public slots:
+	void slot_chooseADirectory();
+	
+	signals:
+	void formatChanged();
+	void exportedAreaChanged();
+	void optionChanged();
+	
+	private:
+	void build();
+	
+	// attributes
+	private:
+	QLabel *dirpath_label;
+	QLineEdit *dirpath;
+	QPushButton *button_browse;
+	QLabel *format_label;
+	QComboBox *format;
+	QCheckBox *draw_grid;
+	QCheckBox *draw_border;
+	QCheckBox *draw_titleblock;
+	QCheckBox *draw_terminals;
+	QCheckBox *draw_colored_conductors;
+	QRadioButton *export_border;
+	QRadioButton *export_elements;
+	QButtonGroup *exported_content_choices;
+};
+#endif

Added: trunk/sources/fileelementdefinition.cpp
===================================================================
--- trunk/sources/fileelementdefinition.cpp	                        (rev 0)
+++ trunk/sources/fileelementdefinition.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,218 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementscollectioncache.h"
+#include "fileelementdefinition.h"
+#include "fileelementscategory.h"
+#include "fileelementscollection.h"
+#include "qetapp.h"
+#include "qet.h"
+
+/**
+	Constructeur
+	@param uri Chemin du fichier contenant la definition de l'element
+	@param category Categorie parente
+	@param collection collection parente
+*/
+FileElementDefinition::FileElementDefinition(const QString &uri, FileElementsCategory *category, FileElementsCollection *collection) :
+	ElementDefinition(category, collection),
+	is_null(true),
+	file_path(uri)
+{
+	reload();
+}
+
+/**
+	Destructeur
+*/
+FileElementDefinition::~FileElementDefinition() {
+}
+
+/**
+	@return la definition XML de l'element
+*/
+QDomElement FileElementDefinition::xml() {
+	// ouvre le fichier
+	QFile file(file_path);
+	
+	// charge le contenu du fichier en s'attendant a du XML
+	is_null = !xml_element_.setContent(&file);
+	if (is_null) {
+		return(QDomElement());
+	} else {
+		// l'ouverture de la definition a reussi
+		return(xml_element_.documentElement());
+	}
+}
+
+/**
+	Change la definition XML de l'element
+	@param xml_element Nouvelle definition XML de l'element
+	@return true si l'operation s'est bien passee, false sinon
+*/
+bool FileElementDefinition::setXml(const QDomElement &xml_element) {
+	xml_element_.clear();
+	xml_element_.appendChild(xml_element_.importNode(xml_element, true));
+	is_null = false;
+	return(true);
+}
+
+/**
+	Enregistre la definition de l'element.
+	@return true si l'operation a reussi, false sinon
+*/
+bool FileElementDefinition::write() {
+	return(QET::writeXmlFile(xml_element_, file_path));
+}
+
+/**
+	@return true si la definition n'est pas disponible
+*/
+bool FileElementDefinition::isNull() const {
+	return(is_null);
+}
+
+/**
+	@return Le nom de cet element dans l'arborescence
+*/
+QString FileElementDefinition::pathName() const {
+	return(QFileInfo(file_path).fileName());
+}
+
+/**
+	@return le chemin virtuel de cet element
+*/
+QString FileElementDefinition::virtualPath() {
+	// il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection
+	if (!hasParentCollection()) return(QString());
+	// recupere le chemin absolu de la racine de la collection
+	QString root_abs_path(parentCollection() -> filePath());
+	
+	if (!file_path.startsWith(root_abs_path)) return(QString());
+	QString virtual_path(file_path);
+	virtual_path.remove(root_abs_path);
+	virtual_path.remove(QRegExp("^/"));
+	return(virtual_path);
+}
+
+/**
+	Recharge l'element
+*/
+void FileElementDefinition::reload() {
+	if (file_path.isEmpty()) {
+		is_null = true;
+		return;
+	}
+	
+	// recupere le chemin du fichier *.elmt correspondant
+	QFileInfo file_info(file_path);
+	if (!file_info.exists() || !file_info.isReadable()) {
+		is_null = true;
+		return;
+	}
+	file_path = file_info.canonicalFilePath();
+	
+	if (parentCollection()) {
+		ElementsCollectionCache *cache = parentCollection() -> cache();
+		if (cache && cache -> fetchNameFromCache(location().toString(), file_info.lastModified())) {
+			// the element file has not been modified since the last time
+			// we put its name in cache: we do not need to load it.
+			is_null = false;
+			return;
+		}
+	}
+	
+	// we need to ensure this is a valid XML document
+	QFile file(file_path);
+	QDomDocument xml_document;
+	is_null = !xml_document.setContent(&file);
+	xml_document.clear();
+}
+
+/**
+	@return true si le fichier existe, false sinon
+*/
+bool FileElementDefinition::exists() {
+	if (isNull()) return(false);
+	return(QFileInfo(file_path).exists());
+}
+
+/**
+	@return true si le fichier representant l'element est accessible en
+	lecture.
+*/
+bool FileElementDefinition::isReadable() {
+	if (isNull()) return(false);
+	return(QFileInfo(file_path).isReadable());
+}
+
+/**
+	@return true si le fichier representant l'element est accessible en
+	ecriture.
+*/
+bool FileElementDefinition::isWritable() {
+	if (isNull()) return(false);
+	return(QFileInfo(file_path).isWritable());
+}
+
+/**
+	Supprime le fichier representant l'element
+	@return true si l'operation s'est bien passee, false sinon
+*/
+bool FileElementDefinition::remove() {
+	QFile elmt_file(file_path);
+	if (!elmt_file.exists()) return(true);
+	return(elmt_file.remove());
+}
+
+/**
+	@return true si cet element est represente quelque part sur le systeme de
+	fichiers
+*/
+bool FileElementDefinition::hasFilePath() {
+	return(!file_path.isEmpty());
+}
+
+/**
+	@return le fichier representant cet element sur le systeme de fichiers
+*/
+QString FileElementDefinition::filePath() {
+	return(file_path);
+}
+
+/**
+	Definit le nouveau chemin de cet element dans le systeme de fichiers
+*/
+void FileElementDefinition::setFilePath(const QString &path) {
+	// recupere le chemin du fichier *.elmt correspondant
+	QFileInfo file_info(path);
+	if (!file_info.exists() || !file_info.isReadable()) {
+		return;
+	}
+	file_path = file_info.canonicalFilePath();
+}
+
+/**
+	@return the time of the last modification (mtime) for this element file
+*/
+QDateTime FileElementDefinition::modificationTime() const {
+	QFileInfo file_info(file_path);
+	if (!file_info.exists() || !file_info.isReadable()) {
+		return QDateTime();
+	}
+	return(file_info.lastModified());
+}

Added: trunk/sources/fileelementdefinition.h
===================================================================
--- trunk/sources/fileelementdefinition.h	                        (rev 0)
+++ trunk/sources/fileelementdefinition.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,59 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef FILE_ELEMENT_DEFINITION
+#define FILE_ELEMENT_DEFINITION
+#include <QtCore>
+#include "elementdefinition.h"
+class FileElementsCategory;
+class FileElementsCollection;
+/**
+	This class represents an element definition stored into a file.
+*/
+class FileElementDefinition : public ElementDefinition {
+	public:
+	FileElementDefinition(const QString &, FileElementsCategory * = 0, FileElementsCollection * = 0);
+	virtual ~FileElementDefinition();
+	
+	private:
+	FileElementDefinition(const FileElementDefinition &);
+	
+	// methods
+	public:
+	virtual QDomElement xml();
+	virtual bool setXml(const QDomElement &);
+	virtual bool write();
+	virtual bool isNull() const;
+	virtual QString pathName() const;
+	virtual QString virtualPath();
+	virtual void reload();
+	virtual bool exists();
+	virtual bool isReadable();
+	virtual bool isWritable();
+	virtual bool remove();
+	virtual bool hasFilePath();
+	virtual QString filePath();
+	virtual void setFilePath(const QString &);
+	virtual QDateTime modificationTime() const;
+	
+	// attributes
+	private:
+	bool is_null;
+	QString file_path;
+	QDomDocument xml_element_;
+};
+#endif

Added: trunk/sources/fileelementscategory.cpp
===================================================================
--- trunk/sources/fileelementscategory.cpp	                        (rev 0)
+++ trunk/sources/fileelementscategory.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,484 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "fileelementscategory.h"
+#include "fileelementscollection.h"
+#include "fileelementdefinition.h"
+#include "qet.h"
+
+/**
+	Constructeur
+	@param path Chemin du dossier de la categorie
+	@param parent Categorie parente
+	@param coll Collection parente
+*/
+FileElementsCategory::FileElementsCategory(const QString &path, FileElementsCategory *parent, FileElementsCollection *coll) :
+	ElementsCategory(parent, coll),
+	file_parent_collection_(coll),
+	file_parent_category_(parent),
+	cat_dir(path)
+{
+	reload();
+}
+
+/**
+	Destructeur
+*/
+FileElementsCategory::~FileElementsCategory() {
+	deleteContent();
+}
+
+/**
+	@return le nom de la categorie utilisable dans un chemin (virtuel ou reel)
+*/
+QString FileElementsCategory::pathName() const {
+	return(cat_dir.dirName());
+}
+
+/**
+	@return le chemin virtuel de la categorie, sans le protocole
+*/
+QString FileElementsCategory::virtualPath() {
+	
+	// il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection
+	if (!hasParentCollection()) return(QString());
+	
+	// recupere le chemin absolu de la racine de la collection
+	QString root_abs_path(parentCollection() -> filePath());
+	if (!filePath().startsWith(root_abs_path)) return(QString());
+	
+	QString virtual_path(filePath());
+	virtual_path.remove(root_abs_path);
+	virtual_path.remove(QRegExp("^/"));
+	return(virtual_path);
+}
+
+/**
+	@return true si la categorie possede un chemin sur un systeme de fichiers
+*/
+bool FileElementsCategory::hasFilePath() {
+	return(!filePath().isEmpty());
+}
+
+/**
+	@return le chemin sur le systeme de fichiers de la categorie
+*/
+QString FileElementsCategory::filePath() {
+	return(cat_dir.path());
+}
+
+/**
+	Definit le chemin du dossier representant la categorie
+	@param p nouveau chemin du dossier representant la categorie
+*/
+void FileElementsCategory::setFilePath(const QString &p) {
+	cat_dir.setPath(p);
+}
+
+/**
+	@return la liste des sous-categories de la categorie
+*/
+QList<ElementsCategory *> FileElementsCategory::categories() {
+	QList<ElementsCategory *> result;
+	
+	QList<QString> keys(categories_.keys());
+	qSort(keys.begin(), keys.end());
+	foreach(QString key, keys) result << categories_[key];
+	
+	return(result);
+}
+
+/**
+	@return la categorie correspondant au chemin virtuel cat_path, ou 0 en cas d'echec
+	@param cat_path Chemin virtuel de la categorie voulue
+*/
+ElementsCategory *FileElementsCategory::category(const QString &cat_path) {
+	// recupere les differentes parties du chemin
+	QString cat_path_(cat_path);
+	QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		return(this);
+	} else {
+		// a-t-on la premiere sous-categorie ?
+		if (!categories_.contains(path_parts.first())) {
+			return(0);
+		}
+		
+		// on a la premiere sous-categorie
+		ElementsCategory *first_sub_cat = categories_.value(path_parts.first());
+		
+		if (path_parts.count() == 1) return(first_sub_cat);
+		
+		// on demande a la premiere sous-categorie de fournir la categorie finale
+		path_parts.removeFirst();
+		return(first_sub_cat -> category(path_parts.join("/")));
+	}
+}
+
+/**
+	Cree une categorie. La categorie parente doit exister.
+	@param path chemin d'une categorie a creer sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3
+	@return la nouvelle categorie demandee, ou 0 en cas d'echec
+*/
+ElementsCategory *FileElementsCategory::createCategory(const QString &path) {
+	// on ne doit pas etre en lecture seule
+	if (!isWritable()) return(0);
+	
+	// recupere les differentes parties du chemin
+	QString cat_path_(path);
+	QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		// le chemin est vide, on renvoie 0
+		return(0);
+	} else if (path_parts.count() == 1) {
+		// il n'y a plus qu'une categorie dans le chemin : il faut la creer ici
+		
+		// on verifie si la categorie n'existe pas deja
+		if (categories_.contains(path_parts[0])) {
+			return(categories_.value(path_parts[0]));
+		}
+		
+		// on cree un objet
+		FileElementsCategory *new_category = new FileElementsCategory(
+			cat_dir.absolutePath() + "/" + path_parts[0],
+			this,
+			file_parent_collection_
+		);
+		
+		// on l'integre dans la liste des sous-categories connues
+		categories_.insert(path_parts[0], new_category);
+		
+		// on le renvoie
+		return(new_category);
+	} else {
+		// il y a plusieurs categories dans le chemin :
+		// on delegue le travail a la premiere sous-categorie
+		
+		// a-t-on la premiere sous-categorie ?
+		if (!categories_.contains(path_parts.first())) {
+			return(0);
+		}
+		
+		// on a la premiere sous-categorie
+		ElementsCategory *first_sub_cat = categories_.value(path_parts.first());
+		
+		// on demande a la premiere sous-categorie de fournir la categorie finale
+		path_parts.removeFirst();
+		return(first_sub_cat -> category(path_parts.join("/")));
+	}
+}
+
+/**
+	@return la liste des elements de la categorie
+*/
+QList<ElementDefinition *> FileElementsCategory::elements() {
+	QList<ElementDefinition *> result;
+	
+	QList<QString> keys(elements_.keys());
+	qSort(keys.begin(), keys.end());
+	foreach(QString key, keys) result << elements_[key];
+	
+	return(result);
+}
+
+/**
+	@return l'element correspondant au chemin virtuel elmt_path, ou 0 en cas d'echec
+	@param elmt_path Chemin virtuel de l'element voulu
+*/
+ElementDefinition *FileElementsCategory::element(const QString &elmt_path) {
+	// recupere les differentes parties du chemin
+	QString elmt_path_(elmt_path);
+	QStringList path_parts = elmt_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		// chemin vide
+		return(0);
+	} else if (path_parts.count() == 1) {
+		// seulement le nom de fichier
+		QString element_filename = path_parts.takeLast();
+		if (!elements_.contains(element_filename)) {
+			return(0);
+		} else {
+			return(elements_.value(element_filename));
+		}
+	} else {
+		// separe le nom de fichier du chemin de la categorie et recupere celle-ci
+		QString element_filename = path_parts.takeLast();
+		ElementsCategory *elmt_cat = category(path_parts.join("/"));
+		if (!elmt_cat) {
+			return(0);
+		} else {
+			return(elmt_cat -> element(element_filename));
+		}
+	}
+}
+
+/**
+	Cree un element. La categorie parente doit exister.
+	@param path chemin d'un element a creer sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3/dog.elmt
+	@return le nouvel element demande, ou 0 en cas d'echec
+*/
+ElementDefinition *FileElementsCategory::createElement(const QString &path) {
+	// on ne doit pas etre en lecture seule
+	if (!isWritable()) return(0);
+	
+	// recupere les differentes parties du chemin
+	QString cat_path_(path);
+	QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		// le chemin est vide, on renvoie 0
+		return(0);
+	} else if (path_parts.count() == 1) {
+		// il n'y a plus que l'element dans le chemin : il faut le creer ici
+		
+		// on verifie si l'element n'existe pas deja
+		if (elements_.contains(path_parts[0])) {
+			return(elements_.value(path_parts[0]));
+		}
+		
+		// on cree un objet
+		FileElementDefinition *new_element = new FileElementDefinition(
+			cat_dir.absolutePath() + "/" + path_parts[0],
+			this,
+			file_parent_collection_
+		);
+		
+		// on l'integre dans la liste des elements connus
+		elements_.insert(path_parts[0], new_element);
+		
+		// on le renvoie
+		return(new_element);
+	} else {
+		// il y a plusieurs categories dans le chemin :
+		// on delegue le travail a la premiere sous-categorie
+		
+		// a-t-on la premiere sous-categorie ?
+		if (!categories_.contains(path_parts.first())) {
+			return(0);
+		}
+		
+		// on a la premiere sous-categorie
+		ElementsCategory *first_sub_cat = categories_.value(path_parts.first());
+		
+		// on demande a la premiere sous-categorie de fournir la categorie finale
+		path_parts.removeFirst();
+		return(first_sub_cat -> createElement(path_parts.join("/")));
+	}
+}
+
+/**
+	@return true si le dossier representant la categorie existe
+*/
+bool FileElementsCategory::exists() {
+	return(cat_dir.exists());
+}
+
+/**
+	@return true si la categorie est accessible en lecture
+	Cett methode retourne true a partir du moment ou le dossier representant
+	cette categorie est accessible en lecture. Il se peut que les elements ou
+	le fichier qet_directory a l'interieur ne soient pas accessibles en
+	ecriture.
+*/
+bool FileElementsCategory::isReadable() {
+	return(QFileInfo(cat_dir.absolutePath()).isReadable());
+}
+
+/**
+	@return true s'il est possible d'ecrire le fichier qet_directory dans la
+	categorie
+*/
+bool FileElementsCategory::isWritable() {
+	// informations sur le dossier de la categorie
+	QFileInfo category(cat_dir.absolutePath());
+	QFileInfo qet_directory(cat_dir.absolutePath() + "/qet_directory");
+	/*
+	soit qet_directory n'existe pas et le dossier est accessible en ecriture,
+	soit qet_directory existe et est accessible en ecriture
+	*/
+	return(
+		category.isWritable() &&           // le dossier lui-meme est accessible en ecriture
+		(
+			!qet_directory.exists() ||
+			(qet_directory.exists() && qet_directory.isWritable())
+		)                                  // le fichier qet_directory est accessible en ecriture
+	);
+}
+
+/**
+	Recharge le contenu et les noms de la categorie
+*/
+void FileElementsCategory::reload() {
+	// supprime l'ancien contenu
+	deleteContent();
+	category_names.clearNames();
+	
+	// la categorie doit exister
+	if (!cat_dir.exists()) return;
+	
+	// charge les noms de la categorie
+	loadNames();
+	
+	// charge les sous-categories
+	QStringList dirs = cat_dir.entryList(QStringList(), QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDir::Name);
+	foreach(QString dir, dirs) {
+		categories_.insert(dir, new FileElementsCategory(cat_dir.absoluteFilePath(dir), this, file_parent_collection_));
+	}
+	
+	// charge les elements
+	QStringList elmts = cat_dir.entryList(QStringList("*.elmt"), QDir::Files, QDir::Name);
+	foreach(QString elmt, elmts) {
+		elements_.insert(elmt, new FileElementDefinition(cat_dir.absoluteFilePath(elmt), this, file_parent_collection_));
+	}
+}
+
+/**
+	Supprime le contenu de la categorie puis la categorie elle-meme
+	@return true si l'operation s'est bien passee, false sinon
+*/
+bool FileElementsCategory::remove() {
+	// suppression du contenu de la categorie
+	if (!removeContent()) return(false);
+	
+	// une categorie racine ne se supprime pas elle-meme
+	if (isRootCategory()) return(true);
+	
+	// suppression du fichier de description de la categorie
+	if (cat_dir.exists("qet_directory")) {
+		if (!cat_dir.remove("qet_directory")) return(false);
+	}
+	
+	// suppression de la categorie elle-meme
+	return(cat_dir.rmdir(cat_dir.absolutePath()));
+}
+
+/**
+	Supprime le contenu de la categorie sans supprimer la categorie elle-meme.
+	@return true si l'operation s'est bien passee, false sinon
+*/
+bool FileElementsCategory::removeContent() {
+	// suppression des sous-categories
+	foreach(QString cat_name, categories_.keys()) {
+		ElementsCategory *cat = categories_.value(cat_name);
+		if (cat -> remove()) {
+			categories_.take(cat_name);
+			delete cat;
+		} else {
+			return(false);
+		}
+	}
+	
+	// suppression des elements
+	foreach(QString elmt_name, elements_.keys()) {
+		ElementDefinition *elmt = elements_.value(elmt_name);
+		if (elmt -> remove()) {
+			elements_.take(elmt_name);
+			delete elmt;
+		} else {
+			return(false);
+		}
+	}
+	return(true);
+}
+
+/**
+	Cree la categorie
+	@return true si la creation a reussi, false sinon
+*/
+bool FileElementsCategory::write() {
+	// cree le dossier de la categorie
+	if (!cat_dir.mkpath(cat_dir.path())) return(false);
+	
+	// prepare la structure XML
+	QDomDocument document;
+	QDomElement root = document.createElement("qet-directory");
+	document.appendChild(root);
+	root.appendChild(category_names.toXml(document));
+	
+	QString filepath = cat_dir.absolutePath() + "/qet_directory";
+	return(QET::writeXmlFile(document, filepath));
+}
+
+/**
+	Supprime un repertoire recursivement.
+	@return true si la suppression a reussie, false sinon
+*/
+bool FileElementsCategory::rmdir(const QString &path) const {
+	QDir directory(path);
+	
+	// supprime les dossiers, recursivement
+	foreach (QString file, directory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
+		if (!rmdir(directory.absolutePath() + "/" + file)) return(false);
+	}
+	
+	// supprime les fichiers
+	foreach (QString file, directory.entryList(QDir::Files | QDir::NoDotAndDotDot)) {
+		if (!directory.remove(file)) return(false);
+	}
+	
+	// supprime le dossier lui-meme
+	return(directory.rmdir(path));
+}
+
+/**
+	Charge la liste des noms possibles pour la categorie
+*/
+void FileElementsCategory::loadNames() {
+	// repere le chemin du fichier de configuration de la categorie
+	QFile directory_conf(cat_dir.absolutePath() + "/qet_directory");
+	
+	// verifie l'existence du fichier
+	if (!directory_conf.exists()) return;
+	
+	// ouvre le fichier
+	if (!directory_conf.open(QIODevice::ReadOnly | QIODevice::Text)) return;
+	
+	// lit le contenu du fichier dans un QDomDocument XML
+	QDomDocument document;
+	if (!document.setContent(&directory_conf)) return;
+	
+	// verifie la racine
+	QDomElement root = document.documentElement();
+	if (root.tagName() != "qet-directory") return;
+	
+	category_names.fromXml(root);
+	
+	// ferme le fichier
+	directory_conf.close();
+}
+
+/**
+	Supprime le contenu de la categorie en memoire
+*/
+void FileElementsCategory::deleteContent() {
+	// suppression des elements
+	foreach(QString elmt_name, elements_.keys()) {
+		FileElementDefinition *elmt = elements_.take(elmt_name);
+		delete elmt;
+	}
+	
+	// suppression des categories
+	foreach(QString cat_name, categories_.keys()) {
+		FileElementsCategory *cat = categories_.take(cat_name);
+		delete cat;
+	}
+}

Added: trunk/sources/fileelementscategory.h
===================================================================
--- trunk/sources/fileelementscategory.h	                        (rev 0)
+++ trunk/sources/fileelementscategory.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,82 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef FILE_ELEMENTS_CATEGORY_H
+#define FILE_ELEMENTS_CATEGORY_H
+#include <QtCore>
+#include "elementscategory.h"
+class FileElementsCollection;
+class FileElementDefinition;
+/**
+	This class represents an elements category stored on a filesystem.
+*/
+class FileElementsCategory : public ElementsCategory {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	FileElementsCategory(const QString & = QString(), FileElementsCategory * = 0, FileElementsCollection * = 0);
+	virtual ~FileElementsCategory();
+	
+	private:
+	FileElementsCategory(const FileElementsCategory &);
+	
+	// methods
+	public:
+	virtual QString pathName() const;
+	virtual QString virtualPath();
+	
+	virtual QString filePath();
+	virtual bool hasFilePath();
+	virtual void setFilePath(const QString &);
+	
+	virtual QList<ElementsCategory *> categories();
+	virtual ElementsCategory *category(const QString &);
+	virtual ElementsCategory *createCategory(const QString &);
+	
+	virtual QList<ElementDefinition *> elements();
+	virtual ElementDefinition *element(const QString &);
+	virtual ElementDefinition *createElement(const QString &);
+	
+	virtual bool exists();
+	virtual bool isReadable();
+	virtual bool isWritable();
+	
+	virtual void reload();
+	virtual bool remove();
+	virtual bool removeContent();
+	virtual bool write();
+	
+	private:
+	bool rmdir(const QString &) const;
+	void loadNames();
+	void deleteContent();
+	
+	// attributes
+	protected:
+	/// Parent collection, stored on filesystem too
+	FileElementsCollection *file_parent_collection_;
+	/// Paremt Collection, stored on filesystem too
+	FileElementsCategory   *file_parent_category_;
+	/// Child sub-categories
+	QHash<QString, FileElementsCategory  *> categories_;
+	/// Child elements
+	QHash<QString, FileElementDefinition *> elements_;
+	/// Directory matching this category on filesystem
+	QDir cat_dir;
+};
+#endif

Added: trunk/sources/fileelementscollection.cpp
===================================================================
--- trunk/sources/fileelementscollection.cpp	                        (rev 0)
+++ trunk/sources/fileelementscollection.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,133 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "fileelementscollection.h"
+#include "fileelementscategory.h"
+
+/**
+	Constructeur
+	@param path Chemin du dossier racine de la collection
+	@param parent QObject parent de la collection
+*/
+FileElementsCollection::FileElementsCollection(const QString &path, ElementsCollectionItem *parent) :
+	ElementsCollection(parent),
+	coll_path(path)
+{
+	protocol_ = "unknown";
+	project_ = 0;
+	root = 0;
+}
+
+/**
+	Destructeur
+*/
+FileElementsCollection::~FileElementsCollection() {
+	deleteContent();
+}
+
+/**
+	@return la categorie racine de la collection
+*/
+ElementsCategory *FileElementsCollection::rootCategory() {
+	return(root);
+}
+
+/**
+	Recharge l'arborescence des categories et elements.
+*/
+void FileElementsCollection::reload() {
+	QMutexLocker reload_lock(&reload_mutex_);
+	// oublie l'arborescence precedente
+	deleteContent();
+	
+	// le dossier doit exister et etre lisible
+	QDir coll_dir(coll_path);
+	if (!coll_dir.exists() || !coll_dir.isReadable()) return;
+	coll_path = coll_dir.canonicalPath();
+	
+	root = new FileElementsCategory(coll_path, 0, this);
+}
+
+/**
+	@return true si cette collection est representee quelque part sur le
+	systeme de fichiers.
+*/
+bool FileElementsCollection::hasFilePath() {
+	return(!coll_path.isEmpty());
+}
+
+/**
+	@return le chemin du repertoire representant cette collection
+*/
+QString FileElementsCollection::filePath() {
+	return(coll_path);
+}
+
+/**
+	@param path Nouveau chemin de la collection
+	Cette methode ne recharge pas la collection
+*/
+void FileElementsCollection::setFilePath(const QString &path) {
+	coll_path = path;
+}
+
+/**
+	Supprime le contenu en memoire de cette collection
+*/
+void FileElementsCollection::deleteContent() {
+	delete root;
+	root = 0;
+}
+
+/**
+	@return ttrue si la categorie racine de la collection existe
+*/
+bool FileElementsCollection::exists() {
+	return(root && root -> exists());
+}
+
+/**
+	@return true si la collection est accessible en lecture
+*/
+bool FileElementsCollection::isReadable() {
+	// une collection est accessible en lecture si sa categorie racine l'est
+	return(root && root -> isReadable());
+}
+
+/**
+	@return true si la collection est accessible en ecriture
+*/
+bool FileElementsCollection::isWritable() {
+	// une collection est accessible en ecriture si sa categorie racine l'est
+	return(root && root -> isWritable());
+}
+
+/**
+	Ne fait rien
+	@return toujours true
+*/
+bool FileElementsCollection::write() {
+	return(true);
+}
+
+/**
+	@return always true, since a file-based elements collection can always be
+	cached.
+*/
+bool FileElementsCollection::isCacheable() const {
+	return(true);
+}

Added: trunk/sources/fileelementscollection.h
===================================================================
--- trunk/sources/fileelementscollection.h	                        (rev 0)
+++ trunk/sources/fileelementscollection.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,61 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef FILE_ELEMENTS_COLLECTION_H
+#define FILE_ELEMENTS_COLLECTION_H
+#include <QtCore>
+#include "elementscollection.h"
+class FileElementsCategory;
+/**
+	This class represents an elements collection stored on filesystem, like the
+	collection provided along with QET or users custom collection.
+*/
+class FileElementsCollection : public ElementsCollection {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	FileElementsCollection(const QString &, ElementsCollectionItem *parent = 0);
+	virtual ~FileElementsCollection();
+	
+	private:
+	FileElementsCollection(const FileElementsCollection &);
+	
+	// methods
+	public:
+	virtual void reload();
+	virtual ElementsCategory *rootCategory();
+	
+	virtual bool hasFilePath();
+	virtual QString filePath();
+	virtual void setFilePath(const QString &);
+	virtual bool exists();
+	virtual bool isReadable();
+	virtual bool isWritable();
+	virtual bool write();
+	virtual bool isCacheable() const;
+	
+	private:
+	void deleteContent();
+	
+	// attributes
+	private:
+	QString coll_path;
+	FileElementsCategory *root;
+	QMutex reload_mutex_;       ///< Mutex used to avoid loading a collection twice at the same time
+};
+#endif

Added: trunk/sources/genericpanel.cpp
===================================================================
--- trunk/sources/genericpanel.cpp	                        (rev 0)
+++ trunk/sources/genericpanel.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1262 @@
+#include "genericpanel.h"
+#include <QTreeWidgetItem>
+#include "qetproject.h"
+#include "diagram.h"
+#include "elementscollection.h"
+#include "elementscategory.h"
+#include "elementdefinition.h"
+#include "titleblock/templatescollection.h"
+#include "titleblock/templatelocation.h"
+#include "elementslocation.h"
+#include "qeticons.h"
+#include "elementscollectioncache.h"
+#include "qetapp.h"
+
+/**
+	Constructor
+	@param parent Parent QWidget
+*/
+GenericPanel::GenericPanel(QWidget *parent) :
+	QTreeWidget(parent),
+	cache_(0),
+	first_activation_(true)
+{
+	header() -> hide();
+	setIconSize(QSize(50, 50));
+}
+
+/**
+	Destructor
+*/
+GenericPanel::~GenericPanel() {
+}
+
+/**
+	@return the elements cache currently used, or 0 if none has been set.
+*/
+ElementsCollectionCache *GenericPanel::elementsCache() {
+	return(cache_);
+}
+
+/**
+	@return the elements cache to be used to render elements collection. If no
+	cache has been explicitly set using setElementsCache(), this method builds
+	a basic cache named "genericpanel.sqlite" in the current working directory.
+*/
+ElementsCollectionCache *GenericPanel::getElementsCache() {
+	if (!cache_) {
+		// build a default cache
+		QString cache_path = "./genericpanel.sqlite";
+		cache_ = new ElementsCollectionCache(cache_path, this);
+		/// @todo we need a unique function to get the good language
+		cache_ -> setLocale(QLocale::system().name().left(2));
+	}
+	return(cache_);
+}
+
+/**
+	@return the type of the current item
+*/
+int GenericPanel::currentItemType() {
+	QTreeWidgetItem *current_qtwi = currentItem();
+	if (!current_qtwi) return(0);
+	return(current_qtwi -> type());
+}
+
+QETProject *GenericPanel::projectForItem(QTreeWidgetItem *item) const {
+	if (item && item -> type() == QET::Project) {
+		return(valueForItem<QETProject *>(item));
+	}
+	return(0);
+	
+}
+
+/**
+	
+*/
+Diagram *GenericPanel::diagramForItem(QTreeWidgetItem *item) const {
+	if (item && item -> type() == QET::Diagram) {
+		return(valueForItem<Diagram *>(item));
+	}
+	return(0);
+}
+
+/**
+	
+*/
+TitleBlockTemplateLocation GenericPanel::templateLocationForItem(QTreeWidgetItem *item) const {
+	if (item && item -> type() & QET::TitleBlockTemplatesCollectionItem) {
+		return(valueForItem<TitleBlockTemplateLocation>(item));
+	}
+	return(TitleBlockTemplateLocation());
+}
+
+/**
+	
+*/
+ElementsLocation GenericPanel::elementLocationForItem(QTreeWidgetItem *item) const {
+	if (item && item -> type() & QET::ElementsCollectionItem) {
+		return(valueForItem<ElementsLocation>(item));
+	}
+	return(ElementsLocation());
+}
+
+/**
+	
+*/
+QETProject *GenericPanel::selectedProject() const {
+	return(projectForItem(currentItem()));
+}
+
+/**
+	
+*/
+Diagram *GenericPanel::selectedDiagram() const {
+	return(diagramForItem(currentItem()));
+}
+
+/**
+	
+*/
+TitleBlockTemplateLocation GenericPanel::selectedTemplateLocation() const {
+	return(templateLocationForItem(currentItem()));
+}
+
+/**
+	
+*/
+ElementsLocation GenericPanel::selectedElementLocation() const {
+	return(elementLocationForItem(currentItem()));
+}
+
+/**
+	@param cache New cache to be used to render elements.
+	@param previous if non-zero, this pointer will be set to the previously used cache
+	@return true if the cache was changed, false otherwise (it may happen if the
+	provided cache is already the one being used).
+*/
+bool GenericPanel::setElementsCache(ElementsCollectionCache *cache, ElementsCollectionCache **previous) {
+	if (cache == cache_) return(false);
+	if (previous) {
+		*previous = cache_;
+	}
+	cache_ = cache;
+	return(true);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::addProject(QETProject *project, QTreeWidgetItem *parent_item, PanelOptions options) {
+	if (!project) return(0);
+	bool creation_required;
+	
+	QTreeWidgetItem *project_qtwi = getItemForProject(project, &creation_required);
+	updateProjectItem(project_qtwi, project, options, creation_required);
+	reparent(project_qtwi, parent_item);
+	fillProjectItem(project_qtwi, project, options, creation_required);
+	
+	return(project_qtwi);
+}
+
+/**
+	@param project A standard project.
+	@return the tree item representing the provided project or 0 if this
+	project does not appear within this panel.
+*/
+QTreeWidgetItem *GenericPanel::itemForProject(QETProject *project) {
+	if (!project) return(0);
+	return(projects_.value(project, 0));
+}
+
+/**
+	@param project A standard project.
+	@param created if provided with a pointer to a boolean, this method will
+	update it to reflect whether the returned item has been freshly created or
+	not.
+	@return the tree item representing the provided project. If it does not
+	appear within this panel, it is created.
+*/
+QTreeWidgetItem *GenericPanel::getItemForProject(QETProject *project, bool *created) {
+	if (!project) return(0);
+	
+	QTreeWidgetItem *project_qtwi = projects_.value(project, 0);
+	if (project_qtwi) {
+		if (created) *created = false;
+		return(project_qtwi);
+	}
+	
+	project_qtwi = makeItem(QET::Project);
+	if (created) *created = true;
+	return(project_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateProjectItem(QTreeWidgetItem *project_qtwi, QETProject *project, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	if (!project_qtwi || !project) return(0);
+	
+	if (freshly_created) {
+		project_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(project));
+		projects_.insert(project, project_qtwi);
+		
+		connect(
+			project, SIGNAL(projectInformationsChanged(QETProject *)),
+			this,    SLOT  (projectInformationsChanged(QETProject *))
+		);
+		connect(
+			project, SIGNAL(readOnlyChanged(QETProject *, bool)),
+			this,    SLOT  (projectInformationsChanged(QETProject *))
+		);
+	}
+	
+	// text
+	project_qtwi -> setText(0, project -> pathNameTitle());
+	// tooltip
+	QString final_tooltip = QDir::toNativeSeparators(project -> filePath());
+	if (final_tooltip.isEmpty()) {
+		final_tooltip = tr(
+			"Pas de fichier",
+			"tooltip for a file-less project in the element panel"
+		);
+	}
+	project_qtwi -> setToolTip(0, final_tooltip);
+	QString project_whatsthis = tr("Ceci est un projet QElectroTech, c'est-\340-dire un fichier d'extension .qet regroupant plusieurs sch\351mas/folios. Il embarque \351galement les \351l\351ments et mod\350les de cartouches utilis\351s dans ces sch\351mas/folios.", "\"What's this\" tip");
+	project_qtwi -> setWhatsThis(0, project_whatsthis);
+	return(updateItem(project_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillProjectItem(QTreeWidgetItem *project_qtwi, QETProject *project, PanelOptions options, bool freshly_created) {
+	if (!project_qtwi || !project) return(0);
+	
+	
+	if (options & AddChildDiagrams) {
+		if (freshly_created) {
+			connect(
+				project, SIGNAL(diagramAdded(QETProject *, Diagram *)),
+				this,    SLOT  (diagramAdded(QETProject *, Diagram *))
+			);
+			connect(
+				project, SIGNAL(diagramRemoved(QETProject *, Diagram *)),
+				this,    SLOT  (diagramRemoved(QETProject *, Diagram *))
+			);
+			connect(
+				project, SIGNAL(projectDiagramsOrderChanged(QETProject *, int, int)),
+				this,    SLOT  (projectDiagramsOrderChanged(QETProject *, int, int))
+			);
+			connect(
+				project, SIGNAL(elementIntegrated(QETProject *, const ElementsLocation &)),
+				this,    SLOT(elementIntegrated(QETProject *, const ElementsLocation &))
+			);
+		} else {
+			// remove diagrams unknown to the project (presumably removed)
+			removeObsoleteItems(project -> diagrams(), project_qtwi, QET::Diagram, false);
+		}
+		int index = 0;
+		foreach (Diagram *diagram, project -> diagrams()) {
+			QTreeWidgetItem *diagram_qtwi = addDiagram(diagram, 0, options);
+			project_qtwi -> insertChild(index, diagram_qtwi);
+			++ index;
+		}
+	}
+	
+	if (options & AddChildTemplatesCollection) {
+		if (freshly_created) {
+			connect(
+				project, SIGNAL(diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &)),
+				this,    SLOT  (diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &))
+			);
+		}
+		addTemplatesCollection(
+			project -> embeddedTitleBlockTemplatesCollection(),
+			project_qtwi,
+			options
+		);
+	}
+	
+	if (options & AddChildElementsCollections) {
+		QTreeWidgetItem *collection_qtwi = addElementsCollection(
+			project -> embeddedCollection(),
+			project_qtwi,
+			options
+		);
+		QString collection_whatsthis = tr("Ceci est une collection embarqu\351e dans un fichier projet. Elle permet de stocker et g\351rer les \351l\351ments utilis\351s dans les sch\351mas du projet parent.", "\"What's this\" tip");
+		collection_qtwi -> setWhatsThis(0, collection_whatsthis);
+		
+		// special instructions for the integration category
+		if (QTreeWidgetItem *integration_qtwi = itemForElementsCategory(project -> integrationCategory())) {
+			QString integration_whats_this = tr("Cette cat\351gorie d'\351l\351ments est utilis\351e pour int\351grer automatiquement dans le projet tout \351l\351ment utilis\351 sur un des sch\351mas de ce projet.", "\"What's this\" tip");
+			integration_qtwi -> setWhatsThis(0, integration_whats_this);
+		}
+		
+	}
+	return(fillItem(project_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::addDiagram(Diagram *diagram, QTreeWidgetItem *parent_item, PanelOptions options) {
+	Q_UNUSED(options)
+	if (!diagram) return(0);
+	
+	bool creation_required;
+	
+	QTreeWidgetItem *diagram_qtwi = getItemForDiagram(diagram, &creation_required);
+	updateDiagramItem(diagram_qtwi, diagram, options, creation_required);
+	reparent(diagram_qtwi, parent_item);
+	fillDiagramItem(diagram_qtwi, diagram, options, creation_required);
+	
+	return(diagram_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForDiagram(Diagram *diagram) {
+	if (!diagram) return(0);
+	return(diagrams_.value(diagram, 0));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::getItemForDiagram(Diagram *diagram, bool *created) {
+	if (!diagram) return(0);
+	
+	QTreeWidgetItem *diagram_qtwi = diagrams_.value(diagram, 0);
+	if (diagram_qtwi) {
+		if (created) *created = false;
+		return(diagram_qtwi);
+	}
+	
+	diagram_qtwi = makeItem(QET::Diagram);
+	if (created) *created = true;
+	return(diagram_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateDiagramItem(QTreeWidgetItem *diagram_qtwi, Diagram *diagram, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	if (!diagram || !diagram_qtwi) return(0);
+	
+	QString displayed_title = diagram -> title();
+	if (displayed_title.isEmpty()) {
+		displayed_title = tr("Sch\351ma sans titre", "Fallback label when a diagram has no title");
+	}
+	
+	QString displayed_label;
+	int diagram_folio_idx = diagram -> folioIndex();
+	if (diagram_folio_idx != -1) {
+		displayed_label = QString(
+			tr(
+				"%1 - %2",
+				"label displayed for a diagram in the panel ; %1 is the folio index, %2 is the diagram title"
+			)
+		).arg(diagram_folio_idx + 1).arg(displayed_title);
+	} else {
+		displayed_label = displayed_title;
+	}
+	
+	
+	diagram_qtwi -> setText(0, displayed_label);
+	if (freshly_created) {
+		diagram_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(diagram));
+		diagrams_.insert(diagram, diagram_qtwi);
+		
+		connect(
+			diagram, SIGNAL(diagramTitleChanged(Diagram *, const QString &)),
+			this,    SLOT  (diagramTitleChanged(Diagram *, const QString &))
+		);
+	}
+	
+	return(updateItem(diagram_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillDiagramItem(QTreeWidgetItem *diagram_qtwi, Diagram *diagram, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(diagram)
+	Q_UNUSED(options)
+	Q_UNUSED(freshly_created)
+	return(fillItem(diagram_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::addTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection, QTreeWidgetItem *parent_item, PanelOptions options) {
+	if (!tbt_collection) return(0);
+	bool creation_required;
+	
+	QTreeWidgetItem *tbt_collection_qtwi = getItemForTemplatesCollection(tbt_collection, &creation_required);
+	updateTemplatesCollectionItem(tbt_collection_qtwi, tbt_collection, options, creation_required);
+	reparent(tbt_collection_qtwi, parent_item);
+	fillTemplatesCollectionItem(tbt_collection_qtwi, tbt_collection, options, creation_required);
+	
+	return(tbt_collection_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection) {
+	if (!tbt_collection) return(0);
+	return(tb_templates_.value(tbt_collection -> location(), 0));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::getItemForTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection, bool *created) {
+	if (!tbt_collection) return(0);
+	QTreeWidgetItem *tbt_collection_item = tb_templates_.value(tbt_collection -> location(), 0);
+	if (tbt_collection_item) {
+		if (created) *created = false;
+		return(tbt_collection_item);
+	}
+	
+	tbt_collection_item = makeItem(QET::TitleBlockTemplatesCollection);
+	if (created) *created = true;
+	return(tbt_collection_item);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateTemplatesCollectionItem(QTreeWidgetItem *tbt_collection_qtwi, TitleBlockTemplatesCollection *tbt_collection, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	QString label = tbt_collection -> title();
+	if (label.isEmpty()) label = tr("Mod\350les de cartouche");
+	
+	tbt_collection_qtwi -> setText(0, label);
+	tbt_collection_qtwi -> setToolTip(0, tbt_collection -> location().toString());
+	
+	if (freshly_created) {
+		tbt_collection_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(tbt_collection -> location()));
+		tb_templates_.insert(tbt_collection -> location(), tbt_collection_qtwi);
+	}
+	
+	return(updateItem(tbt_collection_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillTemplatesCollectionItem(QTreeWidgetItem *tbt_collection_qtwi, TitleBlockTemplatesCollection *tbt_collection, PanelOptions options, bool freshly_created) {
+	if (!tbt_collection_qtwi || !tbt_collection) return(tbt_collection_qtwi);
+	
+	if (options & AddChildTemplates) {
+		if (freshly_created) {
+			connect(
+				tbt_collection, SIGNAL(changed(TitleBlockTemplatesCollection*,QString)),
+				this, SLOT(templatesCollectionChanged(TitleBlockTemplatesCollection*, const QString &))
+			);
+			if (QETProject *project = tbt_collection -> parentProject()) {
+				connect(
+					project, SIGNAL(diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &)),
+					this,    SLOT  (templatesCollectionChanged(TitleBlockTemplatesCollection *, const QString &))
+				);
+			}
+		} else {
+			// remove templates unknown to the collection (presumably removed)
+			removeObsoleteItems(tbt_collection -> templatesLocations(), tbt_collection_qtwi, QET::TitleBlockTemplate, false);
+		}
+		
+		int index = 0;
+		foreach (QString template_name, tbt_collection -> templates()) {
+			QTreeWidgetItem *template_item = addTemplate(tbt_collection -> location(template_name), 0, options);
+			tbt_collection_qtwi -> insertChild(index ++, template_item);
+		}
+	}
+	
+	return(fillItem(tbt_collection_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::addTemplate(const TitleBlockTemplateLocation &tb_template, QTreeWidgetItem *parent_item, PanelOptions options) {
+	if (!tb_template.isValid()) return(0);
+	bool creation_required;
+	
+	QTreeWidgetItem *tb_template_qtwi = getItemForTemplate(tb_template, &creation_required);
+	updateTemplateItem(tb_template_qtwi, tb_template, options, creation_required);
+	reparent(tb_template_qtwi, parent_item);
+	fillTemplateItem(tb_template_qtwi, tb_template, options, creation_required);
+	
+	return(tb_template_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForTemplate(const TitleBlockTemplateLocation &tb_template) {
+	return(tb_templates_.value(tb_template, 0));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::getItemForTemplate(const TitleBlockTemplateLocation &tb_template, bool *created) {
+	if (!tb_template.isValid()) return(0);
+	
+	QTreeWidgetItem *tb_template_qtwi = tb_templates_.value(tb_template, 0);
+	if (tb_template_qtwi) {
+		if (created) *created = false;
+		return(tb_template_qtwi);
+	}
+	
+	tb_template_qtwi = makeItem(QET::TitleBlockTemplate);
+	if (created) *created = true;
+	return(tb_template_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateTemplateItem(QTreeWidgetItem *tb_template_qtwi, const TitleBlockTemplateLocation &tb_template, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	tb_template_qtwi -> setText(0, tr("Mod\350le \"%1\"", "used to display a title block template").arg(tb_template.name()));
+	QString tbt_whatsthis = tr(
+		"Ceci est un mod\350le de cartouche, qui peut \352tre appliqu\351 \340 un sch\351ma.",
+		"\"What's this\" tip"
+	);
+	tb_template_qtwi -> setWhatsThis(0, tbt_whatsthis);
+	// note the following lines are technically marking the template as used
+	tb_template_qtwi -> setToolTip(0, tb_template.toString());
+	tb_template_qtwi -> setBackground(0, QBrush());
+	
+	// special action for templates that belong to a project
+	if (QETProject *tbt_project = tb_template.parentProject()) {
+		// display unused templates using a red background
+		if (!tbt_project -> usesTitleBlockTemplate(tb_template)) {
+			markItemAsUnused(tb_template_qtwi);
+		}
+	}
+	
+	if (freshly_created) {
+		tb_template_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(tb_template));
+		tb_templates_.insert(tb_template, tb_template_qtwi);
+	}
+	return(updateItem(tb_template_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillTemplateItem(QTreeWidgetItem *tb_template_qtwi, const TitleBlockTemplateLocation &tb_template, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(tb_template)
+	Q_UNUSED(options)
+	Q_UNUSED(freshly_created)
+	return(fillItem(tb_template_qtwi, options, freshly_created));
+}
+
+/**
+	Add an elements category to the panel.
+	@param parent_item Parent for the created QTreeWidgetItem
+	@param collection Collection to be added to the panel
+	@param options Control the creation of child items
+	@return the created QTreeWidgetItem
+*/
+QTreeWidgetItem *GenericPanel::addElementsCollection(ElementsCollection *collection, QTreeWidgetItem *parent_item, PanelOptions options) {
+	if (!collection) return(0);
+	bool creation_required;
+	
+	QTreeWidgetItem *collection_qtwi = getItemForElementsCollection(collection, &creation_required);
+	updateElementsCollectionItem(collection_qtwi, collection, options, creation_required);
+	reparent(collection_qtwi, parent_item);
+	fillElementsCollectionItem(collection_qtwi, collection, options, creation_required);
+	
+	return(collection_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForElementsCollection(ElementsCollection *collection) {
+	if (!collection) return(0);
+	return(elements_.value(collection -> rootCategory() -> location(), 0));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::getItemForElementsCollection(ElementsCollection *collection, bool *created) {
+	if (!collection) return(0);
+	
+	QTreeWidgetItem *collection_item = elements_.value(collection -> rootCategory() -> location(), 0);
+	if (collection_item) {
+		if (created) *created = false;
+		return(collection_item);
+	}
+	
+	collection_item  = makeItem(QET::ElementsCollection);
+	if (created) *created = true;
+	return(collection_item);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateElementsCollectionItem(QTreeWidgetItem *collection_qtwi, ElementsCollection *collection, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	if (!collection) return(0);
+	
+	QString collection_title = collection -> title();
+	QIcon collection_icon    = collection -> icon();
+	
+	if (!collection_title.isEmpty()) collection_qtwi -> setText(0, collection_title);
+	if (!collection_icon.isNull())   collection_qtwi -> setIcon(0, collection_icon);
+	
+	if (freshly_created) {
+		collection_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(collection -> rootCategory() -> location()));
+		elements_.insert(collection -> rootCategory() -> location(), collection_qtwi);
+		
+		connect(
+			collection, SIGNAL(elementsCollectionChanged(ElementsCollection*)),
+			this,       SLOT(elementsCollectionChanged(ElementsCollection*))
+		);
+	}
+	
+	return(updateItem(collection_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillElementsCollectionItem(QTreeWidgetItem *collection_qtwi, ElementsCollection *collection, PanelOptions options, bool freshly_created) {
+	// use the cache from the provided collection, if any
+	bool restore_previous_cache = false;
+	ElementsCollectionCache *previous_cache = 0;
+	ElementsCollectionCache *collection_cache = collection -> cache();
+	if (collection_cache) {
+		restore_previous_cache = setElementsCache(collection_cache, &previous_cache);
+	}
+	
+	ElementsCollectionCache *cache = getElementsCache();
+	cache -> beginCollection(collection);
+	fillElementsCategoryItem(collection_qtwi, collection -> rootCategory(), options, freshly_created);
+	cache -> endCollection(collection);
+	
+	// restore the former cache
+	if (restore_previous_cache) {
+		setElementsCache(previous_cache);
+	}
+	return(fillItem(collection_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForElementsLocation(const ElementsLocation &location) {
+	return(elements_.value(location, 0));
+}
+
+/**
+	Add an elements category to the panel.
+	@param category Category to be added to the panel
+	@param parent_item Parent for the created QTreeWidgetItem
+	@param options Control the creation of child items
+	@return the created QTreeWidgetItem
+*/
+QTreeWidgetItem *GenericPanel::addElementsCategory(ElementsCategory *category, QTreeWidgetItem *parent_item, PanelOptions options) {
+	if (!category) return(0);
+	bool creation_required;
+	
+	QTreeWidgetItem *category_qtwi = getItemForElementsCategory(category, &creation_required);
+	updateElementsCategoryItem(category_qtwi, category, options, creation_required);
+	reparent(category_qtwi, parent_item);
+	fillElementsCategoryItem(category_qtwi, category, options, creation_required);
+	
+	return(category_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForElementsCategory(ElementsCategory *category) {
+	if (!category) return(0);
+	return(elements_.value(category -> location()));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::getItemForElementsCategory(ElementsCategory *category, bool *created) {
+	if (!category) return(0);
+	
+	QTreeWidgetItem *category_item = elements_.value(category -> location(), 0);
+	if (category_item) {
+		if (created) *created = false;
+		return(category_item);
+	}
+	
+	category_item = makeItem(QET::ElementsCategory);
+	if (created) *created = true;
+	return(category_item);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateElementsCategoryItem(QTreeWidgetItem *category_qtwi, ElementsCategory *category, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	if (!category || !category_qtwi) return(0);
+	QString category_whatsthis = tr(
+		"Ceci est une cat\351gorie d'\351l\351ments, un simple container permettant d'organiser les collections d'\351l\351ments",
+		"\"What's this\" tip"
+	);
+	category_qtwi -> setWhatsThis(0, category_whatsthis);
+	QString category_tooltip = category -> location().toString();
+	category_qtwi -> setToolTip(0, category_tooltip);
+	category_qtwi -> setText(0, category -> name());
+	markItemAsContainer(category_qtwi);
+	
+	if (freshly_created) {
+		category_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(category -> location()));
+		elements_.insert(category -> location(), category_qtwi);
+	}
+	return(updateItem(category_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillElementsCategoryItem(QTreeWidgetItem *category_qtwi, ElementsCategory *category, PanelOptions options, bool freshly_created) {
+	if (!category || !category_qtwi) return(0);
+	
+	int index = 0;
+	
+	category_qtwi -> setData(0, GenericPanel::PanelFlags, (int)options);
+	
+	if (options & AddChildElementsCategories) {
+		if (!freshly_created) {
+			QList<ElementsLocation> sub_categories;
+			foreach(ElementsCategory *sub_category, category -> categories()) {
+				sub_categories << sub_category -> location();
+			}
+			removeObsoleteItems(sub_categories, category_qtwi, QET::ElementsCategory, false);
+		}
+		
+		foreach (ElementsCategory *sub_category, category -> categories()) {
+			QTreeWidgetItem *sub_category_qtwi = addElementsCategory(sub_category, 0, options);
+			category_qtwi -> insertChild(index ++, sub_category_qtwi);
+		}
+	}
+	
+	if (options & AddChildElements) {
+		if (!freshly_created) {
+			QList<ElementsLocation> sub_elements;
+			foreach(ElementDefinition *sub_element, category -> elements()) {
+				sub_elements << sub_element -> location();
+			}
+			removeObsoleteItems(sub_elements, category_qtwi, QET::Element, false);
+		}
+		foreach (ElementDefinition *sub_element, category -> elements()) {
+			QTreeWidgetItem *sub_element_qtwi = addElement(sub_element, 0, options);
+			category_qtwi -> insertChild(index ++, sub_element_qtwi);
+		}
+	}
+	
+	return(fillItem(category_qtwi, options, freshly_created));
+}
+
+/**
+	Refresh elements category at \a location.
+	@return the refreshed tree item
+*/
+QTreeWidgetItem *GenericPanel::refreshElementsCategory(const ElementsLocation &location) {
+	QTreeWidgetItem *item = itemForElementsLocation(location);
+	if (!item) return(0);
+	if (item -> type() != QET::ElementsCategory && item -> type() != QET::ElementsCollection) return(0);
+	QTreeWidgetItem *result = fillElementsCategoryItem(
+		item,
+		QETApp::collectionItem(location) -> toCategory(),
+		PanelOptions(QFlag(item -> data(0, GenericPanel::PanelFlags).toInt())),
+		false
+	);
+	return(result);
+}
+
+/**
+	Refresh element at \a location.
+	@return the refreshed tree item
+*/
+QTreeWidgetItem *GenericPanel::refreshElement(const ElementsLocation &location) {
+	QTreeWidgetItem *item = itemForElementsLocation(location);
+	if (!item) return(0);
+	if (item -> type() != QET::Element) return(0);
+	
+	QTreeWidgetItem *parent = item -> parent();
+	if (!parent) return(0);
+	
+	QTreeWidgetItem *result = updateElementItem(
+		item,
+		QETApp::collectionItem(location) -> toElement(),
+		PanelOptions(QFlag(parent -> data(0, GenericPanel::PanelFlags).toInt())),
+		false
+	);
+	return(result);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::addElement(ElementDefinition *element, QTreeWidgetItem *parent_item, PanelOptions options) {
+	if (!element) return(0);
+	bool creation_required;
+	
+	QTreeWidgetItem *element_qtwi = getItemForElement(element, &creation_required);
+	updateElementItem(element_qtwi, element, options, creation_required);
+	reparent(element_qtwi, parent_item);
+	fillElementItem(element_qtwi, element, options, creation_required);
+	
+	return(element_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::itemForElement(ElementDefinition *element) {
+	if (!element) return(0);
+	return(elements_.value(element -> location(), 0));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::getItemForElement(ElementDefinition *element, bool *created) {
+	if (!element) return(0);
+	
+	QTreeWidgetItem *element_qtwi = elements_.value(element -> location(), 0);
+	if (element_qtwi) {
+		if (created) *created = false;
+		return(element_qtwi);
+	}
+	
+	element_qtwi = makeItem(QET::Element);
+	if (created) *created = true;
+	return(element_qtwi);
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::updateElementItem(QTreeWidgetItem *element_qtwi, ElementDefinition *element, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(options)
+	Q_UNUSED(freshly_created)
+	if (!element_qtwi || !element) {
+		return(updateItem(element_qtwi, options, freshly_created));
+	}
+	
+	ElementsCollectionCache *cache = getElementsCache();
+	if (!cache -> fetchElement(element)) {
+		return(updateItem(element_qtwi, options, freshly_created));
+	}
+	
+	ElementsLocation element_location = element -> location();
+	element_qtwi -> setText(0, cache -> name());
+	QString element_whatsthis = tr("Ceci est un \351l\351ment que vous pouvez ins\351rer dans un sch\351ma.", "\"What's this\" tip");
+	element_qtwi -> setWhatsThis(0, element_whatsthis);
+	if (options & DisplayElementsPreview) {
+		element_qtwi -> setIcon(0, QIcon(cache -> pixmap()));
+	}
+	
+	// note the following lines are technically marking the element as used
+	element_qtwi -> setToolTip(0, element_location.toString());
+	element_qtwi -> setBackground(0, QBrush());
+	// actions speciales pour les elements appartenant a un projet
+	if (QETProject *element_project = element_location.project()) {
+		// affiche en rouge les elements inutilises dans un projet
+		if (!element_project -> usesElement(element -> location())) {
+			markItemAsUnused(element_qtwi);
+		}
+	}
+	
+	if (freshly_created) {
+		element_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(element_location));
+		elements_.insert(element_location, element_qtwi);
+	}
+	
+	return(updateItem(element_qtwi, options, freshly_created));
+}
+
+/**
+	
+*/
+QTreeWidgetItem *GenericPanel::fillElementItem (QTreeWidgetItem *element_qtwi, ElementDefinition *element, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(element_qtwi)
+	Q_UNUSED(element)
+	Q_UNUSED(options)
+	Q_UNUSED(freshly_created)
+	return(fillItem(element_qtwi, options, freshly_created));
+}
+
+/**
+	This generic method is called at the end of each update*Item method. Its
+	only purpose is being reimplemented in a subclass. The default
+	implementation does nothing.
+*/
+QTreeWidgetItem *GenericPanel::updateItem(QTreeWidgetItem *qtwi, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(qtwi);
+	Q_UNUSED(options);
+	Q_UNUSED(freshly_created);
+	QApplication::processEvents();
+	return(qtwi);
+}
+
+/**
+	This generic method is called at the end of each fill*Item method. Its
+	only purpose is being reimplemented in a subclass. The default
+	implementation does nothing.
+*/
+QTreeWidgetItem *GenericPanel::fillItem(QTreeWidgetItem *qtwi, PanelOptions options, bool freshly_created) {
+	Q_UNUSED(qtwi);
+	Q_UNUSED(options);
+	Q_UNUSED(freshly_created);
+	return(qtwi);
+}
+
+/**
+	
+*/
+void GenericPanel::projectInformationsChanged(QETProject *project) {
+	addProject(project, 0, 0);
+	emit(panelContentChanged());
+}
+
+/**
+	
+*/
+void GenericPanel::diagramAdded(QETProject *project, Diagram *diagram) {
+	Q_UNUSED(diagram)
+	addProject(project, 0, GenericPanel::AddChildDiagrams);
+	emit(panelContentChanged());
+}
+
+/**
+	
+*/
+void GenericPanel::diagramRemoved(QETProject *project, Diagram *diagram) {
+	Q_UNUSED(diagram)
+	addProject(project, 0, GenericPanel::AddChildDiagrams);
+	emit(panelContentChanged());
+}
+
+/**
+	@param project Projet auquel appartiennent les schemas concernes
+	@param from Index de l'onglet avant le deplacement
+	@param to   Index de l'onglet apres le deplacement
+*/
+void GenericPanel::projectDiagramsOrderChanged(QETProject *project, int from, int to) {
+	// get the item representing the provided project
+	QTreeWidgetItem *qtwi_project = itemForProject(project);
+	if (!qtwi_project) return;
+	
+	// get the item representing the moved diagram
+	QTreeWidgetItem *moved_qtwi_diagram = qtwi_project -> child(from);
+	if (!moved_qtwi_diagram) return;
+	
+	// remove the QTWI then insert it back at the adequate location
+	bool was_selected = moved_qtwi_diagram -> isSelected();
+	qtwi_project -> removeChild(moved_qtwi_diagram);
+	qtwi_project -> insertChild(to, moved_qtwi_diagram);
+	
+	// update the QTWI labels because they may display the folio index
+	foreach (int diagram_index, QList<int>() << from << to) {
+		QTreeWidgetItem *qtwi_diagram = qtwi_project -> child(diagram_index);
+		if (!qtwi_diagram) continue;
+		
+		Diagram *diagram = valueForItem<Diagram *>(qtwi_diagram);
+		if (diagram) {
+			updateDiagramItem(qtwi_diagram, diagram);
+		}
+	}
+	
+	if (was_selected) {
+		setCurrentItem(moved_qtwi_diagram);
+	}
+	emit(panelContentChanged());
+}
+
+/**
+	Inform this panel the project \a project has integrated the element at \a location
+*/
+QList<ElementsLocation> GenericPanel::elementIntegrated(QETProject *project, const ElementsLocation &location) {
+	Q_UNUSED(project)
+	QList<ElementsLocation> added_locations;
+	
+	int i = 0;
+	ElementsLocation loc = location;
+	// starting from the provided location, goes up into the tree until a displayed location is reached
+	while (i < 100 && !(itemForElementsLocation(loc))) {
+		added_locations << loc;
+		loc = loc.parent();
+		++ i;
+	}
+	if (added_locations.count()) {
+		refreshElementsCategory(loc);
+	} else {
+		if (refreshElement(location)) {
+			added_locations << location;
+		}
+	}
+	
+	// Since we have refreshed the panel before the element is actually used by
+	// the diagram, it will appear as unused; we force it as used.
+	// FIXME a better solution would be to get warned when an element gets used
+	// or unused.
+	if (QTreeWidgetItem *integrated_element_qtwi = itemForElementsLocation(location)) {
+		integrated_element_qtwi -> setToolTip(0, location.toString());
+		integrated_element_qtwi -> setBackground(0, QBrush());
+	}
+	return(added_locations);
+}
+
+/**
+	Inform this panel the diagram \a diagram has changed its title to \a title.
+*/
+void GenericPanel::diagramTitleChanged(Diagram *diagram, const QString &title) {
+	Q_UNUSED(title)
+	GenericPanel::addDiagram(diagram);
+	emit(panelContentChanged());
+}
+
+/**
+	@param collection Title block templates collection that changed and should be updated
+	@param template_name Name of the changed template (unused)
+*/
+void GenericPanel::templatesCollectionChanged(TitleBlockTemplatesCollection*collection, const QString &template_name) {
+	Q_UNUSED(template_name)
+	addTemplatesCollection(collection);
+	emit(panelContentChanged());
+}
+
+/**
+	
+*/
+void GenericPanel::diagramUsedTemplate(TitleBlockTemplatesCollection *collection, const QString &name) {
+	Q_UNUSED(collection)
+	Q_UNUSED(name)
+	addTemplatesCollection(collection);
+	emit(panelContentChanged());
+}
+
+/**
+	
+*/
+void GenericPanel::elementsCollectionChanged(ElementsCollection *collection) {
+	addElementsCollection(collection, 0, 0);
+	emit(panelContentChanged());
+}
+
+/**
+	
+*/
+QString GenericPanel::defaultText(QET::ItemType type) {
+	switch(type) {
+		case QET::ElementsCollectionItem:
+		case QET::Element: return("element");
+		case QET::ElementsContainer:
+		case QET::ElementsCategory: return("elements category");
+		case QET::ElementsCollection: return("elements collection");
+		case QET::TitleBlockTemplatesCollectionItem:
+		case QET::TitleBlockTemplate: return("title block template");
+		case QET::TitleBlockTemplatesCollection: return("title block templates collection");
+		case QET::Diagram: return("diagram");
+		case QET::Project: return("project");
+		default: return(QString());
+	}
+	return(QString());
+}
+
+/**
+	@param type Item type we want the default icon for
+	@return the default icon for \a type
+*/
+QIcon GenericPanel::defaultIcon(QET::ItemType type) {
+	if (type & QET::ElementsContainer) {
+		return(QET::Icons::Folder);
+	} else if (type & QET::TitleBlockTemplatesCollectionItem) {
+		return(QIcon(QET::Icons::TitleBlock.pixmap(QSize(16, 16))));
+	} else if (type == QET::Diagram) {
+		return(QET::Icons::Diagram);
+	} else if (type == QET::Project) {
+		return(QIcon(QET::Icons::ProjectFile.pixmap(QSize(16, 16))));
+	}
+	return(QIcon());
+}
+
+/**
+	Create a QTreeWidgetItem
+	@param parent Parent for the created item
+	@param type Item type (e.g QET::Diagram, QET::Project, ...)
+	@param label Label for the created item
+	@param icon Icon for the created item
+	@return the create QTreeWidgetItem
+*/
+QTreeWidgetItem *GenericPanel::makeItem(QET::ItemType type, QTreeWidgetItem *parent, const QString &label, const QIcon &icon) {
+	QTreeWidgetItem *qtwi = new QTreeWidgetItem(parent, type);
+	qtwi -> setText(0, label.isEmpty() ? defaultText(type) : label);
+	qtwi -> setIcon(0, icon.isNull() ? defaultIcon(type) : icon);
+	return(qtwi);
+}
+
+/**
+	Delete and item and its children.
+	@param item item to delete
+	@param deleted_on_cascade true if the item is not being directly deleted
+	but is undergoing the deletion of its parent.
+*/
+void GenericPanel::deleteItem(QTreeWidgetItem *item, bool deleted_on_cascade) {
+	Q_UNUSED(deleted_on_cascade)
+	// recursively delete child items first
+	for (int i = item -> childCount() - 1 ; i >= 0 ; -- i) {
+		deleteItem(item -> child(i), true);
+	}
+	
+	// delete the item itself
+	unregisterItem(item);
+	delete item;
+}
+
+/**
+	Mark an item as being a container (collection, category, ...)
+*/
+void GenericPanel::markItemAsContainer(QTreeWidgetItem *qtwi) {
+	if (!qtwi) return;
+	QLinearGradient t(0, 0, 200, 0);
+	t.setColorAt(0, QColor("#e8e8e8"));
+	t.setColorAt(1, QColor("#ffffff"));
+	qtwi -> setBackground(0, QBrush(t));
+}
+
+/**
+	Mark the provided QTreeWidgetItem as unused in its parent project.
+	@param qtwi A QTreeWidgetItem
+*/
+void GenericPanel::markItemAsUnused(QTreeWidgetItem *qtwi) {
+	QLinearGradient t(0, 0, 200, 0);
+	t.setColorAt(0, QColor("#ffc0c0"));
+	t.setColorAt(1, QColor("#ffffff"));
+	qtwi -> setBackground(0, QBrush(t));
+	qtwi -> setToolTip(0, QString(tr("%1 [non utilis\351 dans le projet]")).arg(qtwi -> toolTip(0)));
+}
+
+/**
+	
+*/
+void GenericPanel::reparent(QTreeWidgetItem *item, QTreeWidgetItem *parent) {
+	if (parent && item -> parent() != parent) {
+		parent -> addChild(item);
+	}
+}
+
+/**
+	@return the child items of \a item of type \a type
+	@param item Parent item that will be searched.
+	@param type Type of items to look for.
+	@param recursive Whether to search recursively.
+*/
+QList<QTreeWidgetItem *> GenericPanel::childItems(QTreeWidgetItem *item, QET::ItemType type, bool recursive) const {
+	QList<QTreeWidgetItem *> items;
+	if (!item) return(items);
+	for (int i = 0 ; i < item -> childCount() ; ++ i) {
+		QTreeWidgetItem *current_item = item -> child(i);
+		if (!current_item) continue;
+		if (current_item -> type() == type) {
+			items << current_item;
+		}
+		if (recursive) {
+			items << childItems(current_item, type, true);
+		}
+	}
+	return(items);
+}
+
+/**
+	This variant of childItems() removes any child considered obsolete, i.e.
+	not found in \a expected_items.
+	@param expected_items A list of non-obsolete values
+	@param item Parent item that will be searched.
+	@param type Type of items to look for.
+	@param recursive Whether to search recursively.
+	@see GenericPanel::childItems()
+*/
+template<typename T>
+void GenericPanel::removeObsoleteItems(const QList<T> &expected_items, QTreeWidgetItem *item, QET::ItemType type, bool recursive) {
+	// remove items not found in expected_items
+	foreach (QTreeWidgetItem *child_item, childItems(item, type, recursive)) {
+		T child_value = valueForItem<T>(child_item);
+		if (!expected_items.contains(child_value)) {
+			deleteItem(child_item);
+		}
+	}
+}
+
+/**
+	@return the value stored in \a item
+*/
+template<typename T>
+T GenericPanel::valueForItem(QTreeWidgetItem *item) const {
+	return(qVariantValue<T>(item -> data(0, GenericPanel::Item)));
+}
+
+/**
+	
+*/
+void GenericPanel::unregisterItem(QTreeWidgetItem *item) {
+	if (!item) return;
+	
+	int type = item ->type();
+	if (type & QET::ElementsCollectionItem) {
+		elements_.remove(valueForItem<ElementsLocation>(item));
+	} else if (type & QET::TitleBlockTemplatesCollectionItem) {
+		tb_templates_.remove(valueForItem<TitleBlockTemplateLocation>(item));
+	} else if (type == QET::Diagram) {
+		diagrams_.remove(valueForItem<Diagram *>(item));
+	} else if (type == QET::Project) {
+		projects_.remove(valueForItem<QETProject *>(item));
+	}
+}
+
+/**
+	
+*/
+void GenericPanel::clearPanel() {
+	clear();
+	projects_.clear();
+	diagrams_.clear();
+	tb_templates_.clear();
+	elements_.clear();
+}
+
+/**
+	Handle various events; reimplemented here to emit the signal
+	firstActivated().
+*/
+bool GenericPanel::event(QEvent *event) {
+	if (first_activation_) {
+		if (event -> type() == QEvent::WindowActivate || event -> type() == QEvent::Show) {
+			QTimer::singleShot(250, this, SLOT(emitFirstActivated()));
+			first_activation_ = false;
+		}
+	}
+	return(QTreeWidget::event(event));
+}
+
+/**
+	Emit the signal firstActivated().
+*/
+void GenericPanel::emitFirstActivated() {
+	emit(firstActivated());
+}

Added: trunk/sources/genericpanel.h
===================================================================
--- trunk/sources/genericpanel.h	                        (rev 0)
+++ trunk/sources/genericpanel.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,210 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef GENERIC_PANEL_H
+#define GENERIC_PANEL_H
+#include "qet.h"
+#include <QTreeWidget>
+#include "elementslocation.h"
+class QTreeWidgetItem;
+class QETProject;
+class Diagram;
+class ElementsCollection;
+class ElementsCategory;
+class ElementDefinition;
+class TitleBlockTemplatesCollection;
+class TitleBlockTemplateLocation;
+class ElementsCollectionCache;
+
+/**
+	The generic panel is a QTreeWidget subclass providing extra methods
+	allowing developers to easily add objects (projects, diagrams, title block
+	templates, elements, ...) to it; it also ensures the displayed information
+	remains up to date.
+*/
+class GenericPanel : public QTreeWidget {
+	Q_OBJECT
+	
+	public:
+	enum PanelOption {
+		AddChildDiagrams              =   1,
+		AddChildTemplatesCollection   =   2,
+		AddChildTemplates             =   4,
+		AddAllChildTemplates          =   6,
+		AddChildElementsCollections   =   8,
+		AddChildElementsCategories    =  16,
+		AddChildElementsContainers    =  24,
+		AddChildElements              =  32,
+		AddAllChildElements           =  56,
+		AddAllChild                   =  63,
+		DisplayElementsPreview        =  64,
+		All                           = 127
+	};
+	Q_DECLARE_FLAGS(PanelOptions, PanelOption)
+	
+	enum MetaData {
+		Item = Qt::UserRole + 1,
+		AliasItem,
+		Parent,
+		PanelFlags
+	};
+	
+	// Constructors, destructor
+	public:
+	GenericPanel(QWidget * = 0);
+	virtual ~GenericPanel();
+	
+	// cache-related methods
+	public:
+	virtual ElementsCollectionCache *elementsCache();
+	virtual bool setElementsCache(ElementsCollectionCache *, ElementsCollectionCache ** = 0);
+	
+	protected:
+	virtual ElementsCollectionCache *getElementsCache();
+	
+	public:
+	// convenience methods to obtain what an item represents
+	virtual int currentItemType();
+	virtual QETProject *projectForItem(QTreeWidgetItem *) const;
+	virtual Diagram *diagramForItem(QTreeWidgetItem *) const;
+	virtual TitleBlockTemplateLocation templateLocationForItem(QTreeWidgetItem *) const;
+	virtual ElementsLocation elementLocationForItem(QTreeWidgetItem *) const;
+	
+	// convenience methods to obtain what the selected item represents
+	virtual QETProject *selectedProject() const;
+	virtual Diagram *selectedDiagram() const;
+	virtual TitleBlockTemplateLocation selectedTemplateLocation() const;
+	virtual ElementsLocation selectedElementLocation() const;
+	
+	// project-related methods
+	public:
+	virtual QTreeWidgetItem *addProject(QETProject *, QTreeWidgetItem * = 0, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForProject(QETProject *);
+	protected:
+	virtual QTreeWidgetItem *getItemForProject(QETProject *, bool * = 0);
+	virtual QTreeWidgetItem *updateProjectItem(QTreeWidgetItem *, QETProject *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillProjectItem  (QTreeWidgetItem *, QETProject *, PanelOptions = AddAllChild, bool = false);
+	
+	// diagram-related methods
+	public:
+	virtual QTreeWidgetItem *addDiagram(Diagram *, QTreeWidgetItem * = 0, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForDiagram(Diagram *);
+	protected:
+	virtual QTreeWidgetItem *getItemForDiagram(Diagram *, bool * = 0);
+	virtual QTreeWidgetItem *updateDiagramItem(QTreeWidgetItem *, Diagram *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillDiagramItem  (QTreeWidgetItem *, Diagram *, PanelOptions = AddAllChild, bool = false);
+	
+	// title block templates collections methods
+	public:
+	virtual QTreeWidgetItem *addTemplatesCollection(TitleBlockTemplatesCollection *, QTreeWidgetItem * = 0, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForTemplatesCollection(TitleBlockTemplatesCollection *);
+	protected:
+	virtual QTreeWidgetItem *getItemForTemplatesCollection(TitleBlockTemplatesCollection *, bool * = 0);
+	virtual QTreeWidgetItem *updateTemplatesCollectionItem(QTreeWidgetItem *, TitleBlockTemplatesCollection *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillTemplatesCollectionItem  (QTreeWidgetItem *, TitleBlockTemplatesCollection *, PanelOptions = AddAllChild, bool = false);
+	
+	// title block templates methods
+	public:
+	virtual QTreeWidgetItem *addTemplate(const TitleBlockTemplateLocation &, QTreeWidgetItem * = 0, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForTemplate(const TitleBlockTemplateLocation &);
+	protected:
+	virtual QTreeWidgetItem *getItemForTemplate(const TitleBlockTemplateLocation &, bool * = 0);
+	virtual QTreeWidgetItem *updateTemplateItem(QTreeWidgetItem *, const TitleBlockTemplateLocation &, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillTemplateItem  (QTreeWidgetItem *, const TitleBlockTemplateLocation &, PanelOptions = AddAllChild, bool = false);
+	
+	// elements collections methods
+	public:
+	virtual QTreeWidgetItem *itemForElementsLocation(const ElementsLocation &);
+	virtual QTreeWidgetItem *addElementsCollection(ElementsCollection *, QTreeWidgetItem *, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForElementsCollection(ElementsCollection *);
+	protected:
+	virtual QTreeWidgetItem *getItemForElementsCollection(ElementsCollection *, bool * = 0);
+	virtual QTreeWidgetItem *updateElementsCollectionItem(QTreeWidgetItem *, ElementsCollection *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillElementsCollectionItem  (QTreeWidgetItem *, ElementsCollection *, PanelOptions = AddAllChild, bool = false);
+	
+	// elements categories methods
+	public:
+	virtual QTreeWidgetItem *addElementsCategory(ElementsCategory *, QTreeWidgetItem * = 0, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForElementsCategory(ElementsCategory *);
+	protected:
+	virtual QTreeWidgetItem *getItemForElementsCategory(ElementsCategory *, bool * = 0);
+	virtual QTreeWidgetItem *updateElementsCategoryItem(QTreeWidgetItem *, ElementsCategory *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillElementsCategoryItem  (QTreeWidgetItem *, ElementsCategory *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *refreshElementsCategory(const ElementsLocation &);
+	virtual QTreeWidgetItem *refreshElement(const ElementsLocation &);
+	
+	// elements methods
+	public:
+	virtual QTreeWidgetItem *addElement(ElementDefinition *, QTreeWidgetItem * = 0, PanelOptions = AddAllChild);
+	virtual QTreeWidgetItem *itemForElement(ElementDefinition *);
+	protected:
+	virtual QTreeWidgetItem *getItemForElement(ElementDefinition *, bool * = 0);
+	virtual QTreeWidgetItem *updateElementItem(QTreeWidgetItem *, ElementDefinition *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillElementItem  (QTreeWidgetItem *, ElementDefinition *, PanelOptions = AddAllChild, bool = false);
+	
+	// generic methods
+	protected:
+	virtual QTreeWidgetItem *updateItem(QTreeWidgetItem *, PanelOptions = AddAllChild, bool = false);
+	virtual QTreeWidgetItem *fillItem  (QTreeWidgetItem *, PanelOptions = AddAllChild, bool = false);
+	
+	// slots used to receive change notifications from added objects
+	protected slots:
+	virtual void projectInformationsChanged(QETProject *);
+	virtual void diagramAdded(QETProject *, Diagram *);
+	virtual void diagramRemoved(QETProject *, Diagram *);
+	virtual void projectDiagramsOrderChanged(QETProject *, int, int);
+	virtual QList<ElementsLocation> elementIntegrated(QETProject *, const ElementsLocation &);
+	virtual void diagramTitleChanged(Diagram *, const QString &);
+	virtual void templatesCollectionChanged(TitleBlockTemplatesCollection*, const QString &);
+	virtual void diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &);
+	virtual void elementsCollectionChanged(ElementsCollection *);
+	
+	// various other methods
+	protected:
+	virtual QString defaultText(QET::ItemType);
+	virtual QIcon   defaultIcon(QET::ItemType);
+	virtual QTreeWidgetItem *makeItem(QET::ItemType, QTreeWidgetItem * = 0, const QString & = QString(), const QIcon & = QIcon());
+	virtual void deleteItem(QTreeWidgetItem *, bool = false);
+	virtual void markItemAsContainer(QTreeWidgetItem *);
+	virtual void markItemAsUnused(QTreeWidgetItem *);
+	virtual void reparent(QTreeWidgetItem *, QTreeWidgetItem *);
+	QList<QTreeWidgetItem *> childItems(QTreeWidgetItem *, QET::ItemType, bool = false) const;
+	template<typename T> void removeObsoleteItems(const QList<T> &, QTreeWidgetItem *, QET::ItemType, bool);
+	template<typename T> T valueForItem(QTreeWidgetItem *) const;
+	void unregisterItem(QTreeWidgetItem *);
+	void clearPanel();
+	
+	bool event(QEvent *);
+	
+	signals:
+	bool firstActivated();
+	void panelContentChanged();
+	
+	private slots:
+	void emitFirstActivated();
+	
+	protected:
+	ElementsCollectionCache *cache_; ///< Cache used to render elements
+	
+	private:
+	bool first_activation_; ///< boolean used to track the first time this widget is activated/shown
+	QHash<QETProject *, QTreeWidgetItem *>               projects_;     ///< Allow quick retrieval of the item representing a given project
+	QHash<Diagram *, QTreeWidgetItem *>                  diagrams_;     ///< Allow quick retrieval of the item representing a given diagram
+	QHash<TitleBlockTemplateLocation, QTreeWidgetItem *> tb_templates_; ///< Allow quick retrieval of the item representing a title block template
+	QHash<ElementsLocation, QTreeWidgetItem *>           elements_;     ///< Allow quick retrieval of the item representing an element
+};
+#endif

Added: trunk/sources/integrationmoveelementshandler.cpp
===================================================================
--- trunk/sources/integrationmoveelementshandler.cpp	                        (rev 0)
+++ trunk/sources/integrationmoveelementshandler.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,219 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "integrationmoveelementshandler.h"
+#include <QtGui>
+#include "elementscategory.h"
+#include "elementdefinition.h"
+#include "qfilenameedit.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent a utiliser pour l'affichage des dialogues lors
+	des interactions avec l'utilisateur
+*/
+IntegrationMoveElementsHandler::IntegrationMoveElementsHandler(QWidget *parent) :
+	BasicMoveElementsHandler(parent),
+	parent_widget_(parent),
+	integ_dialog_(0)
+{
+	// actions par defaut : abort
+	setActionIfItemAlreadyExists(QET::Abort);
+	setActionIfItemIsNotReadable(QET::Abort);
+	setActionIfItemIsNotWritable(QET::Abort);
+	setActionIfItemTriggersAnError(QET::Abort);
+}
+
+/**
+	Destructeur
+*/
+IntegrationMoveElementsHandler::~IntegrationMoveElementsHandler() {
+}
+
+/**
+	@param src Element source
+	@param dst Element cible / destination
+	@return l'action a effectuer si l'element cible existe deja
+*/
+QET::Action IntegrationMoveElementsHandler::elementAlreadyExists(ElementDefinition *src, ElementDefinition *dst) {
+	// premiere etape : on verifie si src et dst ne sont pas identiques
+	if (src -> equals(*dst)) {
+		// les deux elements sont identiques - il est inutile d'ecraser l'ancien
+		return(QET::Ignore);
+	}
+	
+	// les deux elements sont differents - on demande a l'utilisateur ce qu'il
+	// prefere : ecrasement ou cohabitation
+	return(askUser(src, dst));
+}
+
+/**
+	@return le nom a utiliser pour le renommage si une methode de cet objet
+	a precedemment renvoye QET::Rename.
+*/
+QString IntegrationMoveElementsHandler::nameForRenamingOperation() {
+	return(rename_);
+}
+
+/**
+	@return la date courante au format yyyyMMddhhmmss
+*/
+QString IntegrationMoveElementsHandler::dateString() const {
+	return(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
+}
+
+/**
+	@param element Une definition d'element
+	@return un nom pour dupliquer l'element passe en parametre. Ce nom est base
+	sur la date courante.
+*/
+QString IntegrationMoveElementsHandler::newNameForElement(const ElementDefinition *element) {
+	QString orig_name = element -> pathName();
+	if (orig_name.endsWith(".elmt")) orig_name.chop(5);
+	return(orig_name + "-" + dateString() + ".elmt");
+}
+
+/**
+	Demande a l'utilisateur s'il souhaite ecraser l'element deja existant,
+	renommer le nouveau ou bien annuler
+	@param src Element source
+	@param dst Element cible
+	@return la reponse de l'utilisateur
+*/
+QET::Action IntegrationMoveElementsHandler::askUser(ElementDefinition *src, ElementDefinition *dst) {
+	Q_UNUSED(src);
+	initDialog();
+	int result = integ_dialog_ -> exec();
+	if (result == QDialog::Accepted) {
+		if (use_existing_elmt_ -> isChecked()) {
+			return(QET::Ignore);
+		} else if (erase_element_ -> isChecked()) {
+			return(QET::Erase);
+		} else {
+			rename_ = newNameForElement(dst);
+			return(QET::Rename);
+		}
+	} else {
+		return(QET::Abort);
+	}
+}
+
+/**
+	Initialise le dialogue
+*/
+void IntegrationMoveElementsHandler::initDialog() {
+	if (integ_dialog_) return;
+	integ_dialog_ = new QDialog(parent_widget_);
+	integ_dialog_ -> setWindowTitle(tr("Int\351gration d'un \351l\351ment"));
+	
+	dialog_label_ = new QLabel(
+		QString(
+			tr(
+				"L'\351l\351ment a d\351j\340 \351t\351 "
+				"int\351gr\351 dans le projet. Toutefois, la version que vous "
+				"tentez de poser semble diff\351rente. Que souhaitez-vous "
+				"faire ?",
+				"dialog content - %1 is an element's path name"
+			)
+		)
+	);
+	
+	use_existing_elmt_ = new QRadioButton(
+		QString(
+			tr(
+				"Utiliser l'\351l\351ment d\351j\340 int\351gr\351",
+				"dialog content"
+			)
+		)
+	);
+	
+	integrate_new_element_ = new QRadioButton(
+		QString(
+			tr(
+				"Int\351grer l'\351l\351ment d\351pos\351",
+				"dialog content"
+			)
+		)
+	);
+	radioButtonleftMargin(integrate_new_element_);
+	
+	erase_element_ = new QRadioButton(
+		QString(
+			tr(
+				"\311craser l'\351l\351ment d\351j\340 int\351gr\351",
+				"dialog content"
+			)
+		)
+	);
+	radioButtonleftMargin(erase_element_);
+	
+	integrate_both_ = new QRadioButton(
+		QString(
+			tr(
+				"Faire cohabiter les deux \351l\351ments",
+				"dialog content"
+			)
+		)
+	);
+	
+	button_group1_ = new QButtonGroup(this);
+	button_group1_ -> addButton(use_existing_elmt_);
+	button_group1_ -> addButton(integrate_new_element_);
+	button_group2_ = new QButtonGroup(this);
+	button_group2_ -> addButton(erase_element_);
+	button_group2_ -> addButton(integrate_both_);
+	
+	integrate_new_element_ -> setChecked(true);
+	integrate_both_ -> setChecked(true);
+	
+	buttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	
+	dialog_glayout = new QGridLayout();
+	dialog_glayout -> setColumnMinimumWidth(0, 20);
+	dialog_glayout -> addWidget(erase_element_,  0, 1);
+	dialog_glayout -> addWidget(integrate_both_, 1, 1);
+	
+	dialog_vlayout_ = new QVBoxLayout(integ_dialog_);
+	dialog_vlayout_ -> addWidget(dialog_label_);
+	dialog_vlayout_ -> addWidget(use_existing_elmt_);
+	dialog_vlayout_ -> addWidget(integrate_new_element_);
+	dialog_vlayout_ -> addLayout(dialog_glayout);
+	dialog_vlayout_ -> addWidget(buttons_);
+	
+	connect(use_existing_elmt_,     SIGNAL(toggled(bool)), this,           SLOT(correctRadioButtons()));
+	connect(integrate_new_element_, SIGNAL(toggled(bool)), this,           SLOT(correctRadioButtons()));
+	connect(buttons_,               SIGNAL(accepted()),    integ_dialog_, SLOT(accept()));
+	connect(buttons_,               SIGNAL(rejected()),    integ_dialog_, SLOT(reject()));
+}
+
+/**
+	S'asure que le dialogue reste coherent
+*/
+void IntegrationMoveElementsHandler::correctRadioButtons() {
+	erase_element_  -> setEnabled(integrate_new_element_ -> isChecked());
+	integrate_both_ -> setEnabled(integrate_new_element_ -> isChecked());
+}
+
+/**
+	@param button bouton radio
+	Augmente la marge gauche d'un bouton radio
+*/
+void IntegrationMoveElementsHandler::radioButtonleftMargin(QRadioButton *button) {
+	int a, b, c, d;
+	button -> getContentsMargins(&a, &b, &c, &d);
+	button -> setContentsMargins(a + 15, b, c, d);
+}

Added: trunk/sources/integrationmoveelementshandler.h
===================================================================
--- trunk/sources/integrationmoveelementshandler.h	                        (rev 0)
+++ trunk/sources/integrationmoveelementshandler.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,72 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef INTEGRATION_MOVE_ELEMENTS_HANDLER_H
+#define INTEGRATION_MOVE_ELEMENTS_HANDLER_H
+#include "basicmoveelementshandler.h"
+#include <QtGui>
+/**
+	This class implements the MoveElementsHandler Strategy class.
+	It acts like a BasiMoveElementsHandler configured to answer QET::Abort to any question.
+	Please note this class was designed with the context of integrating an element definition into a project in mind.
+	For this purpose, the elementAlreadyExists method was redefined to ask users whether they wish to:
+	  * erase a different, already-existing element,
+	  * keep the already-existing element by renaming the new one,
+	  * or cancel the integration.
+*/
+class IntegrationMoveElementsHandler : public BasicMoveElementsHandler {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	IntegrationMoveElementsHandler(QWidget * = 0);
+	virtual ~IntegrationMoveElementsHandler();
+	private:
+	IntegrationMoveElementsHandler(const IntegrationMoveElementsHandler &);
+	
+	// methods
+	public:
+	virtual QET::Action elementAlreadyExists(ElementDefinition *, ElementDefinition *);
+	virtual QString nameForRenamingOperation();
+	
+	private:
+	QString dateString() const;
+	QString newNameForElement(const ElementDefinition *);
+	QET::Action askUser(ElementDefinition *, ElementDefinition *);
+	void initDialog();
+	void radioButtonleftMargin(QRadioButton *);
+	
+	private slots:
+	void correctRadioButtons();
+	
+	// attributes
+	private:
+	QWidget *parent_widget_;              ///< Widget to be used as parent when displaying dialogs
+	QString rename_;                      ///< Name to be used when renaming the integrated element
+	QDialog *integ_dialog_;               ///< Dialog in case of conflict when integration an element
+	QLabel *dialog_label_;
+	QVBoxLayout *dialog_vlayout_;
+	QGridLayout *dialog_glayout;
+	QDialogButtonBox *buttons_;
+	QRadioButton *use_existing_elmt_;
+	QRadioButton *integrate_new_element_;
+	QRadioButton *erase_element_;
+	QRadioButton *integrate_both_;
+	QButtonGroup *button_group1_;
+	QButtonGroup *button_group2_;
+};
+#endif

Added: trunk/sources/interactivemoveelementshandler.cpp
===================================================================
--- trunk/sources/interactivemoveelementshandler.cpp	                        (rev 0)
+++ trunk/sources/interactivemoveelementshandler.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,389 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "interactivemoveelementshandler.h"
+#include <QtGui>
+#include "elementscategory.h"
+#include "elementdefinition.h"
+#include "qfilenameedit.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent a utiliser pour l'affichage des dialogues lors
+	des interactions avec l'utilisateur
+*/
+InteractiveMoveElementsHandler::InteractiveMoveElementsHandler(QWidget *parent) :
+	BasicMoveElementsHandler(parent),
+	parent_widget_(parent),
+	rename_(""),
+	always_erase_(false),
+	always_skip_(false),
+	aborted_(false),
+	conflict_dialog_(0)
+{
+}
+
+/**
+	Destructeur
+*/
+InteractiveMoveElementsHandler::~InteractiveMoveElementsHandler() {
+}
+
+/**
+	@param src Categorie source
+	@param dst Categorie cible / destination
+	@return l'action a effectuer si la categorie cible existe deja
+*/
+QET::Action InteractiveMoveElementsHandler::categoryAlreadyExists(ElementsCategory *src, ElementsCategory  *dst) {
+	// verifie si la reponse n'est pas systematique
+	if (aborted_)      return(QET::Abort);
+	if (always_erase_) return(QET::Erase);
+	if (always_skip_)  return(QET::Ignore);
+	
+	// a ce stade, l'action a effectuer pour gerer le conflit doit etre
+	// demandee a l'utilisateur via un dialogue
+	initConflictDialog();
+	
+	QString src_location(src -> location().toString());
+	QString dst_location(dst -> location().toString());
+	
+	// prepare le dialogue
+	QString dialog_title(QString(tr("Copie de %1 vers %2", "dialog title")).arg(src_location).arg(dst_location));
+	
+	QLabel *question_label = new QLabel(
+		QString(
+			tr(
+				"La cat\351gorie \253\240%1\240\273 (%2) existe d\351j\340. "
+				"Que souhaitez-vous faire ?",
+				"dialog content"
+			)
+		)
+		.arg(dst -> name())
+		.arg(dst_location)
+	);
+	question_label -> setWordWrap(true);
+	
+	setConflictDialogTitle(dialog_title);
+	setConflictDialogMainWidget(question_label);
+	
+	// execute le dialogue
+	conflict_dialog_ -> exec();
+	
+	// enleve et detruit le widget principal
+	setConflictDialogMainWidget(0);
+	delete question_label;
+	
+	// renvoie la reponse obtenue via le dialogue
+	return(conflict_result_);
+}
+
+/**
+	@param src Element source
+	@param dst Element cible / destination
+	@return l'action a effectuer si l'element cible existe deja
+*/
+QET::Action InteractiveMoveElementsHandler::elementAlreadyExists(ElementDefinition *src, ElementDefinition *dst) {
+	// verifie si la reponse n'est pas systematique
+	if (aborted_)      return(QET::Abort);
+	if (always_erase_) return(QET::Erase);
+	if (always_skip_)  return(QET::Ignore);
+	
+	// a ce stade, l'action a effectuer pour gerer le conflit doit etre
+	// demandee a l'utilisateur via un dialogue
+	initConflictDialog();
+	
+	QString src_location(src -> location().toString());
+	QString dst_location(dst -> location().toString());
+	
+	// prepare le dialogue
+	QString dialog_title(QString(tr("Copie de %1 vers %2", "dialog title")).arg(src_location).arg(dst_location));
+	
+	QLabel *question_label = new QLabel(
+		QString(
+			tr(
+				"L'\351l\351ment \253\240%1\240\273 existe d\351j\340. "
+				"Que souhaitez-vous faire ?",
+				"dialog content"
+			)
+		)
+		.arg(dst_location)
+	);
+	question_label -> setWordWrap(true);
+	
+	setConflictDialogTitle(dialog_title);
+	setConflictDialogMainWidget(question_label);
+	
+	// execute le dialogue
+	conflict_dialog_ -> exec();
+	
+	// enleve et detruit le widget principal
+	setConflictDialogMainWidget(0);
+	delete question_label;
+	
+	if (conflict_result_ == QET::Rename) {
+		if (!rename_.endsWith(".elmt")) rename_ += ".elmt";
+	}
+	
+	// renvoie la reponse obtenue via le dialogue
+	return(conflict_result_);
+}
+
+/**
+	Cette methode permet de savoir comment agir lorsqu'une categorie n'est pas lisible
+	@param category La categorie qui n'est pas lisible
+	@return QET::Retry, QET::Ignore ou QET::Abort
+*/
+QET::Action InteractiveMoveElementsHandler::categoryIsNotReadable(ElementsCategory *category) {
+	QString message = QString(tr("La cat\351gorie %1 n'est pas accessible en lecture.", "message box content")).arg(category -> location().toString());
+	return(retryErrorMessage(message));
+}
+
+/**
+	Cette methode permet de savoir comment agir lorsqu'un element n'est pas lisible
+	@param element L'element qui n'est pas lisible
+	@return QET::Retry, QET::Ignore ou QET::Abort
+*/
+QET::Action InteractiveMoveElementsHandler::elementIsNotReadable(ElementDefinition *element) {
+	QString message = QString(tr("L'\351l\351ment %1 n'est pas accessible en lecture.", "message box content")).arg(element -> location().toString());
+	return(retryErrorMessage(message));
+}
+
+/**
+	Cette methode permet de savoir comment agir lorsqu'une categorie n'est pas accessible en ecriture
+	@param category La categorie qui n'est pas lisible
+	@return QET::Retry, QET::Ignore ou QET::Abort
+*/
+QET::Action InteractiveMoveElementsHandler::categoryIsNotWritable(ElementsCategory *category) {
+	QString message = QString(tr("La cat\351gorie %1 n'est pas accessible en \351criture.", "message box content")).arg(category -> location().toString());
+	return(retryErrorMessage(message));
+}
+
+/**
+	Cette methode permet de savoir comment agir lorsqu'un element n'est pas accessible en ecriture
+	@param element L'element qui n'est pas lisible
+	@return QET::Retry, QET::Ignore ou QET::Abort
+*/
+QET::Action InteractiveMoveElementsHandler::elementIsNotWritable(ElementDefinition *element) {
+	QString message = QString(tr("L'\351l\351ment %1 n'est pas accessible en \351criture.", "message box content")).arg(element -> location().toString());
+	return(retryErrorMessage(message));
+}
+
+/**
+	Affiche un message d'erreur relatif a une categorie
+	@param category La categorie concernee par l'erreur
+	@param message Le message d'erreur a afficher
+	@return toujours QET::Ignore
+*/
+QET::Action InteractiveMoveElementsHandler::errorWithACategory(ElementsCategory *category, const QString &message) {
+	QString category_location = category -> location().toString();
+	QString error_message = QString("Une erreur s'est produite avec la cat\351gorie %1\240: %2").arg(category_location).arg(message);
+	simpleErrorMessage(error_message);
+	return(QET::Ignore);
+}
+
+/**
+	Affiche un message d'erreur relatif a un element
+	@param element L'element concerne par l'erreur
+	@param message Le message d'erreur a afficher
+	@return toujours QET::Ignore
+*/
+QET::Action InteractiveMoveElementsHandler::errorWithAnElement(ElementDefinition *element, const QString &message) {
+	QString element_location = element -> location().toString();
+	QString error_message = QString("Une erreur s'est produite avec l'\351l\351ment %1\240: %2").arg(element_location).arg(message);
+	simpleErrorMessage(error_message);
+	return(QET::Ignore);
+}
+
+/**
+	@return le nom a utiliser pour le renommage si une methode de cet objet
+	a precedemment renvoye QET::Rename.
+*/
+QString InteractiveMoveElementsHandler::nameForRenamingOperation() {
+	return(rename_);
+}
+
+/**
+	Initialise le dialogue qui sera utilise pour les conflits
+	elements / categories.
+*/
+void InteractiveMoveElementsHandler::initConflictDialog() {
+	// n'agit qu'une seule fois
+	if (conflict_dialog_) return;
+	
+	conflict_dialog_ = new QDialog(parent_widget_);
+	conflict_dialog_ -> setMaximumSize(600, 200);
+	
+	// initialisation du champ de texte
+	rename_label_ = new QLabel("Nouveau nom :");
+	rename_textfield_ = new QFileNameEdit();
+	connect(
+		rename_textfield_,
+		SIGNAL(textEdited(const QString &)),
+		this,
+		SLOT(conflictDialogFileNameFieldChanged())
+	);
+	
+	// initialisation des boutons
+	rename_button_     = new QPushButton(tr("Renommer"));
+	erase_button_      = new QPushButton(tr("\311craser"));
+	erase_all_button_  = new QPushButton(tr("\311craser tout"));
+	ignore_button_     = new QPushButton(tr("Ignorer"));
+	ignore_all_button_ = new QPushButton(tr("Ignorer tout"));
+	abort_button_      = new QPushButton(tr("Annuler"));
+	
+	conflict_buttons_ = new QDialogButtonBox();
+	conflict_buttons_ -> addButton(rename_button_,       QDialogButtonBox::ActionRole);
+	conflict_buttons_ -> addButton(erase_button_,        QDialogButtonBox::AcceptRole);
+	conflict_buttons_ -> addButton(erase_all_button_,    QDialogButtonBox::AcceptRole);
+	conflict_buttons_ -> addButton(ignore_button_,       QDialogButtonBox::AcceptRole);
+	conflict_buttons_ -> addButton(ignore_all_button_,   QDialogButtonBox::AcceptRole);
+	conflict_buttons_ -> addButton(abort_button_,        QDialogButtonBox::AcceptRole);
+	
+	rename_button_ -> setEnabled(false);
+	connect(
+		conflict_buttons_,
+		SIGNAL(clicked(QAbstractButton *)),
+		this,
+		SLOT(conflictDialogButtonClicked(QAbstractButton *))
+	);
+	
+	// layout
+	conflict_layout1_ = new QHBoxLayout();
+	conflict_layout1_ -> addWidget(rename_label_);
+	conflict_layout1_ -> addWidget(rename_textfield_);
+	
+	conflict_layout0_ = new QVBoxLayout(conflict_dialog_);
+	conflict_layout0_ -> insertLayout(1, conflict_layout1_);
+	conflict_layout0_ -> insertWidget(2, conflict_buttons_);
+}
+
+/**
+	Slot appele lorsque l'utilisateur modifie le contenu du champ 
+*/
+void InteractiveMoveElementsHandler::conflictDialogFileNameFieldChanged() {
+	if (rename_textfield_ -> isValid()) {
+		/// @todo verifier que le nom n'est pas deja pris
+		rename_button_ -> setEnabled(true);
+	} else {
+		rename_button_ -> setEnabled(false);
+	}
+}
+
+/**
+	Slot appele lorsque l'utilisateur presse un des boutons du dialogue de
+	conflit.
+	@param button Bouton presse par l'utilisateur
+*/
+void InteractiveMoveElementsHandler::conflictDialogButtonClicked(QAbstractButton *button) {
+	conflict_dialog_ -> accept();
+	// change la valeur de l'attribut 
+	if (button == rename_button_) {
+		rename_ = rename_textfield_ -> text();
+		conflict_result_= QET::Rename;
+	} else if (button == erase_button_) {
+		conflict_result_= QET::Erase;
+	} else if (button == erase_all_button_) {
+		always_erase_ = true;
+		conflict_result_= QET::Erase;
+	} else if (button == ignore_button_) {
+		conflict_result_= QET::Ignore;
+	} else if (button == ignore_all_button_) {
+		always_skip_ = true;
+		conflict_result_= QET::Ignore;
+	} else if (button == abort_button_) {
+		aborted_ = true;
+		conflict_result_= QET::Abort;
+	}
+}
+
+/**
+	Change le titre du dialogue de conflit
+	@param new_title Nouveau titre pour le dialogue de conflit
+*/
+void InteractiveMoveElementsHandler::setConflictDialogTitle(const QString &new_title) {
+	conflict_dialog_ -> setWindowTitle(new_title);
+}
+
+/**
+	@return le titre du dialogue de conflit
+*/
+QString InteractiveMoveElementsHandler::conflictDialogTitle() const {
+	return(conflict_dialog_ -> windowTitle());
+}
+
+/**
+	Change le widget affiche au centre du dialogue de conflit
+	@param widget Widget a inserer dans le dialogue de conflit
+	Si widget vaut 0, le widget central est retire.
+*/
+void InteractiveMoveElementsHandler::setConflictDialogMainWidget(QWidget *widget) {
+	// gere l'enlevement du widget principal
+	if (!widget) {
+		if (conflict_layout0_ -> count() != 3) return;
+		conflict_layout0_ -> removeItem(conflict_layout0_ -> itemAt(0));
+	} else {
+		conflict_layout0_ -> insertWidget(0, widget);
+	}
+}
+
+/**
+	@return le widget insere dans le dialogue de conflit, ou 0 s'il n'y en a
+	aucun.
+*/
+QWidget *InteractiveMoveElementsHandler::conflictDialogMainWidget() const {
+	if (conflict_layout0_ -> count() != 3) return(0);
+	return(conflict_layout0_ -> itemAt(0) -> widget());
+}
+
+/**
+	Affiche un message d'erreur en donnant la possibilite d'ignorer l'item en cours,
+	d'annuler tout le mouvement ou de le reessayer.
+	@param message Message d'erreur a afficher
+	@return L'action choisie par l'utilisateur
+*/
+QET::Action InteractiveMoveElementsHandler::retryErrorMessage(const QString &message) const {
+	int todo = QET::MessageBox::critical(
+		parent_widget_,
+		tr("Erreur", "message box title"),
+		message,
+		QMessageBox::Abort | QMessageBox::Retry | QMessageBox::Ignore,
+		QMessageBox::Ignore
+	);
+	
+	if (todo == QMessageBox::Abort) {
+		return(QET::Abort);
+	} else if (todo == QMessageBox::Retry) {
+		return(QET::Retry);
+	} else {
+		return(QET::Ignore);
+	}
+}
+
+/**
+	Affiche un simple message d'erreur
+	@param message Message d'erreur a afficher
+*/
+void InteractiveMoveElementsHandler::simpleErrorMessage(const QString &message) const {
+	QET::MessageBox::critical(
+		parent_widget_,
+		tr("Erreur", "message box title"),
+		message,
+		QMessageBox::Ok,
+		QMessageBox::Ok
+	);
+}

Added: trunk/sources/interactivemoveelementshandler.h
===================================================================
--- trunk/sources/interactivemoveelementshandler.h	                        (rev 0)
+++ trunk/sources/interactivemoveelementshandler.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,96 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef INTERACTIVE_MOVE_ELEMENTS_HANDLER_H
+#define INTERACTIVE_MOVE_ELEMENTS_HANDLER_H
+#include "basicmoveelementshandler.h"
+class QDialog;
+class QDialogButtonBox;
+class QAbstractButton;
+class QPushButton;
+class QFileNameEdit;
+class QHBoxLayout;
+class QVBoxLayout;
+class QLabel;
+/**
+	This class implements the MoveElementsHandler Strategy class by asking
+	users how to handle the various expected problems through interactive
+	dialogs.
+*/
+class InteractiveMoveElementsHandler : public BasicMoveElementsHandler {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	InteractiveMoveElementsHandler(QWidget * = 0);
+	virtual ~InteractiveMoveElementsHandler();
+	private:
+	InteractiveMoveElementsHandler(const InteractiveMoveElementsHandler &);
+	
+	// methods
+	public:
+	virtual QET::Action categoryAlreadyExists(ElementsCategory *, ElementsCategory  *);
+	virtual QET::Action elementAlreadyExists(ElementDefinition *, ElementDefinition *);
+	virtual QET::Action categoryIsNotReadable(ElementsCategory *);
+	virtual QET::Action elementIsNotReadable(ElementDefinition *);
+	virtual QET::Action categoryIsNotWritable(ElementsCategory *);
+	virtual QET::Action elementIsNotWritable(ElementDefinition *);
+	virtual QET::Action errorWithACategory(ElementsCategory *, const QString &);
+	virtual QET::Action errorWithAnElement(ElementDefinition *, const QString &);
+	virtual QString nameForRenamingOperation();
+	
+	private slots:
+	void conflictDialogFileNameFieldChanged();
+	void conflictDialogButtonClicked(QAbstractButton *);
+	
+	private:
+	void initConflictDialog();
+	void setConflictDialogTitle(const QString &);
+	QString conflictDialogTitle() const;
+	void setConflictDialogMainWidget(QWidget *);
+	QWidget *conflictDialogMainWidget() const;
+	QET::Action retryErrorMessage(const QString &) const;
+	void simpleErrorMessage(const QString &) const;
+	
+	
+	// attributes
+	private:
+	QWidget *parent_widget_;      ///< Widget to be used as parent when displaying dialogs
+	QString rename_;              ///< Name to be used when renaming an item
+	bool always_erase_;           ///< Whether to systematically erase conflicting targets without bothering users
+	bool always_skip_;            ///< Whether to systematically ignore conflicting targets without bothering users
+	bool aborted_;                ///< Whether the movement has been cancelled
+	
+	// attributes related to the dialog displayed for already existing elements and
+	// categories (= i.e. conflict dialog)
+	QET::Action conflict_result_;
+	QDialog *conflict_dialog_;
+	QVBoxLayout *conflict_layout0_;
+	QHBoxLayout *conflict_layout1_;
+	QLabel *rename_label_;
+	QFileNameEdit *rename_textfield_;
+	
+	/// Buttons for the conflict dialog
+	QDialogButtonBox *conflict_buttons_;
+	QPushButton *rename_button_;
+	QPushButton *erase_button_;
+	QPushButton *erase_all_button_;
+	QPushButton *ignore_button_;
+	QPushButton *ignore_all_button_;
+	QPushButton *abort_button_;
+};
+#endif

Added: trunk/sources/main.cpp
===================================================================
--- trunk/sources/main.cpp	                        (rev 0)
+++ trunk/sources/main.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,27 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetapp.h"
+/**
+	Fonction principale du programme QElectroTech
+	@param argc nombre de parametres
+	@param argv parametres
+*/
+int main(int argc, char **argv) {
+	// Creation et execution de l'application
+	return(QETApp(argc, argv).exec());
+}

Added: trunk/sources/moveelementsdescription.cpp
===================================================================
--- trunk/sources/moveelementsdescription.cpp	                        (rev 0)
+++ trunk/sources/moveelementsdescription.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,180 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "moveelementsdescription.h"
+
+/**
+	Constructeur - construit une description ne comprenant aucun nom internem,
+	aucune destination, aucun handler. Par defaut, la recursivite est activee.
+	@param parent QObject parent
+*/
+MoveElementsDescription::MoveElementsDescription(QObject *parent) :
+	QObject(parent),
+	recursive_(true),
+	handler_(0),
+	destination_(0),
+	dest_internal_name_orig_(""),
+	dest_internal_name_final_(""),
+	created_item_(0),
+	src_deleted_(false),
+	abort_(false)
+{
+}
+
+/**
+	Destructeur
+*/
+MoveElementsDescription::~MoveElementsDescription() {
+}
+
+/**
+	@return true si le mouvement decrit est recursif (c'est-a-dire que la copie
+	d'une categorie entrainera la copie de ses sous-categories et de ses
+	elements)
+*/
+bool MoveElementsDescription::isRecursive() const {
+	return(recursive_);
+}
+
+/**
+	@param r true pour activer la recursivite, false sinon
+	@see isRecursive()
+*/
+void MoveElementsDescription::setRecursive(bool r) {
+	recursive_ = r;
+}
+
+/**
+	@return le MoveElementsHandler utilise pour gerer les erreurs lors de la
+	realisation de ce mouvement. Si aucun handler n'est specifie, cette methode
+	retourne 0.
+	@see MoveElementsHandler
+*/
+MoveElementsHandler *MoveElementsDescription::handler() const {
+	return(handler_);
+}
+
+/**
+	@param handler Le MoveElementHandler a utiliser pour gerer les erreurs lors
+	de la realisation de ce mouvement. Indiquer 0 pour enlever le
+	MoveElementsHandler.
+*/
+void MoveElementsDescription::setHandler(MoveElementsHandler *handler) {
+	handler_ = handler;
+}
+
+/**
+	@return la categorie de destination qui accueillera l'element cree par le
+	mouvement.
+*/
+ElementsCategory *MoveElementsDescription::destinationParentCategory() const {
+	return(destination_);
+}
+
+/**
+	@param destination la categorie de destination qui accueillera l'element
+	cree par le mouvement
+*/
+void MoveElementsDescription::setDestinationParentCategory(ElementsCategory *destination) {
+	destination_ = destination;
+}
+
+/**
+	@return Le nom interne souhaite pour l'item a creer.
+	Typiquement, il s'agit du meme nom que l'item d'origine. Il faut toutefois
+	le specifier explicitement.
+*/
+QString MoveElementsDescription::originalDestinationInternalName() const {
+	return(dest_internal_name_orig_);
+}
+
+/**
+	@param name Le nom interne souhaite pour l'item a creer.
+	Typiquement, il s'agit du meme nom que l'item d'origine. Il faut toutefois
+	le specifier explicitement.
+*/
+void MoveElementsDescription::setOriginalDestinationInternalName(const QString &name) {
+	dest_internal_name_orig_ = name;
+}
+
+/**
+	@return Le nom interne finalement retenu pour creer l'item.
+	Si le nom interne est deja pris dans la categorie de destination, il est
+	courant de changer le nom interne de destination (cette decision revient
+	typiquement au MoveElementsHandler).
+*/
+QString MoveElementsDescription::finalDestinationInternalName() const {
+	return(dest_internal_name_final_);
+}
+
+/**
+	@param name Le nom interne finalement retenu pour creer l'item.
+	Si le nom interne est deja pris dans la categorie de destination, il est
+	courant de changer le nom interne de destination (cette decision revient
+	typiquement au MoveElementsHandler).
+*/
+void MoveElementsDescription::setFinalDestinationInternalName(const QString &name) {
+	dest_internal_name_final_ = name;
+}
+
+/**
+	@return l'item cree par le mouvement, ou 0 si celui-ci n'as pas encore ete
+	cree ou ne sera pas cree.
+*/
+ElementsCollectionItem *MoveElementsDescription::createdItem() const {
+	return(created_item_);
+}
+
+/**
+	@param item l'item cree par le mouvement. Indiquer 0 si celui-ci n'as pas
+	encore ete cree ou ne sera pas cree.
+*/
+void MoveElementsDescription::setCreatedItem(ElementsCollectionItem *item) {
+	created_item_ = item;
+}
+
+/**
+	@return true si, dans le cadre normal du mouvement, l'item source a ete
+	supprime (exemple : deplacement) avec succes.
+*/
+bool MoveElementsDescription::sourceItemWasDeleted() const {
+	return(src_deleted_);
+}
+
+/**
+	@param deleted Definit si oui ou non l'item source a ete supprime avec
+	succes, et ce dans le cadre normal du mouvement (exemple : deplacement).
+*/
+void MoveElementsDescription::setSourceItemDeleted(bool deleted) {
+	src_deleted_ = deleted;
+}
+
+/**
+	@return true si le mouvement, ainsi que les mouvements qui suivent, doivent
+	etre annules.
+*/
+bool MoveElementsDescription::mustAbort() const {
+	return(abort_);
+}
+
+/**
+	Definit ce mouvement ainsi que les mouvements qui suivent comme etant a
+	annuler.
+*/
+void MoveElementsDescription::abort() {
+	abort_ = true;
+}

Added: trunk/sources/moveelementsdescription.h
===================================================================
--- trunk/sources/moveelementsdescription.h	                        (rev 0)
+++ trunk/sources/moveelementsdescription.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,76 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef MOVE_ELEMENTS_DESCRIPTION_H
+#define MOVE_ELEMENTS_DESCRIPTION_H
+#include <QObject>
+class ElementsCollectionItem;
+class ElementsCategory;
+class MoveElementsHandler;
+/**
+	This class describes the evolution of an elements item move (actually
+	either move or copy), i.e. whether the source was deleted, whether the
+	target changed and to what path, etc.
+*/
+class MoveElementsDescription : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	MoveElementsDescription(QObject * = 0);
+	virtual ~MoveElementsDescription();
+	private:
+	MoveElementsDescription(const MoveElementsDescription &);
+	
+	// methods
+	public:
+	bool isRecursive() const;
+	void setRecursive(bool);
+	
+	MoveElementsHandler *handler() const;
+	void setHandler(MoveElementsHandler *);
+	
+	ElementsCategory *destinationParentCategory() const;
+	void setDestinationParentCategory(ElementsCategory *);
+	
+	QString originalDestinationInternalName() const;
+	void setOriginalDestinationInternalName(const QString &);
+	
+	QString finalDestinationInternalName() const;
+	void setFinalDestinationInternalName(const QString &);
+	
+	ElementsCollectionItem *createdItem() const;
+	void setCreatedItem(ElementsCollectionItem *);
+	
+	bool sourceItemWasDeleted() const;
+	void setSourceItemDeleted(bool);
+	
+	bool mustAbort() const;
+	void abort();
+	
+	// attributes
+	private:
+	bool recursive_;
+	MoveElementsHandler *handler_;
+	ElementsCategory *destination_;
+	QString dest_internal_name_orig_;
+	QString dest_internal_name_final_;
+	ElementsCollectionItem *created_item_;
+	bool src_deleted_;
+	bool abort_;
+};
+#endif

Added: trunk/sources/moveelementshandler.h
===================================================================
--- trunk/sources/moveelementshandler.h	                        (rev 0)
+++ trunk/sources/moveelementshandler.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,97 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef MOVE_ELEMENTS_HANDLER_H
+#define MOVE_ELEMENTS_HANDLER_H
+#include <QObject>
+#include "qet.h"
+class ElementDefinition;
+class ElementsCategory;
+/**
+	This class defines the minimum interface required to implement an object able
+	to handle a move/copy operation among elements collections. This kind of
+	objects is typically useful in the move() and copy() method of the
+	ElementDefinition and ElementsCategory classes. These methods simply rely on
+	answers provided by an instance of this class as soon as they encounter
+	typical move/copy problems, such as conflicts or permission issues.
+	
+	For instance, when copying a particular element to a given category, that
+	element may already exist in the target category. It is then possible either
+	to erase the target element, change the target name or cancel the operation.
+	There is no systematic or calculable answer to this kind of questions, hence
+	the rational to delegate the decision process to a dedicated "Strategy" object.
+	
+	All we know from this object is the fact it implements the interface defined
+	below. It is then free to create complicated dialogs to ask a user what to do
+	or even to pick a random decision.
+	
+	@see ElementsCategory
+	@see ElementDefinition
+*/
+class MoveElementsHandler : public QObject {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	MoveElementsHandler(QObject * = 0) {};
+	virtual ~MoveElementsHandler() {};
+	private:
+	MoveElementsHandler(const MoveElementsHandler &);
+	
+	// methods
+	public:
+	/**
+		@return what to do if the target category already exists
+	*/
+	virtual QET::Action categoryAlreadyExists(ElementsCategory *src, ElementsCategory  *dst) = 0;
+	/**
+		@return what to do if the target element already exists
+	*/
+	virtual QET::Action elementAlreadyExists(ElementDefinition *src, ElementDefinition *dst) = 0;
+	
+	/**
+		@return what to do if a category is not readable
+	*/
+	virtual QET::Action categoryIsNotReadable(ElementsCategory *) = 0;
+	/**
+		@return what to do if an element is not readable
+	*/
+	virtual QET::Action elementIsNotReadable(ElementDefinition *) = 0;
+	
+	/**
+		@return what to do if the target category is not writable
+	*/
+	virtual QET::Action categoryIsNotWritable(ElementsCategory *) = 0;
+	/**
+		@return what to do if the target element is not writable
+	*/
+	virtual QET::Action elementIsNotWritable(ElementDefinition *) = 0;
+	
+	/**
+		@return what to do when an error, described by the provided QString, occurs in relation with the given elements category.
+	*/
+	virtual QET::Action errorWithACategory(ElementsCategory *, const QString &) = 0;
+	/**
+		@return what to do when an error, described by the provided QString, occurs in relation with the given element.
+	*/
+	virtual QET::Action errorWithAnElement(ElementDefinition *, const QString &) = 0;
+	
+	/**
+		@return the name to be used along with the latest QET::Rename answer
+	*/
+	virtual QString nameForRenamingOperation() = 0;
+};
+#endif

Added: trunk/sources/nameslist.cpp
===================================================================
--- trunk/sources/nameslist.cpp	                        (rev 0)
+++ trunk/sources/nameslist.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,217 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "nameslist.h"
+// make this class usable with QVariant
+int NamesList::MetaTypeId = qRegisterMetaType<NamesList>("NamesList");
+
+/**
+	Constructeur
+*/
+NamesList::NamesList() {
+}
+
+/**
+	Constructeur de copie
+	@param other La NamesList a copier
+*/
+NamesList::NamesList(const NamesList &other) : hash_names(other.hash_names) {	
+}
+
+/**
+	Destructeur
+*/
+NamesList::~NamesList() {
+}
+
+/**
+	Ajoute un nom a la liste
+	@param lang Sigle de deux lettres representant une langue. Si cela n'est
+	pas respecte, l'insertion n'est pas effectuee.
+	@param name Nom lui-meme. Ce ne doit pas etre une chaine de caractere vide.
+	Si cela n'est pas respecte, l'insertion n'est pas effectuee.
+*/
+void NamesList::addName(const QString &lang, const QString &name) {
+	if (lang.length() != 2) return;
+	hash_names.insert(lang, name);
+}
+
+/**
+	Enleve le nom dont une langue donnee
+	@param lang la langue pour laquelle il faut supprimer le nom
+*/
+void NamesList::removeName(const QString &lang) {
+	hash_names.remove(lang);
+}
+
+/**
+	Supprime tous les noms
+*/
+void NamesList::clearNames() {
+	hash_names.clear();
+}
+
+/**
+	@return La liste de toutes les langues disponibles
+*/
+QList<QString> NamesList::langs() const {
+	return(hash_names.keys());
+}
+
+/**
+	@return true si la liste de noms est vide, false sinon
+*/
+bool NamesList::isEmpty() const {
+	return(hash_names.isEmpty());
+}
+
+/**
+	@return Le nombre de noms dans la liste
+*/
+int NamesList::count() const {
+	return(hash_names.count());
+}
+
+/**
+	@param lang une langue
+	@return Le nom dans la langue donnee ou QString() si ce nom n'est pas
+	defini
+*/
+QString &NamesList::operator[](const QString &lang) {
+	return(hash_names[lang]);
+}
+
+/**
+	@param lang une langue
+	@return Le nom dans la langue donnee ou QString() si ce nom n'est pas
+	defini
+*/
+const QString NamesList::operator[](const QString &lang) const {
+	return(hash_names.value(lang));
+}
+
+
+
+/**
+	Charge la liste de noms depuis un element XML. Cet element est sense etre
+	le parent d'un element "names", qui contient lui meme les "name".
+	Les noms precedemment contenus dans la liste ne sont pas effaces mais
+	peuvent etre ecrases.
+	@param xml_element L'element XML a analyser
+	@param xml_options A set of options related to XML parsing.
+	@see getXmlOptions()
+*/
+void NamesList::fromXml(const QDomElement &xml_element, const QHash<QString, QString> &xml_options) {
+	QHash<QString, QString> xml_opt = getXmlOptions(xml_options);
+	// parcourt les enfants "names" de l'element XML
+	for (QDomNode node = xml_element.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		QDomElement names = node.toElement();
+		if (names.isNull() || names.tagName() != xml_opt["ParentTagName"]) continue;
+		// parcourt les petits-enfants "name"
+		for (QDomNode n = names.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
+			QDomElement name = n.toElement();
+			if (name.isNull() || name.tagName() != xml_opt["TagName"]) continue;
+			addName(name.attribute(xml_opt["LanguageAttribute"]), name.text());
+		}
+	}
+}
+
+/**
+	Exporte la liste des noms vers un element XML. Veillez a verifier que la
+	liste de noms n'est pas vide avant de l'exporter.
+	@param xml_document Le document XML dans lequel l'element XML sera insere
+	@param xml_options A set of options related to XML parsing.
+	@return L'element XML correspondant a la section "names"
+	@see count()
+*/
+QDomElement NamesList::toXml(QDomDocument &xml_document, const QHash<QString, QString> &xml_options) const {
+	QHash<QString, QString> xml_opt = getXmlOptions(xml_options);
+	QDomElement names_elmt = xml_document.createElement(xml_opt["ParentTagName"]);
+	QHashIterator<QString, QString> names_iterator(hash_names);
+	while (names_iterator.hasNext()) {
+		names_iterator.next();
+		QDomElement name_elmt = xml_document.createElement(xml_opt["TagName"]);
+		name_elmt.setAttribute(xml_opt["LanguageAttribute"], names_iterator.key());
+		name_elmt.appendChild(xml_document.createTextNode(names_iterator.value()));
+		names_elmt.appendChild(name_elmt);
+	}
+	return(names_elmt);
+}
+
+/**
+	@param xml_options A set of options related to XML parsing. Available keys:
+		* ParentTagName (falls back to "names")
+		* TagName (falls back to "name")
+		* LanguageAttribute (falls back to "lang")
+	@return the same set, with at least all the known options
+*/
+QHash<QString, QString> NamesList::getXmlOptions(const QHash<QString, QString> &xml_options) const {
+	QHash<QString, QString> new_xml_options = xml_options;
+	if (!xml_options.contains("ParentTagName")) {
+		new_xml_options.insert("ParentTagName", "names");
+	}
+	if (!xml_options.contains("TagName")) {
+		new_xml_options.insert("TagName", "name");
+	}
+	if (!xml_options.contains("LanguageAttribute")) {
+		new_xml_options.insert("LanguageAttribute", "lang");
+	}
+	return new_xml_options;
+}
+
+/**
+	@param nl une autre liste de noms
+	@return true si les listes de noms sont differentes, false sinon
+*/
+bool NamesList::operator!=(const NamesList &nl) const {
+	return(hash_names != nl.hash_names);
+}
+
+/**
+	@param nl une autre liste de noms
+	@return true si les listes de noms sont identiques, false sinon
+*/
+bool NamesList::operator==(const NamesList &nl) const {
+	return(hash_names == nl.hash_names);
+}
+
+/**
+	Return the adequate name regarding the current system locale.
+	By order of preference, this function chooses:
+		- the name in the system language
+		- the English name
+		- the provided fallback name if non-empty
+		- the first language encountered in the list
+		- an empty string
+	@param fallback_name name to be returned when no adequate name has been found
+	@return The adequate name regarding the current system locale.
+*/
+QString NamesList::name(const QString &fallback_name) const {
+	// recupere les deux premiers caracteres de la locale en cours du systeme
+	QString system_language = QLocale::system().name().left(2);
+	QString returned_name;
+	if (!hash_names[system_language].isEmpty()) {
+		returned_name = hash_names[system_language];
+	} else if (!hash_names["en"].isEmpty()) {
+		returned_name = hash_names["en"];
+	} else if (!fallback_name.isEmpty()) {
+		returned_name = fallback_name;
+	} else if (hash_names.count()) {
+		returned_name = hash_names.value(hash_names.keys().first());
+	}
+	return(returned_name);
+}

Added: trunk/sources/nameslist.h
===================================================================
--- trunk/sources/nameslist.h	                        (rev 0)
+++ trunk/sources/nameslist.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,66 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef NAMES_LIST_H
+#define NAMES_LIST_H
+#include <QtXml>
+/**
+	Cette classe represente une liste de noms, utilisee
+	par les elements et categories pour embarquer un meme nom en plusieurs
+	langues.
+	Les langues sont representees par deux lettres (typiquement : les deux
+	premieres de la locale du systeme) ; exemples : en pour l'anglais, fr
+	pour le francais.
+*/
+class NamesList {
+	// constructors, destructor
+	public:
+	NamesList();
+	NamesList(const NamesList &);
+	virtual ~NamesList();
+	
+	// attributes
+	private:
+	QHash<QString, QString> hash_names;
+	
+	public:
+	static int MetaTypeId;
+	
+	// methods
+	public:
+	// methods relatives a la gestion de la liste
+	void addName(const QString &, const QString &);
+	void removeName(const QString &);
+	void clearNames();
+	QList<QString> langs() const;
+	bool isEmpty() const;
+	int count() const;
+	QString &operator[](const QString &);
+	const QString operator[](const QString &) const;
+	bool operator!=(const NamesList &) const;
+	bool operator==(const NamesList &) const;
+	QString name(const QString & = QString()) const;
+	
+	// methods relatives a XML
+	void fromXml(const QDomElement &, const QHash<QString, QString> & = QHash<QString, QString>());
+	QDomElement toXml(QDomDocument &, const QHash<QString, QString> & = QHash<QString, QString>()) const;
+	
+	protected:
+	QHash<QString, QString> getXmlOptions(const QHash<QString, QString> & = QHash<QString, QString>()) const;
+};
+Q_DECLARE_METATYPE(NamesList);
+#endif

Added: trunk/sources/nameslistwidget.cpp
===================================================================
--- trunk/sources/nameslistwidget.cpp	                        (rev 0)
+++ trunk/sources/nameslistwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,173 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "nameslistwidget.h"
+#include "qetmessagebox.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent de la liste de noms
+*/
+NamesListWidget::NamesListWidget(QWidget *parent) : QWidget(parent), read_only(false) {
+	QVBoxLayout *names_list_layout = new QVBoxLayout();
+	setLayout(names_list_layout);
+	
+	tree_names = new QTreeWidget();
+	tree_names -> setRootIsDecorated(false);
+	tree_names -> setColumnCount(2);
+	QStringList headers;
+	headers << tr("Langue") << tr("Texte");
+	tree_names -> setHeaderLabels(headers);
+	tree_names -> setWhatsThis(
+		tr(
+			"Cette liste vous permet de saisir un court texte de fa\347on \340 ce"
+			" qu'il soit traduisible dans d'autres langues. Pour ce faire, elle"
+			" associe des codes ISO 639-1 de langues (ex. : \"fr\" pour"
+			" fran\347ais) aux traductions du texte en question dans ces"
+			" m\352mes langues.",
+			"\"What's this\" tip"
+		)
+	);
+	
+	button_add_line = new QPushButton(QET::Icons::Add, tr("Ajouter une ligne"));
+	button_add_line -> setWhatsThis(
+		tr(
+			"Ce bouton permet d'ajouter une association langue / traduction "
+			"dans la liste ci-dessus.",
+			"\"What's this\" tip"
+		)
+	);
+	connect(button_add_line, SIGNAL(released()), this, SLOT(addLine()));
+	
+	names_list_layout -> addWidget(tree_names);
+	names_list_layout -> addWidget(button_add_line);
+}
+
+/**
+	Destructeur
+*/
+NamesListWidget::~NamesListWidget() {
+}
+
+/**
+	Ajoute une ligne a l'editeur
+*/
+void NamesListWidget::addLine() {
+	clean();
+	if (read_only) return;
+	QTreeWidgetItem *qtwi = new QTreeWidgetItem();
+	qtwi -> setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+	tree_names -> addTopLevelItem(qtwi);
+ 	tree_names -> setCurrentItem(qtwi);
+    	tree_names -> editItem(qtwi);
+}
+
+/**
+	Verifie qu'il y a au moins un nom
+*/
+bool NamesListWidget::checkOneName() {
+	updateHash();
+	if (!hash_names.count()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Il doit y avoir au moins un nom.", "message box title"),
+			tr("Vous devez entrer au moins un nom.", "message box content")
+		);
+		return(false);
+	}
+	return(true);
+}
+
+/**
+	Lit les noms valides dans hash_names
+*/
+void NamesListWidget::updateHash() {
+	hash_names.clearNames();
+	int names_count = tree_names -> topLevelItemCount();
+	for (int i = 0 ; i < names_count ; ++ i) {
+		QString lang  = tree_names -> topLevelItem(i) -> text(0);
+		QString value = tree_names -> topLevelItem(i) -> text(1);
+		if (lang != "" && value != "") hash_names.addName(lang, value);
+	}
+}
+
+/**
+	Nettoie la liste des noms en enlevant les lignes vides
+*/
+void NamesListWidget::clean() {
+	int names_count = tree_names -> topLevelItemCount() - 1;
+	for (int i = names_count ; i >= 0 ; -- i) {
+		if (
+			tree_names -> topLevelItem(i) -> text(0).isEmpty() &&\
+			tree_names -> topLevelItem(i) -> text(1).isEmpty()
+		) {
+			tree_names -> takeTopLevelItem(i);
+		}
+	}
+}
+
+/**
+	@return Les noms entres dans la Names List
+*/
+NamesList NamesListWidget::names() {
+	updateHash();
+	return(hash_names);
+}
+
+/**
+	Definit les noms que le widget doit afficher
+*/
+void NamesListWidget::setNames(const NamesList &provided_names) {
+	foreach(QString lang, provided_names.langs()) {
+		QString value = provided_names[lang];
+		QStringList values;
+		values << lang << value;
+		QTreeWidgetItem *qtwi = new QTreeWidgetItem(values);
+		if (!read_only) qtwi -> setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+		tree_names -> addTopLevelItem(qtwi);
+		tree_names -> sortItems(0, Qt::AscendingOrder);
+	}
+}
+
+/**
+	Verifie qu'il y a au moins un nom de saisi - si c'est le cas, le signal
+	imputChecked() est emis.
+*/
+void NamesListWidget::check() {
+	if (checkOneName()) emit(inputChecked());
+}
+
+/**
+	Definit le mode d'edition du widget
+	@param ro true pour que la liste de noms soit en lecture seule, false sinon
+*/
+void NamesListWidget::setReadOnly(bool ro) {
+	read_only = ro;
+	int names_count = tree_names -> topLevelItemCount() - 1;
+	for (int i = names_count ; i >= 0 ; -- i) {
+		Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+		if (!read_only) flags |= Qt::ItemIsEditable;
+		tree_names -> topLevelItem(i) -> setFlags(flags);
+	}
+	button_add_line -> setEnabled(!read_only);
+}
+
+/// @return true si la liste de noms est en lecture seule, false sinon
+bool NamesListWidget::isReadOnly() const {
+	return(read_only);
+}

Added: trunk/sources/nameslistwidget.h
===================================================================
--- trunk/sources/nameslistwidget.h	                        (rev 0)
+++ trunk/sources/nameslistwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,67 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef NAMES_LIST_WIDGET_H
+#define NAMES_LIST_WIDGET_H
+#include <QtGui>
+#include "nameslist.h"
+/**
+	This class provides a widget enabling users to enter localized names for
+	categories and elements.
+*/
+class NamesListWidget : public QWidget {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	NamesListWidget(QWidget * = 0);
+	virtual ~NamesListWidget();
+	
+	private:
+	NamesListWidget(const NamesListWidget &);
+	
+	// attributes
+	private:
+	QTreeWidget *tree_names;
+	QPushButton *button_add_line;
+	NamesList hash_names;
+	bool read_only;
+	
+	// methods
+	public:
+	bool checkOneName();
+	NamesList names();
+	void setNames(const NamesList &);
+	void setReadOnly(bool);
+	bool isReadOnly() const;
+	
+	private:
+	void clean();
+	void updateHash();
+	
+	public slots:
+	void addLine();
+	void check();
+	
+	signals:
+	/**
+		Signal emitted after the widget successfully checked there was at least one
+		name entered
+	*/
+	void inputChecked();
+};
+#endif

Added: trunk/sources/newelementwizard.cpp
===================================================================
--- trunk/sources/newelementwizard.cpp	                        (rev 0)
+++ trunk/sources/newelementwizard.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,249 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "newelementwizard.h"
+#include "elementscategory.h"
+#include "elementscategorieswidget.h"
+#include "elementscategorieslist.h"
+#include "nameslistwidget.h"
+#include "qetgraphicsitem/element.h"
+#include "qetelementeditor.h"
+#include "qet.h"
+#include "qetapp.h"
+#include "elementscollectionitem.h"
+#include "qfilenameedit.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent de ce dialogue
+	@param f flags pour le dialogue
+*/
+NewElementWizard::NewElementWizard(QWidget *parent, Qt::WindowFlags f) :
+	QWizard(parent, f),
+	chosen_category(0)
+{
+	setOptions(options() & ~QWizard::NoCancelButton);
+
+#ifdef Q_WS_WIN
+	setWizardStyle(QWizard::AeroStyle);
+#elif Q_WS_MAC
+	setWizardStyle(QWizard::MacStyle);
+#endif
+
+	setPixmap(LogoPixmap, QPixmap(":/ico/256x256/qelectrotech.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+	setWindowTitle(tr("Cr\351er un nouvel \351l\351ment : Assistant", "window title"));
+	setButtonText(QWizard::NextButton, tr("&Suivant >"));
+	addPage(buildStep1());
+	addPage(buildStep2());
+	addPage(buildStep3());
+	setFixedSize(705, 325);
+}
+
+/**
+	Destructeur
+*/
+NewElementWizard::~NewElementWizard() {
+}
+
+/**
+	@return la categorie parente selectionnee, ou 0 si celle-ci n'a pas encore
+	ete choisie.
+*/
+ElementsCategory *NewElementWizard::selectedCategory() const {
+	return(chosen_category);
+}
+
+/**
+	@param category Categorie d'elements dans laquelle le nouvel element sera
+	place
+	@return true si ce choix est possible et a ete pris en compte, false sinon
+*/
+bool NewElementWizard::preselectCategory(ElementsCategory *category) {
+	// verifie si la categorie est utilisable
+	if (!category || !category -> exists() || !category -> isWritable()) {
+		return(false);
+	}
+	
+	// selectionne la categorie ainsi demandee dans la liste
+	if (categories_list -> elementsCategoriesList().selectLocation(category -> location())) {
+		chosen_category = category;
+		return(true);
+	}
+	
+	return(false);
+}
+
+/**
+	Met en place l'etape 1 : Categorie
+*/
+QWizardPage *NewElementWizard::buildStep1() {
+	QWizardPage *page = new QWizardPage();
+	page -> setProperty("WizardState", Category);
+	page -> setTitle(tr("\311tape 1/3 : Cat\351gorie parente", "wizard page title"));
+	page -> setSubTitle(tr("S\351lectionnez une cat\351gorie dans laquelle enregistrer le nouvel \351l\351ment.", "wizard page subtitle"));
+	QVBoxLayout *layout = new QVBoxLayout();
+	
+	categories_list = new ElementsCategoriesWidget();
+	layout -> addWidget(categories_list);
+	
+	page -> setLayout(layout);
+	return(page);
+}
+
+/**
+	Met en place l'etape 2 : Nom du fichier
+*/
+QWizardPage *NewElementWizard::buildStep2() {
+	QWizardPage *page = new QWizardPage();
+	page -> setProperty("WizardState", Filename);
+	page -> setTitle(tr("\311tape 2/3 : Nom du fichier", "wizard page title"));
+	page -> setSubTitle(tr("Indiquez le nom du fichier dans lequel enregistrer le nouvel \351l\351ment.", "wizard page subtitle"));
+	QVBoxLayout *layout = new QVBoxLayout();
+	
+	qle_filename = new QFileNameEdit(tr("nouvel_element"));
+	qle_filename -> selectAll();
+	QLabel *explication2 = new QLabel(tr("Vous n'\352tes pas oblig\351 de pr\351ciser l'extension *.elmt. Elle sera ajout\351e automatiquement."));
+	explication2 -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
+	explication2 -> setWordWrap(true);
+	layout -> addWidget(qle_filename);
+	layout -> addWidget(explication2);
+	layout -> addSpacing(100);
+	
+	page -> setLayout(layout);
+	return(page);
+}
+
+/**
+	Met en place l'etape 3 : Noms de l'element
+*/
+QWizardPage *NewElementWizard::buildStep3() {
+	QWizardPage *page = new QWizardPage();
+	page -> setProperty("WizardState", Names);
+	page -> setTitle(tr("\311tape 3/3 : Noms de l'\351l\351ment", "wizard page title"));
+	page -> setSubTitle(tr("Indiquez le ou les noms de l'\351l\351ment.", "wizard page subtitle"));
+	QVBoxLayout *layout = new QVBoxLayout();
+	
+	element_names = new NamesListWidget();
+	NamesList hash_name;
+	hash_name.addName(QLocale::system().name().left(2), tr("Nom du nouvel \351l\351ment", "default name when creating a new element"));
+	element_names -> setNames(hash_name);
+	layout -> addWidget(element_names);
+	
+	page -> setLayout(layout);
+	return(page);
+}
+
+/// @return true si l'etape en cours est validee, false sinon
+bool NewElementWizard::validateCurrentPage() {
+	WizardState wizard_state = static_cast<WizardState>(currentPage() -> property("WizardState").toInt());
+	if      (wizard_state == Category) return(validStep1());
+	else if (wizard_state == Filename) return(validStep2());
+	else if (wizard_state == Names) {
+		// must have one name minimum
+		if (element_names -> checkOneName())
+			createNewElement();
+		return true;
+
+	}
+	else return(true);
+}
+
+/**
+	Valide l'etape 1
+	@return true si l'etape est validee, false sinon
+*/
+bool NewElementWizard::validStep1() {
+	// il doit y avoir une categorie selectionnee
+	bool step1_ok = false;
+	ElementsLocation selected_location = categories_list -> elementsCategoriesList().selectedLocation();
+	if (ElementsCollectionItem *collection_item = QETApp::collectionItem(selected_location, false)) {
+		if (collection_item -> isCategory()) {
+			chosen_category = qobject_cast<ElementsCategory *>(collection_item);
+			step1_ok = chosen_category;
+		}
+	}
+	
+	if (!step1_ok) {
+		QET::MessageBox::critical(
+			parentWidget(),
+			tr("Erreur", "message box title"),
+			tr("Vous devez s\351lectionner une cat\351gorie.", "message box content")
+		);
+	}
+	return(step1_ok);
+}
+
+/**
+	Valide l'etape 2
+	@return true si l'etape est validee, false sinon
+*/
+bool NewElementWizard::validStep2() {
+	// il doit y avoir une categorie selectionnee
+	if (!chosen_category) return(false);
+	QString file_name = qle_filename -> text();
+	
+	// un nom doit avoir ete entre
+	if (file_name.isEmpty()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Vous devez entrer un nom de fichier", "message box content")
+		);
+		return(false);
+	}
+	
+	if (!file_name.endsWith(".elmt")) file_name += ".elmt";
+	
+	// le nom de fichier contient peut etre des caracteres interdits
+	if (QET::containsForbiddenCharacters(file_name)) {
+		QET::MessageBox::critical(
+			this,
+			tr("Erreur", "message box title"),
+			tr("Merci de ne pas utiliser les caract\350res suivants : \\ / : * ? \" < > |", "message box content")
+		);
+		return(false);
+	}
+	
+	// le fichier existe peut etre deja
+	if (chosen_category -> element(file_name)) {
+		QMessageBox::StandardButton answer = QET::MessageBox::question(
+			this,
+			"\311craser le fichier ?",
+			"Le fichier existe d\351j\340. Souhaitez-vous l'\351craser ?",
+			QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+			QMessageBox::No
+		);
+		return(answer == QMessageBox::Yes);
+	}
+	
+	chosen_file = file_name;
+	return(true);
+}
+
+/**
+	Cree le nouvel element
+*/
+void NewElementWizard::createNewElement() {
+	QETElementEditor *edit_new_element = new QETElementEditor(parentWidget());
+	edit_new_element -> setNames(element_names -> names());
+	
+	ElementsLocation new_element_location = chosen_category -> location();
+	new_element_location.addToPath(chosen_file);
+	edit_new_element -> setLocation(new_element_location);
+	edit_new_element -> show();
+}

Added: trunk/sources/newelementwizard.h
===================================================================
--- trunk/sources/newelementwizard.h	                        (rev 0)
+++ trunk/sources/newelementwizard.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,70 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef NEW_ELEMENT_WIZARD_H
+#define NEW_ELEMENT_WIZARD_H
+#include <QtGui>
+class ElementsCategoriesWidget;
+class ElementsCategory;
+class NamesListWidget;
+class QFileNameEdit;
+/**
+	This class provides a wizard dialog enabling users to to specify the basic
+	parameters of the electrical elements they intend to draw.
+	
+	These parameters include:
+	  - the category the element should be saved to
+	  - the filename the element should be saved to
+	  - localized names
+*/
+class NewElementWizard : public QWizard {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	NewElementWizard(QWidget * = 0, Qt::WindowFlags = 0);
+	virtual ~NewElementWizard();
+	
+	private:
+	NewElementWizard(const NewElementWizard &);
+	
+	// methods
+	public:
+	ElementsCategory *selectedCategory() const;
+	bool preselectCategory(ElementsCategory *);
+	
+	// attributes
+	private:
+	enum WizardState { Category, Filename, Names };
+	ElementsCategoriesWidget *categories_list;
+	QFileNameEdit *qle_filename;
+	NamesListWidget *element_names;
+	WizardState current_state;
+	QString chosen_file;
+	ElementsCategory *chosen_category;
+	
+	// methods
+	private:
+	QWizardPage *buildStep1();
+	QWizardPage *buildStep2();
+	QWizardPage *buildStep3();
+	bool validStep1();
+	bool validStep2();
+	bool validateCurrentPage();
+	void createNewElement();
+};
+#endif

Added: trunk/sources/nomenclature.cpp
===================================================================
--- trunk/sources/nomenclature.cpp	                        (rev 0)
+++ trunk/sources/nomenclature.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,108 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <QtDebug>
+
+#include "nomenclature.h"
+#define PR(x) qDebug() << #x " = " << x;
+
+/**
+		Constructor
+		@param an project (QETProject) of QET file 
+*/
+nomenclature::nomenclature(QETProject *project, QWidget *parent):
+	QObject(),
+	m_project(project)
+{
+	m_parent = parent;
+	if(!m_project->isEmpty()){
+		//get list of schema present in project
+		m_list_diagram = m_project -> diagrams();
+	}
+}
+
+/**
+		Destructor
+*/
+nomenclature::~nomenclature() {
+}
+
+/**
+		Save to csv file
+		@param true if success
+*/
+bool nomenclature::saveToCSVFile() {
+	if(m_list_diagram.isEmpty()) return false;
+	
+	//Process...
+	QString data = tr("NOMENCLATURE : ") + m_project -> title() + "\n\n";
+	data += tr("Ref") +";"+ tr("Folio") +";"+ tr("Sch\351ma") +";"+ tr("D\351signation\n");
+	QStringList rows;
+	for(int i=0; i<m_list_diagram.count(); i++){
+		rows = getRows(m_list_diagram.at(i));
+		for(int j=0;j<rows.count();j++){
+			data += rows.at(j);
+		}
+	}
+	
+	// SAVE IN FILE
+	QString name = tr("nomenclature_") + QString(m_project  -> title());
+	QString filename = QFileDialog::getSaveFileName(this->m_parent, tr("Enregister sous... "), name, tr("Fichiers csv (*.csv)"));
+	QFile file(filename);
+	if( !filename.isEmpty() ) {
+		if(QFile::exists ( filename )){
+			// if file already exist -> delete it
+			if(!QFile::remove ( filename ) ){
+				QMessageBox::critical(this->m_parent, tr("Erreur"),
+									  tr("Impossible de remplacer le fichier!\n\n")+
+									  "Destination: "+filename+"\n");
+				return false;
+			}	
+		}
+		if (file.open(QIODevice::WriteOnly | QIODevice::Text)){
+			QTextStream stream(&file);
+			stream << data << endl;
+		}
+		else return false;
+	}
+	else return false;
+	
+	return true;
+}
+
+/**
+		gets rows of nomenclature
+		@return the list of rows
+*/
+QStringList nomenclature::getRows(Diagram *schema) {
+	QString row;
+	QStringList list;
+	QList<Element *> elements_list;
+	//QList<CustomElement *> elements_list;
+	
+	//elements_list = schema->customElements();
+	elements_list = schema->content().elements.toList();
+	for(int j=0;j<elements_list.count();j++){
+		row += QString::number(0) + ";";
+		row += QString::number(0) + ";";
+		row += schema->title() + ";";
+		row += elements_list.at(j)->name() + "\n";
+		list << row;
+	}
+	return list;
+}
+

Added: trunk/sources/nomenclature.h
===================================================================
--- trunk/sources/nomenclature.h	                        (rev 0)
+++ trunk/sources/nomenclature.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,65 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef NOMENCLATURE_H
+#define NOMENCLATURE_H
+
+#include <QtGui>
+
+#include "qetproject.h"
+#include "diagram.h"
+#include "qetgraphicsitem/element.h"
+#include "diagramcontent.h"
+#include "qetgraphicsitem/customelement.h"
+
+class QETProject;
+class Diagram;
+class Element;
+class DiagramContent;
+class CustomeElement;
+
+/**
+		This class represents a nomenclature...
+*/
+class nomenclature : public QObject {
+	Q_OBJECT
+	
+	private:       
+	QETProject *m_project;
+	QList<Diagram *> m_list_diagram;
+	QWidget *m_parent;
+	
+	// constructors, destructor
+	public:
+	nomenclature(QETProject *project =0, QWidget *parent =0);
+	virtual ~nomenclature();
+	
+	// attributes
+	public:
+	
+	// methods
+	public:
+	bool saveToCSVFile();
+
+	
+	private:
+	QStringList getRows(Diagram *schema);
+	
+};
+
+#endif
+

Added: trunk/sources/numerotationcontext.cpp
===================================================================
--- trunk/sources/numerotationcontext.cpp	                        (rev 0)
+++ trunk/sources/numerotationcontext.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,153 @@
+/*
+	Copyright 2006-2013 The QElectroTech team
+	This file is part of QElectroTech.
+
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "numerotationcontext.h"
+#include "qet.h"
+
+/**
+ * Constructor
+ */
+NumerotationContext::NumerotationContext(){
+}
+
+/**
+ * Constructor from xml
+ */
+NumerotationContext::NumerotationContext(QDomElement &e) {
+	fromXml(e);
+}
+
+/**
+ * @brief NumerotationContext::clear, clear the content
+ */
+void NumerotationContext::clear () {
+	content_.clear();
+}
+
+/**
+ * @brief NumerotationContext::addValue, add a new value on the contexte
+ * @param type the type of value
+ * @param value the value itself
+ * @param increase the increase number of value
+ * @return true if value is append
+ */
+bool NumerotationContext::addValue(const QString &type, const QVariant &value, const int increase) {
+	if (!keyIsAcceptable(type) && !value.canConvert(QVariant::String)) return false;
+	if (keyIsNumber(type) && !value.canConvert(QVariant::Int)) return false;
+
+	QString valuestr = value.toString();
+	valuestr.remove("|");
+	content_ << type + "|" + valuestr + "|" + QString::number(increase);
+	return true;
+}
+
+/**
+ * @brief NumerotationContext::operator []
+ * @return the string at position @i
+ */
+QString NumerotationContext::operator [] (const int &i) const {
+	return (content_.at(i));
+}
+
+/**
+ * @brief NumerotationContext::operator << , append other
+ */
+void NumerotationContext::operator << (const NumerotationContext &other) {
+	for (int i=0; i<other.size(); ++i) content_.append(other[i]);
+}
+
+/**
+ * @brief NumerotationContext::size
+ * @return size of context
+ */
+int NumerotationContext::size() const {
+	return (content_.size());
+}
+
+/**
+ * @brief NumerotationContext::isEmpty
+ * @return true if numerotation contet is empty
+ */
+bool NumerotationContext::isEmpty() const {
+	if (content_.size() > 0) return false;
+	return true;
+}
+/**
+ * @brief NumerotationContext::itemAt
+ * @return the content at position @i 1:type 2:value 3:increase
+ */
+QStringList NumerotationContext::itemAt(const int i) const {
+	return (content_.at(i).split("|"));
+}
+
+/**
+ * @brief validRegExpNum
+ * @return all type use to numerotation
+ */
+QString NumerotationContext::validRegExpNum () const {
+	return ("unit|ten|hundred|string|folio");
+}
+
+/**
+ * @brief NumerotationContext::validRegExpNumber
+ * @return all type represents a number
+ */
+QString NumerotationContext::validRegExpNumber() const {
+	return ("unit|ten|hundred");
+}
+
+/**
+ * @brief NumerotationContext::keyIsAcceptable
+ * @return true if @type is acceptable
+ */
+bool NumerotationContext::keyIsAcceptable(const QString &type) const {
+	return (type.contains(QRegExp(validRegExpNum())));
+}
+
+/**
+ * @brief NumerotationContext::keyIsNumber
+ * @return true if @type represent a number
+ */
+bool NumerotationContext::keyIsNumber(const QString &type) const {
+	return (type.contains(QRegExp(validRegExpNumber())));
+}
+
+/**
+ * @brief NumerotationContext::toXml
+ * Save the numerotation context in a QDomElement under the element name @str
+ */
+QDomElement NumerotationContext::toXml(QDomDocument &d, QString str) {
+	QDomElement num_auto = d.createElement(str);
+	for (int i=0; i<content_.size(); ++i) {
+		QStringList strl = itemAt(i);
+		QDomElement part = d.createElement("part");
+		part.setAttribute("type", strl.at(0));
+		part.setAttribute("value", strl.at(1));
+		part.setAttribute("increase", strl.at(2));
+		num_auto.appendChild(part);
+	}
+	return num_auto;
+}
+
+/**
+ * @brief NumerotationContext::fromXml
+ * load numerotation context from @e
+ */
+void NumerotationContext::fromXml(QDomElement &e) {
+	clear();
+	foreach(QDomElement qde, QET::findInDomElement(e, "part")) addValue(qde.attribute("type"), qde.attribute("value"), qde.attribute("increase").toInt());
+}

Added: trunk/sources/numerotationcontext.h
===================================================================
--- trunk/sources/numerotationcontext.h	                        (rev 0)
+++ trunk/sources/numerotationcontext.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,53 @@
+/*
+	Copyright 2006-2013 The QElectroTech team
+	This file is part of QElectroTech.
+
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef NUMEROTATIONCONTEXT_H
+#define NUMEROTATIONCONTEXT_H
+
+#include <QStringList>
+#include <QVariant>
+#include <QDomElement>
+
+/**
+	This class represents a numerotation context, i.e. the data (type, value, increase)
+	of a numerotation at a given time. It is notably used by conductor
+	to store the informations they need to do their autonumerotation.
+ */
+class NumerotationContext
+{
+	public:
+	NumerotationContext ();
+	NumerotationContext (QDomElement &);
+	void clear();
+	bool addValue(const QString &, const QVariant & = QVariant(1), const int = 1);
+	QString operator[] (const int &) const;
+	void operator << (const NumerotationContext &);
+	int size() const;
+	bool isEmpty() const;
+	QStringList itemAt(const int) const;
+	QString validRegExpNum () const;
+	QString validRegExpNumber() const;
+	bool keyIsAcceptable (const QString &) const;
+	bool keyIsNumber(const QString &) const;
+	QDomElement toXml(QDomDocument &, QString);
+	void fromXml(QDomElement &);
+
+	private:
+	QStringList content_;
+};
+
+#endif // NUMEROTATIONCONTEXT_H

Added: trunk/sources/numerotationcontextcommands.cpp
===================================================================
--- trunk/sources/numerotationcontextcommands.cpp	                        (rev 0)
+++ trunk/sources/numerotationcontextcommands.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,240 @@
+#include "numerotationcontextcommands.h"
+
+/**
+ * @brief Constructor
+ */
+NumerotationContextCommands::NumerotationContextCommands(Diagram *d, const NumerotationContext &nc):
+	diagram_ (d),
+	context_ (nc),
+	strategy_ (NULL)
+{}
+
+/**
+ * @brief Destructor
+ */
+NumerotationContextCommands::~NumerotationContextCommands() {
+	if (strategy_) delete strategy_;
+}
+
+/**
+ * @brief NumerotationContextCommands::next
+ * @return the next numerotation context
+ */
+NumerotationContext NumerotationContextCommands::next() {
+	NumerotationContext contextnum;
+
+	for (int i=0; i<context_.size(); ++i) {
+		QStringList str = context_.itemAt(i);
+		setNumStrategy(str.at(0));
+		contextnum << strategy_ -> next(context_, i);
+	}
+	return contextnum;
+}
+
+/**
+ * @brief NumerotationContextCommands::toFinalString
+ * @return the string represented by the numerotation context
+ */
+QString NumerotationContextCommands::toRepresentedString() {
+	QString num;
+	if (context_.size()) {
+		for (int i=0; i<context_.size(); i++) {
+			QStringList  str = context_.itemAt(i);
+			setNumStrategy(str.at(0));
+			num += strategy_ -> toRepresentedString(str.at(1));
+		}
+		return num;
+	}
+	return (diagram_ -> defaultConductorProperties.text);
+}
+
+/**
+ * @brief NumerotationContextCommands::setNumStrategy
+ * apply the good strategy relative to @str
+ */
+void NumerotationContextCommands::setNumStrategy(const QString &str) {
+	if (strategy_) delete strategy_;
+	if (str == "unit") {
+		strategy_ = new UnitNum(diagram_);
+		return;
+	}
+	else if (str == "ten") {
+		strategy_ = new TenNum (diagram_);
+		return;
+	}
+	else if (str == "hundred") {
+		strategy_ = new HundredNum (diagram_);
+		return;
+	}
+	else if (str == "string") {
+		strategy_ = new StringNum (diagram_);
+		return;
+	}
+	else if (str == "folio") {
+		strategy_ = new FolioNum (diagram_);
+		return;
+	}
+}
+
+
+
+/**
+ * Constructor
+ */
+NumStrategy::NumStrategy (Diagram *d):
+	diagram_ (d)
+{}
+
+NumStrategy::~NumStrategy() {}
+
+/**
+ * @brief NumStrategy::nextString
+ * @return the next value of @nc at position @i
+ */
+NumerotationContext NumStrategy::nextString (const NumerotationContext &nc, const int i) const {
+	QStringList strl = nc.itemAt(i);
+	NumerotationContext newnc;
+	newnc.addValue(strl.at(0), strl.at(1), strl.at(2).toInt());
+	return (newnc);
+}
+
+/**
+ * @brief NumStrategy::nextNumber
+ * @return the next value of @nc at position @i
+ */
+NumerotationContext NumStrategy::nextNumber (const NumerotationContext &nc, const int i) const {
+	QStringList strl = nc.itemAt(i);
+	NumerotationContext newnc;
+	QString value = QString::number( (strl.at(1).toInt()) + (strl.at(2).toInt()) );
+	newnc.addValue(strl.at(0), value, strl.at(2).toInt());
+	return (newnc);
+}
+
+/**
+ * Constructor
+ */
+UnitNum::UnitNum(Diagram *d):
+	NumStrategy(d)
+{}
+
+/**
+ * @brief UnitNum::toRepresentedString
+ * @return the represented string of num
+ */
+QString UnitNum::toRepresentedString(const QString num) const {
+	return (num);
+}
+
+/**
+ * @brief UnitNum::next
+ * @return the next NumerotationContext nc at position i
+ */
+NumerotationContext UnitNum::next (const NumerotationContext &nc, const int i) const {
+	return (nextNumber(nc, i));
+}
+
+/**
+ * Constructor
+ */
+TenNum::TenNum (Diagram *d):
+	NumStrategy (d)
+{}
+
+/**
+ * @brief TenNum::toRepresentedString
+ * @return the represented string of num
+ */
+QString TenNum::toRepresentedString(const QString num) const {
+	int numint = num.toInt();
+	QString numstr = num;
+	if (numint<10) numstr.prepend("0");
+	return (numstr);
+}
+
+/**
+ * @brief TenNum::next
+ * @return the next NumerotationContext nc at position i
+ */
+NumerotationContext TenNum::next (const NumerotationContext &nc, const int i) const {
+	return (nextNumber(nc, i));
+}
+
+/**
+ * Constructor
+ */
+HundredNum::HundredNum (Diagram *d):
+	NumStrategy (d)
+{}
+
+/**
+ * @brief HundredNum::toRepresentedString
+ * @return the represented string of num
+ */
+QString HundredNum::toRepresentedString(const QString num) const {
+	int numint = num.toInt();
+	QString numstr = num;
+	if (numint<100) {
+		if (numint<10) {
+			numstr.prepend("00");
+		}
+		else numstr.prepend("0");
+	}
+	return (numstr);
+}
+
+/**
+ * @brief HundredNum::next
+ * @return the next NumerotationContext nc at position i
+ */
+NumerotationContext HundredNum::next (const NumerotationContext &nc, const int i) const {
+	return (nextNumber(nc, i));
+}
+
+/**
+ * Constructor
+ */
+StringNum::StringNum (Diagram *d):
+	NumStrategy (d)
+{}
+
+/**
+ * @brief StringNum::toRepresentedString
+ * @return the represented string of num
+ */
+QString StringNum::toRepresentedString(const QString str) const {
+	return (str);
+}
+
+/**
+ * @brief StringNum::next
+ * @return the next NumerotationContext nc at position i
+ */
+NumerotationContext StringNum::next (const NumerotationContext &nc, const int i) const {
+	return (nextString(nc, i));
+}
+
+
+/**
+ * Constructor
+ */
+FolioNum::FolioNum (Diagram *d):
+	NumStrategy (d)
+{}
+
+/**
+ * @brief FolioNum::toRepresentedString
+ * @return the represented string of num
+ */
+QString FolioNum::toRepresentedString(const QString str) const {
+	Q_UNUSED(str);
+	return (QString::number(diagram_ -> folioIndex() + 1));
+}
+
+/**
+ * @brief FolioNum::next
+ * @return the next NumerotationContext nc at position i
+ */
+NumerotationContext FolioNum::next (const NumerotationContext &nc, const int i) const {
+	return (nextString(nc, i));
+}
+

Added: trunk/sources/numerotationcontextcommands.h
===================================================================
--- trunk/sources/numerotationcontextcommands.h	                        (rev 0)
+++ trunk/sources/numerotationcontextcommands.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,83 @@
+#ifndef NUMEROTATIONCONTEXTCOMMANDS_H
+#define NUMEROTATIONCONTEXTCOMMANDS_H
+
+#include "numerotationcontext.h"
+#include "diagram.h"
+
+class NumStrategy;
+
+/**
+ * this class provide methods to handle content of NumerotationContext.
+ */
+class NumerotationContextCommands
+{
+	public:
+	NumerotationContextCommands (Diagram *, const NumerotationContext &);
+	~NumerotationContextCommands ();
+	NumerotationContext next ();
+	QString toRepresentedString ();
+
+	private:
+	void setNumStrategy (const QString &);
+
+	Diagram *diagram_;
+	NumerotationContext context_;
+	NumStrategy *strategy_;
+};
+
+class NumStrategy
+{
+	public:
+	NumStrategy (Diagram *);
+	virtual ~NumStrategy ();
+	virtual QString toRepresentedString (const QString) const = 0;
+	virtual NumerotationContext next (const NumerotationContext &, const int) const = 0;
+
+	protected:
+	NumerotationContext nextString (const NumerotationContext &, const int) const;
+	NumerotationContext nextNumber (const NumerotationContext &, const int) const;
+
+	Diagram *diagram_;
+};
+
+class UnitNum: public NumStrategy
+{
+	public:
+	UnitNum (Diagram *);
+	QString toRepresentedString(const QString) const;
+	NumerotationContext next (const NumerotationContext &, const int) const;
+};
+
+class TenNum: public NumStrategy
+{
+	public:
+	TenNum (Diagram *);
+	QString toRepresentedString(const QString) const;
+	NumerotationContext next (const NumerotationContext &, const int) const;
+};
+
+class HundredNum: public NumStrategy
+{
+	public:
+	HundredNum (Diagram *);
+	QString toRepresentedString(const QString) const;
+	NumerotationContext next (const NumerotationContext &, const int) const;
+};
+
+class StringNum: public NumStrategy
+{
+	public:
+	StringNum (Diagram *);
+	QString toRepresentedString(const QString) const;
+	NumerotationContext next (const NumerotationContext &, const int) const;
+};
+
+class FolioNum: public NumStrategy
+{
+	public:
+	FolioNum (Diagram *);
+	QString toRepresentedString(const QString) const;
+	NumerotationContext next (const NumerotationContext &, const int) const;
+};
+
+#endif // NUMEROTATIONCONTEXTCOMMANDS_H

Added: trunk/sources/orientationset.cpp
===================================================================
--- trunk/sources/orientationset.cpp	                        (rev 0)
+++ trunk/sources/orientationset.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,273 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "orientationset.h"
+
+/**
+	Constructeur
+	Par defaut, toutes les orientations sont autorisees. L'orientation courante
+	vaut l'orientation par defaut, c'est-a-dire Nord.
+*/
+OrientationSet::OrientationSet() :
+	north_ori(true),
+	east_ori(true),
+	south_ori(true),
+	west_ori(true),
+	default_ori(QET::North),
+	current_ori(QET::North)
+{}
+
+/**
+	@param ori true pour autoriser l'orientation vers le Nord, false pour l'interdire
+	@return true si le changement d'autorisation a reussi, false sinon
+*/
+bool OrientationSet::setNorth (bool ori) {
+	// pour desactiver une orientation, il doit y avoir au moins une autre orientation possible
+	bool can_set_ori = ori ? true : east_ori || south_ori || west_ori;
+	if (can_set_ori) {
+		north_ori = ori;
+		// en cas de desactivation d'une orientation, il faut verifier voire corriger les orientations courante et par defaut 
+		if (!ori) {
+			if (default_ori == QET::North) default_ori = next();
+			if (current_ori == QET::North) current_ori = next();
+		}
+	}
+	return(can_set_ori);
+}
+
+/**
+	@param ori true pour autoriser l'orientation vers l'Est, false pour l'interdire
+	@return true si le changement d'autorisation a reussi, false sinon
+*/
+bool OrientationSet::setEast (bool ori) {
+	// pour desactiver une orientation, il doit y avoir au moins une autre orientation possible
+	bool can_set_ori = ori ? true : south_ori || west_ori || north_ori;
+	if (can_set_ori) {
+		east_ori = ori;
+		// en cas de desactivation d'une orientation, il faut verifier voire corriger les orientations courante et par defaut 
+		if (!ori) {
+			if (default_ori == QET::East) default_ori = next();
+			if (current_ori == QET::East) current_ori = next();
+		}
+	}
+	return(can_set_ori);
+}
+
+/**
+	@param ori true pour autoriser l'orientation vers le Sud, false pour l'interdire
+	@return true si le changement d'autorisation a reussi, false sinon
+*/
+bool OrientationSet::setSouth (bool ori) {
+	// pour desactiver une orientation, il doit y avoir au moins une autre orientation possible
+	bool can_set_ori = ori ? true : west_ori || north_ori || east_ori;
+		if (can_set_ori) {
+		south_ori = ori;
+		// en cas de desactivation d'une orientation, il faut verifier voire corriger les orientations courante et par defaut 
+		if (!ori) {
+			if (default_ori == QET::South) default_ori = next();
+			if (current_ori == QET::South) current_ori = next();
+		}
+	}
+	return(can_set_ori);
+}
+
+/**
+	@param ori true pour autoriser l'orientation vers l'Ouest, false pour l'interdire
+	@return true si le changement d'autorisation a reussi, false sinon
+*/
+bool OrientationSet::setWest (bool ori) {
+	// pour desactiver une orientation, il doit y avoir au moins une autre orientation possible
+	bool can_set_ori = ori ? true : north_ori || east_ori || south_ori;
+	if (can_set_ori) {
+		west_ori = ori;
+		// en cas de desactivation d'une orientation, il faut verifier voire corriger les orientations courante et par defaut 
+		if (!ori) {
+			if (default_ori == QET::West) default_ori = next();
+			if (current_ori == QET::West) current_ori = next();
+		}
+	}
+	return(can_set_ori);
+}
+
+/**
+	Definit l'orientation courante
+	@param ori nouvelle orientation courante
+	@return true si le changement d'orientation a reussi, false sinon
+*/
+bool OrientationSet::setCurrent(QET::Orientation ori) {
+	bool can_set_ori = accept(ori);
+	if (can_set_ori) current_ori = ori;
+	return(can_set_ori);
+}
+
+/**
+	@return l'orientation suivant l'orientation courante
+*/
+QET::Orientation OrientationSet::next() const {
+	QET::Orientation result = current_ori;
+	do result = QET::nextOrientation(result); while (!accept(result));
+	return(result);
+}
+
+/**
+	@return l'orientation precedant l'orientation courante
+*/
+QET::Orientation OrientationSet::previous() const {
+	QET::Orientation result = current_ori;
+	do result = QET::previousOrientation(result); while (!accept(result));
+	return(result);
+}
+
+/**
+	Equivaut a setNext()
+	@return l'OrientationSet precedent
+*/
+const OrientationSet OrientationSet::operator++(int) {
+	OrientationSet before(*this);
+	setNext();
+	return(before);
+}
+
+/**
+	Equivaut a setPrevious()
+	@return l'OrientationSet precedent
+*/
+const OrientationSet OrientationSet::operator--(int) {
+	OrientationSet before(*this);
+	setPrevious();
+	return(before);
+}
+
+/**
+	Permet de savoir si une orientation donnee peut etre utilisee.
+	@param ori L'orientation en question
+	@return true si l'orientation est utilisable, false sinon
+*/
+bool OrientationSet::accept(QET::Orientation ori) const {
+	bool accepted_ori = false;
+	switch(ori) {
+		case QET::North: accepted_ori = north_ori; break;
+		case QET::East : accepted_ori = east_ori;  break;
+		case QET::South: accepted_ori = south_ori; break;
+		case QET::West : accepted_ori = west_ori;  break;
+	}
+	return(accepted_ori);
+}
+
+/**
+	Definit l'orientation suivante comme etant l'orientation courante
+	@return la nouvelle orientation courante
+*/
+QET::Orientation OrientationSet::setNext() {
+	setCurrent(next());
+	return(current_ori);
+}
+
+/**
+	Definit l'orientation precedente comme etant l'orientation courante
+	@return la nouvelle orientation courante
+*/
+QET::Orientation OrientationSet::setPrevious() {
+	setCurrent(previous());
+	return(current_ori);
+}
+
+/**
+	Equivaut a setNext()
+	@return l'OrientationSet courant
+*/
+const OrientationSet OrientationSet::operator++() {
+	setNext();
+	return(*this);
+}
+
+/**
+	Equivaut a setPrevious()
+	@return l'OrientationSet courant
+*/
+const OrientationSet OrientationSet::operator--() {
+	setPrevious();
+	return(*this);
+}
+
+/**
+	@param os autre OrientationSet
+	@return true si os et cet OrientationSet sont identiques, false sinon
+*/
+bool OrientationSet::operator==(const OrientationSet &os) const {
+	if (north_ori   != os.north_ori)   return(false);
+	if (east_ori    != os.east_ori)    return(false);
+	if (south_ori   != os.south_ori)   return(false);
+	if (west_ori    != os.west_ori)    return(false);
+	if (default_ori != os.default_ori) return(false);
+	if (current_ori != os.current_ori) return(false);
+	return(true);
+}
+
+/**
+	@param os autre OrientationSet
+	@return false si os et cet OrientationSet sont identiques, true sinon
+*/
+bool OrientationSet::operator!=(const OrientationSet &os) const {
+	return(!(this -> operator==(os)));
+}
+
+/**
+	Charge l'orientationSet depuis une chaine de caractere.
+	Cette chaine doit faire 4 caracteres, representant respectivement
+	le Nord, l'Est, le Sud et l'Ouest. Le caractere y indique que l'orientation
+	est autorisee, le caractere n indique que l'orientation est interdite et le
+	caractere d designe l'orientation par defaut. L'orientation courante est
+	celle par defaut.
+	@param str Chaine de caracteres a analyser et charger
+	@return true si l'analyse a reussie, false sinon
+*/
+bool OrientationSet::fromString(const QString &str) {
+	QRegExp osv("^([dyn])([dyn])([dyn])([dyn])$");	// osv : Orientation String Validator
+	if (osv.indexIn(str) == -1) return(false);
+	QStringList matches = osv.capturedTexts();
+	
+	// il doit y avoir exactement UN d dans les 4 lettres capturees
+	if (matches.count("d") != 1) return(false);
+	
+	bool *ori_pointers[4] = { &north_ori, &east_ori, &south_ori, &west_ori };
+	QET::Orientation ori_ints[4] = { QET::North, QET::East, QET::South, QET::West };
+	for(int i = 0 ; i < 4 ; ++ i) {
+		QString current = matches.at(i + 1);
+		if (current == "d") {
+			current_ori = default_ori = ori_ints[i];
+			current = "y";
+		}
+		*(ori_pointers[i]) = (current == "y");
+	}
+	return(true);
+}
+
+/**
+	@return Une chaine de caracteres representant cet OrientationSet.
+	@see fromString
+*/
+QString OrientationSet::toString() const {
+	bool ori_pointers[4] = { north_ori, east_ori, south_ori, west_ori };
+	QET::Orientation ori_ints[4] = { QET::North, QET::East, QET::South, QET::West };
+	QString result("");
+	for(int i = 0 ; i < 4 ; ++ i) {
+		if (default_ori == ori_ints[i]) result += "d";
+		else result += (ori_pointers[i] ? "y" : "n");
+	}
+	return(result);
+}

Added: trunk/sources/orientationset.h
===================================================================
--- trunk/sources/orientationset.h	                        (rev 0)
+++ trunk/sources/orientationset.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,122 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ORIENTATION_SET_H
+#define ORIENTATION_SET_H
+#include "qet.h"
+/**
+	This class provides a container for element orientations, i.e. a data
+	structure that stores, for a particular element:
+	  - its default orientation,
+	  - its current orientation,
+	  - whether the North, East, South and West orientations are allowed.
+*/
+class OrientationSet {
+	
+	// constructors, destructor
+	public:
+	OrientationSet();
+	virtual ~OrientationSet() {};
+	
+	// attributes
+	private:
+	bool north_ori;
+	bool east_ori;
+	bool south_ori;
+	bool west_ori;
+	QET::Orientation default_ori;
+	QET::Orientation current_ori;
+	
+	// methods
+	public:
+	bool north() const;
+	bool east() const;
+	bool south() const;
+	bool west() const;
+	bool setNorth(bool);
+	bool setEast(bool);
+	bool setSouth(bool);
+	bool setWest(bool);
+	QET::Orientation defaultOrientation() const;
+	void setDefaultOrientation(const QET::Orientation &);
+	QET::Orientation current() const;
+	bool setCurrent(QET::Orientation);
+	QET::Orientation next() const;
+	QET::Orientation previous() const;
+	QET::Orientation setNext();
+	QET::Orientation setPrevious();
+	bool accept(QET::Orientation) const;
+	const OrientationSet operator++(int);
+	const OrientationSet operator--(int);
+	const OrientationSet operator++();
+	const OrientationSet operator--();
+	bool operator==(const OrientationSet &) const;
+	bool operator!=(const OrientationSet &) const;
+	bool fromString(const QString &);
+	QString toString() const;
+};
+
+/**
+	@return whether the Northern orientation is allowed
+*/
+inline bool OrientationSet::north() const {
+	return(north_ori);
+}
+
+/**
+	@return whether the Eastern orientation is allowed
+*/
+inline bool OrientationSet::east() const {
+	return(east_ori);
+}
+
+/**
+	@return whether the Southern orientation is allowed
+*/
+inline bool OrientationSet::south() const {
+	return(south_ori);
+}
+
+/**
+	@return whether the Western orientation is allowed
+*/
+inline bool OrientationSet::west() const {
+	return(west_ori);
+}
+
+/**
+	@param new_default_orientation The new default orientation
+*/
+inline void OrientationSet::setDefaultOrientation(const QET::Orientation& new_default_orientation) {
+	default_ori = new_default_orientation;
+}
+
+/**
+	@return the default orientation
+*/
+inline QET::Orientation OrientationSet::defaultOrientation() const {
+	return(default_ori);
+}
+
+/**
+	@return the current orientation
+*/
+inline QET::Orientation OrientationSet::current() const {
+	return(current_ori);
+}
+
+#endif

Added: trunk/sources/projectconfigpages.cpp
===================================================================
--- trunk/sources/projectconfigpages.cpp	                        (rev 0)
+++ trunk/sources/projectconfigpages.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,303 @@
+#include "projectconfigpages.h"
+#include "qeticons.h"
+#include "qetproject.h"
+#include "borderpropertieswidget.h"
+#include "conductorpropertieswidget.h"
+#include "diagramcontextwidget.h"
+#include "titleblockpropertieswidget.h"
+#include <QtGui>
+
+/**
+	Constructor
+	@param project Project this page is editing.
+	@param parent Parent QWidget
+*/
+ProjectConfigPage::ProjectConfigPage(QETProject *project, QWidget *parent) :
+	ConfigPage(parent),
+	project_(project)
+{
+}
+
+/**
+	Destructor
+*/
+ProjectConfigPage::~ProjectConfigPage() {
+}
+
+/**
+	@return the project being edited by this page
+*/
+QETProject *ProjectConfigPage::project() const {
+	return(project_);
+}
+
+/**
+	Set \a new_project as the project being edited by this page.
+	@param read_values True to read values from the project into widgets before setting them read only accordingly, false otherwise. Defaults to true.
+	@return the former project
+*/
+QETProject *ProjectConfigPage::setProject(QETProject *new_project, bool read_values) {
+	if (new_project == project_) return(project_);
+	
+	QETProject *former_project = project_;
+	project_ = new_project;
+	if (project_ && read_values) {
+		readValuesFromProject();
+		adjustReadOnly();
+	}
+	return(former_project);
+}
+
+/**
+	Apply the configuration after user input
+*/
+void ProjectConfigPage::applyConf() {
+	if (!project_ || project_ -> isReadOnly()) return;
+	applyProjectConf();
+}
+
+/**
+	Initialize the page by calling initWidgets() and initLayout(). Also call
+	readValuesFromProject() and adjustReadOnly() if a non-zero project has been
+	set. Typically, you should call this function in your subclass constructor.
+*/
+void ProjectConfigPage::init() {
+	initWidgets();
+	initLayout();
+	if (project_) {
+		readValuesFromProject();
+		adjustReadOnly();
+	}
+}
+
+/**
+	Constructor
+	@param project Project this page is editing.
+	@param parent Parent QWidget
+*/
+ProjectMainConfigPage::ProjectMainConfigPage(QETProject *project, QWidget *parent) :
+	ProjectConfigPage(project, parent)
+{
+	init();
+}
+
+/**
+	Destructor
+*/
+ProjectMainConfigPage::~ProjectMainConfigPage() {
+}
+
+/**
+	@return the title for this page
+*/
+QString ProjectMainConfigPage::title() const {
+	return(tr("G\351n\351ral", "configuration page title"));
+}
+
+/**
+	@return the icon for this page
+*/
+QIcon ProjectMainConfigPage::icon() const {
+	return(QET::Icons::Settings);
+}
+
+/**
+	Apply the configuration after user input
+*/
+void ProjectMainConfigPage::applyProjectConf() {
+	bool modified_project = false;
+	
+	QString new_title = title_value_ -> text();
+	if (project_ -> title() != new_title) {
+		project_ -> setTitle(new_title);
+		modified_project = true;
+	}
+	
+	DiagramContext new_properties = project_variables_ -> context();
+	if (project_ -> projectProperties() != new_properties) {
+		project_ -> setProjectProperties(new_properties);
+		modified_project = true;
+	}
+	
+	if (modified_project) {
+		project_ -> setModified(true);
+	}
+}
+
+/**
+	@return the project title entered by the user
+*/
+QString ProjectMainConfigPage::projectTitle() const {
+	return(title_value_ -> text());
+}
+
+/**
+	Initialize widgets displayed by the page.
+*/
+void ProjectMainConfigPage::initWidgets() {
+	title_label_ = new QLabel(tr("Titre du projet\240:", "label when configuring"));
+	title_value_ = new QLineEdit();
+	title_information_ = new QLabel(tr("Ce titre sera disponible pour tous les sch\351mas de ce projet en tant que %projecttitle.", "informative label"));
+	project_variables_label_ = new QLabel(
+		tr(
+			"Vous pouvez d\351finir ci-dessous des propri\351t\351s personnalis\351es qui seront disponibles pour tous les sch\351mas de ce projet (typiquement pour les cartouches).",
+			 "informative label"
+		)
+	);
+	project_variables_label_ -> setWordWrap(true);
+	project_variables_ = new DiagramContextWidget();
+	project_variables_ -> setContext(DiagramContext());
+}
+
+/**
+	Initialize the layout of this page.
+*/
+void ProjectMainConfigPage::initLayout() {
+	QVBoxLayout *main_layout0 = new QVBoxLayout();
+	QHBoxLayout *title_layout0 = new QHBoxLayout();
+	title_layout0 -> addWidget(title_label_);
+	title_layout0 -> addWidget(title_value_);
+	main_layout0 -> addLayout(title_layout0);
+	main_layout0 -> addWidget(title_information_);
+	main_layout0 -> addSpacing(10);
+	main_layout0 -> addWidget(project_variables_label_);
+	main_layout0 -> addWidget(project_variables_);
+	setLayout(main_layout0);
+}
+
+/**
+	Read properties from the edited project then fill widgets with them.
+*/
+void ProjectMainConfigPage::readValuesFromProject() {
+	title_value_ -> setText(project_ -> title());
+	project_variables_ -> setContext(project_ -> projectProperties());
+}
+
+/**
+	Set the content of this page read only if the project is read only,
+	editable if the project is editable.
+ */
+void ProjectMainConfigPage::adjustReadOnly() {
+	bool is_read_only = project_ -> isReadOnly();
+	title_value_ -> setReadOnly(is_read_only);
+}
+
+/**
+	Constructor
+	@param project Project this page is editing.
+	@param parent Parent QWidget
+*/
+ProjectNewDiagramConfigPage::ProjectNewDiagramConfigPage(QETProject *project, QWidget *parent) :
+	ProjectConfigPage(project, parent)
+{
+	init();
+}
+
+/**
+	Destructor
+*/
+ProjectNewDiagramConfigPage::~ProjectNewDiagramConfigPage() {
+}
+
+/**
+	@return the title for this page
+*/
+QString ProjectNewDiagramConfigPage::title() const {
+	return(tr("Nouveau sch\351ma", "project configuration page title"));
+}
+
+/**
+	@return the icon for this page
+*/
+QIcon ProjectNewDiagramConfigPage::icon() const {
+	return(QET::Icons::NewDiagram);
+}
+
+/**
+	Apply the configuration after user input
+*/
+void ProjectNewDiagramConfigPage::applyProjectConf() {
+	bool modified_project = false;
+	
+	BorderProperties new_border_prop = border_ -> borderProperties();
+	if (project_ -> defaultBorderProperties() != new_border_prop) {
+		project_ -> setDefaultBorderProperties(border_ -> borderProperties());
+		modified_project = true;
+	}
+	
+	TitleBlockProperties new_tbt_prop = titleblock_ -> titleBlockProperties();
+	if (project_ -> defaultTitleBlockProperties() != new_tbt_prop) {
+		project_ -> setDefaultTitleBlockProperties(titleblock_ -> titleBlockProperties());
+		modified_project = true;
+	}
+	
+	ConductorProperties new_conductor_prop = conductor_ -> conductorProperties();
+	if (project_ -> defaultConductorProperties() != new_conductor_prop) {
+		project_ -> setDefaultConductorProperties(conductor_ -> conductorProperties());
+		modified_project = true;
+	}
+	
+	if (modified_project) {
+		project_ -> setModified(modified_project);
+	}
+}
+
+/**
+	Initialize widgets displayed by the page.
+*/
+void ProjectNewDiagramConfigPage::initWidgets() {
+	informative_label_ = new QLabel(
+		tr(
+			"Propri\351t\351s \340 utiliser lors de l'ajout d'un nouveau sch\351ma au projet :",
+			"explicative label"
+		)
+	);
+	border_ = new BorderPropertiesWidget(BorderProperties()); 
+	titleblock_ = new TitleBlockPropertiesWidget(TitleBlockProperties(), true);
+	conductor_ = new ConductorPropertiesWidget();
+	conductor_ -> setContentsMargins(0, 0, 0, 0);
+}
+
+/**
+	Initialize the layout of this page.
+*/
+void ProjectNewDiagramConfigPage::initLayout() {
+	// put border properties above title block properties
+	QVBoxLayout *vlayout2 = new QVBoxLayout();
+	vlayout2 -> addWidget(border_);
+	vlayout2 -> addWidget(titleblock_);
+	vlayout2 -> setSpacing(5);
+	
+	// add conductor properties on the right
+	QHBoxLayout *hlayout1 = new QHBoxLayout();
+	hlayout1 -> addLayout(vlayout2);
+	hlayout1 -> addWidget(conductor_);
+	hlayout1 -> setAlignment(conductor_, Qt::AlignTop);
+	
+	// add the informative label above previous widgets
+	QVBoxLayout *vlayout1 = new QVBoxLayout();
+	vlayout1 -> addWidget(informative_label_);
+	vlayout1 -> addLayout(hlayout1);
+	vlayout1 -> addStretch();
+	setLayout(vlayout1);
+}
+
+/**
+	Read properties from the edited project then fill widgets with them.
+*/
+void ProjectNewDiagramConfigPage::readValuesFromProject() {
+	border_ -> setEditedBorder(project_ -> defaultBorderProperties());
+	conductor_ -> setConductorProperties(project_ -> defaultConductorProperties());
+	titleblock_ -> setTitleBlockProperties(project_ -> defaultTitleBlockProperties());
+}
+
+/**
+	Set the content of this page read only if the project is read only,
+	editable if the project is editable.
+ */
+void ProjectNewDiagramConfigPage::adjustReadOnly() {
+	bool is_read_only = project_ -> isReadOnly();
+	border_ -> setReadOnly(is_read_only);
+	titleblock_ -> setReadOnly(is_read_only);
+	conductor_ -> setReadOnly(is_read_only);
+}

Added: trunk/sources/projectconfigpages.h
===================================================================
--- trunk/sources/projectconfigpages.h	                        (rev 0)
+++ trunk/sources/projectconfigpages.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,130 @@
+#ifndef PROJECTCONFIGPAGES_H
+#define PROJECTCONFIGPAGES_H
+#include "configpage.h"
+class QLabel;
+class QLineEdit;
+class QETProject;
+class BorderPropertiesWidget;
+class TitleBlockPropertiesWidget;
+class ConductorPropertiesWidget;
+class DiagramContextWidget;
+
+/**
+	This class, derived from ConfigPage, aims at providing the basic skeleton
+	for a project configuration page.
+*/
+class ProjectConfigPage : public ConfigPage {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	ProjectConfigPage(QETProject *, QWidget * = 0);
+	virtual ~ProjectConfigPage();
+	private:
+	ProjectConfigPage(const ProjectConfigPage &);
+	
+	// methods
+	public:
+	virtual QETProject *project() const;
+	virtual QETProject *setProject(QETProject *project, bool = true);
+	virtual void applyConf();
+	/**
+		Apply configuration to the project after user input. This method is
+		automatically called when the ConfigDialog is validated, and only if the
+		project is both non-zero and not read-only.
+	*/
+	virtual void applyProjectConf() = 0;
+	
+	protected:
+	virtual void init();
+	/**
+		Use this pure virtual method to initialize your page widgets.
+	*/
+	virtual void initWidgets() = 0;
+	/**
+		Use this pure virtual method to initialize your page layout. This method is
+		always called after initWidgets().
+	*/
+	virtual void initLayout() = 0;
+	/**
+		Use this pure virtual method to fill widgets with project values.
+	*/
+	virtual void readValuesFromProject()  = 0;
+	/**
+		Use this pure virtual method to adjust the "read only" state of your page
+		widgets according to the currently edited project.
+	*/
+	virtual void adjustReadOnly() = 0;
+	
+	// attributes
+	protected:
+	QETProject *project_; ///< Currently edited project
+};
+
+/**
+	This page enables users to configure the main properties of a project.
+*/
+class ProjectMainConfigPage : public ProjectConfigPage {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	ProjectMainConfigPage(QETProject *, QWidget * = 0);
+	virtual ~ProjectMainConfigPage();
+	private:
+	ProjectMainConfigPage(const ProjectMainConfigPage &);
+	
+	// methods
+	public:
+	QString title() const;
+	QIcon icon() const;
+	void applyProjectConf();
+	QString projectTitle() const;
+	
+	protected:
+	void initWidgets();
+	void initLayout();
+	void readValuesFromProject();
+	void adjustReadOnly();
+	
+	// attributes
+	protected:
+	QLabel *title_label_;
+	QLineEdit *title_value_;
+	QLabel *title_information_;
+	QLabel *project_variables_label_;
+	DiagramContextWidget *project_variables_;
+};
+
+/**
+	This page enables users to configure the default properties of diagrams
+	newly added to the edited project.
+*/
+class ProjectNewDiagramConfigPage : public ProjectConfigPage {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	ProjectNewDiagramConfigPage(QETProject *, QWidget * = 0);
+	virtual ~ProjectNewDiagramConfigPage();
+	private:
+	ProjectNewDiagramConfigPage(const ProjectNewDiagramConfigPage &);
+	
+	// methods
+	public:
+	QString title() const;
+	QIcon icon() const;
+	void applyProjectConf();
+	
+	protected:
+	void initWidgets();
+	void initLayout();
+	void readValuesFromProject();
+	void adjustReadOnly();
+	
+	// attributes
+	private:
+	QLabel *informative_label_;
+	BorderPropertiesWidget *border_;
+	TitleBlockPropertiesWidget *titleblock_;
+	ConductorPropertiesWidget *conductor_;
+};
+
+#endif

Added: trunk/sources/projectview.cpp
===================================================================
--- trunk/sources/projectview.cpp	                        (rev 0)
+++ trunk/sources/projectview.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,918 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "projectview.h"
+#include "qetproject.h"
+#include "configdialog.h"
+#include "closediagramsdialog.h"
+#include "projectconfigpages.h"
+#include "diagramview.h"
+#include "diagram.h"
+#include "diagramprintdialog.h"
+#include "exportdialog.h"
+#include "qetapp.h"
+#include "qettabwidget.h"
+#include "qetelementeditor.h"
+#include "interactivemoveelementshandler.h"
+#include "borderpropertieswidget.h"
+#include "titleblockpropertieswidget.h"
+#include "conductorpropertieswidget.h"
+#include "qeticons.h"
+#include "qetmessagebox.h"
+#include "qettabbar.h"
+#include "qettemplateeditor.h"
+
+/**
+	Constructeur
+	@param project projet a visualiser
+	@param parent Widget parent
+*/
+ProjectView::ProjectView(QETProject *project, QWidget *parent) :
+	QWidget(parent),
+	project_(0)
+{
+	initActions();
+	initWidgets();
+	initLayout();
+	
+	setProject(project);
+}
+
+/**
+	Destructeur
+	Supprime les DiagramView embarquees
+*/
+ProjectView::~ProjectView() {
+	// qDebug() << "Suppression du ProjectView" << ((void *)this);
+	foreach(int id, diagram_ids_.keys()) {
+		DiagramView *diagram_view = diagram_ids_.take(id);
+		delete diagram_view;
+	}
+}
+
+/**
+	@return le projet actuellement visualise par le ProjectView
+*/
+QETProject *ProjectView::project() {
+	return(project_);
+}
+
+/**
+	Definit le projet visualise par le ProjectView. Ne fait rien si le projet a
+	deja ete defini.
+	@param project projet a visualiser
+*/
+void ProjectView::setProject(QETProject *project) {
+	if (!project_) {
+		project_ = project;
+		connect(project_, SIGNAL(projectTitleChanged(QETProject *, const QString &)), this, SLOT(updateWindowTitle()));
+		connect(project_, SIGNAL(projectModified    (QETProject *, bool)),            this, SLOT(updateWindowTitle()));
+		connect(project_, SIGNAL(readOnlyChanged    (QETProject *, bool)),            this, SLOT(adjustReadOnlyState()));
+		adjustReadOnlyState();
+		loadDiagrams();
+	}
+}
+
+/**
+	@return la liste des schemas ouverts dans le projet
+*/
+QList<DiagramView *> ProjectView::diagrams() const {
+	return(diagrams_);
+}
+
+/**
+	@return A list containing child diagrams matching provided \a options.
+*/
+QList<Diagram *> ProjectView::getDiagrams(ProjectSaveOptions options) {
+	QList<Diagram *> selection;
+	if ((options & AllDiagrams) == AllDiagrams) {
+		selection << project_ -> diagrams();
+	} else {
+		Diagram *current = 0;
+		if (DiagramView *view = currentDiagram()) {
+			current = view -> diagram();
+		}
+		if (options & CurrentDiagram) {
+			if (current) selection << current;
+		} else if (options & AllDiagramsButCurrent) {
+			selection = project_ -> diagrams();
+			selection.removeOne(current);
+		}
+	}
+	
+	if (options & ModifiedDiagramsOnly) {
+		foreach (Diagram *diagram, selection) {
+			if (!diagram -> undoStack().isClean() || !diagram -> wasWritten()) continue;
+			selection.removeOne(diagram);
+		}
+	}
+	
+	return(selection);
+}
+
+/**
+	@return le schema actuellement active
+*/
+DiagramView *ProjectView::currentDiagram() const {
+	int current_tab_index = tabs_ -> currentIndex();
+	return(diagram_ids_[current_tab_index]);
+}
+
+/**
+	Gere la fermeture du schema.
+	@param qce Le QCloseEvent decrivant l'evenement
+*/
+void ProjectView::closeEvent(QCloseEvent *qce) {
+	bool can_close_project = tryClosing();
+	if (can_close_project) {
+		qce -> accept();
+		emit(projectClosed(this));
+	} else {
+		qce -> ignore();
+	}
+}
+
+/**
+	Cette methode essaye de fermer successivement les editeurs d'element puis
+	les schemas du projet. L'utilisateur peut refuser de fermer un schema ou un
+	editeur.
+	@return true si tout a pu etre ferme, false sinon
+	@see tryClosingElementEditors()
+	@see tryClosingDiagrams()
+*/
+bool ProjectView::tryClosing() {
+	if (!project_) return(true);
+	
+	// First step: require external editors closing -- users may either cancel
+	// the whole closing process or save (and therefore add) content into this
+	// project. Of course, they may also discard them.
+	if (!tryClosingElementEditors()) {
+		return(false);
+	}
+	
+	// Check how different the current situation is from a brand new, untouched project
+	if (project_ -> filePath().isEmpty() && !project_ -> projectWasModified()) {
+		return(true);
+	}
+	
+	// Second step: users are presented with a dialog that enables them to
+	// choose whether they want to:
+	//   - cancel the closing process,
+	//   - discard all modifications,
+	//   - or specify what is to be saved, i.e. they choose whether they wants to
+	// save/give up/remove diagrams considered as modified.
+	int user_input = tryClosingDiagrams();
+	if (user_input == QDialogButtonBox::RejectRole) {
+		return(false); // the closing process was cancelled
+	} else if (user_input == QDialogButtonBox::DestructiveRole) {
+		return(true); // all modifications were discarded
+	}
+	
+	// Check how different the current situation is from a brand new, untouched project (yes , again)
+	if (project_ -> filePath().isEmpty() && !project_ -> projectWasModified()) {
+		return(true);
+	}
+	
+	if (project_ -> filePath().isEmpty()) {
+		QString filepath = askUserForFilePath();
+		if (filepath.isEmpty()) return(false); // users may cancel the closing
+	}
+	QETResult result = project_ -> write();
+	updateWindowTitle();
+	if (!result.isOk()) emit(errorEncountered(result.errorMessage()));
+	return(result.isOk());
+}
+
+/**
+	Un projet comporte des elements integres. Cette methode ferme les editeurs
+	d'elements associes a ce projet. L'utilisateur peut refuser la fermeture
+	d'un editeur d'element.
+	@return true si tous les editeurs d'element ont pu etre fermes, false sinon
+*/
+bool ProjectView::tryClosingElementEditors() {
+	if (!project_) return(true);
+	/*
+		La QETApp permet d'acceder rapidement aux editeurs d'element
+		editant un element du projet.
+	*/
+	QList<QETElementEditor *> editors = QETApp::elementEditors(project_);
+	foreach(QETElementEditor *editor, editors) {
+		if (!editor -> close()) return(false);
+	}
+	
+	QList<QETTitleBlockTemplateEditor *> template_editors = QETApp::titleBlockTemplateEditors(project_);
+	foreach(QETTitleBlockTemplateEditor *template_editor, template_editors) {
+		if (!template_editor -> close()) return(false);
+	}
+	return(true);
+}
+
+/**
+	Un projet comporte 0 a n schemas.
+	Cette methode parcourt les schemas et demande a l'utilisateur s'il veut
+	enregistrer les schemas modifies afin de les fermer. L'utilisateur peut
+	refuser la fermeture d'un schema.
+	Si un schema a ete ajoute sans jamais etre modifie, cette methode demande a
+	l'utilisateur s'il souhaite l'enlever.
+	@return true si tous les schemas peuvent etre fermes, false sinon
+*/
+int ProjectView::tryClosingDiagrams() {
+	if (!project_) return(QDialogButtonBox::DestructiveRole);
+	
+	bool project_modified = project_ -> projectOptionsWereModified();
+	QList<Diagram *> modified_diagrams = getDiagrams(AllDiagrams | ModifiedDiagramsOnly);
+	bool has_filepath = !project_ -> filePath().isEmpty();
+	if (!project_modified && !modified_diagrams.count() && has_filepath) {
+		// nothing was modified, and we have a filepath, i.e. everything was already
+		// saved, i.e we can close the project right now
+		return(QDialogButtonBox::DestructiveRole);
+	}
+	
+	CloseDiagramsDialog close_dialog(modified_diagrams, this);
+	if (!project_ -> title().isEmpty()) {
+		close_dialog.setWindowTitle(
+			QString(
+				tr(
+					"Fermer le projet \"%1\"",
+					"project closing dialog title -- %1 is a project title"
+				)
+			).arg(project_ -> title())
+		);
+	}
+	connect(&close_dialog, SIGNAL(showDiagram(Diagram*)), this, SLOT(showDiagram(Diagram*)));
+	if (close_dialog.exec() == QDialog::Rejected) {
+		return(QDialogButtonBox::RejectRole);
+	}
+	
+	if (close_dialog.answer() == QDialogButtonBox::AcceptRole) {
+		// save diagrams the user marked as to be saved
+		QList<Diagram *> to_save = close_dialog.diagramsByAction(CloseDiagramsDialog::Save);
+		saveDiagrams(to_save);
+		
+		// remove diagrams the user marked as to be removed
+		QList<Diagram *> to_close = close_dialog.diagramsByAction(CloseDiagramsDialog::Remove);
+		foreach (Diagram *diagram, to_close) {
+			removeDiagram(diagram);
+		}
+	}
+	
+	return(close_dialog.answer());
+}
+
+/**
+	Ask the user to provide a file path in which the currently edited project will
+	be saved.
+	@param assign When true, assign the provided filepath to the project through
+	setFilePath(). Defaults to true.
+	@return the file path, or an empty string if none were provided
+*/
+QString ProjectView::askUserForFilePath(bool assign) {
+	// ask the user for a filepath in order to save the project
+	QString filepath = QFileDialog::getSaveFileName(
+		this,
+		tr("Enregistrer sous", "dialog title"),
+		project_ -> currentDir(),
+		tr("Sch\351ma QElectroTech (*.qet)", "filetypes allowed when saving a diagram file")
+	);
+	
+	// if no filepath is provided, return an empty string
+	if (filepath.isEmpty()) return(filepath);
+	
+	// if the name does not end with the .qet extension, append it
+	if (!filepath.endsWith(".qet", Qt::CaseInsensitive)) filepath += ".qet";
+	
+	if (assign) {
+		// assign the provided filepath to the currently edited project
+		project_ -> setFilePath(filepath);
+	}
+	
+	return(filepath);
+}
+
+/**
+	@return the QETResult object to be returned when it appears this project
+	view is not associated to any project.
+*/
+QETResult ProjectView::noProjectResult() const {
+	QETResult no_project(tr("aucun projet affich\351", "error message"), false);
+	return(no_project);
+}
+
+/**
+	Ajoute un nouveau schema au ProjectView
+*/
+void ProjectView::addNewDiagram() {
+	if (project_ -> isReadOnly()) return;
+	
+	Diagram *new_diagram = project_ -> addNewDiagram();
+	DiagramView *new_diagram_view = new DiagramView(new_diagram);
+	addDiagram(new_diagram_view);
+	showDiagram(new_diagram_view);
+}
+
+/**
+	Ajoute un schema au ProjectView
+	@param diagram Schema a ajouter
+*/
+void ProjectView::addDiagram(DiagramView *diagram) {
+	if (!diagram) return;
+	
+	// verifie que le schema n'est pas deja present dans le projet
+	if (diagram_ids_.values().contains(diagram)) return;
+	
+	// ajoute un nouvel onglet pour le nouveau schema
+	tabs_ -> addTab(diagram, QET::Icons::Diagram, diagram -> title());
+	diagram -> setFrameStyle(QFrame::Plain | QFrame::NoFrame);
+	diagrams_ << diagram;
+	rebuildDiagramsMap();
+	connect(diagram, SIGNAL(titleChanged(DiagramView *, const QString &)), this, SLOT(updateTabTitle(DiagramView *, const QString &)));
+	connect(diagram, SIGNAL(findElementRequired(const ElementsLocation &)), this, SIGNAL(findElementRequired(const ElementsLocation &)));
+	connect(diagram, SIGNAL(editElementRequired(const ElementsLocation &)), this, SIGNAL(editElementRequired(const ElementsLocation &)));
+	connect(diagram, SIGNAL(editTitleBlockTemplate(const QString &, bool)), this, SLOT(editTitleBlockTemplateRequired(const QString &, bool)));
+	
+	// signale l'ajout du schema
+	emit(diagramAdded(diagram));
+}
+
+/**
+	Enleve un schema du ProjectView
+	@param diagram_view Schema a enlever
+*/
+void ProjectView::removeDiagram(DiagramView *diagram_view) {
+	if (!diagram_view) return;
+	if (project_ -> isReadOnly()) return;
+	
+	// verifie que le schema est bien present dans le projet
+	if (!diagram_ids_.values().contains(diagram_view)) return;
+	
+	// demande confirmation a l'utilisateur
+	if (
+		diagram_view -> diagram() -> wasWritten() ||\
+		!diagram_view -> diagram() -> undoStack().isClean()
+	) {
+		int answer = QET::MessageBox::question(
+			this,
+			tr("Supprimer le sch\351ma ?", "message box title"),
+			tr("\312tes-vous s\373r de vouloir supprimer ce sch\351ma du projet ? Ce changement est irr\351versible.", "message box content"),
+			QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+			QMessageBox::No
+		);
+		if (answer != QMessageBox::Yes) {
+			return;
+		}
+	}
+	
+	// notifie le reste du monde que le DiagramView va disparaitre
+	emit(diagramAboutToBeRemoved(diagram_view));
+	
+	// enleve le DiagramView des onglets
+	int diagram_tab_id = diagram_ids_.key(diagram_view);
+	tabs_ -> removeTab(diagram_tab_id);
+	diagrams_.removeAll(diagram_view);
+	rebuildDiagramsMap();
+	
+	// supprime le DiagramView, puis le Diagram
+	project_ -> removeDiagram(diagram_view -> diagram());
+	delete diagram_view;
+	
+	// signale le retrait du schema
+	emit(diagramRemoved(diagram_view));
+	
+	// rend definitif le retrait du schema
+	project_ -> write();
+}
+
+/**
+	Enleve un schema du ProjectView
+	@param diagram Schema a enlever
+*/
+void ProjectView::removeDiagram(Diagram *diagram) {
+	if (!diagram) return;
+	
+	if (DiagramView *diagram_view = findDiagram(diagram)) {
+		removeDiagram(diagram_view);
+	}
+}
+
+/**
+	Active l'onglet adequat pour afficher le schema passe en parametre
+	@param diagram Schema a afficher
+*/
+void ProjectView::showDiagram(DiagramView *diagram) {
+	if (!diagram) return;
+	tabs_ -> setCurrentWidget(diagram);
+}
+
+/**
+	Active l'onglet adequat pour afficher le schema passe en parametre
+	@param diagram Schema a afficher
+*/
+void ProjectView::showDiagram(Diagram *diagram) {
+	if (!diagram) return;
+	if (DiagramView *diagram_view = findDiagram(diagram)) {
+		tabs_ -> setCurrentWidget(diagram_view);
+	}
+}
+
+/**
+	Enable the user to edit properties of the current project through a
+	configuration dialog.
+*/
+void ProjectView::editProjectProperties() {
+	if (!project_) return;
+	
+	ConfigDialog properties_dialog(parentWidget());
+	properties_dialog.setWindowTitle(tr("Propri\351t\351s du projet", "window title"));
+	properties_dialog.addPage(new ProjectMainConfigPage(project_));
+	properties_dialog.addPage(new ProjectNewDiagramConfigPage(project_));
+	properties_dialog.exec();
+}
+
+/**
+	Edite les proprietes du schema courant
+*/
+void ProjectView::editCurrentDiagramProperties() {
+	editDiagramProperties(currentDiagram());
+}
+
+/**
+	Edite les proprietes du schema diagram_view
+*/
+void ProjectView::editDiagramProperties(DiagramView *diagram_view) {
+	if (!diagram_view) return;
+	showDiagram(diagram_view);
+	diagram_view -> editDiagramProperties();
+}
+
+/**
+	Edite les proprietes du schema diagram
+*/
+void ProjectView::editDiagramProperties(Diagram *diagram) {
+	editDiagramProperties(findDiagram(diagram));
+}
+
+/**
+	Deplace le schema diagram_view vers le haut / la gauche
+*/
+void ProjectView::moveDiagramUp(DiagramView *diagram_view) {
+	if (!diagram_view) return;
+	
+	int diagram_view_position = diagram_ids_.key(diagram_view);
+	if (!diagram_view_position) {
+		// le schema est le premier du projet
+		return;
+	}
+	tabs_ -> moveTab(diagram_view_position, diagram_view_position - 1);
+}
+
+/**
+	Deplace le schema diagram vers le haut / la gauche
+*/
+void ProjectView::moveDiagramUp(Diagram *diagram) {
+	moveDiagramUp(findDiagram(diagram));
+}
+
+/**
+	Deplace le schema diagram_view vers le bas / la droite
+*/
+void ProjectView::moveDiagramDown(DiagramView *diagram_view) {
+	if (!diagram_view) return;
+	
+	int diagram_view_position = diagram_ids_.key(diagram_view);
+	if (diagram_view_position + 1 == diagram_ids_.count()) {
+		// le schema est le dernier du projet
+		return;
+	}
+	tabs_ -> moveTab(diagram_view_position, diagram_view_position + 1);
+}
+
+/**
+	Deplace le schema diagram vers le bas / la droite
+*/
+void ProjectView::moveDiagramDown(Diagram *diagram) {
+	moveDiagramDown(findDiagram(diagram));
+}
+
+/**
+	Ce slot demarre un dialogue permettant a l'utilisateur de parametrer et de
+	lancer l'impression de toute ou partie du projet.
+*/
+void ProjectView::printProject() {
+	if (!project_) return;
+	
+	// transforme le titre du projet en nom utilisable pour le document
+	QString doc_name;
+	if (!(project_ -> title().isEmpty())) {
+		doc_name = project_ -> title();
+	} else if (!project_ -> filePath().isEmpty()) {
+		doc_name = QFileInfo(project_ -> filePath()).baseName();
+	}
+	doc_name = QET::stringToFileName(doc_name);
+	if (doc_name.isEmpty()) {
+		doc_name = tr("projet", "string used to generate a filename");
+	}
+	
+	// recupere le dossier contenant le fichier courant
+	QString dir_path = project_ -> currentDir();
+	
+	// determine un chemin pour le pdf / ps
+	QString file_name = QDir::toNativeSeparators(QDir::cleanPath(dir_path + "/" + doc_name));
+	
+	DiagramPrintDialog print_dialog(project_, this);
+	print_dialog.setDocName(doc_name);
+	print_dialog.setFileName(file_name);
+	print_dialog.exec();
+}
+
+/**
+	Exporte le schema.
+*/
+void ProjectView::exportProject() {
+	if (!project_) return;
+	
+	ExportDialog ed(project_, parentWidget());
+#ifdef Q_WS_MAC
+	ed.setWindowFlags(Qt::Sheet);
+#endif
+	ed.exec();
+}
+
+/**
+	Save project properties along with all modified diagrams.
+	@see filePath()
+	@see setFilePath()
+	@return a QETResult object reflecting the situation
+*/
+QETResult ProjectView::save() {
+	return(doSave(AllDiagrams | ModifiedDiagramsOnly));
+}
+
+/**
+	Ask users for a filepath in order to save the project.
+	@param options May be used to specify what should be saved; defaults to
+	all modified diagrams.
+	@return a QETResult object reflecting the situation; note that a valid
+	QETResult object is returned if the operation was cancelled.
+*/
+QETResult ProjectView::saveAs(ProjectSaveOptions options) {
+	if (!project_) return(noProjectResult());
+	
+	QString filepath = askUserForFilePath();
+	if (filepath.isEmpty()) return(QETResult());
+	return(doSave(options));
+}
+
+/**
+	Save the current diagram.
+	@return A QETResult object reflecting the situation.
+*/
+QETResult ProjectView::saveCurrentDiagram() {
+	return(doSave(CurrentDiagram));
+}
+
+/**
+	Save project content according to \a options, then write the project file. May
+	call saveAs if no filepath was provided before.
+	@param options May be used to specify what should be saved (e.g. modified
+	diagrams only).
+	@return a QETResult object reflecting the situation; note that a valid
+	QETResult object is returned if the operation was cancelled.
+*/
+QETResult ProjectView::doSave(ProjectSaveOptions options) {
+	if (!project_) return(noProjectResult());
+	
+	if (project_ -> filePath().isEmpty()) {
+		// The project has not been saved to a file yet,
+		// so save() actually means saveAs().
+		return(saveAs(options));
+	}
+	
+	// look for diagrams matching the required save options
+	saveDiagrams(getDiagrams(options));
+	
+	// write to file
+	QETResult result = project_ -> write();
+	updateWindowTitle();
+	return(result);
+}
+
+/**
+	Save \a diagrams without emitting the written() signal and without writing
+	the project file itself.
+*/
+void ProjectView::saveDiagrams(const QList<Diagram *> &diagrams) {
+	foreach (Diagram *diagram, diagrams) {
+		// Diagram::write() emits the written() signal, which is connected
+		// to QETProject::write() through QETProject::componentWritten().
+		// We do not want to write the project immediately, so we block
+		// this signal.
+		diagram -> blockSignals(true);
+		diagram -> write();
+		diagram -> blockSignals(false);
+	}
+}
+
+/**
+	Allow the user to clean the project, which includes:
+	  * deleting unused title block templates
+	  * deleting unused elements
+	  * deleting empty categories
+	@return an integer value above zero if elements and/or categories were
+	cleaned.
+*/
+int ProjectView::cleanProject() {
+	if (!project_) return(0);
+	
+	// s'assure que le schema n'est pas en lecture seule
+	if (project_ -> isReadOnly()) {
+		QET::MessageBox::critical(
+			this,
+			tr("Projet en lecture seule", "message box title"),
+			tr("Ce projet est en lecture seule. Il n'est donc pas possible de le nettoyer.", "message box content")
+		);
+		return(0);
+	}
+	
+	// construit un petit dialogue pour parametrer le nettoyage
+	QCheckBox *clean_tbt        = new QCheckBox(tr("Supprimer les mod\350les de cartouche inutilis\351s dans le projet"));
+	QCheckBox *clean_elements   = new QCheckBox(tr("Supprimer les \351l\351ments inutilis\351s dans le projet"));
+	QCheckBox *clean_categories = new QCheckBox(tr("Supprimer les cat\351gories vides"));
+	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	
+	clean_tbt        -> setChecked(true);
+	clean_elements   -> setChecked(true);
+	clean_categories -> setChecked(true);
+	
+	QDialog clean_dialog(parentWidget());
+#ifdef Q_WS_MAC
+	clean_dialog.setWindowFlags(Qt::Sheet);
+#endif
+	
+	clean_dialog.setWindowTitle(tr("Nettoyer le projet", "window title"));
+	QVBoxLayout *clean_dialog_layout = new QVBoxLayout();
+	clean_dialog_layout -> addWidget(clean_tbt);
+	clean_dialog_layout -> addWidget(clean_elements);
+	clean_dialog_layout -> addWidget(clean_categories);
+	clean_dialog_layout -> addWidget(buttons);
+	clean_dialog.setLayout(clean_dialog_layout);
+	
+	connect(buttons, SIGNAL(accepted()), &clean_dialog, SLOT(accept()));
+	connect(buttons, SIGNAL(rejected()), &clean_dialog, SLOT(reject()));
+	
+	int clean_count = 0;
+	if (clean_dialog.exec() == QDialog::Accepted) {
+		if (clean_tbt -> isChecked()) {
+			project_ -> cleanUnusedTitleBlocKTemplates();
+		}
+		if (clean_elements -> isChecked()) {
+			InteractiveMoveElementsHandler *handler = new InteractiveMoveElementsHandler(this);
+			project_ -> cleanUnusedElements(handler);
+			delete handler;
+			++ clean_count;
+		}
+		if (clean_categories -> isChecked()) {
+			InteractiveMoveElementsHandler *handler = new InteractiveMoveElementsHandler(this);
+			project_ -> cleanEmptyCategories(handler);
+			delete handler;
+			++ clean_count;
+		}
+	}
+	return(clean_count);
+}
+
+/**
+	Initialize actions for this widget.
+*/
+void ProjectView::initActions() {
+	add_new_diagram_ = new QAction(QET::Icons::Add, tr("Ajouter un sch\351ma"), this);
+	connect(add_new_diagram_, SIGNAL(triggered()), this, SLOT(addNewDiagram()));
+}
+
+/**
+	Initialize child widgets for this widget.
+*/
+void ProjectView::initWidgets() {
+	setObjectName("ProjectView");
+	setWindowIcon(QET::Icons::ProjectFile);
+	
+	// initialize the "fallback" widget
+	fallback_widget_ = new QWidget();
+	fallback_label_ = new QLabel(
+		tr(
+			"Ce projet ne contient aucun sch\351ma",
+			"label displayed when a project contains no diagram"
+		)
+	);
+	fallback_label_ -> setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
+	
+	// initialize tabs
+	tabs_ = new QETTabWidget();
+	tabs_ -> setMovable(true);
+	
+	QToolButton *add_new_diagram_button = new QToolButton();
+	add_new_diagram_button -> setDefaultAction(add_new_diagram_);
+	add_new_diagram_button -> setAutoRaise(true);
+	tabs_ -> setCornerWidget(add_new_diagram_button, Qt::TopRightCorner);
+	
+	connect(tabs_, SIGNAL(currentChanged(int)),   this, SLOT(tabChanged(int)));
+	connect(tabs_, SIGNAL(tabDoubleClicked(int)), this, SLOT(tabDoubleClicked(int)));
+	connect(tabs_, SIGNAL(firstTabInserted()),    this, SLOT(firstTabInserted()));
+	connect(tabs_, SIGNAL(lastTabRemoved()),      this, SLOT(lastTabRemoved()));
+	connect(tabs_, SIGNAL(tabMoved(int, int)),    this, SLOT(tabMoved(int, int)));
+	
+	fallback_widget_ -> setVisible(false);
+	tabs_ -> setVisible(false);
+}
+
+/**
+	Initialize layout for this widget.
+*/
+void ProjectView::initLayout() {
+	QVBoxLayout *fallback_widget_layout_ = new QVBoxLayout(fallback_widget_);
+	fallback_widget_layout_ -> addWidget(fallback_label_);
+	
+	layout_ = new QVBoxLayout(this);
+#ifdef Q_WS_MAC
+	layout_ -> setContentsMargins(0, 8, 0, 0);
+#else
+	layout_ -> setContentsMargins(0, 0, 0, 0);
+#endif
+	layout_ -> setSpacing(0);
+	layout_ -> addWidget(fallback_widget_);
+	layout_ -> addWidget(tabs_);
+}
+
+/**
+	Charge les schemas du projet
+*/
+void ProjectView::loadDiagrams() {
+	if (!project_) return;
+	
+	setDisplayFallbackWidget(project_ -> diagrams().isEmpty());
+	
+	foreach(Diagram *diagram, project_ -> diagrams()) {
+		DiagramView *sv = new DiagramView(diagram);
+		addDiagram(sv);
+	}
+}
+
+/**
+	Met a jour le titre du ProjectView
+*/
+void ProjectView::updateWindowTitle() {
+	QString title;
+	if (project_) {
+		title = project_ -> pathNameTitle();
+	} else {
+		title = tr("Projet", "window title for a project-less ProjectView");
+	}
+	setWindowTitle(title);
+}
+
+/**
+	Effectue les actions necessaires lorsque le projet visualise entre ou sort
+	du mode lecture seule.
+*/
+void ProjectView::adjustReadOnlyState() {
+	bool editable = !(project_ -> isReadOnly());
+	
+	// prevent users from moving existing diagrams
+	tabs_ -> setMovable(editable);
+	// prevent users from adding new diagrams
+	add_new_diagram_ -> setEnabled(editable);
+	
+	// on met a jour le titre du widget, qui reflete l'etat de lecture seule
+	updateWindowTitle();
+}
+
+/**
+	Met a jour le titre d'un onglet
+	@param diagram Schema
+	@param diagram_title Titre du schema
+*/
+void ProjectView::updateTabTitle(DiagramView *diagram, const QString &diagram_title) {
+	int diagram_tab_id = diagram_ids_.key(diagram, -1);
+	if (diagram_tab_id != -1) {
+		tabs_ -> setTabText(diagram_tab_id, diagram_title);
+	}
+}
+
+/**
+	@param from Index de l'onglet avant le deplacement
+	@param to   Index de l'onglet apres le deplacement
+*/
+void ProjectView::tabMoved(int from, int to) {
+	if (!project_) return;
+	
+	// signale au QETProject le changement d'ordre des schemas
+	project_ -> diagramOrderChanged(from, to);
+	
+	// reconstruit la liste associant les index des onglets aux schemas
+	rebuildDiagramsMap();
+	
+	// emet un signal pour informer le reste du monde que l'ordre des schemas a change
+	emit(diagramOrderChanged(this, from, to));
+}
+
+/**
+	Require the edition of the \a template_name title blocke template.
+	@param template_name Name of the tempalte to be edited
+	@param duplicate If true, this methd will ask the user for a template name
+	in order to duplicate the \a template_name template
+*/
+void ProjectView::editTitleBlockTemplateRequired(const QString &template_name, bool duplicate) {
+	if (!project_) return;
+	emit(
+		editTitleBlockTemplate(
+			project_ -> embeddedTitleBlockTemplatesCollection() -> location(template_name),
+			duplicate
+		)
+	);
+}
+
+/**
+	@param diagram Schema a trouver
+	@return le DiagramView correspondant au schema passe en parametre, ou 0 si
+	le schema n'est pas trouve
+*/
+DiagramView *ProjectView::findDiagram(Diagram *diagram) {
+	foreach(DiagramView *diagram_view, diagrams()) {
+		if (diagram_view -> diagram() == diagram) {
+			return(diagram_view);
+		}
+	}
+	return(0);
+}
+
+/**
+	Reconstruit la map associant les index des onglets avec les DiagramView
+*/
+void ProjectView::rebuildDiagramsMap() {
+	// vide la map
+	diagram_ids_.clear();
+	
+	foreach(DiagramView *diagram_view, diagrams_) {
+		int dv_idx = tabs_ -> indexOf(diagram_view);
+		if (dv_idx == -1) continue;
+		diagram_ids_.insert(dv_idx, diagram_view);
+	}
+}
+
+/**
+	Gere les changements d'onglets
+	@param tab_id Index de l'onglet actif
+*/
+void ProjectView::tabChanged(int tab_id) {
+	emit(diagramActivated(diagram_ids_[tab_id]));
+}
+
+/**
+	Gere le double-clic sur un onglet : edite les proprietes du schema
+	@param tab_id Index de l'onglet concerne
+*/
+void ProjectView::tabDoubleClicked(int tab_id) {
+	// repere le schema concerne
+	DiagramView *diagram_view = diagram_ids_[tab_id];
+	if (!diagram_view) return;
+	
+	diagram_view -> editDiagramProperties();
+}
+
+/**
+	Gere le fait que le premier schema d'un projet soit insere
+*/
+void ProjectView::firstTabInserted() {
+	setDisplayFallbackWidget(false);
+}
+
+/**
+	Gere le fait que le dernier schema d'un projet soit enleve
+*/
+void ProjectView::lastTabRemoved() {
+	setDisplayFallbackWidget(true);
+}
+
+/**
+	@param fallback true pour afficher le widget de fallback, false pour
+	afficher les onglets.
+	Le widget de Fallback est le widget affiche lorsque le projet ne comporte
+	aucun schema.
+*/
+void ProjectView::setDisplayFallbackWidget(bool fallback) {
+	fallback_widget_ -> setVisible(fallback);
+	tabs_ -> setVisible(!fallback);
+}

Added: trunk/sources/projectview.h
===================================================================
--- trunk/sources/projectview.h	                        (rev 0)
+++ trunk/sources/projectview.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,135 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef PROJECT_VIEW_H
+#define PROJECT_VIEW_H
+#include <QtGui>
+#include "templatelocation.h"
+#include "qetresult.h"
+class QETProject;
+class DiagramView;
+class Diagram;
+class ElementsLocation;
+class QETTabWidget;
+/**
+	This class provides a widget displaying the diagrams of a particular
+	project using tabs.
+*/
+class ProjectView : public QWidget {
+	Q_OBJECT
+	
+	public:
+	enum ProjectSaveOption {
+		ModifiedDiagramsOnly = 1,
+		CurrentDiagram = 2,
+		AllDiagramsButCurrent = 4,
+		AllDiagrams = 6
+	};
+	Q_DECLARE_FLAGS(ProjectSaveOptions, ProjectSaveOption)
+	
+	
+	// constructors, destructor
+	public:
+	ProjectView(QETProject *, QWidget * = 0);
+	virtual ~ProjectView();
+	private:
+	ProjectView(const ProjectView &);
+	
+	// methods
+	public:
+	QETProject *project();
+	void setProject(QETProject *);
+	QList<DiagramView *> diagrams() const;
+	QList<Diagram *> getDiagrams(ProjectSaveOptions options);
+	DiagramView *currentDiagram() const;
+	void closeEvent(QCloseEvent *);
+	
+	public slots:
+	void addNewDiagram();
+	void addDiagram(DiagramView *);
+	void removeDiagram(DiagramView *);
+	void removeDiagram(Diagram *);
+	void showDiagram(DiagramView *);
+	void showDiagram(Diagram *);
+	void editProjectProperties();
+	void editCurrentDiagramProperties();
+	void editDiagramProperties(DiagramView *);
+	void editDiagramProperties(Diagram *);
+	void moveDiagramUp(DiagramView *);
+	void moveDiagramUp(Diagram *);
+	void moveDiagramDown(DiagramView *);
+	void moveDiagramDown(Diagram *);
+	void printProject();
+	void exportProject();
+	QETResult save();
+	QETResult saveAs(ProjectSaveOptions = ProjectSaveOptions(AllDiagrams | ModifiedDiagramsOnly));
+	QETResult saveCurrentDiagram();
+	QETResult doSave(ProjectSaveOptions);
+	void saveDiagrams(const QList<Diagram *> &);
+	int cleanProject();
+	void updateWindowTitle();
+	void updateTabTitle(DiagramView *, const QString &);
+	void tabMoved(int, int);
+	void editTitleBlockTemplateRequired(const QString &, bool);
+	
+	signals:
+	void diagramAdded(DiagramView *);
+	void diagramAboutToBeRemoved(DiagramView *);
+	void diagramRemoved(DiagramView *);
+	void diagramActivated(DiagramView *);
+	void diagramOrderChanged(ProjectView *, int, int);
+	void projectClosed(ProjectView *);
+	void errorEncountered(const QString &);
+	// relayed signals
+	void findElementRequired(const ElementsLocation &);
+	void editElementRequired(const ElementsLocation &);
+	void editTitleBlockTemplate(const TitleBlockTemplateLocation &, bool);
+	
+	private:
+	void initActions();
+	void initWidgets();
+	void initLayout();
+	void loadDiagrams();
+	DiagramView *findDiagram(Diagram *);
+	void rebuildDiagramsMap();
+	bool tryClosing();
+	bool tryClosingElementEditors();
+	int tryClosingDiagrams();
+	QString askUserForFilePath(bool = true);
+	QETResult noProjectResult() const;
+	
+	private slots:
+	void tabChanged(int);
+	void tabDoubleClicked(int);
+	void firstTabInserted();
+	void lastTabRemoved();
+	void setDisplayFallbackWidget(bool);
+	void adjustReadOnlyState();
+	
+	// attributes
+	private:
+	QAction *add_new_diagram_;
+	QETProject *project_;
+	QVBoxLayout *layout_;
+	QWidget *fallback_widget_;
+	QLabel *fallback_label_;
+	QETTabWidget *tabs_;
+	QMap<int, DiagramView *> diagram_ids_;
+	QList<DiagramView *> diagrams_;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(ProjectView::ProjectSaveOptions)
+#endif

Added: trunk/sources/qet.cpp
===================================================================
--- trunk/sources/qet.cpp	                        (rev 0)
+++ trunk/sources/qet.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,676 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qet.h"
+#include <limits>
+#include <QGraphicsSceneContextMenuEvent>
+
+/**
+	Permet de convertir une chaine de caracteres ("n", "s", "e" ou "w")
+	en orientation. Si la chaine fait plusieurs caracteres, seul le
+	premier est pris en compte. En cas d'incoherence, QET::North est
+	retourne.
+	@param s Chaine de caractere cense representer une orientation
+	@return l'orientation designee par la chaine de caractere
+*/
+QET::Orientation QET::orientationFromString(const QString &s) {
+	QChar c = s[0];
+	if (c == 'e') return(QET::East);
+	else if (c == 's') return(QET::South);
+	else if (c == 'w') return (QET::West);
+	else return(QET::North);
+}
+
+/**
+	@param o une orientation
+	@return une chaine de caractere representant l'orientation
+*/
+QString QET::orientationToString(QET::Orientation o) {
+	QString ret;
+	switch(o) {
+		case QET::North: ret = "n"; break;
+		case QET::East : ret = "e"; break;
+		case QET::South: ret = "s"; break;
+		case QET::West : ret = "w"; break;
+	}
+	return(ret);
+}
+
+/**
+	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 QET::surLeMemeAxe(QET::Orientation a, QET::Orientation b) {
+	if ((a == QET::North || a == QET::South) && (b == QET::North || b == QET::South)) return(true);
+	else if ((a == QET::East || a == QET::West) && (b == QET::East || b == QET::West)) 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 QET::estHorizontale(QET::Orientation a) {
+	return(a == QET::East || a == QET::West);
+}
+
+/**
+	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 QET::estVerticale(QET::Orientation a) {
+	return(a == QET::North || a == QET::South);
+}
+
+/**
+	Permet de connaitre l'orientation suivante apres celle donnee en parametre.
+	Les orientations sont generalement presentees dans l'ordre suivant : Nord,
+	Est, Sud, Ouest.
+	@param o une orientation
+	@return l'orientation suivante
+*/
+QET::Orientation QET::nextOrientation(QET::Orientation o) {
+	if (o < 0 || o > 2) return(QET::North);
+	return((QET::Orientation)(o + 1));
+}
+
+/**
+	Permet de connaitre l'orientation precedant celle donnee en parametre.
+	Les orientations sont generalement presentees dans l'ordre suivant : Nord,
+	Est, Sud, Ouest.
+	@param o une orientation
+	@return l'orientation precedente
+*/
+QET::Orientation QET::previousOrientation(QET::Orientation o) {
+	if (o < 0 || o > 3) return(QET::North);
+	if (o == QET::North) return(QET::West);
+	return((QET::Orientation)(o - 1));
+}
+
+/**
+	@param line Un segment de droite
+	@param point Un point
+	@return true si le point appartient au segment de droite, false sinon
+*/
+bool QET::lineContainsPoint(const QLineF &line, const QPointF &point) {
+	QLineF point_line(line.p1(), point);
+	if (point_line.unitVector() != line.unitVector()) return(false);
+	return(point_line.length() <= line.length());
+}
+
+/**
+	@param point Un point donne
+	@param line Un segment de droite donnee
+	@param intersection si ce pointeur est different de 0, le QPointF ainsi
+	designe contiendra les coordonnees du projete orthogonal, meme si celui-ci
+	n'appartient pas au segment de droite
+	@return true si le projete orthogonal du point sur la droite appartient au
+	segment de droite.
+*/
+bool QET::orthogonalProjection(const QPointF &point, const QLineF &line, QPointF *intersection) {
+	// recupere le vecteur normal de `line'
+	QLineF line_normal_vector(line.normalVector());
+	QPointF normal_vector(line_normal_vector.dx(), line_normal_vector.dy());
+	
+	// cree une droite perpendiculaire a `line' passant par `point'
+	QLineF perpendicular_line(point, point + normal_vector);
+	
+	// determine le point d'intersection des deux droites = le projete orthogonal
+	QPointF intersection_point;
+	QLineF::IntersectType it = line.intersect(perpendicular_line, &intersection_point);
+	
+	// ne devrait pas arriver (mais bon...)
+	if (it == QLineF::NoIntersection) return(false);
+	
+	// fournit le point d'intersection a l'appelant si necessaire
+	if (intersection) {
+		*intersection = intersection_point;
+	}
+	
+	// determine si le point d'intersection appartient au segment de droite
+	if (QET::lineContainsPoint(line, intersection_point)) {
+		return(true);
+	}
+	return(false);
+}
+
+/**
+	Permet de savoir si l'attribut nom_attribut d'un element XML e est bien un
+	entier. Si oui, sa valeur est copiee dans entier.
+	@param e Element XML
+	@param nom_attribut Nom de l'attribut a analyser
+	@param entier Pointeur facultatif vers un entier
+	@return true si l'attribut est bien un entier, false sinon
+*/
+bool QET::attributeIsAnInteger(const 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);
+}
+
+/**
+	Permet de savoir si l'attribut nom_attribut d'un element XML e est bien un
+	reel. Si oui, sa valeur est copiee dans reel.
+	@param e Element XML
+	@param nom_attribut Nom de l'attribut a analyser
+	@param reel Pointeur facultatif vers un double
+	@return true si l'attribut est bien un reel, false sinon
+*/
+bool QET::attributeIsAReal(const QDomElement &e, QString nom_attribut, qreal *reel) {
+	// verifie la presence de l'attribut
+	if (!e.hasAttribute(nom_attribut)) return(false);
+	// verifie la validite de l'attribut
+	bool ok;
+	qreal tmp = e.attribute(nom_attribut).toDouble(&ok);
+	if (!ok) return(false);
+	if (reel != NULL) *reel = tmp;
+	return(true);
+}
+
+/**
+	Permet de composer rapidement la proposition "x elements et y conducteurs"
+	ou encore "x elements, y conducteurs et z champs de texte".
+	@param elements_count nombre d'elements
+	@param conductors_count nombre de conducteurs
+	@param texts_count nombre de champs de texte
+	@param images_count nombre d'images
+	@return la proposition decrivant le nombre d'elements, de conducteurs et de
+	textes
+*/
+QString QET::ElementsAndConductorsSentence(int elements_count, int conductors_count, int texts_count, int images_count) {
+	QString text;
+	if (elements_count) {
+		text += QObject::tr(
+			"%n \351l\351ment(s)",
+			"part of a sentence listing the content of a diagram",
+			elements_count
+		);
+		if ((conductors_count && texts_count) ||
+			(conductors_count && images_count) ||
+			(texts_count && images_count)) {
+			text += QObject::tr(
+				", ",
+				"separator between elements and conductors in a sentence "
+				"listing the content of a diagram"
+			);
+		} else if (conductors_count || texts_count || images_count) {
+			text += QObject::tr(
+				" et ",
+				"separator between elements and conductors (or texts) in a "
+				"sentence listing the content of a diagram"
+			);
+		}
+	}
+	
+	if (conductors_count) {
+		text += QObject::tr(
+			"%n conducteur(s)",
+			"part of a sentence listing the content of a diagram",
+			conductors_count
+		);
+		if (texts_count && images_count) {
+			text += QObject::tr(
+				", ",
+				"separator between elements and conductors in a sentence "
+				"listing the content of a diagram"
+			);
+		}
+		else if (texts_count || images_count) {
+			text += QObject::tr(
+				" et ",
+				"separator between conductors and texts in a sentence listing "
+				"the content of a diagram"
+			);
+		}
+	}
+	
+	if (texts_count) {
+		text += QObject::tr(
+			"%n champ(s) de texte",
+			"part of a sentence listing the content of a diagram",
+			texts_count
+		);
+		if (images_count) {
+			text += QObject::tr(
+				" et ",
+				"separator between conductors and texts in a sentence listing "
+				"the content of a diagram"
+			);
+		}
+	}
+
+	if (images_count) {
+		text += QObject::tr(
+			"%n image(s)",
+			"part of a sentence listing the content of a diagram",
+			images_count
+		);
+	}
+	return(text);
+}
+
+/**
+	@return the list of \a tag_name elements directly under the \a e XML element.
+*/
+QList<QDomElement> QET::findInDomElement(const QDomElement &e, const QString &tag_name) {
+	QList<QDomElement> return_list;
+	for (QDomNode node = e.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		if (!node.isElement()) continue;
+		QDomElement element = node.toElement();
+		if (element.isNull() || element.tagName() != tag_name) continue;
+		return_list << element;
+	}
+	return(return_list);
+}
+
+/**
+	Etant donne un element XML e, renvoie la liste de tous les elements
+	children imbriques dans les elements parent, eux-memes enfants de l'elememt e
+	@param e Element XML a explorer
+	@param parent tag XML intermediaire
+	@param children tag XML a rechercher
+	@return La liste des elements XML children
+*/
+QList<QDomElement> QET::findInDomElement(const QDomElement &e, const QString &parent, const QString &children) {
+	QList<QDomElement> return_list;
+	
+	// parcours des elements parents
+	for (QDomNode enfant = e.firstChild() ; !enfant.isNull() ; enfant = enfant.nextSibling()) {
+		// on s'interesse a l'element XML "parent"
+		QDomElement parents = enfant.toElement();
+		if (parents.isNull() || parents.tagName() != parent) continue;
+		// parcours des enfants de l'element XML "parent"
+		for (QDomNode node_children = parents.firstChild() ; !node_children.isNull() ; node_children = node_children.nextSibling()) {
+			// on s'interesse a l'element XML "children"
+			QDomElement n_children = node_children.toElement();
+			if (!n_children.isNull() && n_children.tagName() == children) return_list.append(n_children);
+		}
+	}
+	return(return_list);
+}
+
+/// @return le texte de la licence de QElectroTech (GNU/GPL)
+QString QET::license() {
+	// Recuperation du texte de la GNU/GPL dans un fichier integre a l'application
+	QFile *file_license = new QFile(":/LICENSE");
+	QString txt_license;
+	// verifie que le fichier existe
+	if (!file_license -> exists()) {
+		txt_license = QString(QObject::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 (!file_license -> open(QIODevice::ReadOnly | QIODevice::Text)) {
+			txt_license = QString(QObject::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(file_license);
+			txt_license = QString("");
+			while (!in.atEnd()) txt_license += in.readLine()+"\n";
+			// ferme le fichier
+			file_license -> close();
+		}
+	}
+	return(txt_license);
+};
+
+
+/**
+	@return la liste des caracteres interdits dans les noms de fichiers sous
+	Windows
+*/
+QList<QChar> QET::forbiddenCharacters() {
+	return(QList<QChar>() << '\\' << '/' << ':' << '*' << '?' << '"' << '<' << '>' << '|');
+}
+
+/**
+	@return une chaine listant les caracteres interdits dans les noms de fichiers sous
+	Windows
+	@param escape true pour remplacer les caracteres < et > par leurs entites HTML
+*/
+QString QET::forbiddenCharactersString(bool escape) {
+	QString result;
+	foreach(QChar c, QET::forbiddenCharacters()) {
+		if (escape) {
+			if (c == '<')      result += "&lt;";
+			else if (c == '>') result += "&gt;";
+			else               result += QString(c);
+		} else {
+			result += QString(c);
+		}
+		result += " ";
+	}
+	return(result);
+}
+
+/**
+	@param string une chaine de caracteres
+	@return true si string contient un caractere interdit dans les noms de
+	fichiers sous Windows
+*/
+bool QET::containsForbiddenCharacters(const QString &string) {
+	foreach(QChar c, QET::forbiddenCharacters()) {
+		if (string.contains(c)) return(true);
+	}
+	return(false);
+}
+
+/**
+	Cette fonction transforme une chaine de caracteres (typiquement : un nom de
+	schema, de projet, d'element) en un nom de fichier potable.
+	Par nom de fichier potable, on entend un nom :
+	  * ne comprenant pas de caracteres interdits sous Windows
+	  * ne comprenant pas d'espace
+	@param name Chaine de caractere a transformer en nom de fichier potable
+	@todo virer les caracteres accentues ?
+*/
+QString QET::stringToFileName(const QString &name) {
+	QString file_name(name.toLower());
+	
+	// remplace les caracteres interdits par des tirets
+	foreach(QChar c, QET::forbiddenCharacters()) {
+		file_name.replace(c, '-');
+	}
+	
+	// remplace les espaces par des underscores
+	file_name.replace(' ', '_');
+	
+	return(file_name);
+}
+
+/**
+	@param string une chaine de caracteres
+	@return la meme chaine de caracteres, mais avec les espaces et backslashes
+	echappes
+*/
+QString QET::escapeSpaces(const QString &string) {
+	return(QString(string).replace('\\', "\\\\").replace(' ', "\\ "));
+}
+
+/**
+	@param string une chaine de caracteres
+	@return la meme chaine de caracteres, mais avec les espaces et backslashes
+	non echappes
+*/
+QString QET::unescapeSpaces(const QString &string) {
+	return(QString(string).replace("\\\\", "\\").replace("\\ ", " "));
+}
+
+/**
+	Assemble une liste de chaines en une seule. Un espace separe chaque chaine.
+	Les espaces et backslashes des chaines sont echappes.
+	@param string_list une liste de chaine
+	@return l'assemblage des chaines
+*/
+QString QET::joinWithSpaces(const QStringList &string_list) {
+	QString returned_string;
+	
+	for (int i = 0 ; i < string_list.count() ; ++ i) {
+		returned_string += QET::escapeSpaces(string_list.at(i));
+		if (i != string_list.count() - 1) returned_string += " ";
+	}
+	
+	return(returned_string);
+}
+
+/**
+	@param string Une chaine de caracteres contenant des sous-chaines a
+	extraire separees par des espaces non echappes. Les espaces des sous-chaines
+	sont echappes.
+	@return La liste des sous-chaines, sans echappement.
+*/
+QStringList QET::splitWithSpaces(const QString &string) {
+	// les chaines sont separees par des espaces non echappes = avec un nombre nul ou pair de backslashes devant
+	QStringList escaped_strings = string.split(QRegExp("[^\\]?(?:\\\\)* "), QString::SkipEmptyParts);
+	
+	QStringList returned_list;
+	foreach(QString escaped_string, escaped_strings) {
+		returned_list << QET::unescapeSpaces(escaped_string);
+	}
+	return(returned_list);
+}
+
+/**
+	@param end_type un type d'extremite
+	@return une chaine representant le type d'extremite
+*/
+QString QET::endTypeToString(const QET::EndType &end_type) {
+	switch(end_type) {
+		case QET::Simple:   return("simple");
+		case QET::Triangle: return("triangle");
+		case QET::Circle:   return("circle");
+		case QET::Diamond:  return("diamond");
+		case QET::None:
+		default:
+			return("none");
+	}
+}
+
+/**
+	@param string une chaine representant un type d'extremite
+	@return le type d'extremite correspondant ; si la chaine est invalide,
+	QET::None est retourne.
+*/
+QET::EndType QET::endTypeFromString(const QString &string) {
+	if (string == "simple")        return(QET::Simple);
+	else if (string == "triangle") return(QET::Triangle);
+	else if (string == "circle")   return(QET::Circle);
+	else if (string == "diamond")  return(QET::Diamond);
+	else return(QET::None);
+}
+
+/**
+	@param diagram_area un type de zone de schema
+	@return une chaine representant le type de zone de schema
+*/
+QString QET::diagramAreaToString(const QET::DiagramArea &diagram_area) {
+	if (diagram_area == ElementsArea) return("elements");
+	else return("border");
+}
+
+/**
+	@param string une chaine representant un type de zone de schema
+	@return le type de zone de schema correspondant ; si la chaine est invalide,
+	QET::ElementsArea est retourne.
+*/
+QET::DiagramArea QET::diagramAreaFromString(const QString &string) {
+	if (!string.compare("border", Qt::CaseInsensitive)) return(QET::BorderArea);
+	else return(QET::ElementsArea);
+}
+
+/**
+	@param ptr pointeur quelconque
+	@return une representation hexadecimale de l'adresse du pointeur
+*/
+QString QET::pointerString(void *ptr) {
+	static int hexa_digits = -1;
+	
+	if (hexa_digits == -1) {
+		// determine le nombre de bits dans un unsigned long int
+		hexa_digits = std::numeric_limits<unsigned long int>::digits / 4;
+	}
+	
+	return(
+		QString("0x%1").arg(
+			reinterpret_cast<unsigned long int>(ptr),
+			hexa_digits,
+			16,
+			QChar('0')
+		)
+	);
+}
+
+/**
+	Round \a x to the nearest multiple of the invert of \a epsilon.
+	For instance, epsilon = 10 will round to 1/10 = 0.1
+*/
+qreal QET::round(qreal x, qreal epsilon) {
+	return(int(x * epsilon) / epsilon);
+}
+
+/**
+	Round the coordinates of \a p to the nearest multiple of \a epsilon.
+*/
+QPointF QET::roundPoint(const QPointF &p, qreal epsilon) {
+	return(QPointF(QET::round(p.x(), epsilon), QET::round(p.y(), epsilon)));
+}
+
+/**
+	@param angle Un angle quelconque
+	@return l'angle passe en parametre, mais ramene entre -360.0 + 360.0 degres
+*/
+qreal QET::correctAngle(const qreal &angle) {
+	// ramene l'angle demande entre -360.0 et +360.0 degres
+	qreal corrected_angle = angle;
+	while (corrected_angle <= -360.0) corrected_angle += 360.0;
+	while (corrected_angle >=  360.0) corrected_angle -= 360.0;
+	return(corrected_angle);
+}
+
+/**
+	@param first  Un premier chemin vers un fichier
+	@param second Un second chemin vers un fichier
+	@return true si les deux chemins existent existent et sont identiques
+	lorsqu'ils sont exprimes sous forme canonique
+*/
+bool QET::compareCanonicalFilePaths(const QString &first, const QString &second) {
+	QString first_canonical_path = QFileInfo(first).canonicalFilePath();
+	if (first_canonical_path.isEmpty()) return(false);
+
+	QString second_canonical_path = QFileInfo(second).canonicalFilePath();
+	if (second_canonical_path.isEmpty()) return(false);
+	
+#ifdef Q_WS_WIN
+	// sous Windows, on ramene les chemins en minuscules
+	first_canonical_path  = first_canonical_path.toLower();
+	second_canonical_path = second_canonical_path.toLower();
+#endif
+	
+	return(first_canonical_path == second_canonical_path);
+}
+
+/**
+	@param icl an TitleBlockColumnLength object
+	@see TitleBlockColumnLength
+	@return a string describing the type of this TitleBlockColumnLength object
+*/
+QString QET::titleBlockColumnLengthToString(const TitleBlockColumnLength  &icl) {
+	QString type_str;
+	if (icl== Absolute) type_str = "absolute";
+	else if (icl == RelativeToTotalLength) type_str = "relative to total";
+	else if (icl == RelativeToRemainingLength) type_str = "relative to remaining";
+	return(type_str);
+}
+
+/**
+	Export an XML document to an UTF-8 text file indented with 4 spaces, with LF
+	end of lines and no BOM.
+	@param xml_doc An XML document to be exported
+	@param filepath Path to the file to be written
+	@param error_message If non-zero, will contain an error message explaining
+	what happened when this function returns false.
+	@return false if an error occured, true otherwise
+*/
+bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *error_message) {
+	QFile file(filepath);
+	
+	// Note: we do not set QIODevice::Text to avoid generating CRLF end of lines
+	bool file_opening = file.open(QIODevice::WriteOnly);
+	if (!file_opening) {
+		if (error_message) {
+			*error_message = QString(
+				QObject::tr(
+					"Impossible d'ouvrir le fichier %1 en \351criture, erreur %2 rencontr\351e.",
+					"error message when attempting to write an XML file"
+				)
+			).arg(filepath).arg(file.error());
+		}
+		return(false);
+	}
+	
+	QTextStream out(&file);
+	out.setCodec("UTF-8");
+	out.setGenerateByteOrderMark(false);
+	out << xml_doc.toString(4);
+	file.close();
+	
+	return(true);
+}
+
+/**
+	@return the scene position where \a event occurred, provided it is
+	QGraphicsScene-related event; otherwise, this function returns a null
+	QPointF.
+*/
+QPointF QET::graphicsSceneEventPos(QEvent *event) {
+	QPointF event_scene_pos;
+	if (event -> type() < QEvent::GraphicsSceneContextMenu) return(event_scene_pos);
+	if (event -> type() > QEvent::GraphicsSceneWheel) return(event_scene_pos);
+	
+	switch (event -> type()) {
+		case QEvent::GraphicsSceneContextMenu: {
+			QGraphicsSceneContextMenuEvent *qgs_event = static_cast<QGraphicsSceneContextMenuEvent *>(event);
+			event_scene_pos = qgs_event -> scenePos();
+			break;
+		}
+		case QEvent::GraphicsSceneDragEnter:
+		case QEvent::GraphicsSceneDragLeave:
+		case QEvent::GraphicsSceneDragMove:
+		case QEvent::GraphicsSceneDrop: {
+			QGraphicsSceneDragDropEvent *qgs_event = static_cast<QGraphicsSceneDragDropEvent *>(event);
+			event_scene_pos = qgs_event -> scenePos();
+			break;
+		}
+		case QEvent::GraphicsSceneHelp: {
+			QGraphicsSceneHelpEvent *qgs_event = static_cast<QGraphicsSceneHelpEvent *>(event);
+			event_scene_pos = qgs_event -> scenePos();
+			break;
+		}
+		
+		case QEvent::GraphicsSceneHoverEnter:
+		case QEvent::GraphicsSceneHoverLeave:
+		case QEvent::GraphicsSceneHoverMove: {
+			QGraphicsSceneHoverEvent *qgs_event = static_cast<QGraphicsSceneHoverEvent *>(event);
+			event_scene_pos = qgs_event -> scenePos();
+			break;
+		}
+		case QEvent::GraphicsSceneMouseDoubleClick:
+		case QEvent::GraphicsSceneMouseMove:
+		case QEvent::GraphicsSceneMousePress:
+		case QEvent::GraphicsSceneMouseRelease: {
+			QGraphicsSceneMouseEvent *qgs_event = static_cast<QGraphicsSceneMouseEvent *>(event);
+			event_scene_pos = qgs_event -> scenePos();
+			break;
+		}
+		case QEvent::GraphicsSceneWheel: {
+			QGraphicsSceneWheelEvent *qgs_event = static_cast<QGraphicsSceneWheelEvent *>(event);
+			event_scene_pos = qgs_event -> scenePos();
+			break;
+		}
+		default:
+			break;
+	}
+	return(event_scene_pos);
+}

Added: trunk/sources/qet.h
===================================================================
--- trunk/sources/qet.h	                        (rev 0)
+++ trunk/sources/qet.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,179 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _QET_H
+#define _QET_H
+#include <QtXml>
+/**
+	This file provides useful functions and enums that may be used from
+	anywhere else within the QElectroTech application.
+*/
+namespace QET {
+	/// QElectroTech version (as string, used to mark projects and elements XML documents)
+	const QString version = "0.3";
+	/// QElectroTech displayed version
+	const QString displayedVersion = "0.3";
+	QString license();
+	/// Orientation (used for electrical elements and their terminals)
+	enum Orientation {North, East, South, West};
+	
+	/// Oriented movements
+	enum OrientedMovement {
+		ToNorth,
+		ToNorthEast,
+		ToEast,
+		ToSouthEast,
+		ToSouth,
+		ToSouthWest,
+		ToWest,
+		ToNorthWest
+	};
+	
+	/// List areas related to some common operations
+	enum OperationAreas {
+		ChangeInnerPoints = -4,
+		RotateArea = -3,
+		MoveArea = -2,
+		NoOperation = -1,
+		ResizeFromTopLeftCorner = 0,
+		ResizeFromTopCenterCorner = 1,
+		ResizeFromTopRightCorner = 2,
+		ResizeFromMiddleLeftCorner = 3,
+		ResizeFromMiddleRightCorner = 4,
+		ResizeFromBottomLeftCorner = 5,
+		ResizeFromBottomCenterCorner = 6,
+		ResizeFromBottomRightCorner = 7
+	};
+	
+	/// Supported types of interactive scaling, typically for a single element primitive
+	enum ScalingMethod {
+		FreeScaling,              ///< do not interfer with the default scaling process
+		SnapScalingPointToGrid,   ///< snap the point used to define the new bounding rectangle to the grid
+		RoundScaleRatios          ///< adjust the scaling movement so that the induced scaling ratios are rounded
+	};
+	
+	/// Known kinds of conductor segments
+	enum ConductorSegmentType {
+		Horizontal = 1, ///< Horizontal segment 
+		Vertical = 2,   ///< Vertical segment 
+		Both = 3        ///< Invalid segment
+	};
+	
+	/**
+		This enum lists the various available endings for line primitives when drawing
+		an electrical element.
+	*/
+	enum EndType {
+		None,      ///< Regular line
+		Simple,    ///< Base-less triangle
+		Triangle,  ///< Triangle
+		Circle,    ///< Circle
+		Diamond    ///< Diamond
+	};
+	
+	/**
+		This enums lists the various kind of items users can manage within the
+		application.
+	*/
+	enum ItemType {
+		Element                           =    1,
+		ElementsCategory                  =    2,
+		ElementsCollection                =    4,
+		ElementsContainer                 =    6,
+		ElementsCollectionItem            =    7,
+		TitleBlockTemplate                =    8,
+		TitleBlockTemplatesCollection     =   16,
+		TitleBlockTemplatesCollectionItem =   24,
+		Diagram                           =   32,
+		Project                           =   64,
+		All                               =  127
+	};
+	
+	/**
+		This enum represents the various steps when applying a filter.
+	*/
+	enum Filtering {
+		BeginFilter,
+		RegularFilter,
+		EndFilter
+	};
+	
+	/**
+		This enums lists the various ways to handle a standard problem when copying
+		or moving element items (collections, categories, elements).
+		@see MoveElementsHandler
+	*/
+	enum Action {
+		Retry,   ///< The operation must be tried again
+		Ignore,  ///< Skip the current item
+		Erase,   ///< Erase the target content
+		Abort,   ///< abort the whole operation, ignoring the curent item
+		Managed, ///< the current item was handled by the Strategy object: do not treat it and continue
+		Rename   ///< the target has to be renamed
+	};
+	
+	/**
+		This enum represents diagram areas that may be exported/printed.
+	*/
+	enum DiagramArea {
+		BorderArea,      ///< Export the diagram along with its border and title block
+		ElementsArea     ///< Export the content of the diagram only
+	};
+	
+	/// enum used to specify the type of a length
+	enum TitleBlockColumnLength {
+		Absolute,                   ///< the length is absolute and should be applied as is
+		RelativeToTotalLength,      ///< the length is just a fraction of the total available length
+		RelativeToRemainingLength   ///< the length is just a fraction of the length that is still available when other types of lengths have been removed
+	};
+	
+	QET::Orientation nextOrientation(QET::Orientation);
+	QET::Orientation previousOrientation(QET::Orientation);
+	QET::Orientation orientationFromString(const QString &);
+	QString orientationToString(QET::Orientation);
+	bool surLeMemeAxe(QET::Orientation, QET::Orientation);
+	bool estHorizontale(QET::Orientation);
+	bool estVerticale(QET::Orientation);
+	bool lineContainsPoint(const QLineF &, const QPointF &);
+	bool orthogonalProjection(const QPointF &, const QLineF &, QPointF * = 0);
+	bool attributeIsAnInteger(const QDomElement &, QString , int * = NULL);
+	bool attributeIsAReal(const QDomElement &, QString , qreal * = NULL);
+	QString ElementsAndConductorsSentence(int, int, int = 0, int = 0);
+	QList<QDomElement> findInDomElement(const QDomElement &, const QString &);
+	QList<QDomElement> findInDomElement(const QDomElement &, const QString &, const QString &);
+	QList<QChar> forbiddenCharacters();
+	QString forbiddenCharactersString(bool = false);
+	bool containsForbiddenCharacters(const QString &);
+	QString stringToFileName(const QString &);
+	QString escapeSpaces(const QString &);
+	QString unescapeSpaces(const QString &);
+	QString joinWithSpaces(const QStringList &);
+	QStringList splitWithSpaces(const QString &);
+	QString endTypeToString(const QET::EndType &);
+	QET::EndType endTypeFromString(const QString &);
+	QString diagramAreaToString(const QET::DiagramArea &);
+	QET::DiagramArea diagramAreaFromString(const QString &);
+	QString pointerString(void *);
+	qreal round(qreal, qreal);
+	QPointF roundPoint(const QPointF &, qreal);
+	qreal correctAngle(const qreal &);
+	bool compareCanonicalFilePaths(const QString &, const QString &);
+	QString titleBlockColumnLengthToString(const TitleBlockColumnLength  &);
+	bool writeXmlFile(QDomDocument &, const QString &, QString * = 0);
+	QPointF graphicsSceneEventPos(QEvent *);
+}
+#endif

Added: trunk/sources/qetapp.cpp
===================================================================
--- trunk/sources/qetapp.cpp	                        (rev 0)
+++ trunk/sources/qetapp.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1736 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetapp.h"
+#include "aboutqet.h"
+#include "configdialog.h"
+#include "configpages.h"
+#include "qetdiagrameditor.h"
+#include "qetelementeditor.h"
+#include "elementscollectionitem.h"
+#include "elementscollectioncache.h"
+#include "fileelementscollection.h"
+#include "titleblocktemplate.h"
+#include "qettemplateeditor.h"
+#include "qetproject.h"
+#include "qtextorientationspinboxwidget.h"
+#include "recentfiles.h"
+#include "qeticons.h"
+#include "templatescollection.h"
+#include <cstdlib>
+#include <iostream>
+#define QUOTE(x) STRINGIFY(x)
+#define STRINGIFY(x) #x
+
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+QString QETApp::common_elements_dir = QString();
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+QString QETApp::common_tbt_dir_ = QString();
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+QString QETApp::config_dir = QString();
+#endif
+QString QETApp::lang_dir = QString();
+FileElementsCollection *QETApp::common_collection = 0;
+FileElementsCollection *QETApp::custom_collection = 0;
+TitleBlockTemplatesFilesCollection *QETApp::common_tbt_collection_;
+TitleBlockTemplatesFilesCollection *QETApp::custom_tbt_collection_;
+ElementsCollectionCache *QETApp::collections_cache_ = 0;
+QMap<uint, QETProject *> QETApp::registered_projects_ = QMap<uint, QETProject *>();
+uint QETApp::next_project_id = 0;
+RecentFiles *QETApp::projects_recent_files_ = 0;
+RecentFiles *QETApp::elements_recent_files_ = 0;
+AboutQET *QETApp::about_dialog_ = 0;
+TitleBlockTemplate *QETApp::default_titleblock_template_ = 0;
+
+/**
+	Constructeur
+	@param argc Nombre d'arguments passes a l'application
+	@param argv Arguments passes a l'application
+*/
+QETApp::QETApp(int &argc, char **argv) :
+	QETSingleApplication(argc, argv, QString("qelectrotech-" + QETApp::userName())),
+	splash_screen_(0),
+	non_interactive_execution_(false)
+{
+	parseArguments();
+	initLanguage();
+	QET::Icons::initIcons();
+	initConfiguration();
+	initStyle();
+	
+	if (!non_interactive_execution_ && isRunning()) {
+		// envoie les arguments a l'instance deja existante
+		non_interactive_execution_ = sendMessage(
+			"launched-with-args: " +
+			QET::joinWithSpaces(QStringList(qet_arguments_.arguments()))
+		);
+	}
+	
+	if (non_interactive_execution_) {
+		std::exit(EXIT_SUCCESS);
+	}
+	
+	initSplashScreen();
+	initSystemTray();
+	
+	// prise en compte des messages des autres instances
+	connect(this, SIGNAL(messageAvailable(QString)), this, SLOT(messageReceived(const QString&)));
+	
+	// nettoyage avant de quitter l'application
+	connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
+	
+	// connexion pour le signalmapper
+	connect(&signal_map, SIGNAL(mapped(QWidget *)), this, SLOT(invertMainWindowVisibility(QWidget *)));
+	
+	setQuitOnLastWindowClosed(false);
+	connect(this, SIGNAL(lastWindowClosed()), this, SLOT(checkRemainingWindows()));
+	
+	setSplashScreenStep(tr("Chargement... Initialisation du cache des collections d'\351l\351ments", "splash screen caption"));
+	if (!collections_cache_) {
+		QString cache_path = QETApp::configDir() + "/elements_cache.sqlite";
+		collections_cache_ = new ElementsCollectionCache(cache_path, this);
+		collections_cache_ -> setLocale(QLocale::system().name().left(2)); // @todo we need a unique function to get the good language
+	}
+	
+	// loads known collections into memory (this does not include items rendering made in elements panels)
+	setSplashScreenStep(tr("Chargement... Lecture des collections d'\351l\351ments", "splash screen caption"));
+	foreach(ElementsCollection *collection, availableCollections()) {
+		collection -> reload();
+	}
+	
+	// on ouvre soit les fichiers passes en parametre soit un nouvel editeur de projet
+	if (qet_arguments_.files().isEmpty()) {
+		setSplashScreenStep(tr("Chargement... \311diteur de sch\351mas", "splash screen caption"));
+		new QETDiagramEditor();
+	} else {
+		setSplashScreenStep(tr("Chargement... Ouverture des fichiers", "splash screen caption"));
+		openFiles(qet_arguments_);
+	}
+	buildSystemTrayMenu();
+	splash_screen_ -> hide();
+}
+
+/// Destructeur
+QETApp::~QETApp() {
+	elements_recent_files_ -> save();
+	projects_recent_files_ -> save();
+	delete elements_recent_files_;
+	delete projects_recent_files_;
+	if (about_dialog_) {
+		delete about_dialog_;
+	}
+	delete qsti;
+	delete custom_collection;
+	delete common_collection;
+	if (custom_tbt_collection_) delete custom_tbt_collection_;
+	if (common_tbt_collection_) delete common_tbt_collection_;
+}
+
+/**
+	@return l'instance de la QETApp
+*/
+QETApp *QETApp::instance() {
+	return(static_cast<QETApp *>(qApp));
+}
+
+/**
+	Change le langage utilise par l'application.
+	@param desired_language langage voulu
+*/
+void QETApp::setLanguage(const QString &desired_language) {
+	QString languages_path = languagesPath();
+	
+	// load Qt library translations
+	QString qt_l10n_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+	if (!qtTranslator.load("qt_" + desired_language, qt_l10n_path)) {
+		qtTranslator.load("qt_" + desired_language, languages_path);
+	}
+	installTranslator(&qtTranslator);
+	
+	// charge les traductions pour l'application QET
+	if (!qetTranslator.load("qet_" + desired_language, languages_path)) {
+		// en cas d'echec, on retombe sur les chaines natives pour le francais
+		if (desired_language != "fr") {
+			// utilisation de la version anglaise par defaut
+			qetTranslator.load("qet_en", languages_path);
+		}
+	}
+	installTranslator(&qetTranslator);
+	
+	QString ltr_special_string = tr(
+		"LTR",
+		"Translate this string to RTL if you are translating to a Right-to-Left language, else translate to LTR"
+	);
+	if (ltr_special_string == "RTL") switchLayout(Qt::RightToLeft);
+}
+
+/**
+	Switches the application to the provided layout.
+*/
+void QETApp::switchLayout(Qt::LayoutDirection direction) {
+	setLayoutDirection(direction);
+}
+
+/**
+	Gere les evenements relatifs au QSystemTrayIcon
+	@param reason un entier representant l'evenement survenu sur le systray
+*/
+void QETApp::systray(QSystemTrayIcon::ActivationReason reason) {
+	if (!QSystemTrayIcon::isSystemTrayAvailable()) return;
+	switch(reason) {
+		case QSystemTrayIcon::Context:
+			// affichage du menu
+			buildSystemTrayMenu();
+			qsti -> contextMenu() -> show();
+			break;
+		case QSystemTrayIcon::DoubleClick:
+		case QSystemTrayIcon::Trigger:
+			// reduction ou restauration de l'application
+			fetchWindowStats(diagramEditors(), elementEditors(), titleBlockTemplateEditors());
+			if (every_editor_reduced) restoreEveryEditor(); else reduceEveryEditor();
+			break;
+		case QSystemTrayIcon::Unknown:
+		default: // ne rien faire
+		break;
+	}
+}
+
+/// Reduit toutes les fenetres de l'application dans le systray
+void QETApp::reduceEveryEditor() {
+	reduceDiagramEditors();
+	reduceElementEditors();
+	reduceTitleBlockTemplateEditors();
+	every_editor_reduced = true;
+}
+
+/// Restaure toutes les fenetres de l'application dans le systray
+void QETApp::restoreEveryEditor() {
+	restoreDiagramEditors();
+	restoreElementEditors();
+	restoreTitleBlockTemplateEditors();
+	every_editor_reduced = false;
+}
+
+/// Reduit tous les editeurs de schemas dans le systray
+void QETApp::reduceDiagramEditors() {
+	setMainWindowsVisible<QETDiagramEditor>(false);
+}
+
+/// Restaure tous les editeurs de schemas dans le systray
+void QETApp::restoreDiagramEditors() {
+	setMainWindowsVisible<QETDiagramEditor>(true);
+}
+
+/// Reduit tous les editeurs d'element dans le systray
+void QETApp::reduceElementEditors() {
+	setMainWindowsVisible<QETElementEditor>(false);
+}
+
+/// Restaure tous les editeurs d'element dans le systray
+void QETApp::restoreElementEditors() {
+	setMainWindowsVisible<QETElementEditor>(true);
+}
+
+/// Reduce all known template editors
+void QETApp::reduceTitleBlockTemplateEditors() {
+	setMainWindowsVisible<QETTitleBlockTemplateEditor>(false);
+}
+
+/// Restore all known template editors
+void QETApp::restoreTitleBlockTemplateEditors() {
+	setMainWindowsVisible<QETTitleBlockTemplateEditor>(true);
+}
+
+/// lance un nouvel editeur de schemas
+void QETApp::newDiagramEditor() {
+	new QETDiagramEditor();
+}
+
+/// lance un nouvel editeur d'element
+void QETApp::newElementEditor() {
+	new QETElementEditor();
+}
+
+/**
+	@return la collection commune
+*/
+ElementsCollection *QETApp::commonElementsCollection() {
+	if (!common_collection) {
+		common_collection = new FileElementsCollection(QETApp::commonElementsDir());
+		common_collection -> setTitle(tr("Collection QET"));
+		common_collection -> setIcon(QIcon(":/ico/16x16/qet.png"));
+		common_collection -> setProtocol("common");
+		common_collection -> setCache(collections_cache_);
+	}
+	return(common_collection);
+}
+
+/**
+	@return la collection utilisateur
+*/
+ElementsCollection *QETApp::customElementsCollection() {
+	if (!custom_collection) {
+		custom_collection = new FileElementsCollection(QETApp::customElementsDir());
+		custom_collection -> setTitle(tr("Collection utilisateur"));
+		custom_collection -> setIcon(QIcon(":/ico/16x16/go-home.png"));
+		custom_collection -> setProtocol("custom");
+		custom_collection -> setCache(collections_cache_);
+	}
+	return(custom_collection);
+}
+
+/**
+	@return la liste des collections disponibles
+	Cela inclut typiquement la collection commune, la collection perso
+	ainsi que les collections embarquees dans les projets.
+*/
+QList<ElementsCollection *> QETApp::availableCollections() {
+	QList<ElementsCollection *> coll_list;
+	
+	// collection commune
+	coll_list << commonElementsCollection();
+	
+	// collection perso
+	coll_list << customElementsCollection();
+	
+	// collections embarquees
+	foreach(QETProject *opened_project, registered_projects_.values()) {
+		coll_list << opened_project -> embeddedCollection();
+	}
+	
+	return(coll_list);
+}
+
+/**
+	@return the collection cache provided by the application itself.
+*/
+ElementsCollectionCache *QETApp::collectionCache() {
+	return(collections_cache_);
+}
+
+/**
+	@return the common title block templates collection, i.e. the one provided
+	by QElecrotTech
+*/
+TitleBlockTemplatesFilesCollection *QETApp::commonTitleBlockTemplatesCollection() {
+	if (!common_tbt_collection_) {
+		common_tbt_collection_ = new TitleBlockTemplatesFilesCollection(QETApp::commonTitleBlockTemplatesDir());
+		common_tbt_collection_ -> setTitle(tr("Cartouches QET", "title of the title block templates collection provided by QElectroTech"));
+		common_tbt_collection_ -> setProtocol(QETAPP_COMMON_TBT_PROTOCOL);
+	}
+	return(common_tbt_collection_);
+}
+
+/**
+	@return the custom title block templates collection, i.e. the one managed
+	by the end user
+*/
+TitleBlockTemplatesFilesCollection *QETApp::customTitleBlockTemplatesCollection() {
+	if (!custom_tbt_collection_) {
+		custom_tbt_collection_ = new TitleBlockTemplatesFilesCollection(QETApp::customTitleBlockTemplatesDir());
+		custom_tbt_collection_ -> setTitle(tr("Cartouches utilisateur", "title of the user's title block templates collection"));
+		custom_tbt_collection_ -> setProtocol(QETAPP_CUSTOM_TBT_PROTOCOL);
+	}
+	return(custom_tbt_collection_);
+}
+
+/**
+	@return the list of all available title block tempaltes collections,
+	beginning with the common and custom ones, plus the projects-embedded ones.
+*/
+QList<TitleBlockTemplatesCollection *> QETApp::availableTitleBlockTemplatesCollections() {
+	QList<TitleBlockTemplatesCollection *> collections_list;
+	
+	collections_list << common_tbt_collection_;
+	collections_list << custom_tbt_collection_;
+	
+	foreach(QETProject *opened_project, registered_projects_) {
+		collections_list << opened_project -> embeddedTitleBlockTemplatesCollection();
+	}
+	
+	return(collections_list);
+}
+
+/**
+	@param protocol Protocol string
+	@return the templates collection matching the provided protocol, or 0 if none could be found
+*/
+TitleBlockTemplatesCollection *QETApp::titleBlockTemplatesCollection(const QString &protocol) {
+	if (protocol == QETAPP_COMMON_TBT_PROTOCOL) {
+		return(common_tbt_collection_);
+	} else if (protocol == QETAPP_CUSTOM_TBT_PROTOCOL) {
+		return(custom_tbt_collection_);
+	} else {
+		QETProject *project = QETApp::projectFromString(protocol);
+		if (project) {
+			return(project -> embeddedTitleBlockTemplatesCollection());
+		}
+	}
+	return(0);
+}
+
+/**
+	@return le nom de l'utilisateur courant
+*/
+QString QETApp::userName() {
+#ifndef Q_OS_WIN32
+	return(QString(getenv("USER")));
+#else
+	return(QString(getenv("USERNAME")));
+#endif
+}
+
+/**
+	Renvoie le dossier des elements communs, c-a-d le chemin du dossier dans
+	lequel QET doit chercher les definitions XML des elements de la collection QET.
+	@return Le chemin du dossier des elements communs
+*/
+QString QETApp::commonElementsDir() {
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	if (common_elements_dir != QString()) return(common_elements_dir);
+#endif
+#ifndef QET_COMMON_COLLECTION_PATH
+	// en l'absence d'option de compilation, on utilise le dossier elements, situe a cote du binaire executable
+	return(QCoreApplication::applicationDirPath() + "/elements/");
+#else
+	#ifndef QET_COMMON_COLLECTION_PATH_RELATIVE_TO_BINARY_PATH
+		// l'option de compilation represente un chemin absolu ou relatif classique
+		return(QUOTE(QET_COMMON_COLLECTION_PATH));
+	#else
+		// l'option de compilation represente un chemin relatif au dossier contenant le binaire executable
+		return(QCoreApplication::applicationDirPath() + "/" + QUOTE(QET_COMMON_COLLECTION_PATH));
+	#endif
+#endif
+}
+
+/**
+	Renvoie le dossier des elements de l'utilisateur, c-a-d le chemin du dossier
+	dans lequel QET chercher les definitions XML des elements propres a
+	l'utilisateur.
+	@return Le chemin du dossier des elements persos
+*/
+QString QETApp::customElementsDir() {
+	return(configDir() + "elements/");
+}
+
+/**
+	@return the path of the directory containing the common title block
+	templates collection.
+*/
+QString QETApp::commonTitleBlockTemplatesDir() {
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	if (common_tbt_dir_ != QString()) return(common_tbt_dir_);
+#endif
+#ifndef QET_COMMON_TBT_PATH
+	// without any compile-time option, use the "titleblocks" directory next to the executable binary
+	return(QCoreApplication::applicationDirPath() + "/titleblocks/");
+#else
+	#ifndef QET_COMMON_COLLECTION_PATH_RELATIVE_TO_BINARY_PATH
+		// the compile-time option represents a usual path (be it absolute or relative)
+		return(QUOTE(QET_COMMON_TBT_PATH));
+	#else
+		// the compile-time option represents a path relative to the directory that contains the executable binary
+		return(QCoreApplication::applicationDirPath() + "/" + QUOTE(QET_COMMON_TBT_PATH));
+	#endif
+#endif
+}
+
+/**
+	@return the path of the directory containing the custom title block
+	templates collection.
+*/
+QString QETApp::customTitleBlockTemplatesDir() {
+	return(configDir() + "titleblocks/");
+}
+
+/**
+	Renvoie le dossier de configuration de QET, c-a-d le chemin du dossier dans
+	lequel QET lira les informations de configuration et de personnalisation
+	propres a l'utilisateur courant. Ce dossier est generalement
+	C:\\Documents And Settings\\utilisateur\\Application Data\\qet sous Windows et
+	~/.qet sous les systemes type UNIX.
+	@return Le chemin du dossier de configuration de QElectroTech
+*/
+QString QETApp::configDir() {
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	if (config_dir != QString()) return(config_dir);
+#endif
+#ifdef Q_OS_WIN32
+	// recupere l'emplacement du dossier Application Data
+	char *app_data_env = getenv("APPDATA");
+	QString app_data_str(app_data_env);
+	delete app_data_env;
+	if (app_data_str.isEmpty()) {
+		app_data_str = QDir::homePath() + "/Application Data";
+	}
+	return(app_data_str + "/qet/");
+#else
+	return(QDir::homePath() + "/.qet/");
+#endif
+}
+
+/**
+	Permet de connaitre le chemin absolu du fichier *.elmt correspondant a un
+	chemin symbolique (du type custom://outils_pervers/sado_maso/contact_bizarre)
+	@param sym_path Chaine de caracteres representant le chemin absolu du fichier
+	@return Une chaine de caracteres vide en cas d'erreur ou le chemin absolu du
+	fichier *.elmt.
+*/
+QString QETApp::realPath(const QString &sym_path) {
+	QString directory;
+	if (sym_path.startsWith("common://")) {
+		directory = commonElementsDir();
+	} else if (sym_path.startsWith("custom://")) {
+		directory = customElementsDir();
+	} else if (sym_path.startsWith(QETAPP_COMMON_TBT_PROTOCOL "://")) {
+		directory = commonTitleBlockTemplatesDir();
+	} else if (sym_path.startsWith(QETAPP_CUSTOM_TBT_PROTOCOL "://")) {
+		directory = customTitleBlockTemplatesDir();
+	} else return(QString());
+	return(directory + QDir::toNativeSeparators(sym_path.right(sym_path.length() - 9)));
+}
+
+/**
+	Construit le chemin symbolique (du type custom://outils_pervers/sado_maso/
+	contact_bizarre) correspondant a un fichier.
+	@param real_path Chaine de caracteres representant le chemin symbolique du fichier
+	@return Une chaine de caracteres vide en cas d'erreur ou le chemin
+	symbolique designant l'element.
+*/
+QString QETApp::symbolicPath(const QString &real_path) {
+	// recupere les dossier common et custom
+	QString commond = commonElementsDir();
+	QString customd = customElementsDir();
+	QString chemin;
+	// analyse le chemin de fichier passe en parametre
+	if (real_path.startsWith(commond)) {
+		chemin = "common://" + real_path.right(real_path.length() - commond.length());
+	} else if (real_path.startsWith(customd)) {
+		chemin = "custom://" + real_path.right(real_path.length() - customd.length());
+	} else chemin = QString();
+	return(chemin);
+}
+
+/**
+	@return the list of file extensions QElectroTech is able to open and
+	supposed to handle. Note they are provided with no leading point.
+*/
+QStringList QETApp::handledFileExtensions() {
+	static QStringList ext;
+	if (!ext.count()) {
+		ext << "qet";
+		ext << "elmt";
+		ext << QString(TITLEBLOCKS_FILE_EXTENSION).remove(QRegExp("^\\."));
+	}
+	return(ext);
+}
+
+/**
+	@param an URLs list
+	@return the list of filepaths QElectroTech is able to open.
+*/
+QStringList QETApp::handledFiles(const QList<QUrl> &urls) {
+	QList<QString> filepaths;
+	foreach (QUrl url, urls) {
+		if (url.scheme() != "file") continue;
+		QString local_path = url.toLocalFile();
+		QFileInfo local_path_info(local_path);
+		QString local_path_ext = local_path_info.completeSuffix();
+		if (handledFileExtensions().contains(local_path_ext)) {
+			filepaths << local_path;
+		}
+	}
+	return(filepaths);
+}
+
+/**
+	@param filepath Un chemin de fichier
+	Note : si filepath est une chaine vide, cette methode retourne 0.
+	@return le QETDiagramEditor editant le fichier filepath, ou 0 si ce fichier
+	n'est pas edite par l'application.
+*/
+QETDiagramEditor *QETApp::diagramEditorForFile(const QString &filepath) {
+	if (filepath.isEmpty()) return(0);
+	
+	QETApp *qet_app(QETApp::instance());
+	foreach (QETDiagramEditor *diagram_editor, qet_app -> diagramEditors()) {
+		if (diagram_editor -> viewForFile(filepath)) {
+			return(diagram_editor);
+		}
+	}
+	
+	return(0);
+}
+
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+/**
+	Redefinit le chemin du dossier des elements communs
+	@param new_ced Nouveau chemin du dossier des elements communs
+*/
+void QETApp::overrideCommonElementsDir(const QString &new_ced) {
+	QFileInfo new_ced_info(new_ced);
+	if (new_ced_info.isDir()) {
+		common_elements_dir = new_ced_info.absoluteFilePath();
+		if (!common_elements_dir.endsWith("/")) common_elements_dir += "/";
+	}
+}
+#endif
+
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+/**
+	Define the path of the directory containing the common title block
+	tempaltes collection.
+*/
+void QETApp::overrideCommonTitleBlockTemplatesDir(const QString &new_ctbtd) {
+	QFileInfo new_ctbtd_info(new_ctbtd);
+	if (new_ctbtd_info.isDir()) {
+		common_tbt_dir_ = new_ctbtd_info.absoluteFilePath();
+		if (!common_tbt_dir_.endsWith("/")) common_tbt_dir_ += "/";
+	}
+}
+#endif
+
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+/**
+	Redefinit le chemin du dossier de configuration
+	@param new_cd Nouveau chemin du dossier de configuration
+*/
+void QETApp::overrideConfigDir(const QString &new_cd) {
+	QFileInfo new_cd_info(new_cd);
+	if (new_cd_info.isDir()) {
+		config_dir = new_cd_info.absoluteFilePath();
+		if (!config_dir.endsWith("/")) config_dir += "/";
+	}
+}
+#endif
+
+/**
+	Redefinit le chemin du dossier contenant les fichiers de langue
+	@param new_ld Nouveau chemin du dossier contenant les fichiers de langue
+*/
+void QETApp::overrideLangDir(const QString &new_ld) {
+	QFileInfo new_ld_info(new_ld);
+	if (new_ld_info.isDir()) {
+		lang_dir = new_ld_info.absoluteFilePath();
+		if (!lang_dir.endsWith("/")) lang_dir += "/";
+	}
+}
+
+/**
+	@return Le chemin du dossier contenant les fichiers de langue
+*/
+QString QETApp::languagesPath() {
+	if (!lang_dir.isEmpty()) {
+		return(lang_dir);
+	} else {
+#ifndef QET_LANG_PATH
+	// en l'absence d'option de compilation, on utilise le dossier lang, situe a cote du binaire executable
+	return(QCoreApplication::applicationDirPath() + "/lang/");
+#else
+	#ifndef QET_LANG_PATH_RELATIVE_TO_BINARY_PATH
+		// l'option de compilation represente un chemin absolu ou relatif classique
+		return(QUOTE(QET_LANG_PATH));
+	#else
+		// l'option de compilation represente un chemin relatif au dossier contenant le binaire executable
+		return(QCoreApplication::applicationDirPath() + "/" + QUOTE(QET_LANG_PATH));
+	#endif
+#endif
+	}
+}
+
+/**
+	Ferme tous les editeurs
+	@return true si l'utilisateur a accepte toutes les fermetures, false sinon
+*/
+bool QETApp::closeEveryEditor() {
+	// s'assure que toutes les fenetres soient visibles avant de quitter
+	restoreEveryEditor();
+	foreach(QETProject *project, registered_projects_) {
+		project -> close();
+	}
+	bool every_window_closed = true;
+	foreach(QETDiagramEditor *e, diagramEditors()) {
+		every_window_closed = every_window_closed && e -> close();
+	}
+	foreach(QETElementEditor *e, elementEditors()) {
+		every_window_closed = every_window_closed && e -> close();
+	}
+	return(every_window_closed);
+}
+
+/**
+	@param size taille voulue - si aucune taille n'est specifiee, la valeur
+	specifiee dans la configuration (diagramsize) est utilisee. La valeur par
+	defaut est 9.
+	@return la police a utiliser pour rendre les textes sur les schemas
+	La famille "Sans Serif" est utilisee par defaut mais peut etre surchargee
+	dans la configuration (diagramfont).
+*/
+QFont QETApp::diagramTextsFont(qreal size) {
+	// acces a la configuration de l'application
+	QSettings &qet_settings = QETApp::settings();
+	
+	// police a utiliser pour le rendu de texte
+	QString diagram_texts_family = qet_settings.value("diagramfont", "Sans Serif").toString();
+	qreal diagram_texts_size     = qet_settings.value("diagramsize", 9.0).toDouble();
+	
+	if (size != -1.0) {
+		diagram_texts_size = size;
+	}
+	QFont diagram_texts_font = QFont(diagram_texts_family);
+	diagram_texts_font.setPointSizeF(diagram_texts_size);
+	if (diagram_texts_size <= 4.0) {
+		diagram_texts_font.setWeight(QFont::Light);
+	}
+	return(diagram_texts_font);
+}
+
+/**
+	@return les editeurs de schemas
+*/
+QList<QETDiagramEditor *> QETApp::diagramEditors() {
+	return(QETApp::instance() -> detectWindows<QETDiagramEditor>());
+}
+
+/**
+	@return les editeurs d'elements
+*/
+QList<QETElementEditor *> QETApp::elementEditors() {
+	return(QETApp::instance() -> detectWindows<QETElementEditor>());
+}
+
+/**
+	@return the title block template editors
+*/
+QList<QETTitleBlockTemplateEditor *> QETApp::titleBlockTemplateEditors() {
+	return(QETApp::instance() -> detectWindows<QETTitleBlockTemplateEditor>());
+}
+
+/**
+	@param project Opened project object.
+	@return the list of title block template editors which are currently
+	editing a template embedded within \a project.
+*/
+QList<QETTitleBlockTemplateEditor *> QETApp::titleBlockTemplateEditors(QETProject *project) {
+	QList<QETTitleBlockTemplateEditor *> editors;
+	if (!project) return(editors);
+	
+	// foreach known template editor
+	foreach (QETTitleBlockTemplateEditor *tbt_editor, titleBlockTemplateEditors()) {
+		if (tbt_editor -> location().parentProject() == project) {
+			editors << tbt_editor;
+		}
+	}
+	
+	return(editors);
+}
+
+/**
+	Instancie un QTextOrientationSpinBoxWidget et configure :
+	  * sa police de caracteres
+	  * ses chaines de caracteres
+	A noter que la suppression du widget ainsi alloue est a la charge de
+	l'appelant.
+	@return un QTextOrientationSpinBoxWidget adapte pour une utilisation
+	"directe" dans QET.
+	@see QTextOrientationSpinBoxWidget
+*/
+QTextOrientationSpinBoxWidget *QETApp::createTextOrientationSpinBoxWidget() {
+	QTextOrientationSpinBoxWidget *widget = new QTextOrientationSpinBoxWidget();
+	widget -> orientationWidget() -> setFont(QETApp::diagramTextsFont());
+	widget -> orientationWidget() -> setUsableTexts(QList<QString>()
+		<< QETApp::tr("Q",            "Single-letter example text - translate length, not meaning")
+		<< QETApp::tr("QET",          "Small example text - translate length, not meaning")
+		<< QETApp::tr("Schema",       "Normal example text - translate length, not meaning")
+		<< QETApp::tr("Electrique",   "Normal example text - translate length, not meaning")
+		<< QETApp::tr("QElectroTech", "Long example text - translate length, not meaning")
+	);
+	return(widget);
+}
+
+/**
+	@return the default titleblock template for diagrams
+*/
+TitleBlockTemplate *QETApp::defaultTitleBlockTemplate() {
+	if (!QETApp::default_titleblock_template_) {
+		TitleBlockTemplate *titleblock_template = new TitleBlockTemplate(QETApp::instance());
+		if (titleblock_template -> loadFromXmlFile(":/titleblocks/default.titleblock")) {
+			QETApp::default_titleblock_template_ = titleblock_template;
+		}
+	}
+	return(default_titleblock_template_);
+}
+
+
+/**
+	@param project un projet
+	@return les editeurs d'elements editant un element appartenant au projet
+	project
+*/
+QList<QETElementEditor *> QETApp::elementEditors(QETProject *project) {
+	QList<QETElementEditor *> editors;
+	if (!project) return(editors);
+	
+	// pour chaque editeur d'element...
+	foreach(QETElementEditor *elmt_editor, elementEditors()) {
+		// on recupere l'emplacement de l'element qu'il edite
+		ElementsLocation elmt_editor_loc(elmt_editor -> location());
+		
+		// il se peut que l'editeur edite un element non enregistre ou un fichier
+		if (elmt_editor_loc.isNull()) continue;
+		
+		if (elmt_editor_loc.project() == project) {
+			editors << elmt_editor;
+		}
+	}
+	
+	return(editors);
+}
+
+/**
+	Nettoie certaines choses avant que l'application ne quitte
+*/
+void QETApp::cleanup() {
+	qsti -> hide();
+}
+
+/**
+	@param T a class inheriting QMainWindow
+	@return the list of windows of type T
+*/
+template <class T> QList<T *> QETApp::detectWindows() const {
+	QList<T *> windows;
+	foreach(QWidget *widget, topLevelWidgets()) {
+		if (!widget -> isWindow()) continue;
+		if (T *window = qobject_cast<T *>(widget)) {
+			windows << window;
+		}
+	}
+	return(windows);
+}
+
+/**
+	@param T a class inheriting QMainWindow
+	@param visible whether detected main windows should be visible
+*/
+template <class T> void QETApp::setMainWindowsVisible(bool visible) {
+	foreach(T *e, detectWindows<T>()) {
+		setMainWindowVisible(e, visible);
+	}
+}
+
+/**
+	@return La liste des fichiers recents pour les projets
+*/
+RecentFiles *QETApp::projectsRecentFiles() {
+	return(projects_recent_files_);
+}
+
+/**
+	@return La liste des fichiers recents pour les elements
+*/
+RecentFiles *QETApp::elementsRecentFiles() {
+	return(elements_recent_files_);
+}
+
+/**
+	Affiche ou cache une fenetre (editeurs de schemas / editeurs d'elements)
+	@param window fenetre a afficher / cacher
+	@param visible true pour affiche la fenetre, false sinon
+*/
+void QETApp::setMainWindowVisible(QMainWindow *window, bool visible) {
+	if (window -> isVisible() == visible) return;
+	if (!visible) {
+		window_geometries.insert(window, window -> saveGeometry());
+		window_states.insert(window, window -> saveState());
+		window -> hide();
+		// cache aussi les toolbars et les docks
+		foreach (QWidget *qw, floatingToolbarsAndDocksForMainWindow(window)) {
+			qw -> hide();
+		}
+	} else {
+		window -> show();
+#ifndef Q_OS_WIN32
+		window -> restoreGeometry(window_geometries[window]);
+#endif
+		window -> restoreState(window_states[window]);
+	}
+}
+
+/**
+	Affiche une fenetre (editeurs de schemas / editeurs d'elements) si
+	celle-ci est cachee ou la cache si elle est affichee.
+	@param window fenetre a afficher / cacher
+*/
+void QETApp::invertMainWindowVisibility(QWidget *window) {
+	if (QMainWindow *w = qobject_cast<QMainWindow *>(window)) setMainWindowVisible(w, !w -> isVisible());
+}
+
+/**
+	Change la palette de l'application
+	@param use true pour utiliser les couleurs du systeme, false pour utiliser celles du theme en cours
+*/
+void QETApp::useSystemPalette(bool use) {
+	if (use) {
+		setPalette(initial_palette_);
+	} else {
+		setPalette(style() -> standardPalette());
+	}
+	
+	// reapplique les feuilles de style
+	setStyleSheet(
+		"QAbstractScrollArea#mdiarea {"
+		"	background-color:white;"
+		"	background-image: url(':/ico/mdiarea_bg.png');"
+		"	background-repeat: no-repeat;"
+		"	background-position: center middle;"
+		"}"
+	);
+}
+
+/**
+	Demande la fermeture de toutes les fenetres ; si l'utilisateur les accepte,
+	l'application quitte
+*/
+void QETApp::quitQET() {
+	if (closeEveryEditor()) {
+		quit();
+	}
+}
+
+/**
+	Verifie s'il reste des fenetres (cachees ou non) et quitte s'il n'en reste
+	plus.
+*/
+void QETApp::checkRemainingWindows() {
+	/* petite bidouille : le slot se rappelle apres 500 ms d'attente
+	afin de compenser le fait que certaines fenetres peuvent encore
+	paraitre vivantes alors qu'elles viennent d'etre fermees
+	*/
+	static bool sleep = true;
+	if (sleep) {
+		QTimer::singleShot(500, this, SLOT(checkRemainingWindows()));
+	} else {
+		if (!diagramEditors().count() && !elementEditors().count()) {
+			quit();
+		}
+	}
+	sleep = !sleep;
+}
+
+/**
+	Gere les messages recus
+	@param message Message recu
+*/
+void QETApp::messageReceived(const QString &message) {
+	if (message.startsWith("launched-with-args: ")) {
+		QString my_message(message.mid(20));
+		// les arguments sont separes par des espaces non echappes
+		QStringList args_list = QET::splitWithSpaces(my_message);
+		openFiles(QETArguments(args_list));
+	}
+}
+
+/**
+	Ouvre les fichiers passes en arguments
+	@param args Objet contenant des arguments ; les fichiers 
+	@see openProjectFiles openElementFiles
+*/
+void QETApp::openFiles(const QETArguments &args) {
+	openProjectFiles(args.projectFiles());
+	openElementFiles(args.elementFiles());
+	openTitleBlockTemplateFiles(args.titleBlockTemplateFiles());
+}
+
+/**
+	Ouvre une liste de fichiers.
+	Les fichiers sont ouverts dans le premier editeur de schemas visible venu.
+	Sinon, le premier editeur de schemas existant venu devient visible et est
+	utilise. S'il n'y a aucun editeur de schemas ouvert, un nouveau est cree et
+	utilise.
+	@param files_list Fichiers a ouvrir
+*/
+void QETApp::openProjectFiles(const QStringList &files_list) {
+	if (files_list.isEmpty()) return;
+	
+	// liste des editeurs de schema ouverts
+	QList<QETDiagramEditor *> diagrams_editors = diagramEditors();
+	
+	// s'il y a des editeur de schemas ouvert, on cherche ceux qui sont visibles
+	if (diagrams_editors.count()) {
+		QList<QETDiagramEditor *> visible_diagrams_editors;
+		foreach(QETDiagramEditor *de, diagrams_editors) {
+			if (de -> isVisible()) visible_diagrams_editors << de;
+		}
+		
+		// on choisit soit le premier visible soit le premier tout court
+		QETDiagramEditor *de_open;
+		if (visible_diagrams_editors.count()) {
+			de_open = visible_diagrams_editors.first();
+		} else {
+			de_open = diagrams_editors.first();
+			de_open -> setVisible(true);
+		}
+		
+		// ouvre les fichiers dans l'editeur ainsi choisi
+		foreach(QString file, files_list) {
+			de_open -> openAndAddProject(file);
+		}
+	} else {
+		// cree un nouvel editeur qui ouvrira les fichiers
+		new QETDiagramEditor(files_list);
+	}
+}
+
+/**
+	Ouvre les fichiers elements passes en parametre. Si un element est deja
+	ouvert, la fenetre qui l'edite est activee.
+	@param files_list Fichiers a ouvrir
+*/
+void QETApp::openElementFiles(const QStringList &files_list) {
+	if (files_list.isEmpty()) return;
+	
+	// evite autant que possible les doublons dans la liste fournie
+	QSet<QString> files_set;
+	foreach(QString file, files_list) {
+		QString canonical_filepath = QFileInfo(file).canonicalFilePath();
+		if (!canonical_filepath.isEmpty()) files_set << canonical_filepath;
+	}
+	// a ce stade, tous les fichiers dans le Set existent et sont a priori differents
+	if (files_set.isEmpty()) return;
+	
+	// liste des editeurs d'element ouverts
+	QList<QETElementEditor *> element_editors = elementEditors();
+	
+	// on traite les fichiers a la queue leu leu...
+	foreach(QString element_file, files_set) {
+		bool already_opened_in_existing_element_editor = false;
+		foreach(QETElementEditor *element_editor, element_editors) {
+			if (element_editor -> isEditing(element_file)) {
+				// ce fichier est deja ouvert dans un editeur
+				already_opened_in_existing_element_editor = true;
+				element_editor -> setVisible(true);
+				element_editor -> raise();
+				element_editor -> activateWindow();
+				break;
+			}
+		}
+		if (!already_opened_in_existing_element_editor) {
+			// ce fichier n'est ouvert dans aucun editeur
+			QETElementEditor *element_editor = new QETElementEditor();
+			element_editor -> fromFile(element_file);
+		}
+	}
+}
+
+/**
+	Ouvre les elements dont l'emplacement est passe en parametre. Si un element
+	est deja ouvert, la fentre qui l'edite est activee.
+	@param locations_list Emplacements a ouvrir
+*/
+void QETApp::openElementLocations(const QList<ElementsLocation> &locations_list) {
+	if (locations_list.isEmpty()) return;
+	
+	// liste des editeurs d'element ouverts
+	QList<QETElementEditor *> element_editors = elementEditors();
+	
+	// on traite les emplacements  a la queue leu leu...
+	foreach(ElementsLocation element_location, locations_list) {
+		bool already_opened_in_existing_element_editor = false;
+		foreach(QETElementEditor *element_editor, element_editors) {
+			if (element_editor -> isEditing(element_location)) {
+				// cet emplacement est deja ouvert dans un editeur
+				already_opened_in_existing_element_editor = true;
+				element_editor -> setVisible(true);
+				element_editor -> raise();
+				element_editor -> activateWindow();
+				break;
+			}
+		}
+		if (!already_opened_in_existing_element_editor) {
+			// cet emplacement n'est ouvert dans aucun editeur
+			QETElementEditor *element_editor = new QETElementEditor();
+			element_editor -> fromLocation(element_location);
+		}
+	}
+}
+
+/**
+	Launch a new title block template editor to edit the given template
+	@param location location of the title block template to be edited
+	
+	@param duplicate if true, the template is opened for duplication, which means
+	the user will be prompter for a new template name.
+	@see QETTitleBlockTemplateEditor::setOpenForDuplication()
+*/
+void QETApp::openTitleBlockTemplate(const TitleBlockTemplateLocation &location, bool duplicate) {
+	QETTitleBlockTemplateEditor *qet_template_editor = new QETTitleBlockTemplateEditor();
+	qet_template_editor -> setOpenForDuplication(duplicate);
+	qet_template_editor -> edit(location);
+	qet_template_editor -> show();
+}
+
+/**
+	Launch a new title block template editor to edit the given template
+	@param filepath Path of the .titleblock file to be opened
+*/
+void QETApp::openTitleBlockTemplate(const QString &filepath) {
+	QETTitleBlockTemplateEditor *qet_template_editor = new QETTitleBlockTemplateEditor();
+	qet_template_editor -> edit(filepath);
+	qet_template_editor -> show();
+}
+
+/**
+	Open provided title block template files. If a title block template is already
+	opened, the adequate window is activated.
+	@param files_list Files to be opened
+*/
+void QETApp::openTitleBlockTemplateFiles(const QStringList &files_list) {
+	if (files_list.isEmpty()) return;
+	
+	// avoid duplicates in the provided files list
+	QSet<QString> files_set;
+	foreach (QString file, files_list) {
+		QString canonical_filepath = QFileInfo(file).canonicalFilePath();
+		if (!canonical_filepath.isEmpty()) files_set << canonical_filepath;
+	}
+	// here, we can assume all files in the set exist and are different
+	if (files_set.isEmpty()) return;
+	
+	// opened title block template editors
+	QList<QETTitleBlockTemplateEditor *> tbt_editors = titleBlockTemplateEditors();
+	
+	foreach(QString tbt_file, files_set) {
+		bool already_opened_in_existing_tbt_editor = false;
+		foreach(QETTitleBlockTemplateEditor *tbt_editor, tbt_editors) {
+			if (tbt_editor -> isEditing(tbt_file)) {
+				// this file is already opened
+				already_opened_in_existing_tbt_editor = true;
+				tbt_editor -> setVisible(true);
+				tbt_editor -> raise();
+				tbt_editor -> activateWindow();
+				break;
+			}
+		}
+		if (!already_opened_in_existing_tbt_editor) {
+			// this file is not opened yet
+			openTitleBlockTemplate(tbt_file);
+		}
+	}
+}
+
+/**
+	Permet a l'utilisateur de configurer QET en lancant un dialogue approprie.
+	@see ConfigDialog
+*/
+void QETApp::configureQET() {
+	// determine le widget parent a utiliser pour le dialogue
+	QWidget *parent_widget = activeWindow();
+	
+	// cree le dialogue
+	ConfigDialog cd;
+	cd.setWindowTitle(tr("Configurer QElectroTech", "window title"));
+	cd.addPage(new GeneralConfigurationPage());
+	cd.addPage(new NewDiagramPage());
+	cd.addPage(new ExportConfigPage());
+	cd.addPage(new PrintConfigPage());
+	
+	
+	// associe le dialogue a un eventuel widget parent
+	if (parent_widget) {
+		cd.setParent(parent_widget, cd.windowFlags());
+	}
+	
+	// affiche le dialogue puis evite de le lier a un quelconque widget parent
+	cd.exec();
+	cd.setParent(0, cd.windowFlags());
+}
+
+/**
+	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 l'application.
+*/
+void QETApp::aboutQET() {
+	// determine le widget parent a utiliser pour le dialogue
+	QWidget *parent_widget = activeWindow();
+	
+	// cree le dialogue si cela n'a pas deja ete fait
+	if (!about_dialog_) {
+		about_dialog_ = new AboutQET();
+	}
+	
+	// associe le dialogue a un eventuel widget parent
+	if (parent_widget) {
+#ifdef Q_WS_MAC
+		about_dialog_ -> setWindowFlags(Qt::Sheet);
+#endif
+		about_dialog_ -> setParent(parent_widget, about_dialog_ -> windowFlags());
+	}
+	
+	// affiche le dialogue puis evite de le lier a un quelconque widget parent
+	about_dialog_ -> exec();
+	about_dialog_ -> setParent(0, about_dialog_ -> windowFlags());
+}
+
+/**
+	@param window fenetre dont il faut trouver les barres d'outils et dock flottants
+	@return les barres d'outils et dock flottants de la fenetre
+*/
+QList<QWidget *> QETApp::floatingToolbarsAndDocksForMainWindow(QMainWindow *window) const {
+	QList<QWidget *> widgets;
+	foreach(QWidget *qw, topLevelWidgets()) {
+		if (!qw -> isWindow()) continue;
+		if (qobject_cast<QToolBar *>(qw) || qobject_cast<QDockWidget *>(qw)) {
+			if (qw -> parent() == window) widgets << qw;
+		}
+	}
+	return(widgets);
+}
+
+/**
+	Parse les arguments suivants :
+	  * --common-elements-dir=
+	  * --config-dir
+	  * --help
+	  * --version
+	  * -v
+	  * --license
+	Les autres arguments sont normalement des chemins de fichiers.
+	S'ils existent, ils sont juste memorises dans l'attribut arguments_files_.
+	Sinon, ils sont memorises dans l'attribut arguments_options_.
+*/
+void QETApp::parseArguments() {
+	// recupere les arguments
+	QList<QString> arguments_list(arguments());
+	
+	// enleve le premier argument : il s'agit du fichier binaire
+	arguments_list.takeFirst();
+	
+	// analyse les arguments
+	qet_arguments_ = QETArguments(arguments_list);
+	
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	if (qet_arguments_.commonElementsDirSpecified()) {
+		overrideCommonElementsDir(qet_arguments_.commonElementsDir());
+	}
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	if (qet_arguments_.commonTitleBlockTemplatesDirSpecified()) {
+		overrideCommonTitleBlockTemplatesDir(qet_arguments_.commonTitleBlockTemplatesDir());
+	}
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	if (qet_arguments_.configDirSpecified()) {
+		overrideConfigDir(qet_arguments_.configDir());
+	}
+#endif
+	
+	if (qet_arguments_.langDirSpecified()) {
+		overrideLangDir(qet_arguments_.langDir());
+	}
+	
+	if (qet_arguments_.printLicenseRequested()) {
+		printLicense();
+		non_interactive_execution_ = true;
+	}
+	if (qet_arguments_.printHelpRequested()) {
+		printHelp();
+		non_interactive_execution_ = true;
+	}
+	if (qet_arguments_.printVersionRequested()) {
+		printVersion();
+		non_interactive_execution_ = true;
+	}
+}
+
+/**
+	Initialise le splash screen si et seulement si l'execution est interactive.
+	Autrement, l'attribut splash_screen_ vaut 0.
+*/
+void QETApp::initSplashScreen() {
+	if (non_interactive_execution_) return;
+	splash_screen_ = new QSplashScreen(QPixmap(":/ico/splash.png"));
+	splash_screen_ -> show();
+	setSplashScreenStep(tr("Chargement...", "splash screen caption"));
+}
+
+/**
+	Change le texte du splash screen et prend en compte les evenements.
+	Si l'application s'execute de facon non interactive, cette methode ne fait
+	rien.
+*/
+void QETApp::setSplashScreenStep(const QString &message) {
+	if (!splash_screen_) return;
+	if (!message.isEmpty()) {
+		splash_screen_ -> showMessage(message, Qt::AlignBottom | Qt::AlignLeft);
+	}
+	processEvents();
+}
+
+/**
+	Determine et applique le langage a utiliser pour l'application
+*/
+void QETApp::initLanguage() {
+	// selectionne le langage du systeme
+	QString system_language = QLocale::system().name().left(2);
+	setLanguage(system_language);
+}
+
+/**
+	Met en place tout ce qui concerne le style graphique de l'application
+*/
+void QETApp::initStyle() {
+	initial_palette_ = palette();
+	
+	// lorsque le style Plastique est active, on le remplace par une version amelioree
+	if (qobject_cast<QPlastiqueStyle *>(style())) {
+		setStyle(new QETStyle());
+	}
+	
+	// applique ou non les couleurs de l'environnement
+	useSystemPalette(settings().value("usesystemcolors", true).toBool());
+}
+
+/**
+	Lit et prend en compte la configuration de l'application.
+	Cette methode creera, si necessaire :
+	  * le dossier de configuration
+	  * le dossier de la collection perso
+	  * the directory for custom title blocks
+*/
+void QETApp::initConfiguration() {
+	// cree les dossiers de configuration si necessaire
+	QDir config_dir(QETApp::configDir());
+	if (!config_dir.exists()) config_dir.mkpath(QETApp::configDir());
+	
+	QDir custom_elements_dir(QETApp::customElementsDir());
+	if (!custom_elements_dir.exists()) custom_elements_dir.mkpath(QETApp::customElementsDir());
+	
+	QDir custom_tbt_dir(QETApp::customTitleBlockTemplatesDir());
+	if (!custom_tbt_dir.exists()) custom_tbt_dir.mkpath(QETApp::customTitleBlockTemplatesDir());
+	
+	// lit le fichier de configuration
+	qet_settings = new QSettings(configDir() + "qelectrotech.conf", QSettings::IniFormat, this);
+	
+	// fichiers recents
+	// note : les icones doivent etre initialisees avant ces instructions (qui creent des menus en interne)
+	projects_recent_files_ = new RecentFiles("projects");
+	projects_recent_files_ -> setIconForFiles(QET::Icons::ProjectFile);
+	elements_recent_files_ = new RecentFiles("elements");
+	elements_recent_files_ -> setIconForFiles(QET::Icons::Element);
+}
+
+/**
+	Construit l'icone dans le systray et son menu
+*/
+void QETApp::initSystemTray() {
+	setSplashScreenStep(tr("Chargement... ic\364ne du systray", "splash screen caption"));
+	// initialisation des menus de l'icone dans le systray
+	menu_systray = new QMenu(tr("QElectroTech", "systray menu title"));
+	
+	quitter_qet       = new QAction(QET::Icons::ApplicationExit,       tr("&Quitter"),                                        this);
+	reduce_appli      = new QAction(QET::Icons::Hide,    tr("&Masquer"),                                        this);
+	restore_appli     = new QAction(QET::Icons::Restore,  tr("&Restaurer"),                                      this);
+	reduce_diagrams   = new QAction(QET::Icons::Hide,    tr("&Masquer tous les \351diteurs de sch\351ma"),      this);
+	restore_diagrams  = new QAction(QET::Icons::Restore,  tr("&Restaurer tous les \351diteurs de sch\351ma"),    this);
+	reduce_elements   = new QAction(QET::Icons::Hide,    tr("&Masquer tous les \351diteurs d'\351l\351ment"),   this);
+	restore_elements  = new QAction(QET::Icons::Restore,  tr("&Restaurer tous les \351diteurs d'\351l\351ment"), this);
+	reduce_templates  = new QAction(QET::Icons::Hide,      tr("&Masquer tous les \351diteurs de cartouche",   "systray submenu entry"), this);
+	restore_templates = new QAction(QET::Icons::Restore,   tr("&Restaurer tous les \351diteurs de cartouche", "systray submenu entry"), this);
+	new_diagram       = new QAction(QET::Icons::WindowNew, tr("&Nouvel \351diteur de sch\351ma"),                 this);
+	new_element       = new QAction(QET::Icons::WindowNew, tr("&Nouvel \351diteur d'\351l\351ment"),              this);
+	
+	quitter_qet   -> setStatusTip(tr("Ferme l'application QElectroTech"));
+	reduce_appli  -> setToolTip(tr("R\351duire QElectroTech dans le systray"));
+	restore_appli -> setToolTip(tr("Restaurer QElectroTech"));
+	
+	connect(quitter_qet,      SIGNAL(triggered()), this, SLOT(quitQET()));
+	connect(reduce_appli,     SIGNAL(triggered()), this, SLOT(reduceEveryEditor()));
+	connect(restore_appli,    SIGNAL(triggered()), this, SLOT(restoreEveryEditor()));
+	connect(reduce_diagrams,  SIGNAL(triggered()), this, SLOT(reduceDiagramEditors()));
+	connect(restore_diagrams, SIGNAL(triggered()), this, SLOT(restoreDiagramEditors()));
+	connect(reduce_elements,  SIGNAL(triggered()), this, SLOT(reduceElementEditors()));
+	connect(restore_elements, SIGNAL(triggered()), this, SLOT(restoreElementEditors()));
+	connect(reduce_templates, SIGNAL(triggered()), this, SLOT(reduceTitleBlockTemplateEditors()));
+	connect(restore_templates,SIGNAL(triggered()), this, SLOT(restoreTitleBlockTemplateEditors()));
+	connect(new_diagram,      SIGNAL(triggered()), this, SLOT(newDiagramEditor()));
+	connect(new_element,      SIGNAL(triggered()), this, SLOT(newElementEditor()));
+	
+	// initialisation de l'icone du systray
+	qsti = new QSystemTrayIcon(QET::Icons::QETLogo, this);
+	qsti -> setToolTip(tr("QElectroTech", "systray icon tooltip"));
+	connect(qsti, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(systray(QSystemTrayIcon::ActivationReason)));
+	qsti -> setContextMenu(menu_systray);
+	qsti -> show();
+}
+
+/**
+	Add a list of \a windows to \a menu.
+	This template function assumes it will be given a QList of pointers to
+	objects inheriting the QMainWindow class.
+	@param T the class inheriting QMainWindow
+	@param menu the menu windows will be added to
+	@param windows A list of top-level windows.
+*/
+template <class T> void QETApp::addWindowsListToMenu(QMenu *menu, const QList<T *> &windows) {
+	menu -> addSeparator();
+	foreach (QMainWindow *window, windows) {
+		QAction *current_menu = menu -> addAction(window -> windowTitle());
+		current_menu -> setCheckable(true);
+		current_menu -> setChecked(window -> isVisible());
+		connect(current_menu, SIGNAL(triggered()), &signal_map, SLOT(map()));
+		signal_map.setMapping(current_menu, window);
+	}
+}
+
+/**
+	@param url The location of a collection item (title block template,
+	element, category, ...).
+	@return the id of the project mentionned in the URL, or -1 if none could be
+	found.
+*/
+int QETApp::projectIdFromString(const QString &url) {
+	QRegExp embedded("^project([0-9]+)\\+embed.*$", Qt::CaseInsensitive);
+	if (embedded.exactMatch(url)) {
+		bool conv_ok = false;
+		int project_id = embedded.capturedTexts().at(1).toInt(&conv_ok);
+		if (conv_ok) {
+			return(project_id);
+		}
+	}
+	return(-1);
+}
+
+/**
+	@param url The location of a collection item (title block template,
+	element, category, ...).
+	@return the project mentionned in the URL, or 0 if none could be
+	found.
+*/
+QETProject *QETApp::projectFromString(const QString &url) {
+	int project_id = projectIdFromString(url);
+	if (project_id == -1) return(0);
+	return(project(project_id));
+}
+
+/// construit le menu de l'icone dans le systray
+void QETApp::buildSystemTrayMenu() {
+	menu_systray -> clear();
+	
+	// recupere les editeurs
+	QList<QETDiagramEditor *> diagrams = diagramEditors();
+	QList<QETElementEditor *> elements = elementEditors();
+	QList<QETTitleBlockTemplateEditor *> tbtemplates = titleBlockTemplateEditors();
+	fetchWindowStats(diagrams, elements, tbtemplates);
+	
+	// ajoute le bouton reduire / restaurer au menu
+	menu_systray -> addAction(every_editor_reduced ? restore_appli : reduce_appli);
+	
+	// ajoute les editeurs de schemas dans un sous-menu
+	QMenu *diagrams_submenu = menu_systray -> addMenu(tr("\311diteurs de sch\351mas"));
+	diagrams_submenu -> addAction(reduce_diagrams);
+	diagrams_submenu -> addAction(restore_diagrams);
+	diagrams_submenu -> addAction(new_diagram);
+	reduce_diagrams -> setEnabled(!diagrams.isEmpty() && !every_diagram_reduced);
+	restore_diagrams -> setEnabled(!diagrams.isEmpty() && !every_diagram_visible);
+	addWindowsListToMenu<QETDiagramEditor>(diagrams_submenu, diagrams);
+	
+	// ajoute les editeurs d'elements au menu
+	QMenu *elements_submenu = menu_systray -> addMenu(tr("\311diteurs d'\351l\351ment"));
+	elements_submenu -> addAction(reduce_elements);
+	elements_submenu -> addAction(restore_elements);
+	elements_submenu -> addAction(new_element);
+	reduce_elements -> setEnabled(!elements.isEmpty() && !every_element_reduced);
+	restore_elements -> setEnabled(!elements.isEmpty() && !every_element_visible);
+	elements_submenu -> addSeparator();
+	addWindowsListToMenu<QETElementEditor>(elements_submenu, elements);
+	
+	// add title block template editors in a submenu
+	QMenu *tbtemplates_submenu = menu_systray -> addMenu(tr("\311diteurs de cartouche", "systray menu entry"));
+	tbtemplates_submenu -> addAction(reduce_templates);
+	tbtemplates_submenu -> addAction(restore_templates);
+	reduce_templates  -> setEnabled(!tbtemplates.isEmpty() && !every_template_reduced);
+	restore_templates -> setEnabled(!tbtemplates.isEmpty() && !every_template_visible);
+	addWindowsListToMenu<QETTitleBlockTemplateEditor>(tbtemplates_submenu, tbtemplates);
+	
+	// ajoute le bouton quitter au menu
+	menu_systray -> addSeparator();
+	menu_systray -> addAction(quitter_qet);
+}
+
+/// Met a jour les booleens concernant l'etat des fenetres
+void QETApp::fetchWindowStats(
+	const QList<QETDiagramEditor *> &diagrams,
+	const QList<QETElementEditor *> &elements,
+	const QList<QETTitleBlockTemplateEditor *> &tbtemplates
+) {
+	// compte le nombre de schemas visibles
+	int visible_diagrams = 0;
+	foreach(QMainWindow *w, diagrams) if (w -> isVisible()) ++ visible_diagrams;
+	every_diagram_reduced = !visible_diagrams;
+	every_diagram_visible = visible_diagrams == diagrams.count();
+	
+	// compte le nombre de schemas visibles
+	int visible_elements = 0;
+	foreach(QMainWindow *w, elements) if (w -> isVisible()) ++ visible_elements;
+	every_element_reduced = !visible_elements;
+	every_element_visible = visible_elements == elements.count();
+	
+	// count visible template editors
+	int visible_templates = 0;
+	foreach(QMainWindow *window, tbtemplates) {
+		if (window -> isVisible()) ++ visible_templates;
+	}
+	every_template_reduced = !visible_templates;
+	every_template_visible = visible_templates == tbtemplates.count();
+	
+	// determine si tous les elements sont reduits
+	every_editor_reduced = every_element_reduced && every_diagram_reduced;
+}
+
+#ifdef Q_OS_DARWIN
+/**
+	Gere les evenements, en particulier l'evenement FileOpen sous MacOs.
+	@param e Evenement a gerer
+*/
+bool QETApp::event(QEvent *e) {
+	// gere l'ouverture de fichiers (sous MacOs)
+	if (e -> type() == QEvent::FileOpen) {
+		// nom du fichier a ouvrir
+		QString filename = static_cast<QFileOpenEvent *>(e) -> file();
+		openFiles(QStringList() << filename);
+		return(true);
+	} else {
+		return(QApplication::event(e));
+	}
+}
+#endif
+
+/**
+	Affiche l'aide et l'usage sur la sortie standard
+*/
+void QETApp::printHelp() {
+	QString help(
+		tr("Usage : ") + QFileInfo(applicationFilePath()).fileName() + tr(" [options] [fichier]...\n\n") +
+		tr("QElectroTech, une application de r\351alisation de sch\351mas \351lectriques.\n\n"
+		"Options disponibles : \n"
+		"  --help                        Afficher l'aide sur les options\n"
+		"  -v, --version                 Afficher la version\n"
+		"  --license                     Afficher la licence\n")
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+		+ tr("  --common-elements-dir=DIR     Definir le dossier de la collection d'elements\n")
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+		+ tr("  --common-tbt-dir=DIR          Definir le dossier de la collection de modeles de cartouches\n")
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+		+ tr("  --config-dir=DIR              Definir le dossier de configuration\n")
+#endif
+		+ tr("  --lang-dir=DIR                Definir le dossier contenant les fichiers de langue\n")
+	);
+	std::cout << qPrintable(help) << std::endl;
+}
+
+/**
+	Affiche la version sur la sortie standard
+*/
+void QETApp::printVersion() {
+	std::cout << qPrintable(QET::displayedVersion) << std::endl;
+}
+
+/**
+	Affiche la licence sur la sortie standard
+*/
+void QETApp::printLicense() {
+	std::cout << qPrintable(QET::license()) << std::endl;
+}
+
+/// Constructeur
+QETStyle::QETStyle() : QPlastiqueStyle() {
+}
+
+/// Destructeur
+QETStyle::~QETStyle() {
+}
+
+/// Gere les parametres de style
+int QETStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returndata) const {
+	if (hint == QStyle::SH_DialogButtonBox_ButtonsHaveIcons) {
+		return(int(true));
+	} else {
+		return(QPlastiqueStyle::styleHint(hint, option, widget, returndata));
+	}
+}
+
+/// Gere les icones standard
+QIcon QETStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option, const QWidget* widget) const {
+	switch(standardIcon) {
+		case QStyle::SP_DialogSaveButton:
+			return(QET::Icons::DocumentSave);
+		case QStyle::SP_DialogOpenButton:
+			return(QET::Icons::DocumentOpen);
+		case QStyle::SP_DialogCancelButton:
+			return(QET::Icons::DialogCancel);
+		case QStyle::SP_DialogOkButton:
+		case QStyle::SP_DialogApplyButton:
+			return(QET::Icons::DialogOk);
+		case QStyle::SP_DialogCloseButton:
+			return(QET::Icons::DocumentClose);
+		case QStyle::SP_DialogYesButton:
+			return(QET::Icons::Allowed);
+		case QStyle::SP_DialogNoButton:
+			return(QET::Icons::Forbidden);
+		case QStyle::SP_DialogResetButton:
+			return(QET::Icons::EditUndo);
+		case QStyle::SP_DialogHelpButton:
+		case QStyle::SP_DialogDiscardButton:
+			return(QIcon());
+		default:
+			return(QPlastiqueStyle::standardIconImplementation(standardIcon, option, widget));
+	}
+}
+
+/// @return une reference vers les parametres de QElectroTEch
+QSettings &QETApp::settings() {
+	return(*(instance() -> qet_settings));
+}
+
+/**
+	@param location adresse virtuelle d'un item (collection, categorie, element, ...)
+	@param prefer_collections true pour renvoyer la collection lorsque le
+	chemin correspond aussi bien a une collection qu'a sa categorie racine
+	@return l'item correspondant a l'adresse virtuelle path, ou 0 si celui-ci n'a pas ete trouve
+*/
+ElementsCollectionItem *QETApp::collectionItem(const ElementsLocation &location, bool prefer_collections) {
+	if (QETProject *target_project = location.project()) {
+		return(target_project -> embeddedCollection() -> item(location.path(), prefer_collections));
+	} else {
+		QString path(location.path());
+		if (path.startsWith("common://")) {
+			return(common_collection -> item(path, prefer_collections));
+		} else if (path.startsWith("custom://")) {
+			return(custom_collection -> item(path, prefer_collections));
+		}
+	}
+	return(0);
+}
+
+/**
+	@param location adresse virtuelle de la categorie a creer
+	@return la categorie creee, ou 0 en cas d'echec
+*/
+ElementsCategory *QETApp::createCategory(const ElementsLocation &location) {
+	if (QETProject *target_project = location.project()) {
+		return(target_project -> embeddedCollection() -> createCategory(location.path()));
+	} else {
+		QString path(location.path());
+		if (path.startsWith("common://")) {
+			return(common_collection -> createCategory(path));
+		} else if (path.startsWith("custom://")) {
+			return(custom_collection -> createCategory(path));
+		}
+	}
+	return(0);
+}
+
+/**
+	@param location adresse virtuelle de l'element a creer
+	@return l'element cree, ou 0 en cas d'echec
+*/
+ElementDefinition *QETApp::createElement(const ElementsLocation &location) {
+	if (QETProject *target_project = location.project()) {
+		return(target_project -> embeddedCollection() -> createElement(location.path()));
+	} else {
+		QString path(location.path());
+		if (path.startsWith("common://")) {
+			return(common_collection -> createElement(path));
+		} else if (path.startsWith("custom://")) {
+			return(custom_collection -> createElement(path));
+		}
+	}
+	return(0);
+}
+
+/**
+	@return la liste des projets avec leurs ids associes
+*/
+QMap<uint, QETProject *> QETApp::registeredProjects() {
+	return(registered_projects_);
+}
+
+/**
+	@param project Projet a enregistrer aupres de l'application
+	@return true si le projet a pu etre enregistre, false sinon
+	L'echec de l'enregistrement d'un projet signifie generalement qu'il est deja enregistre.
+*/
+bool QETApp::registerProject(QETProject *project) {
+	// le projet doit sembler valide
+	if (!project) return(false);
+	
+	// si le projet est deja enregistre, renvoie false
+	if (projectId(project) != -1) return(false);
+	
+	// enregistre le projet
+	registered_projects_.insert(next_project_id ++, project);
+	return(true);
+}
+
+/**
+	Annule l'enregistrement du projet project
+	@param project Projet dont il faut annuler l'enregistrement
+	@return true si l'annulation a reussi, false sinon
+	L'echec de cette methode signifie generalement que le projet n'etait pas enregistre.
+*/
+bool QETApp::unregisterProject(QETProject *project) {
+	int project_id = projectId(project);
+	
+	// si le projet n'est pas enregistre, renvoie false
+	if (project_id == -1) return(false);
+	
+	// annule l'enregistrement du projet
+	return(registered_projects_.remove(project_id) == 1);
+}
+
+/**
+	@param id Id du projet voulu
+	@return le projet correspond a l'id passe en parametre
+*/
+QETProject *QETApp::project(const uint &id) {
+	if (registered_projects_.contains(id)) {
+		return(registered_projects_[id]);
+	} else {
+		return(0);
+	}
+}
+
+/**
+	@param project Projet dont on souhaite recuperer l'id
+	@return l'id du projet en parametre si celui-ci est enregistre, -1 sinon
+*/
+int QETApp::projectId(const QETProject *project) {
+	foreach(int id, registered_projects_.keys()) {
+		if (registered_projects_[id] == project) {
+			return(id);
+		}
+	}
+	return(-1);
+}

Added: trunk/sources/qetapp.h
===================================================================
--- trunk/sources/qetapp.h	                        (rev 0)
+++ trunk/sources/qetapp.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,250 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_APP_H
+#define QET_APP_H
+#include "qetsingleapplication.h"
+#include <QTranslator>
+#include <QtGui>
+#include "elementslocation.h"
+#include "templatelocation.h"
+#include "qetarguments.h"
+
+#define QETAPP_COMMON_TBT_PROTOCOL "commontbt"
+#define QETAPP_CUSTOM_TBT_PROTOCOL "customtbt"
+
+class AboutQET;
+class QETDiagramEditor;
+class QETElementEditor;
+class ElementsCollection;
+class ElementsCollectionCache;
+class ElementsCollectionItem;
+class FileElementsCollection;
+class ElementsCategory;
+class ElementDefinition;
+class TitleBlockTemplate;
+class TitleBlockTemplatesCollection;
+class TitleBlockTemplatesFilesCollection;
+class QETProject;
+class QETTitleBlockTemplateEditor;
+class QTextOrientationSpinBoxWidget;
+class RecentFiles;
+
+/**
+	This class represents the QElectroTech application.
+*/
+class QETApp : public QETSingleApplication {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	QETApp(int &, char **);
+	virtual ~QETApp();
+	
+	private:
+	QETApp(const QETApp &);
+	
+	// methods
+	public:
+	static QETApp *instance();
+	void setLanguage(const QString &);
+	void switchLayout(Qt::LayoutDirection);
+	static void printHelp();
+	static void printVersion();
+	static void printLicense();
+	
+	static ElementsCollectionItem *collectionItem(const ElementsLocation &, bool = true);
+	static ElementsCategory *createCategory(const ElementsLocation &);
+	static ElementDefinition *createElement(const ElementsLocation &);
+	static ElementsCollection *commonElementsCollection();
+	static ElementsCollection *customElementsCollection();
+	static QList<ElementsCollection *> availableCollections();
+	static ElementsCollectionCache *collectionCache();
+	
+	static TitleBlockTemplatesFilesCollection *commonTitleBlockTemplatesCollection();
+	static TitleBlockTemplatesFilesCollection *customTitleBlockTemplatesCollection();
+	static QList<TitleBlockTemplatesCollection *> availableTitleBlockTemplatesCollections();
+	static TitleBlockTemplatesCollection *titleBlockTemplatesCollection(const QString &);
+	
+	static QString userName();
+	static QString commonElementsDir();
+	static QString customElementsDir();
+	static QString commonTitleBlockTemplatesDir();
+	static QString customTitleBlockTemplatesDir();
+	static bool registerProject(QETProject *);
+	static bool unregisterProject(QETProject *);
+	static QMap<uint, QETProject *> registeredProjects();
+	static QETProject *project(const uint &);
+	static int projectId(const QETProject *);
+	static QString configDir();
+	static QSettings &settings();
+	static QString languagesPath();
+	static QString realPath(const QString &);
+	static QString symbolicPath(const QString &);
+	static QStringList handledFileExtensions();
+	static QStringList handledFiles(const QList<QUrl> &);
+	static RecentFiles *projectsRecentFiles();
+	static RecentFiles *elementsRecentFiles();
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	public:
+	static void overrideCommonElementsDir(const QString &);
+	private:
+	static QString common_elements_dir; ///< Directory containing the common elements collection
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	public:
+	static void overrideCommonTitleBlockTemplatesDir(const QString &);
+	private:
+	static QString common_tbt_dir_; ///< Directory containing the common title block templates collection
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	public:
+	static void overrideConfigDir(const QString &);
+	private:
+	static QString config_dir; ///< Directory containing the user configuration and the custom elements collection
+#endif
+	public:
+	static void overrideLangDir(const QString &);
+	static QString lang_dir; ///< Directory containing localization files.
+	static QFont diagramTextsFont(qreal = -1.0);
+	static QETDiagramEditor *diagramEditorForFile(const QString &);
+	static QList<QETDiagramEditor *> diagramEditors();
+	static QList<QETElementEditor *> elementEditors();
+	static QList<QETElementEditor *> elementEditors(QETProject *);
+	static QList<QETTitleBlockTemplateEditor *> titleBlockTemplateEditors();
+	static QList<QETTitleBlockTemplateEditor *> titleBlockTemplateEditors(QETProject *);
+	static QTextOrientationSpinBoxWidget *createTextOrientationSpinBoxWidget();
+	static TitleBlockTemplate *defaultTitleBlockTemplate();
+	
+	protected:
+#ifdef Q_OS_DARWIN
+	bool event(QEvent *);
+#endif
+	
+	// attributes
+	private:
+	QTranslator qtTranslator;
+	QTranslator qetTranslator;
+	QSystemTrayIcon *qsti;
+	QSplashScreen *splash_screen_;
+	QMenu *menu_systray;
+	QAction *quitter_qet;
+	QAction *reduce_appli;
+	QAction *restore_appli;
+	QAction *reduce_diagrams;
+	QAction *restore_diagrams;
+	QAction *reduce_elements;
+	QAction *restore_elements;
+	QAction *reduce_templates;
+	QAction *restore_templates;
+	QAction *new_diagram;
+	QAction *new_element;
+	QHash<QMainWindow *, QByteArray> window_geometries;
+	QHash<QMainWindow *, QByteArray> window_states;
+	bool every_editor_reduced;
+	bool every_diagram_reduced;
+	bool every_diagram_visible;
+	bool every_element_reduced;
+	bool every_element_visible;
+	bool every_template_reduced;
+	bool every_template_visible;
+	QSignalMapper signal_map;
+	QSettings *qet_settings;
+	QETArguments qet_arguments_;        ///< Comand-line arguments parser
+	bool non_interactive_execution_;    ///< Whether the application will end without any user interaction
+	QPalette initial_palette_;          ///< System color palette
+	
+	static FileElementsCollection *common_collection;
+	static FileElementsCollection *custom_collection;
+	static TitleBlockTemplatesFilesCollection *common_tbt_collection_;
+	static TitleBlockTemplatesFilesCollection *custom_tbt_collection_;
+	static ElementsCollectionCache *collections_cache_;
+	static QMap<uint, QETProject *> registered_projects_;
+	static uint next_project_id;
+	static RecentFiles *projects_recent_files_;
+	static RecentFiles *elements_recent_files_;
+	static AboutQET *about_dialog_;
+	static TitleBlockTemplate *default_titleblock_template_;
+	
+	public slots:
+	void systray(QSystemTrayIcon::ActivationReason);
+	void reduceEveryEditor();
+	void restoreEveryEditor();
+	void reduceDiagramEditors();
+	void restoreDiagramEditors();
+	void reduceElementEditors();
+	void restoreElementEditors();
+	void reduceTitleBlockTemplateEditors();
+	void restoreTitleBlockTemplateEditors();
+	void newDiagramEditor();
+	void newElementEditor();
+	bool closeEveryEditor();
+	void setMainWindowVisible(QMainWindow *, bool);
+	void invertMainWindowVisibility(QWidget *);
+	void useSystemPalette(bool);
+	void quitQET();
+	void checkRemainingWindows();
+	void messageReceived(const QString &);
+	void openFiles(const QETArguments &);
+	void openProjectFiles(const QStringList &);
+	void openElementFiles(const QStringList &);
+	void openElementLocations(const QList<ElementsLocation> &);
+	void openTitleBlockTemplate(const TitleBlockTemplateLocation &, bool = false);
+	void openTitleBlockTemplate(const QString &);
+	void openTitleBlockTemplateFiles(const QStringList &);
+	void configureQET();
+	void aboutQET();
+	
+	private slots:
+	void cleanup();
+	
+	private:
+	template <class T> QList<T *> detectWindows() const;
+	template <class T> void setMainWindowsVisible(bool);
+	QList<QWidget *> floatingToolbarsAndDocksForMainWindow(QMainWindow *) const;
+	void parseArguments();
+	void initSplashScreen();
+	void setSplashScreenStep(const QString & = QString());
+	void initLanguage();
+	void initStyle();
+	void initConfiguration();
+	void initSystemTray();
+	void buildSystemTrayMenu();
+	void fetchWindowStats(
+		const QList<QETDiagramEditor *> &,
+		const QList<QETElementEditor *> &,
+		const QList<QETTitleBlockTemplateEditor *> &
+	);
+	template <class T> void addWindowsListToMenu(QMenu *, const QList<T *> &);
+	static int projectIdFromString(const QString &);
+	static QETProject *projectFromString(const QString &);
+};
+
+/**
+	This class represents the custom QElectroTech style.
+	It implements simple modification such as extra standard icons.
+*/
+class QETStyle : public QPlastiqueStyle {
+	Q_OBJECT
+	public:
+	QETStyle();
+	virtual ~QETStyle();
+	virtual int styleHint(StyleHint hint, const QStyleOption * = 0, const QWidget * = 0, QStyleHintReturn * = 0) const;
+	
+	protected slots:
+	virtual QIcon standardIconImplementation(StandardPixmap, const QStyleOption * = 0, const QWidget * = 0) const;
+};
+#endif

Added: trunk/sources/qetarguments.cpp
===================================================================
--- trunk/sources/qetarguments.cpp	                        (rev 0)
+++ trunk/sources/qetarguments.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,378 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetarguments.h"
+#include "titleblock/templatescollection.h"
+
+/**
+	Constructeur par defaut
+	Cree un objet sans argument.
+*/
+QETArguments::QETArguments(QObject *parent) :
+	QObject(parent),
+	print_help_(false),
+	print_license_(false),
+	print_version_(false)
+{
+}
+
+/**
+	Constructeur
+	@param args Arguments a analyser et memoriser
+	@param parent QObject parent
+*/
+QETArguments::QETArguments(const QList<QString> &args, QObject *parent) :
+	QObject(parent),
+	print_help_(false),
+	print_license_(false),
+	print_version_(false)
+{
+	parseArguments(args);
+}
+
+/**
+	Constructeur de copie - la copie reprend le parent de l'original
+	@param qet_arguments Objet a copier
+*/
+QETArguments::QETArguments(const QETArguments &qet_arguments) :
+	QObject(qet_arguments.parent()),
+	project_files_(qet_arguments.project_files_),
+	element_files_(qet_arguments.element_files_),
+	tbt_files_(qet_arguments.tbt_files_),
+	options_(qet_arguments.options_),
+	unknown_options_(qet_arguments.unknown_options_),
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	common_elements_dir_(qet_arguments.common_elements_dir_),
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+common_tbt_dir_(qet_arguments.common_tbt_dir_),
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	config_dir_(qet_arguments.config_dir_),
+#endif
+	lang_dir_(qet_arguments.lang_dir_),
+	print_help_(qet_arguments.print_help_),
+	print_license_(qet_arguments.print_license_),
+	print_version_(qet_arguments.print_version_)
+{
+}
+
+/**
+	Operateur d'affectation - la copie ne reprend pas le parent de l'original
+	@param qet_arguments Objet a copier
+*/
+QETArguments &QETArguments::operator=(const QETArguments &qet_arguments) {
+	project_files_   = qet_arguments.project_files_;
+	element_files_   = qet_arguments.element_files_;
+	tbt_files_       = qet_arguments.tbt_files_;
+	options_         = qet_arguments.options_;
+	unknown_options_ = qet_arguments.unknown_options_;
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	common_elements_dir_ = qet_arguments.common_elements_dir_;
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	common_tbt_dir_ = qet_arguments.common_tbt_dir_;
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	config_dir_ = qet_arguments.config_dir_;
+#endif
+	lang_dir_        = qet_arguments.lang_dir_;
+	print_help_      = qet_arguments.print_help_;
+	print_license_   = qet_arguments.print_license_;
+	print_version_   = qet_arguments.print_version_;
+	return(*this);
+}
+
+/**
+	Destructeur
+*/
+QETArguments::~QETArguments() {
+}
+
+/**
+	Definit les arguments de cet objet.
+	Si cet objet contenait deja des arguments, ceux-ci sont oublies.
+	@param args Arguments a analyser et memoriser
+*/
+void QETArguments::setArguments(const QList<QString> &args) {
+	parseArguments(args);
+}
+
+/**
+	@return tous les arguments (projets et elements) passes en parametres
+	dans l'ordre suivant : options connues puis inconnues, fichiers de types
+	projet puis element.
+*/
+QList<QString> QETArguments::arguments() const {
+	return(options_ + unknown_options_ + project_files_ + element_files_ + tbt_files_);
+}
+
+/**
+	@return tous les fichiers (projets et elements) passes en parametres.
+	Les fichiers de type projet viennent avant les fichiers de type element.
+*/
+QList<QString> QETArguments::files() const {
+	return(project_files_ + element_files_ + tbt_files_);
+}
+
+/**
+	@return les fichiers de type projet
+*/
+QList<QString> QETArguments::projectFiles() const {
+	return(project_files_);
+}
+
+/**
+	@return les fichiers de type element
+*/
+QList<QString> QETArguments::elementFiles() const {
+	return(element_files_);
+}
+
+/**
+	@return title block template files
+*/
+QList<QString> QETArguments::titleBlockTemplateFiles() const {
+	return(tbt_files_);
+}
+
+/**
+	@return les options reconnues
+*/
+QList<QString> QETArguments::options() const {
+	return(options_);
+}
+
+/**
+	@return les options non reconnues
+*/
+QList<QString> QETArguments::unknownOptions() const {
+	return(unknown_options_);
+}
+
+/**
+	Oublie tous les arguments de cet objet
+*/
+void QETArguments::clear() {
+	project_files_.clear();
+	element_files_.clear();
+	options_.clear();
+	unknown_options_.clear();
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	common_elements_dir_.clear();
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	common_tbt_dir_.clear();
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	config_dir_.clear();
+#endif
+}
+
+/**
+	Analyse des arguments et les retient dans cet objet.
+	Si cet objet contenait deja des arguments, ceux-ci sont oublies.
+	@param arguments Arguments a analyser
+*/
+void QETArguments::parseArguments(const QList<QString> &arguments) {
+	// oublie les eventuels arguments precedents
+	clear();
+	
+	// separe les fichiers des options
+	foreach(QString argument, arguments) {
+		QFileInfo argument_info(argument);
+		if (argument_info.exists()) {
+		// on exprime les chemins des fichiers en absolu
+			QString can_argument = argument_info.canonicalFilePath();
+			handleFileArgument(can_argument);
+		} else {
+			handleOptionArgument(argument);
+		}
+	}
+}
+
+/**
+	Gere les arguments correspondant a un fichier existant.
+*/
+void QETArguments::handleFileArgument(const QString &file) {
+	if (file.endsWith(".elmt")) {
+		if (!element_files_.contains(file)) {
+			element_files_ << file;
+		}
+	} else if (file.endsWith(TITLEBLOCKS_FILE_EXTENSION)) {
+		if (!tbt_files_.contains(file)) {
+			tbt_files_ << file;
+		}
+	} else {
+		if (!project_files_.contains(file)) {
+			project_files_ << file;
+		}
+	}
+}
+
+/**
+	Gere les arguments correspondant potentiellement a une option.
+	Les options reconnues sont : 
+	  * --common-elements-dir=
+	  * --common-tbt-dir
+	  * --config-dir=
+	  * --lang-dir=
+	  * --help
+	  * --version
+	  * -v
+	  * --license
+*/
+void QETArguments::handleOptionArgument(const QString &option) {
+	if (option == QString("--help")) {
+		print_help_ = true;
+		options_ << option;
+		return;
+	} else if (option == QString("--version") || option == QString("-v")) {
+		print_version_ = true;
+		options_ << option;
+		return;
+	} else if (option == QString("--license")) {
+		print_license_ = true;
+		options_ << option;
+		return;
+	}
+	
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	QString ced_arg("--common-elements-dir=");
+	if (option.startsWith(ced_arg)) {
+		common_elements_dir_ = option.mid(ced_arg.length());
+		return;
+	}
+	
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	QString ctbtd_arg("--common-tbt-dir=");
+	if (option.startsWith(ctbtd_arg)) {
+		common_tbt_dir_ = option.mid(ctbtd_arg.length());
+		return;
+	}
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	QString cd_arg("--config-dir=");
+	if (option.startsWith(cd_arg)) {
+		config_dir_ = option.mid(cd_arg.length());
+		return;
+	}
+	
+#endif
+	
+	QString ld_arg("--lang-dir=");
+	if (option.startsWith(ld_arg)) {
+		lang_dir_ = option.mid(ld_arg.length());
+		return;
+	}
+	
+	// a ce stade, l'option est inconnue
+	unknown_options_ << option;
+}
+
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+/**
+	@return true si l'utilisateur a specifie un dossier pour la collection
+	commune.
+*/
+bool QETArguments::commonElementsDirSpecified() const {
+	return(!common_elements_dir_.isEmpty());
+}
+
+/**
+	@return le dossier de la collection commune specifie par l'utilisateur.
+	Si l'utilisateur n'en a pas specifie, une chaine vide est retournee.
+*/
+QString QETArguments::commonElementsDir() const {
+	return(common_elements_dir_);
+}
+#endif
+
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+/**
+	@return true if the user has specified a directory for the common title
+	block templates collection
+*/
+bool QETArguments::commonTitleBlockTemplatesDirSpecified() const {
+	return(!common_tbt_dir_.isEmpty());
+}
+
+/**
+	@return the directory of the common title block templates collection
+	specified by the user. If none were specified, return an empty string.
+*/
+QString QETArguments::commonTitleBlockTemplatesDir() const {
+	return(common_tbt_dir_);
+}
+#endif
+
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+/**
+	@return true si l'utilisateur a specifie un dossier pour la configuration.
+*/
+bool QETArguments::configDirSpecified() const {
+	return(!config_dir_.isEmpty());
+}
+
+/**
+	@return le dossier de configuration specifie par l'utilisateur.
+	Si l'utilisateur n'en a pas specifie, une chaine vide est retournee.
+*/
+QString QETArguments::configDir() const {
+	return(config_dir_);
+}
+#endif
+
+/**
+	@return true si l'utilisateur a specifie un dossier pour les fichiers de langue
+*/
+bool QETArguments::langDirSpecified() const {
+	return(!lang_dir_.isEmpty());
+}
+
+/**
+	@return le dossier de langue specifie par l'utilisateur.
+	Si l'utilisateur n'en a pas specifie, une chaine vide est retournee.
+*/
+QString QETArguments::langDir() const {
+	return(lang_dir_);
+}
+
+/**
+	@return true si les arguments comportent une demande d'affichage de l'aide,
+	false sinon
+*/
+bool QETArguments::printHelpRequested() const {
+	return(print_help_);
+}
+
+/**
+	@return true si les arguments comportent une demande d'affichage de la
+	licence, false sinon
+*/
+bool QETArguments::printLicenseRequested() const {
+	return(print_license_);
+}
+
+/**
+	@return true si les arguments comportent une demande d'affichage de la
+	version, false sinon
+*/
+bool QETArguments::printVersionRequested() const {
+	return(print_version_);
+}

Added: trunk/sources/qetarguments.h
===================================================================
--- trunk/sources/qetarguments.h	                        (rev 0)
+++ trunk/sources/qetarguments.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,92 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_ARGUMENTS_H
+#define QET_ARGUMENTS_H
+#include <QtCore>
+/**
+	This class represents a set of arguments the application has received as
+	parameters. Initialized from a list of strings, an instance of this class
+	provides access to the differents options and files given on the command line.
+*/
+class QETArguments : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETArguments(QObject * = 0);
+	QETArguments(const QList<QString> &, QObject * = 0);
+	QETArguments(const QETArguments &);
+	QETArguments &operator=(const QETArguments &);
+	virtual ~QETArguments();
+	
+	// methods
+	public:
+	virtual void setArguments(const QList<QString> &);
+	virtual QList<QString> arguments() const;
+	virtual QList<QString> files() const;
+	virtual QList<QString> projectFiles() const;
+	virtual QList<QString> elementFiles() const;
+	virtual QList<QString> titleBlockTemplateFiles() const;
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	virtual bool commonElementsDirSpecified() const;
+	virtual QString commonElementsDir() const;
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	virtual bool commonTitleBlockTemplatesDirSpecified() const;
+	virtual QString commonTitleBlockTemplatesDir() const;
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	virtual bool configDirSpecified() const;
+	virtual QString configDir() const;
+#endif
+	virtual bool langDirSpecified() const;
+	virtual QString langDir() const;
+	virtual bool printHelpRequested() const;
+	virtual bool printLicenseRequested() const;
+	virtual bool printVersionRequested() const;
+	virtual QList<QString> options() const;
+	virtual QList<QString> unknownOptions() const;
+	
+	private:
+	void clear();
+	void parseArguments(const QList<QString> &);
+	void handleFileArgument(const QString &);
+	void handleOptionArgument(const QString &);
+	
+	// attributes
+	private:
+	QList<QString> project_files_;
+	QList<QString> element_files_;
+	QList<QString> tbt_files_;
+	QList<QString> options_;
+	QList<QString> unknown_options_;
+#ifdef QET_ALLOW_OVERRIDE_CED_OPTION
+	QString common_elements_dir_;
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
+	QString common_tbt_dir_;
+#endif
+#ifdef QET_ALLOW_OVERRIDE_CD_OPTION
+	QString config_dir_;
+#endif
+	QString lang_dir_;
+	bool print_help_;
+	bool print_license_;
+	bool print_version_;
+};
+#endif

Added: trunk/sources/qetdiagrameditor.cpp
===================================================================
--- trunk/sources/qetdiagrameditor.cpp	                        (rev 0)
+++ trunk/sources/qetdiagrameditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1978 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetdiagrameditor.h"
+#include "qetapp.h"
+#include "diagramcontent.h"
+#include "diagramview.h"
+#include "diagram.h"
+#include "qetgraphicsitem/element.h"
+#include "elementspanelwidget.h"
+#include "conductorpropertieswidget.h"
+#include "qetgraphicsitem/customelement.h"
+#include "qetproject.h"
+#include "projectview.h"
+#include "recentfiles.h"
+#include "qeticons.h"
+#include "qetelementeditor.h"
+#include "qetmessagebox.h"
+#include "qetresult.h"
+#include "genericpanel.h"
+#include "nomenclature.h"
+
+#include "ui/dialogautonum.h"
+
+#include <QMessageBox>
+
+/**
+	constructeur
+	@param files Liste de fichiers a ouvrir
+	@param parent le widget parent de la fenetre principale
+ */
+QETDiagramEditor::QETDiagramEditor(const QStringList &files, QWidget *parent) :
+	QETMainWindow(parent),
+	open_dialog_dir(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)),
+	can_update_actions(true)
+{
+	// mise en place de l'interface MDI au centre de l'application
+	setCentralWidget(&workspace);
+	
+	// nomme l'objet afin qu'il soit reperable par les feuilles de style
+	workspace.setBackground(QBrush(Qt::NoBrush));
+	workspace.setObjectName("mdiarea");
+	
+#if QT_VERSION >= 0x040800
+	workspace.setTabsClosable(true);
+#endif
+	
+	// mise en place du signalmapper
+	connect(&windowMapper, SIGNAL(mapped(QWidget *)), this, SLOT(activateWidget(QWidget *)));
+	
+	// titre de la fenetre
+	setWindowTitle(tr("QElectroTech", "window title"));
+	
+	// icone de la fenetre
+	setWindowIcon(QET::Icons::QETLogo);
+	
+	// barre de statut de la fenetre
+	statusBar() -> showMessage(tr("QElectroTech", "status bar message"));
+	
+	// ajout du panel d'Elements en tant que QDockWidget
+	qdw_pa = new QDockWidget(tr("Panel d'\351l\351ments", "dock title"), this);
+	qdw_pa -> setObjectName("elements panel");
+	qdw_pa -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+	qdw_pa -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	qdw_pa -> setMinimumWidth(160);
+	qdw_pa -> setWidget(pa = new ElementsPanelWidget(qdw_pa));
+	
+	connect(pa, SIGNAL(requestForDiagram(Diagram *)),                     this, SLOT(activateDiagram(Diagram *)));
+	connect(pa, SIGNAL(requestForProject(QETProject *)),                  this, SLOT(activateProject(QETProject *)));
+	connect(pa, SIGNAL(requestForProjectClosing(QETProject *)),           this, SLOT(closeProject(QETProject *)));
+	connect(pa, SIGNAL(requestForProjectPropertiesEdition(QETProject *)), this, SLOT(editProjectProperties(QETProject *)));
+	connect(pa, SIGNAL(requestForDiagramPropertiesEdition(Diagram *)),    this, SLOT(editDiagramProperties(Diagram *)));
+	connect(pa, SIGNAL(requestForNewDiagram(QETProject *)),               this, SLOT(addDiagramToProject(QETProject *)));
+	connect(pa, SIGNAL(requestForDiagramDeletion(Diagram *)),             this, SLOT(removeDiagram(Diagram *)));
+	connect(pa, SIGNAL(requestForDiagramMoveUp(Diagram *)),               this, SLOT(moveDiagramUp(Diagram *)));
+	connect(pa, SIGNAL(requestForDiagramMoveDown(Diagram *)),             this, SLOT(moveDiagramDown(Diagram *)));
+	
+	qdw_undo = new QDockWidget(tr("Annulations", "dock title"));
+	qdw_undo -> setObjectName("diagram_undo");
+	qdw_pa -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+	qdw_undo -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	qdw_undo -> setMinimumWidth(160);
+	tabifyDockWidget(qdw_pa, qdw_undo);
+	QUndoView *undo_view = new QUndoView(&undo_group, this);
+	undo_view -> setEmptyLabel(tr("Aucune modification"));
+	undo_view -> setStatusTip(tr("Cliquez sur une action pour revenir en arri\350re dans l'\351dition de votre sch\351ma", "Status tip"));
+	undo_view -> setWhatsThis(tr("Ce panneau liste les diff\351rentes actions effectu\351es sur le sch\351ma courant. Cliquer sur une action permet de revenir \340 l'\351tat du sch\351ma juste apr\350s son application.", "\"What's this\" tip"));
+	qdw_undo -> setWidget(undo_view);
+	
+	addDockWidget(Qt::LeftDockWidgetArea, qdw_undo);
+	tabifyDockWidget(qdw_undo, qdw_pa);
+	
+	// mise en place des actions
+	actions();
+	
+	// mise en place de la barre d'outils
+	toolbar();
+	
+	// mise en place des menus
+	menus();
+	
+	// la fenetre est maximisee par defaut
+	setMinimumSize(QSize(500, 350));
+	setWindowState(Qt::WindowMaximized);
+	
+	// connexions signaux / slots pour une interface sensee
+	connect(&workspace,                SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(slot_updateWindowsMenu()));
+	connect(&workspace,                SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(slot_updateActions()));
+	connect(QApplication::clipboard(), SIGNAL(dataChanged()),              this, SLOT(slot_updatePasteAction()));
+	
+	// lecture des parametres
+	readSettings();
+	
+	// affichage
+	show();
+	
+	// si des chemins de fichiers valides sont passes en arguments
+	uint opened_projects = 0;
+	if (files.count()) {
+		// alors on ouvre ces fichiers
+		foreach(QString file, files) {
+			bool project_opening = openAndAddProject(file, false);
+			if (project_opening) {
+				++ opened_projects;
+			}
+		}
+	}
+	
+	// si aucun schema n'a ete ouvert jusqu'a maintenant, on ouvre un nouveau schema
+	if (!opened_projects) newProject();
+}
+
+/**
+	Destructeur
+*/
+QETDiagramEditor::~QETDiagramEditor() {
+}
+
+/**
+	Permet de quitter l'application lors de la fermeture de la fenetre principale
+	@param qce Le QCloseEvent correspondant a l'evenement de fermeture
+*/
+void QETDiagramEditor::closeEvent(QCloseEvent *qce) {
+	// quitte directement s'il n'y a aucun projet ouvert
+	bool can_quit = true;
+	if (openedProjects().count()) {
+		// s'assure que la fenetre soit visible s'il y a des projets a fermer
+		if (!isVisible() || isMinimized()) {
+			if (isMaximized()) showMaximized();
+			else showNormal();
+		}
+		// sinon demande la permission de fermer chaque projet
+		foreach(ProjectView *project, openedProjects()) {
+			if (!closeProject(project)) {
+				can_quit = false;
+				qce -> ignore();
+				break;
+			}
+		}
+	}
+	if (can_quit) {
+		writeSettings();
+		setAttribute(Qt::WA_DeleteOnClose);
+		qce -> accept();
+	}
+}
+
+/**
+	Mise en place des actions
+*/
+void QETDiagramEditor::actions() {
+	// icones et labels
+	new_file          = new QAction(QET::Icons::DocumentNew,           tr("&Nouveau"),                             this);
+	open_file         = new QAction(QET::Icons::DocumentOpen,          tr("&Ouvrir"),                              this);
+	close_file        = new QAction(QET::Icons::DocumentClose,         tr("&Fermer"),                              this);
+	save_file         = new QAction(QET::Icons::DocumentSave,          tr("&Enregistrer"),                         this);
+	save_file_as      = new QAction(QET::Icons::DocumentSaveAs,        tr("Enregistrer sous"),                     this);
+	save_cur_diagram  = new QAction(QET::Icons::DocumentSaveAll,       tr("&Enregistrer le sch\351ma courant"),    this);
+	import_diagram    = new QAction(QET::Icons::DocumentImport,        tr("&Importer"),                            this);
+	export_diagram    = new QAction(QET::Icons::DocumentExport,        tr("E&xporter"),                            this);
+	print             = new QAction(QET::Icons::DocumentPrint,         tr("Imprimer"),                             this);
+	quit_editor       = new QAction(QET::Icons::ApplicationExit,       tr("&Quitter"),                             this);
+	
+	undo = undo_group.createUndoAction(this, tr("Annuler"));
+	undo -> setIcon(QET::Icons::EditUndo);
+	redo = undo_group.createRedoAction(this, tr("Refaire"));
+	redo -> setIcon(QET::Icons::EditRedo);
+	cut               = new QAction(QET::Icons::EditCut,               tr("Co&uper"),                              this);
+	copy              = new QAction(QET::Icons::EditCopy,              tr("Cop&ier"),                              this);
+	paste             = new QAction(QET::Icons::EditPaste,             tr("C&oller"),                              this);
+	select_all        = new QAction(QET::Icons::EditSelectAll,         tr("Tout s\351lectionner"),                 this);
+	select_nothing    = new QAction(                                   tr("D\351s\351lectionner tout"),            this);
+	select_invert     = new QAction(                                   tr("Inverser la s\351lection"),             this);
+	delete_selection  = new QAction(QET::Icons::EditDelete,            tr("Supprimer"),                            this);
+	rotate_selection  = new QAction(QET::Icons::ObjectRotateRight,     tr("Pivoter"),                              this);
+	rotate_texts      = new QAction(QET::Icons::ObjectRotateRight,     tr("Orienter les textes"),                  this);
+	find_element      = new QAction(                                   tr("Retrouver dans le panel"),              this);
+	edit_selection    = new QAction(QET::Icons::ElementEdit,           tr("\311diter l'item s\351lectionn\351"),            this);
+	selection_prop    = new QAction(QET::Icons::DialogInformation,     tr("Propri\351t\351s de la s\351lection"),  this);
+	conductor_reset   = new QAction(QET::Icons::ConductorSettings,     tr("R\351initialiser les conducteurs"),     this);
+	infos_diagram     = new QAction(QET::Icons::DialogInformation,     tr("Propri\351t\351s du sch\351ma"),        this);
+	add_text          = new QAction(QET::Icons::PartTextField,         tr("Ajouter un champ de texte"),            this);
+	add_column        = new QAction(QET::Icons::EditTableInsertColumnRight, tr("Ajouter une colonne"),             this);
+	add_image		  = new QAction(QET::Icons::adding_image,          tr("Ajouter une image"),             this);
+	remove_column     = new QAction(QET::Icons::EditTableDeleteColumn,      tr("Enlever une colonne"),             this);
+	add_row           = new QAction(QET::Icons::EditTableInsertRowUnder,    tr("Ajouter une ligne"),               this);
+	remove_row        = new QAction(QET::Icons::EditTableDeleteRow,         tr("Enlever une ligne"),               this);
+	
+	prj_edit_prop     = new QAction(QET::Icons::DialogInformation,     tr("Propri\351t\351s du projet"),           this);
+	prj_add_diagram   = new QAction(QET::Icons::DiagramAdd,            tr("Ajouter un sch\351ma"),                 this);
+	prj_del_diagram   = new QAction(QET::Icons::DiagramDelete,         tr("Supprimer le sch\351ma"),               this);
+	prj_clean         = new QAction(QET::Icons::EditClear,             tr("Nettoyer le projet"),                   this);
+	prj_diagramNum    = new QAction(QET::Icons::ConductorSettings,     tr("Annoter les sch\351mas"),        this);
+	prj_nomenclature  = new QAction(QET::Icons::DocumentExport,        tr("Exporter une nomenclature (beta)"),     this);
+	
+	zoom_in           = new QAction(QET::Icons::ZoomIn,                tr("Zoom avant"),                           this);
+	zoom_out          = new QAction(QET::Icons::ZoomOut,               tr("Zoom arri\350re"),                      this);
+	zoom_content      = new QAction(QET::Icons::ZoomDraw,              tr("Zoom sur le contenu"),                  this);
+	zoom_fit          = new QAction(QET::Icons::ZoomFitBest,           tr("Zoom adapt\351"),                       this);
+	zoom_reset        = new QAction(QET::Icons::ZoomOriginal,          tr("Pas de zoom"),                          this);
+	
+	tabbed_view_mode  = new QAction(                                   tr("en utilisant des onglets"),             this);
+	windowed_view_mode= new QAction(                                   tr("en utilisant des fen\352tres"),         this);
+	
+	mode_selection    = new QAction(QET::Icons::PartSelect,            tr("Mode Selection"),                       this);
+	mode_visualise    = new QAction(QET::Icons::ViewMove,              tr("Mode Visualisation"),                   this);
+	
+	tile_window        = new QAction(                                  tr("&Mosa\357que"),                         this);
+	cascade_window     = new QAction(                                  tr("&Cascade"),                             this);
+	next_window        = new QAction(                                  tr("Projet suivant"),                       this);
+	prev_window        = new QAction(                                  tr("Projet pr\351c\351dent"),               this);
+	
+	// raccourcis clavier
+	new_file          -> setShortcut(QKeySequence::New);
+	open_file         -> setShortcut(QKeySequence::Open);
+	close_file        -> setShortcut(QKeySequence::Close);
+	save_file         -> setShortcut(QKeySequence::Save);
+	import_diagram    -> setShortcut(QKeySequence(tr("Ctrl+Shift+I")));
+	export_diagram    -> setShortcut(QKeySequence(tr("Ctrl+Shift+X")));
+	print             -> setShortcut(QKeySequence(QKeySequence::Print));
+	quit_editor       -> setShortcut(QKeySequence(tr("Ctrl+Q")));
+	
+	undo              -> setShortcut(QKeySequence::Undo);
+	redo              -> setShortcut(QKeySequence::Redo);
+	cut               -> setShortcut(QKeySequence::Cut);
+	copy              -> setShortcut(QKeySequence::Copy);
+	paste             -> setShortcut(QKeySequence::Paste);
+	select_all        -> setShortcut(QKeySequence::SelectAll);
+	select_nothing    -> setShortcut(QKeySequence(tr("Ctrl+Shift+A")));
+	select_invert     -> setShortcut(QKeySequence(tr("Ctrl+I")));
+#ifndef Q_WS_MAC
+	delete_selection  -> setShortcut(QKeySequence(Qt::Key_Delete));
+#else
+	delete_selection  -> setShortcut(QKeySequence(tr("Backspace")));
+#endif
+	
+	rotate_selection  -> setShortcut(QKeySequence(tr("Space")));
+	rotate_texts      -> setShortcut(QKeySequence(tr("Ctrl+Space")));
+	selection_prop    -> setShortcut(QKeySequence(tr("Ctrl+J")));
+	conductor_reset   -> setShortcut(QKeySequence(tr("Ctrl+K")));
+	infos_diagram     -> setShortcut(QKeySequence(tr("Ctrl+L")));
+	edit_selection	  -> setShortcut(QKeySequence(tr("Ctrl+E")));
+	
+	prj_add_diagram   -> setShortcut(QKeySequence(tr("Ctrl+T")));
+	
+	zoom_in           -> setShortcut(QKeySequence::ZoomIn);
+	zoom_out          -> setShortcut(QKeySequence::ZoomOut);
+	zoom_content      -> setShortcut(QKeySequence(tr("Ctrl+8")));
+	zoom_fit          -> setShortcut(QKeySequence(tr("Ctrl+9")));
+	zoom_reset        -> setShortcut(QKeySequence(tr("Ctrl+0")));
+	
+	next_window       -> setShortcut(QKeySequence::NextChild);
+	prev_window       -> setShortcut(QKeySequence::PreviousChild);
+	
+	// affichage dans la barre de statut
+	new_file          -> setStatusTip(tr("Cr\351e un nouveau sch\351ma", "status bar tip"));
+	open_file         -> setStatusTip(tr("Ouvre un sch\351ma existant", "status bar tip"));
+	close_file        -> setStatusTip(tr("Ferme le sch\351ma courant", "status bar tip"));
+	save_file         -> setStatusTip(tr("Enregistre le projet courant et tous ses sch\351mas", "status bar tip"));
+	save_file_as      -> setStatusTip(tr("Enregistre le project courant avec un autre nom de fichier", "status bar tip"));
+	save_cur_diagram  -> setStatusTip(tr("Enregistre le sch\351ma courant du projet courant", "status bar tip"));
+	import_diagram    -> setStatusTip(tr("Importe un sch\351ma dans le sch\351ma courant", "status bar tip"));
+	export_diagram    -> setStatusTip(tr("Exporte le sch\351ma courant dans un autre format", "status bar tip"));
+	print             -> setStatusTip(tr("Imprime le sch\351ma courant", "status bar tip"));
+	quit_editor       -> setStatusTip(tr("Ferme l'application QElectroTech", "status bar tip"));
+	
+	undo              -> setStatusTip(tr("Annule l'action pr\351c\351dente", "status bar tip"));
+	redo              -> setStatusTip(tr("Restaure l'action annul\351e", "status bar tip"));
+	cut               -> setStatusTip(tr("Transf\350re les \351l\351ments s\351lectionn\351s dans le presse-papier", "status bar tip"));
+	copy              -> setStatusTip(tr("Copie les \351l\351ments s\351lectionn\351s dans le presse-papier", "status bar tip"));
+	paste             -> setStatusTip(tr("Place les \351l\351ments du presse-papier sur le sch\351ma", "status bar tip"));
+	select_all        -> setStatusTip(tr("S\351lectionne tous les \351l\351ments du sch\351ma", "status bar tip"));
+	select_nothing    -> setStatusTip(tr("D\351s\351lectionne tous les \351l\351ments du sch\351ma", "status bar tip"));
+	select_invert     -> setStatusTip(tr("D\351s\351lectionne les \351l\351ments s\351lectionn\351s et s\351lectionne les \351l\351ments non s\351lectionn\351s", "status bar tip"));
+	delete_selection  -> setStatusTip(tr("Enl\350ve les \351l\351ments s\351lectionn\351s du sch\351ma", "status bar tip"));
+	rotate_selection  -> setStatusTip(tr("Pivote les \351l\351ments et textes s\351lectionn\351s", "status bar tip"));
+	rotate_texts      -> setStatusTip(tr("Pivote les textes s\351lectionn\351s \340 un angle pr\351cis", "status bar tip"));
+	find_element      -> setStatusTip(tr("Retrouve l'\351l\351ment s\351lectionn\351 dans le panel", "status bar tip"));
+	selection_prop    -> setStatusTip(tr("\311dite les propri\351t\351s des objets s\351lectionn\351", "status bar tip"));
+	conductor_reset   -> setStatusTip(tr("Recalcule les chemins des conducteurs sans tenir compte des modifications", "status bar tip"));
+	infos_diagram     -> setStatusTip(tr("\311dite les informations affich\351es par le cartouche", "status bar tip"));
+	add_column        -> setStatusTip(tr("Ajoute une colonne au sch\351ma", "status bar tip"));
+	remove_column     -> setStatusTip(tr("Enl\350ve une colonne au sch\351ma", "status bar tip"));
+	add_row           -> setStatusTip(tr("Agrandit le sch\351ma en hauteur", "status bar tip"));
+	remove_row        -> setStatusTip(tr("R\351tr\351cit le sch\351ma en hauteur", "status bar tip"));
+	
+	zoom_in           -> setStatusTip(tr("Agrandit le sch\351ma", "status bar tip"));
+	zoom_out          -> setStatusTip(tr("R\351tr\351cit le sch\351ma", "status bar tip"));
+	zoom_content      -> setStatusTip(tr("Adapte le zoom de fa\347on \340 afficher tout le contenu ind\351pendamment du cadre"));
+	zoom_fit          -> setStatusTip(tr("Adapte la taille du sch\351ma afin qu'il soit enti\350rement visible", "status bar tip"));
+	zoom_reset        -> setStatusTip(tr("Restaure le zoom par d\351faut", "status bar tip"));
+	
+	windowed_view_mode -> setStatusTip(tr("Pr\351sente les diff\351rents projets ouverts dans des sous-fen\352tres", "status bar tip"));
+	tabbed_view_mode   -> setStatusTip(tr("Pr\351sente les diff\351rents projets ouverts des onglets", "status bar tip"));
+	
+	mode_selection    -> setStatusTip(tr("Permet de s\351lectionner les \351l\351ments", "status bar tip"));
+	mode_visualise    -> setStatusTip(tr("Permet de visualiser le sch\351ma sans pouvoir le modifier", "status bar tip"));
+	
+	tile_window       -> setStatusTip(tr("Dispose les fen\352tres en mosa\357que", "status bar tip"));
+	cascade_window    -> setStatusTip(tr("Dispose les fen\352tres en cascade", "status bar tip"));
+	next_window       -> setStatusTip(tr("Active le projet suivant", "status bar tip"));
+	prev_window       -> setStatusTip(tr("Active le projet pr\351c\351dent", "status bar tip"));
+	
+	// traitements speciaux
+	add_text           -> setCheckable(true);
+	add_image		   -> setCheckable(true);
+	windowed_view_mode -> setCheckable(true);
+	tabbed_view_mode   -> setCheckable(true);
+	mode_selection     -> setCheckable(true);
+	mode_visualise     -> setCheckable(true);
+	mode_selection     -> setChecked(true);
+	
+	grp_visu_sel = new QActionGroup(this);
+	grp_visu_sel -> addAction(mode_selection);
+	grp_visu_sel -> addAction(mode_visualise);
+	grp_visu_sel -> setExclusive(true);
+	
+	grp_view_mode = new QActionGroup(this);
+	grp_view_mode -> addAction(windowed_view_mode);
+	grp_view_mode -> addAction(tabbed_view_mode);
+	grp_view_mode -> setExclusive(true);
+	
+	// connexion a des slots
+	connect(quit_editor,        SIGNAL(triggered()), this,       SLOT(close())                     );
+	connect(select_all,         SIGNAL(triggered()), this,       SLOT(slot_selectAll())            );
+	connect(select_nothing,     SIGNAL(triggered()), this,       SLOT(slot_selectNothing())        );
+	connect(select_invert,      SIGNAL(triggered()), this,       SLOT(slot_selectInvert())         );
+	connect(delete_selection,   SIGNAL(triggered()), this,       SLOT(slot_delete())               );
+	connect(rotate_selection,   SIGNAL(triggered()), this,       SLOT(slot_rotate())               );
+	connect(rotate_texts,       SIGNAL(triggered()), this,       SLOT(slot_rotateTexts())          );
+	connect(find_element,       SIGNAL(triggered()), this,       SLOT(findSelectedElementInPanel()));
+	connect(edit_selection,     SIGNAL(triggered()), this,       SLOT(slot_editSelection())        );
+	connect(windowed_view_mode, SIGNAL(triggered()), this,       SLOT(setWindowedMode())           );
+	connect(tabbed_view_mode,   SIGNAL(triggered()), this,       SLOT(setTabbedMode())             );
+	connect(mode_selection,     SIGNAL(triggered()), this,       SLOT(slot_setSelectionMode())     );
+	connect(mode_visualise,     SIGNAL(triggered()), this,       SLOT(slot_setVisualisationMode()) );
+	connect(prj_edit_prop,      SIGNAL(triggered()), this,       SLOT(editCurrentProjectProperties()));
+	connect(prj_add_diagram,    SIGNAL(triggered()), this,       SLOT(addDiagramToProject())       );
+	connect(prj_del_diagram,    SIGNAL(triggered()), this,       SLOT(removeDiagramFromProject())  );
+	connect(prj_clean,          SIGNAL(triggered()), this,       SLOT(cleanCurrentProject())       );
+	connect(prj_diagramNum,     SIGNAL(triggered()), this,       SLOT(diagramNumProject())   );
+	connect(prj_nomenclature,   SIGNAL(triggered()), this,       SLOT(nomenclatureProject())       );
+	connect(zoom_in,            SIGNAL(triggered()), this,       SLOT(slot_zoomIn())               );
+	connect(zoom_out,           SIGNAL(triggered()), this,       SLOT(slot_zoomOut())              );
+	connect(zoom_content,       SIGNAL(triggered()), this,       SLOT(slot_zoomContent())          );
+	connect(zoom_fit,           SIGNAL(triggered()), this,       SLOT(slot_zoomFit())              );
+	connect(zoom_reset,         SIGNAL(triggered()), this,       SLOT(slot_zoomReset())            );
+	connect(print,              SIGNAL(triggered()), this,       SLOT(printDialog())               );
+	connect(export_diagram,     SIGNAL(triggered()), this,       SLOT(exportDialog())              );
+	connect(save_file_as,       SIGNAL(triggered()), this,       SLOT(saveAs())                    );
+	connect(save_file,          SIGNAL(triggered()), this,       SLOT(save())                      );
+	connect(save_cur_diagram,   SIGNAL(triggered()), this,       SLOT(saveCurrentDiagram())        );
+	connect(new_file,           SIGNAL(triggered()), this,       SLOT(newProject())                );
+	connect(open_file,          SIGNAL(triggered()), this,       SLOT(openProject())               );
+	connect(close_file,         SIGNAL(triggered()), this,       SLOT(closeCurrentProject())       );
+	connect(cut,                SIGNAL(triggered()), this,       SLOT(slot_cut())                  );
+	connect(copy,               SIGNAL(triggered()), this,       SLOT(slot_copy())                 );
+	connect(paste,              SIGNAL(triggered()), this,       SLOT(slot_paste())                );
+	connect(tile_window,        SIGNAL(triggered()), &workspace, SLOT(tileSubWindows())            );
+	connect(cascade_window,     SIGNAL(triggered()), &workspace, SLOT(cascadeSubWindows())         );
+	connect(next_window,        SIGNAL(triggered()), &workspace, SLOT(activateNextSubWindow())     );
+	connect(prev_window,        SIGNAL(triggered()), &workspace, SLOT(activatePreviousSubWindow()) );
+	connect(selection_prop,     SIGNAL(triggered()), this,       SLOT(editSelectionProperties())   );
+	connect(conductor_reset,    SIGNAL(triggered()), this,       SLOT(slot_resetConductors())      );
+	connect(infos_diagram,      SIGNAL(triggered()), this,       SLOT(editCurrentDiagramProperties()));
+	connect(add_text,           SIGNAL(triggered()), this,       SLOT(slot_addText())              );
+	connect(add_image,			SIGNAL(triggered()), this,       SLOT(slot_addImage())              );
+	connect(add_column,         SIGNAL(triggered()), this,       SLOT(slot_addColumn())            );
+	connect(remove_column,      SIGNAL(triggered()), this,       SLOT(slot_removeColumn())         );
+	connect(add_row,            SIGNAL(triggered()), this,       SLOT(slot_addRow())               );
+	connect(remove_row,         SIGNAL(triggered()), this,       SLOT(slot_removeRow())            );
+}
+
+/**
+	Gere les evenements du l'editeur de schema
+	Reimplemente ici pour :
+	  * eviter un conflit sur le raccourci clavier "Ctrl+W" (QKeySequence::Close)
+	@param e Evenement
+*/
+bool QETDiagramEditor::event(QEvent *e) {
+	if (e -> type() == QEvent::ShortcutOverride) {
+		QKeyEvent *shortcut_event = static_cast<QKeyEvent *>(e);
+		if (shortcut_event && shortcut_event -> matches(QKeySequence::Close)) {
+			close_file -> trigger();
+			e -> accept();
+			return(true);
+		}
+	}
+	return(QETMainWindow::event(e));
+}
+
+/**
+	Mise en place des menus
+*/
+void QETDiagramEditor::menus() {
+	
+	QMenu *menu_fichier   = new QMenu(tr("&Fichier"));
+	QMenu *menu_edition   = new QMenu(tr("&\311dition"));
+	QMenu *menu_project   = new QMenu(tr("&Projet"));
+	QMenu *menu_affichage = new QMenu(tr("Afficha&ge"));
+	//QMenu *menu_outils    = new QMenu(tr("O&utils"));
+	windows_menu          = new QMenu(tr("Fe&n\352tres"));
+	
+	insertMenu(settings_menu_, menu_fichier);
+	insertMenu(settings_menu_, menu_edition);
+	insertMenu(settings_menu_, menu_project);
+	insertMenu(settings_menu_, menu_affichage);
+	insertMenu(help_menu_, windows_menu);
+	
+	// menu Fichier
+	menu_fichier -> addAction(new_file);
+	menu_fichier -> addAction(open_file);
+	menu_fichier -> addMenu(QETApp::projectsRecentFiles() -> menu());
+	connect(QETApp::projectsRecentFiles(), SIGNAL(fileOpeningRequested(const QString &)), this, SLOT(openRecentFile(const QString &)));
+	menu_fichier -> addAction(save_file);
+	menu_fichier -> addAction(save_file_as);
+	menu_fichier -> addAction(save_cur_diagram);
+	menu_fichier -> addAction(close_file);
+	menu_fichier -> addSeparator();
+	//menu_fichier -> addAction(import_diagram);
+	menu_fichier -> addAction(export_diagram);
+	//menu_fichier -> addSeparator();
+	menu_fichier -> addAction(print);
+	menu_fichier -> addSeparator();
+	menu_fichier -> addAction(quit_editor);
+	
+	// menu Edition
+	menu_edition -> addAction(undo);
+	menu_edition -> addAction(redo);
+	menu_edition -> addSeparator();
+	menu_edition -> addAction(cut);
+	menu_edition -> addAction(copy);
+	menu_edition -> addAction(paste);
+	menu_edition -> addSeparator();
+	menu_edition -> addAction(select_all);
+	menu_edition -> addAction(select_nothing);
+	menu_edition -> addAction(select_invert);
+	menu_edition -> addSeparator();
+	menu_edition -> addAction(delete_selection);
+	menu_edition -> addAction(rotate_selection);
+	menu_edition -> addAction(rotate_texts);
+	menu_edition -> addAction(edit_selection);
+	menu_edition -> addAction(selection_prop);
+	menu_edition -> addSeparator();
+	menu_edition -> addAction(conductor_reset);
+	menu_edition -> addSeparator();
+	menu_edition -> addAction(infos_diagram);
+	menu_edition -> addAction(add_column);
+	menu_edition -> addAction(remove_column);
+	menu_edition -> addAction(add_row);
+	menu_edition -> addAction(remove_row);
+	
+	// menu Projet
+	menu_project -> addAction(prj_edit_prop);
+	menu_project -> addAction(prj_add_diagram);
+	menu_project -> addAction(prj_del_diagram);
+	menu_project -> addAction(prj_clean);
+	menu_project -> addSeparator();
+	menu_project -> addAction(prj_diagramNum);
+	menu_project -> addAction(prj_nomenclature);
+	
+	main_bar    -> toggleViewAction() -> setStatusTip(tr("Affiche ou non la barre d'outils principale"));
+	view_bar    -> toggleViewAction() -> setStatusTip(tr("Affiche ou non la barre d'outils Affichage"));
+	diagram_bar -> toggleViewAction() -> setStatusTip(tr("Affiche ou non la barre d'outils Sch\351ma"));
+	qdw_pa      -> toggleViewAction() -> setStatusTip(tr("Affiche ou non le panel d'appareils"));
+	qdw_undo    -> toggleViewAction() -> setStatusTip(tr("Affiche ou non la liste des modifications"));
+	
+	// menu Affichage
+	QMenu *projects_view_mode = menu_affichage -> addMenu(tr("Afficher les projets"));
+	projects_view_mode -> setTearOffEnabled(true);
+	projects_view_mode -> addAction(windowed_view_mode);
+	projects_view_mode -> addAction(tabbed_view_mode);
+	
+	menu_affichage -> addSeparator();
+	menu_affichage -> addAction(mode_selection);
+	menu_affichage -> addAction(mode_visualise);
+	menu_affichage -> addSeparator();
+	menu_affichage -> addAction(zoom_in);
+	menu_affichage -> addAction(zoom_out);
+	menu_affichage -> addAction(zoom_content);
+	menu_affichage -> addAction(zoom_fit);
+	menu_affichage -> addAction(zoom_reset);
+
+	// menu Fenetres
+	slot_updateWindowsMenu();
+}
+
+/**
+	Mise en place de la barre d'outils
+*/
+void QETDiagramEditor::toolbar() {
+	main_bar = new QToolBar(tr("Outils"), this);
+	main_bar -> setObjectName("toolbar");
+	
+	view_bar = new QToolBar(tr("Affichage"), this);
+	view_bar -> setObjectName("display");
+	
+	diagram_bar = new QToolBar(tr("Sch\351ma"), this);
+	diagram_bar -> setObjectName("diagram");
+	
+	main_bar -> addAction(new_file);
+	main_bar -> addAction(open_file);
+	main_bar -> addAction(save_file);
+	main_bar -> addAction(save_file_as);
+	main_bar -> addAction(save_cur_diagram);
+	main_bar -> addAction(close_file);
+	main_bar -> addAction(print);
+	main_bar -> addSeparator();
+	main_bar -> addAction(undo);
+	main_bar -> addAction(redo);
+	main_bar -> addSeparator();
+	main_bar -> addAction(cut);
+	main_bar -> addAction(copy);
+	main_bar -> addAction(paste);
+	main_bar -> addSeparator();
+	main_bar -> addAction(delete_selection);
+	main_bar -> addAction(rotate_selection);
+	main_bar -> addAction(selection_prop);
+	
+	// Modes selection / visualisation et zoom
+	view_bar -> addAction(mode_selection);
+	view_bar -> addAction(mode_visualise);
+	view_bar -> addSeparator();
+	view_bar -> addAction(zoom_content);
+	view_bar -> addAction(zoom_fit);
+	view_bar -> addAction(zoom_reset);
+	
+	diagram_bar -> addAction(infos_diagram);
+	diagram_bar -> addAction(conductor_reset);
+	diagram_bar -> addAction(add_text);
+	diagram_bar -> addAction(add_image);
+
+	// ajout de la barre d'outils a la fenetre principale
+	addToolBar(Qt::TopToolBarArea, main_bar);
+	addToolBar(Qt::TopToolBarArea, view_bar);
+	addToolBar(Qt::TopToolBarArea, diagram_bar);
+}
+
+/**
+	Imprime le schema courant
+*/
+void QETDiagramEditor::printDialog() {
+	ProjectView *current_project = currentProject();
+	if (!current_project) return;
+	current_project -> printProject();
+}
+
+/**
+	Gere l'export de schema sous forme d'image
+*/
+void QETDiagramEditor::exportDialog() {
+	ProjectView *current_project = currentProject();
+	if (!current_project) return;
+	current_project -> exportProject();
+}
+
+/**
+	Methode enregistrant le schema dans le dernier nom de fichier connu.
+	@return true si l'enregistrement a reussi, false sinon
+*/
+void QETDiagramEditor::save() {
+	if (ProjectView *project_view = currentProject()) {
+		QETResult save_file = project_view -> save();
+		if (save_file.isOk()) {
+			QETApp::projectsRecentFiles() -> fileWasOpened(project_view -> project() -> filePath());
+		} else {
+			showError(save_file);
+		}
+	}
+}
+
+/**
+	Cette methode demande un nom de fichier a l'utilisateur pour enregistrer le schema
+	@return true si l'enregistrement a reussi, false sinon
+*/
+void QETDiagramEditor::saveAs() {
+	if (ProjectView *project_view = currentProject()) {
+		QETResult save_file = project_view -> saveAs();
+		if (save_file.isOk()) {
+			QETApp::projectsRecentFiles() -> fileWasOpened(project_view -> project() -> filePath());
+		} else {
+			showError(save_file);
+		}
+	}
+}
+
+/**
+	Methode enregistrant tous les schemas.
+	@return true si l'enregistrement a reussi, false sinon
+*/
+void QETDiagramEditor::saveCurrentDiagram() {
+	if (ProjectView *project_view = currentProject()) {
+		QETResult save_file = project_view -> saveCurrentDiagram();
+		if (save_file.isOk()) {
+			QETApp::projectsRecentFiles() -> fileWasOpened(project_view -> project() -> filePath());
+		} else {
+			showError(save_file);
+		}
+	}
+}
+
+/**
+	Cree un nouveau projet vide
+*/
+bool QETDiagramEditor::newProject() {
+	// cree un nouveau projet sans schema
+	QETProject *new_project = new QETProject(0);
+	
+	// transmet les proprietes par defaut des nouveaux schemas
+	new_project -> setDefaultBorderProperties(defaultBorderProperties());
+	new_project -> setDefaultConductorProperties(defaultConductorProperties());
+	new_project -> setDefaultTitleBlockProperties(defaultTitleBlockProperties());
+	
+	// ajoute un schema au projet
+	new_project -> addNewDiagram();
+	
+	return(addProject(new_project));
+}
+
+/**
+	Slot utilise pour ouvrir un fichier recent.
+	Transfere filepath au slot openAndAddDiagram seulement si cet editeur est
+	actif
+	@param filepath Fichier a ouvrir
+	@see openAndAddDiagram
+*/
+bool QETDiagramEditor::openRecentFile(const QString &filepath) {
+	// small hack to prevent all diagram editors from trying to topen the required
+	// recent file at the same time
+	if (qApp -> activeWindow() != this) return(false);
+	return(openAndAddProject(filepath));
+}
+
+/**
+	Cette fonction demande un nom de fichier a ouvrir a l'utilisateur
+	@return true si l'ouverture a reussi, false sinon
+*/
+bool QETDiagramEditor::openProject() {
+	// demande un chemin de fichier a ouvrir a l'utilisateur
+	QString filepath = QFileDialog::getOpenFileName(
+		this,
+		tr("Ouvrir un fichier"),
+		open_dialog_dir.absolutePath(),
+		tr("Sch\351mas QElectroTech (*.qet);;Fichiers XML (*.xml);;Tous les fichiers (*)")
+	);
+	if (filepath.isEmpty()) return(false);
+	
+	// retient le dossier contenant le dernier projet ouvert
+	open_dialog_dir = QDir(filepath);
+	
+	// ouvre le fichier
+	return(openAndAddProject(filepath));
+}
+
+/**
+	Ferme un projet
+	@param project_view Projet a fermer
+	@return true si la fermeture du projet a reussi, false sinon
+	Note : cette methode renvoie true si project est nul
+*/
+bool QETDiagramEditor::closeProject(ProjectView *project_view) {
+	if (project_view) {
+		activateProject(project_view);
+		if (QMdiSubWindow *sub_window = subWindowForWidget(project_view)) {
+			return(sub_window -> close());
+		}
+	}
+	return(true);
+}
+
+/**
+	Ferme un projet
+	@param project projet a fermer
+	@return true si la fermeture du fichier a reussi, false sinon
+	Note : cette methode renvoie true si project est nul
+*/
+bool QETDiagramEditor::closeProject(QETProject *project) {
+	if (ProjectView *project_view = findProject(project)) {
+		return(closeProject(project_view));
+	}
+	return(true);
+}
+
+/**
+	Ferme le projet courant
+	@return true si la fermeture du fichier a reussi, false sinon
+	Note : cette methode renvoie true s'il n'y a pas de projet courant
+*/
+bool QETDiagramEditor::closeCurrentProject() {
+	if (ProjectView *project_view = currentProject()) {
+		return(closeProject(project_view));
+	}
+	return(true);
+}
+
+/**
+	Ouvre un projet depuis un fichier et l'ajoute a cet editeur
+	@param filepath Chemin du projet a ouvrir
+	@param interactive true pour afficher des messages a l'utilisateur, false sinon
+	@param update_panel Whether the elements panel should be warned this
+	project has been added. Defaults to true.
+	@return true si l'ouverture a reussi, false sinon
+*/
+bool QETDiagramEditor::openAndAddProject(const QString &filepath, bool interactive, bool update_panel) {
+	if (filepath.isEmpty()) return(false);
+	
+	QFileInfo filepath_info(filepath);
+	// verifie que le projet n'est pas deja ouvert dans un editeur
+	QString my_filepath = filepath_info.canonicalFilePath();
+	if (QETDiagramEditor *diagram_editor = QETApp::diagramEditorForFile(filepath)) {
+		if (diagram_editor == this) {
+			if (ProjectView *project_view = viewForFile(filepath)) {
+				activateWidget(project_view);
+				show();
+				activateWindow();
+			}
+			return(false);
+		} else {
+			// demande a l'autre editeur d'afficher le fichier
+			return(diagram_editor -> openAndAddProject(filepath));
+		}
+	}
+	
+	// check the file exists
+	if (!filepath_info.exists()) {
+		if (interactive) {
+			QET::MessageBox::critical(
+				this,
+				tr("Impossible d'ouvrir le fichier", "message box title"),
+				QString(
+					tr("Il semblerait que le fichier %1 que vous essayez d'ouvrir"
+					" n'existe pas ou plus.")
+				).arg(filepath)
+			);
+		}
+		return(false);
+	}
+	
+	// verifie que le fichier est accessible en lecture
+	if (!filepath_info.isReadable()) {
+		if (interactive) {
+			QET::MessageBox::critical(
+				this,
+				tr("Impossible d'ouvrir le fichier", "message box title"),
+				tr("Il semblerait que le fichier que vous essayez d'ouvrir ne "
+				"soit pas accessible en lecture. Il est donc impossible de "
+				"l'ouvrir. Veuillez v\351rifier les permissions du fichier.")
+			);
+		}
+		return(false);
+	}
+	
+	// gere le fait que le fichier puisse etre en lecture seule
+	if (!filepath_info.isWritable()) {
+		if (interactive) {
+			QET::MessageBox::warning(
+				this,
+				tr("Ouverture du projet en lecture seule", "message box title"),
+				tr("Il semblerait que le projet que vous essayez d'ouvrir ne "
+				"soit pas accessible en \351criture. Il sera donc ouvert en "
+				"lecture seule.")
+			);
+		}
+	}
+	
+	// cree le projet a partir du fichier
+	QETProject *project = new QETProject(filepath);
+	if (project -> state() != QETProject::Ok) {
+		if (interactive && project -> state() != QETProject::FileOpenDiscard) {
+			QET::MessageBox::warning(
+				this,
+				tr("\311chec de l'ouverture du projet", "message box title"),
+				QString(
+					tr(
+						"Il semblerait que le fichier %1 ne soit pas un fichier"
+						" projet QElectroTech. Il ne peut donc \352tre ouvert.",
+						"message box content"
+					)
+				).arg(filepath)
+			);
+		}
+		delete project;
+		return(false);
+	}
+
+	// a ce stade, l'ouverture du fichier a reussi
+	// on l'ajoute a la liste des fichiers recents
+	QETApp::projectsRecentFiles() -> fileWasOpened(filepath);
+	// ... et on l'ajoute dans l'application
+	// Note: we require the panel not to be updated when the project is added
+	// because it will update itself as soon as it becomes visible
+	return(addProject(project), update_panel);
+}
+
+/**
+	Ajoute un projet
+	@param project projet a ajouter
+	@param update_panel Whether the elements panel should be warned this
+	project has been added. Defaults to true.
+*/
+bool QETDiagramEditor::addProject(QETProject *project, bool update_panel) {
+	// enregistre le projet
+	QETApp::registerProject(project);
+	
+	// cree un ProjectView pour visualiser le projet
+	ProjectView *project_view = new ProjectView(project);
+	addProjectView(project_view);
+	
+	// met a jour le panel d'elements
+	if (update_panel) {
+		pa -> elementsPanel().projectWasOpened(project);
+	}
+	
+	return(true);
+}
+
+/**
+	@return la liste des projets ouverts dans cette fenetre
+*/
+QList<ProjectView *> QETDiagramEditor::openedProjects() const {
+	QList<ProjectView *> result;
+	QList<QMdiSubWindow *> window_list(workspace.subWindowList());
+	foreach(QMdiSubWindow *window, window_list) {
+		if (ProjectView *project_view = qobject_cast<ProjectView *>(window -> widget())) {
+			result << project_view;
+		}
+	}
+	return(result);
+}
+
+/**
+	@return Le projet actuellement edite (= qui a le focus dans l'interface
+	MDI) ou 0 s'il n'y en a pas
+*/
+ProjectView *QETDiagramEditor::currentProject() const {
+	QMdiSubWindow *current_window = workspace.activeSubWindow();
+	if (!current_window) return(0);
+	
+	QWidget *current_widget = current_window -> widget();
+	if (!current_widget) return(0);
+	
+	if (ProjectView *project_view = qobject_cast<ProjectView *>(current_widget)) {
+		return(project_view);
+	}
+	return(0);
+}
+
+/**
+	@return Le schema actuellement edite (= l'onglet ouvert dans le projet
+	courant) ou 0 s'il n'y en a pas
+*/
+DiagramView *QETDiagramEditor::currentDiagram() const {
+	if (ProjectView *project_view = currentProject()) {
+		return(project_view -> currentDiagram());
+	}
+	return(0);
+}
+
+/**
+	@return the selected element in the current diagram view, or 0 if:
+	  * no diagram is being viewed in this editor.
+	  * no element is selected
+	  * more than one element is selected
+*/
+Element *QETDiagramEditor::currentElement() const {
+	DiagramView *dv = currentDiagram();
+	if (!dv) return(0);
+	
+	QList<Element *> selected_elements = dv -> diagram() -> selectedContent().elements.toList();
+	if (selected_elements.count() != 1) return(0);
+	
+	return(selected_elements.first());
+}
+
+/**
+	@return the selected element in the current diagram view, or 0 if:
+	  * no diagram is being viewed in this editor.
+	  * no element is selected
+	  * more than one element is selected
+	  * the selected element is not a custom element
+*/
+CustomElement *QETDiagramEditor::currentCustomElement() const {
+	return(dynamic_cast<CustomElement *>(currentElement()));
+}
+
+/**
+	Cette methode permet de retrouver le projet contenant un schema donne.
+	@param diagram_view Schema dont il faut retrouver
+	@return la vue sur le projet contenant ce schema ou 0 s'il n'y en a pas
+*/
+ProjectView *QETDiagramEditor::findProject(DiagramView *diagram_view) const {
+	foreach(ProjectView *project_view, openedProjects()) {
+		if (project_view -> diagrams().contains(diagram_view)) {
+			return(project_view);
+		}
+	}
+	return(0);
+}
+
+/**
+	Cette methode permet de retrouver le projet contenant un schema donne.
+	@param diagram Schema dont il faut retrouver
+	@return la vue sur le projet contenant ce schema ou 0 s'il n'y en a pas
+*/
+ProjectView *QETDiagramEditor::findProject(Diagram *diagram) const {
+	foreach(ProjectView *project_view, openedProjects()) {
+		foreach(DiagramView *diagram_view, project_view -> diagrams()) {
+			if (diagram_view -> diagram() == diagram) {
+				return(project_view);
+			}
+		}
+	}
+	return(0);
+}
+
+/**
+	@param project Projet dont il faut trouver la vue
+	@return la vue du projet passe en parametre
+*/
+ProjectView *QETDiagramEditor::findProject(QETProject *project) const {
+	foreach(ProjectView *opened_project, openedProjects()) {
+		if (opened_project -> project() == project) {
+			return(opened_project);
+		}
+	}
+	return(0);
+}
+
+/**
+	@param filepath Chemin de fichier d'un projet
+	@return le ProjectView correspondant au chemin passe en parametre, ou 0 si
+	celui-ci n'a pas ete trouve
+*/
+ProjectView *QETDiagramEditor::findProject(const QString &filepath) const {
+	foreach(ProjectView *opened_project, openedProjects()) {
+		if (QETProject *project = opened_project -> project()) {
+			if (project -> filePath() == filepath) {
+				return(opened_project);
+			}
+		}
+	}
+	return(0);
+}
+
+/**
+	@param widget Widget a rechercher dans la zone MDI
+	@return La sous-fenetre accueillant le widget passe en parametre, ou 0 si
+	celui-ci n'a pas ete trouve.
+*/
+QMdiSubWindow *QETDiagramEditor::subWindowForWidget(QWidget *widget) const {
+	foreach(QMdiSubWindow *sub_window, workspace.subWindowList()) {
+		if (sub_window -> widget() == widget) {
+			return(sub_window);
+		}
+	}
+	return(0);
+}
+
+/**
+	@param widget Widget a activer
+*/
+void QETDiagramEditor::activateWidget(QWidget *widget) {
+	QMdiSubWindow *sub_window = subWindowForWidget(widget);
+	if (sub_window) {
+		workspace.setActiveSubWindow(sub_window);
+	}
+}
+
+/**
+	Effectue l'action "couper" sur le schema en cours
+*/
+void QETDiagramEditor::slot_cut() {
+	if(currentDiagram()) currentDiagram() -> cut();
+}
+
+/**
+	Effectue l'action "copier" sur le diagram en cours
+*/
+void QETDiagramEditor::slot_copy() {
+	if(currentDiagram()) currentDiagram() -> copy();
+}
+
+/**
+	Effectue l'action "coller" sur le schema en cours
+*/
+void QETDiagramEditor::slot_paste() {
+	if(currentDiagram()) currentDiagram() -> paste();
+}
+
+/**
+	Effectue l'action "zoom avant" sur le diagram en cours
+*/
+void QETDiagramEditor::slot_zoomIn() {
+	if(currentDiagram()) currentDiagram() -> zoomIn();
+}
+
+/**
+	Effectue l'action "zoom arriere" sur le schema en cours
+*/
+void QETDiagramEditor::slot_zoomOut() {
+	if(currentDiagram()) currentDiagram() -> zoomOut();
+}
+
+/**
+	Effectue l'action "zoom arriere" sur le diagram en cours
+*/
+void QETDiagramEditor::slot_zoomFit() {
+	if(currentDiagram()) currentDiagram() -> zoomFit();
+}
+
+/**
+	Call the "zoom content" action for the current diagram.
+*/
+void QETDiagramEditor::slot_zoomContent() {
+	if(currentDiagram()) currentDiagram() -> zoomContent();
+}
+
+/**
+	Effectue l'action "zoom par defaut" sur le schema en cours
+*/
+void QETDiagramEditor::slot_zoomReset() {
+	if(currentDiagram()) currentDiagram() -> zoomReset();
+}
+
+/**
+	Effectue l'action "selectionner tout" sur le schema en cours
+*/
+void QETDiagramEditor::slot_selectAll() {
+	if(currentDiagram()) currentDiagram() -> selectAll();
+}
+
+/**
+	Effectue l'action "deselectionenr tout" sur le schema en cours
+*/
+void QETDiagramEditor::slot_selectNothing() {
+	if(currentDiagram()) currentDiagram() -> selectNothing();
+}
+
+/**
+	Effectue l'action "inverser la selection" sur le schema en cours
+*/
+void QETDiagramEditor::slot_selectInvert() {
+	if(currentDiagram()) currentDiagram() -> selectInvert();
+}
+
+/**
+	Effectue l'action "supprimer" sur le schema en cours
+*/
+void QETDiagramEditor::slot_delete() {
+	if(currentDiagram()) currentDiagram() -> deleteSelection();
+}
+
+/**
+	Effectue l'action "pivoter" sur le schema en cours
+*/
+void QETDiagramEditor::slot_rotate() {
+	if(currentDiagram()) currentDiagram() -> rotateSelection();
+}
+
+/**
+	Effectue l'action "Orienter les textes selectionnes" sur le schema en cours
+*/
+void QETDiagramEditor::slot_rotateTexts() {
+	if (currentDiagram()) currentDiagram() -> rotateTexts();
+}
+
+/**
+	Effectue l'action "mode selection" sur le schema en cours
+*/
+void QETDiagramEditor::slot_setSelectionMode() {
+	if(currentDiagram()) currentDiagram() -> setSelectionMode();
+}
+
+/**
+	Effectue l'action "mode visualisation" sur le schema en cours
+*/
+void QETDiagramEditor::slot_setVisualisationMode() {
+	if(currentDiagram()) currentDiagram() -> setVisualisationMode();
+}
+
+/**
+	gere les actions
+*/
+void QETDiagramEditor::slot_updateActions() {
+	DiagramView *dv = currentDiagram();
+	ProjectView *pv = currentProject();
+	bool opened_project = pv;
+	bool opened_diagram = dv;
+	bool editable_project = (pv && !pv -> project() -> isReadOnly());
+	bool editable_diagram = (dv && !dv -> diagram() -> isReadOnly());
+	
+	// actions ayant juste besoin d'un document ouvert
+	close_file        -> setEnabled(opened_project);
+	save_file         -> setEnabled(editable_project);
+	save_file_as      -> setEnabled(opened_project);
+	save_cur_diagram  -> setEnabled(editable_diagram);
+	prj_edit_prop     -> setEnabled(opened_project);
+	prj_add_diagram   -> setEnabled(editable_project);
+	prj_del_diagram   -> setEnabled(editable_project);
+	prj_clean         -> setEnabled(editable_project);
+	prj_diagramNum    -> setEnabled(editable_project);
+	prj_nomenclature  -> setEnabled(editable_project);
+	import_diagram    -> setEnabled(editable_project);
+	export_diagram    -> setEnabled(opened_diagram);
+	print             -> setEnabled(opened_diagram);
+	select_all        -> setEnabled(opened_diagram);
+	select_nothing    -> setEnabled(opened_diagram);
+	select_invert     -> setEnabled(opened_diagram);
+	zoom_in           -> setEnabled(opened_diagram);
+	zoom_out          -> setEnabled(opened_diagram);
+	zoom_content      -> setEnabled(opened_diagram);
+	zoom_fit          -> setEnabled(opened_diagram);
+	zoom_reset        -> setEnabled(opened_diagram);
+	infos_diagram     -> setEnabled(opened_diagram);
+	add_text          -> setEnabled(editable_diagram);
+	add_column        -> setEnabled(editable_diagram);
+	remove_column     -> setEnabled(editable_diagram);
+	add_row           -> setEnabled(editable_diagram);
+	remove_row        -> setEnabled(editable_diagram);
+	add_image		  ->setEnabled(editable_diagram);
+	
+	//display the beta feature only in debug mode
+#ifdef QT_NO_DEBUG
+	prj_nomenclature  -> setVisible(false);
+#endif
+	
+	// affiche les actions correspondant au diagram view en cours
+	if (dv) {
+		if (can_update_actions) {
+			undo_group.setActiveStack(&(dv -> diagram() -> undoStack()));
+			undo -> setEnabled(undo_group.canUndo());
+			redo -> setEnabled(undo_group.canRedo());
+		}
+	} else {
+		undo -> setEnabled(false);
+		redo -> setEnabled(false);
+	}
+	
+	slot_updateModeActions();
+	slot_updatePasteAction();
+	slot_updateComplexActions();
+}
+
+/**
+	gere les actions ayant des besoins precis pour etre active ou non
+	Cette methode ne fait rien si aucun document n'est ouvert
+*/
+void QETDiagramEditor::slot_updateComplexActions() {
+	DiagramView *dv = currentDiagram();
+	bool editable_diagram = (dv && !dv -> diagram() -> isReadOnly());
+	
+	// nombre de conducteurs selectionnes
+	int selected_conductors_count = dv ? dv -> diagram() -> selectedConductors().count() : 0;
+	conductor_reset  -> setEnabled(editable_diagram && selected_conductors_count);
+	
+	// number of selected elements
+	int selected_elements_count = dv ? dv -> diagram() -> selectedContent().count(DiagramContent::Elements) : 0;
+	find_element -> setEnabled(selected_elements_count == 1);
+	
+	// actions ayant aussi besoin d'items (elements, conducteurs, textes, ...) selectionnes
+	bool copiable_items  = dv ? (dv -> hasCopiableItems()) : false;
+	bool deletable_items = dv ? (dv -> hasDeletableItems()) : false;
+	cut              -> setEnabled(editable_diagram && copiable_items);
+	copy             -> setEnabled(copiable_items);
+	delete_selection -> setEnabled(editable_diagram && deletable_items);
+	rotate_selection -> setEnabled(editable_diagram && dv -> diagram() -> canRotateSelection());
+	selection_prop   -> setEnabled(deletable_items);
+	prj_diagramNum   -> setEnabled(editable_diagram);
+	
+	// actions ayant besoin de textes selectionnes
+	int selected_texts = dv ? (dv -> diagram() -> selectedTexts().count()) : 0;
+	int selected_conductor_texts = dv ? (dv -> diagram() -> selectedConductorTexts().count()) : 0;
+	rotate_texts -> setEnabled(editable_diagram && selected_texts);
+
+	// actions need only one editable item
+	int selected_image = dv ? dv -> diagram() -> selectedContent().count(DiagramContent::Images) : 0;
+	int selected_editable = selected_elements_count + (selected_texts - selected_conductor_texts) + selected_image;
+
+	if (selected_editable == 1) {
+		edit_selection -> setEnabled(true);
+		//edit element
+		if (selected_elements_count) {
+			edit_selection -> setText(tr("\311diter l'\351lement", "edit element"));
+			edit_selection -> setIcon(QET::Icons::ElementEdit);
+		}
+		//edit text field
+		else if (selected_texts) {
+			edit_selection -> setText(tr("\311diter le champ de texte", "edit text field"));
+			edit_selection -> setIcon(QET::Icons::EditText);
+		}
+		//edit image
+		else if (selected_image) {
+			edit_selection -> setText(tr("\311diter l'image", "edit image"));
+			edit_selection -> setIcon(QET::Icons::resize_image);
+		}
+	}
+	//not an editable item
+	else {
+		edit_selection -> setText(tr("\311diter l'objet s\351lectionn\351", "edit selected item"));
+		edit_selection -> setIcon(QET::Icons::ElementEdit);
+		edit_selection -> setEnabled(false);
+	}
+}
+
+
+/**
+	Gere les actions relatives au mode du schema
+*/
+void QETDiagramEditor::slot_updateModeActions() {
+	DiagramView *dv = currentDiagram();
+	
+	// actions ayant aussi besoin d'un document ouvert et de la connaissance de son mode
+	if (!dv) {
+		grp_visu_sel -> setEnabled(false);
+	} else {
+		switch((int)(dv -> dragMode())) {
+			case QGraphicsView::NoDrag:
+				grp_visu_sel -> setEnabled(false);
+				break;
+			case QGraphicsView::ScrollHandDrag:
+				grp_visu_sel -> setEnabled(true);
+				mode_visualise -> setChecked(true);
+				break;
+			case QGraphicsView::RubberBandDrag:
+				grp_visu_sel -> setEnabled(true);
+				mode_selection -> setChecked(true);
+				break;
+		}
+	}
+}
+
+/**
+	Gere les actions ayant besoin du presse-papier
+*/
+void QETDiagramEditor::slot_updatePasteAction() {
+	DiagramView *dv = currentDiagram();
+	bool editable_diagram = (dv && !dv -> diagram() -> isReadOnly());
+	
+	// pour coller, il faut un schema ouvert et un schema dans le presse-papier
+	paste -> setEnabled(editable_diagram && Diagram::clipboardMayContainDiagram());
+}
+
+/**
+	Ajoute un projet dans l'espace de travail
+	@param project_view Le projet a ajouter dans l'espace de travail
+*/
+void QETDiagramEditor::addProjectView(ProjectView *project_view) {
+	if (!project_view) return;
+	
+	// on maximise la nouvelle fenetre si la fenetre en cours est inexistante ou bien maximisee
+	QWidget *current_window = workspace.activeSubWindow();
+	bool maximise = ((!current_window) || (current_window -> windowState() & Qt::WindowMaximized));
+	
+	// ajoute la fenetre
+	QMdiSubWindow *sub_window = workspace.addSubWindow(project_view);
+	sub_window -> setWindowIcon(project_view -> windowIcon());
+	sub_window -> systemMenu() -> clear();
+	
+	// lie les schemas du projet a l'editeur :
+	// quand on change de schemas a l'interieur d'un projet, on met a jour les menus
+	connect(project_view, SIGNAL(diagramActivated(DiagramView *)), this, SLOT(slot_updateWindowsMenu()));
+	connect(project_view, SIGNAL(diagramActivated(DiagramView *)), this, SLOT(slot_updateActions()));
+	foreach(DiagramView *dv, project_view -> diagrams()) {
+		diagramWasAdded(dv);
+	}
+	
+	// gere la fermeture du projet
+	connect(project_view, SIGNAL(projectClosed(ProjectView*)), this, SLOT(projectWasClosed(ProjectView *)));
+	
+	// gere l'ajout et le retrait de schema du projet
+	connect(project_view, SIGNAL(diagramAdded(DiagramView *)),   this, SLOT(diagramWasAdded(DiagramView *)));
+	connect(project_view, SIGNAL(diagramAdded(DiagramView *)),   this, SLOT(slot_updateActions()));
+	connect(project_view, SIGNAL(diagramAboutToBeRemoved(DiagramView *)), this, SLOT(diagramIsAboutToBeRemoved(DiagramView *)));
+	connect(project_view, SIGNAL(diagramRemoved(DiagramView *)), this, SLOT(diagramWasRemoved(DiagramView *)));
+	connect(project_view, SIGNAL(diagramRemoved(DiagramView *)), this, SLOT(slot_updateActions()));
+	if (QETProject *project = project_view -> project()) {
+		// on met aussi les menus a jour quand un projet passe en lecture seule ou non
+		connect(project, SIGNAL(readOnlyChanged(QETProject *, bool)), this, SLOT(slot_updateActions()));
+	}
+	
+	// gere les demandes consistant a retrouver un element dans le panel
+	connect(project_view, SIGNAL(findElementRequired(const ElementsLocation &)), this, SLOT(findElementInPanel(const ElementsLocation &)));
+	
+	// gere les demandes pour l'edition d'un element
+	connect(project_view, SIGNAL(editElementRequired(const ElementsLocation &)), this, SLOT(editElementInEditor(const ElementsLocation &)));
+	
+	// handles requests to edit and/or duplicate an existing title block template
+	connect(
+		project_view, SIGNAL(editTitleBlockTemplate(const TitleBlockTemplateLocation &, bool)),
+		QETApp::instance(), SLOT(openTitleBlockTemplate(TitleBlockTemplateLocation, bool))
+	);
+	
+	// display error messages sent by the project view
+	connect(project_view, SIGNAL(errorEncountered(QString)), this, SLOT(showError(const QString &)));
+	
+	// affiche la fenetre
+	if (maximise) project_view -> showMaximized();
+	else project_view -> show();
+}
+
+/**
+	@return la liste des fichiers edites par cet editeur de schemas
+*/
+QList<QString> QETDiagramEditor::editedFiles() const {
+	QList<QString> edited_files_list;
+	foreach (ProjectView *project_view, openedProjects()) {
+		QString diagram_file(project_view -> project() -> filePath());
+		if (!diagram_file.isEmpty()) {
+			edited_files_list << QFileInfo(diagram_file).canonicalFilePath();
+		}
+	}
+	return(edited_files_list);
+}
+
+/**
+	@param filepath Un chemin de fichier
+	Note : si filepath est une chaine vide, cette methode retourne 0.
+	@return le ProjectView editant le fichier filepath, ou 0 si ce fichier n'est
+	pas edite par cet editeur de schemas.
+*/
+ProjectView *QETDiagramEditor::viewForFile(const QString &filepath) const {
+	if (filepath.isEmpty()) return(0);
+	
+	QString searched_can_file_path = QFileInfo(filepath).canonicalFilePath();
+	if (searched_can_file_path.isEmpty()) {
+		// QFileInfo returns an empty path for non-existent files
+		return(0);
+	}
+	foreach (ProjectView *project_view, openedProjects()) {
+		QString project_can_file_path = QFileInfo(project_view -> project() -> filePath()).canonicalFilePath();
+		if (project_can_file_path == searched_can_file_path) {
+			return(project_view);
+		}
+	}
+	return(0);
+}
+
+/**
+	met a jour le menu "Fenetres"
+*/
+void QETDiagramEditor::slot_updateWindowsMenu() {
+	// nettoyage du menu
+	foreach(QAction *a, windows_menu -> actions()) windows_menu -> removeAction(a);
+	
+	// actions de fermeture
+	windows_menu -> addAction(close_file);
+	//windows_menu -> addAction(closeAllAct);
+	
+	// actions de reorganisation des fenetres
+	windows_menu -> addSeparator();
+	windows_menu -> addAction(tile_window);
+	windows_menu -> addAction(cascade_window);
+	
+	// actions de deplacement entre les fenetres
+	windows_menu -> addSeparator();
+	windows_menu -> addAction(next_window);
+	windows_menu -> addAction(prev_window);
+	
+	// liste des fenetres
+	QList<ProjectView *> windows = openedProjects();
+	
+	tile_window    -> setEnabled(!windows.isEmpty() && workspace.viewMode() == QMdiArea::SubWindowView);
+	cascade_window -> setEnabled(!windows.isEmpty() && workspace.viewMode() == QMdiArea::SubWindowView);
+	next_window    -> setEnabled(windows.count() > 1);
+	prev_window    -> setEnabled(windows.count() > 1);
+	
+	if (!windows.isEmpty()) windows_menu -> addSeparator();
+	QActionGroup *windows_actions = new QActionGroup(this);
+	foreach(ProjectView *project_view, windows) {
+		QString pv_title = project_view -> windowTitle();
+		QAction *action  = windows_menu -> addAction(pv_title);
+		windows_actions -> addAction(action);
+		action -> setStatusTip(QString(tr("Active le projet \253\240%1\240\273")).arg(pv_title));
+		action -> setCheckable(true);
+		action -> setChecked(project_view == currentProject());
+		connect(action, SIGNAL(triggered()), &windowMapper, SLOT(map()));
+		windowMapper.setMapping(action, project_view);
+	}
+}
+
+/**
+	Edite les informations du schema en cours
+*/
+void QETDiagramEditor::editCurrentDiagramProperties() {
+	if (ProjectView *project_view = currentProject()) {
+		activateProject(project_view);
+		project_view -> editCurrentDiagramProperties();
+	}
+}
+
+/**
+	Edite les proprietes du schema diagram
+	@param diagram_view schema dont il faut editer les proprietes
+*/
+void QETDiagramEditor::editDiagramProperties(DiagramView *diagram_view) {
+	if (ProjectView *project_view = findProject(diagram_view)) {
+		activateProject(project_view);
+		project_view -> editDiagramProperties(diagram_view);
+	}
+}
+
+/**
+	Edite les proprietes du schema diagram
+	@param diagram schema dont il faut editer les proprietes
+*/
+void QETDiagramEditor::editDiagramProperties(Diagram *diagram) {
+	if (ProjectView *project_view = findProject(diagram)) {
+		activateProject(project_view);
+		project_view -> editDiagramProperties(diagram);
+	}
+}
+
+/**
+	Ajoute une colonne au schema en cours
+*/
+void QETDiagramEditor::slot_addColumn() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> addColumn();
+	}
+}
+
+/**
+	Enleve une colonne au schema en cours
+*/
+void QETDiagramEditor::slot_removeColumn() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> removeColumn();
+	}
+}
+
+/**
+	Allonge le schema en cours en hauteur
+*/
+void QETDiagramEditor::slot_addRow() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> addRow();
+	}
+}
+
+/**
+	Retrecit le schema en cours en hauteur
+*/
+void QETDiagramEditor::slot_removeRow() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> removeRow();
+	}
+}
+
+/**
+	Edite les proprietes des objets selectionnes
+*/
+void QETDiagramEditor::editSelectionProperties() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> editSelectionProperties();
+	}
+}
+
+/**
+	Edite les proprietes du conducteur selectionne
+*/
+void QETDiagramEditor::slot_editConductor() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> editConductor();
+	}
+}
+
+/**
+	Reinitialise les conducteurs selectionnes
+*/
+void QETDiagramEditor::slot_resetConductors() {
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> resetConductors();
+	}
+}
+
+/**
+	Ajoute un texte au schema courant
+*/
+void QETDiagramEditor::slot_addText() {
+	add_image -> setChecked(false);
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> addText();
+	}
+}
+/**
+	Ajoute une image au schema courant
+*/
+void QETDiagramEditor::slot_addImage() {
+	add_text -> setChecked(false);
+	if (DiagramView *dv = currentDiagram()) {
+		dv -> addImage();
+	}
+}
+
+/**
+ * @brief QETDiagramEditor::slot_editSelection
+ * edit the selected item if he can be edited and if only  one item is selected
+ */
+void QETDiagramEditor::slot_editSelection() {
+	if (DiagramView *dv = currentDiagram()) {
+		DiagramContent dc = dv -> diagram() -> selectedContent();
+		if (dc.count(DiagramContent::SelectedOnly | DiagramContent::All) != 1) return;
+
+		if (dc.count(DiagramContent::Elements)) {
+			findSelectedElementInPanel();
+			editSelectedElementInEditor();
+		}
+		else if (dc.count(DiagramContent::TextFields)) dv -> editText();
+		else if (dc.count(DiagramContent::Images)) dv -> editImage();
+	}
+}
+
+/**
+	Affiche les projets dans des fenetres.
+*/
+void QETDiagramEditor::setWindowedMode() {
+	workspace.setViewMode(QMdiArea::SubWindowView);
+	windowed_view_mode -> setChecked(true);
+	slot_updateWindowsMenu();
+}
+
+/**
+	Affiche les projets dans des onglets.
+*/
+void QETDiagramEditor::setTabbedMode() {
+	workspace.setViewMode(QMdiArea::TabbedView);
+	tabbed_view_mode -> setChecked(true);
+	slot_updateWindowsMenu();
+}
+
+/// Lit les parametres de l'editeur de schemas
+void QETDiagramEditor::readSettings() {
+	QSettings &settings = QETApp::settings();
+	
+	// dimensions et position de la fenetre
+	QVariant geometry = settings.value("diagrameditor/geometry");
+	if (geometry.isValid()) restoreGeometry(geometry.toByteArray());
+	
+	// etat de la fenetre (barres d'outils, docks...)
+	QVariant state = settings.value("diagrameditor/state");
+	if (state.isValid()) restoreState(state.toByteArray());
+	
+	// gestion des projets (onglets ou fenetres)
+	bool tabbed = settings.value("diagrameditor/viewmode", "tabbed") == "tabbed";
+	if (tabbed) {
+		setTabbedMode();
+	} else {
+		setWindowedMode();
+	}
+}
+
+/// Enregistre les parametres de l'editeur de schemas
+void QETDiagramEditor::writeSettings() {
+	QSettings &settings = QETApp::settings();
+	settings.setValue("diagrameditor/geometry", saveGeometry());
+	settings.setValue("diagrameditor/state", saveState());
+}
+
+/**
+	Active le schema passe en parametre
+	@param diagram Schema a activer
+*/
+void QETDiagramEditor::activateDiagram(Diagram *diagram) {
+	if (QETProject *project = diagram -> project()) {
+		if (ProjectView *project_view = findProject(project)) {
+			activateWidget(project_view);
+			project_view -> showDiagram(diagram);
+		}
+	} else {
+		/// @todo gerer ce cas
+	}
+}
+
+/**
+	Active le projet passe en parametre
+	@param project Projet a activer
+*/
+void QETDiagramEditor::activateProject(QETProject *project) {
+	activateProject(findProject(project));
+}
+
+/**
+	Active le projet passe en parametre
+	@param project_view Projet a activer
+*/
+void QETDiagramEditor::activateProject(ProjectView *project_view) {
+	if (!project_view) return;
+	activateWidget(project_view);
+}
+
+/**
+	Gere la fermeture d'une ProjectView
+	@param project_view ProjectView fermee
+*/
+void QETDiagramEditor::projectWasClosed(ProjectView *project_view) {
+	QETProject *project = project_view -> project();
+	if (project) {
+		pa -> elementsPanel().projectWasClosed(project);
+		QETApp::unregisterProject(project);
+	}
+	project_view -> deleteLater();
+	project -> deleteLater();
+}
+
+/**
+	Edite les proprietes du projet courant.
+*/
+void QETDiagramEditor::editCurrentProjectProperties() {
+	editProjectProperties(currentProject());
+}
+
+/**
+	Edite les proprietes du projet project_view.
+	@param project_view Vue sur le projet dont il faut editer les proprietes
+*/
+void QETDiagramEditor::editProjectProperties(ProjectView *project_view) {
+	if (!project_view) return;
+	activateProject(project_view);
+	project_view -> editProjectProperties();
+}
+
+/**
+	Edite les proprietes du projet project.
+	@param project Projet dont il faut editer les proprietes
+*/
+void QETDiagramEditor::editProjectProperties(QETProject *project) {
+	editProjectProperties(findProject(project));
+}
+
+/**
+	Ajoute un nouveau schema au projet courant
+*/
+void QETDiagramEditor::addDiagramToProject() {
+	if (ProjectView *current_project = currentProject()) {
+		current_project -> addNewDiagram();
+	}
+}
+
+/**
+	Ajoute un nouveau schema a un projet
+	@param project Projet auquel il faut ajouter un schema
+*/
+void QETDiagramEditor::addDiagramToProject(QETProject *project) {
+	if (!project) return;
+	
+	// recupere le ProjectView visualisant ce projet
+	if (ProjectView *project_view = findProject(project)) {
+		
+		// affiche le projet en question
+		activateProject(project);
+		
+		// ajoute un schema au projet
+		project_view -> addNewDiagram();
+	}
+}
+
+/**
+	Supprime un schema de son projet
+	@param diagram Schema a supprimer
+*/
+void QETDiagramEditor::removeDiagram(Diagram *diagram) {
+	if (!diagram) return;
+	
+	// recupere le projet contenant le schema
+	if (QETProject *diagram_project = diagram -> project()) {
+		// recupere la vue sur ce projet
+		if (ProjectView *project_view = findProject(diagram_project)) {
+			
+			// affiche le schema en question
+			project_view -> showDiagram(diagram);
+			
+			// supprime le schema
+			project_view -> removeDiagram(diagram);
+		}
+	}
+}
+
+/**
+	Change l'ordre des schemas d'un projet, en decalant le schema vers le haut /
+	la gauche
+	@param diagram Schema a decaler vers le haut / la gauche
+*/
+void QETDiagramEditor::moveDiagramUp(Diagram *diagram) {
+	if (!diagram) return;
+	
+	// recupere le projet contenant le schema
+	if (QETProject *diagram_project = diagram -> project()) {
+		if (diagram_project -> isReadOnly()) return;
+		
+		// recupere la vue sur ce projet
+		if (ProjectView *project_view = findProject(diagram_project)) {
+			project_view -> moveDiagramUp(diagram);
+		}
+	}
+}
+
+/**
+	Change l'ordre des schemas d'un projet, en decalant le schema vers le bas /
+	la droite
+	@param diagram Schema a decaler vers le bas / la droite
+*/
+void QETDiagramEditor::moveDiagramDown(Diagram *diagram) {
+	if (!diagram) return;
+	
+	// recupere le projet contenant le schema
+	if (QETProject *diagram_project = diagram -> project()) {
+		if (diagram_project -> isReadOnly()) return;
+		
+		// recupere la vue sur ce projet
+		if (ProjectView *project_view = findProject(diagram_project)) {
+			project_view -> moveDiagramDown(diagram);
+		}
+	}
+}
+
+/**
+	Nettoie le projet courant
+*/
+void QETDiagramEditor::cleanCurrentProject() {
+	if (ProjectView *current_project = currentProject()) {
+		int clean_count = current_project -> cleanProject();
+		if (clean_count) pa -> reloadAndFilter();
+	}
+}
+
+/**
+ * @brief launch dialog for numerate diagram
+ */
+void QETDiagramEditor::diagramNumProject() {
+	DialogAutoNum *dg = new DialogAutoNum(currentDiagram()->diagram(), this);
+	dg->setModal(true);
+	dg->exec();
+	
+	delete dg;
+}
+
+/**
+ * @brief export nomemclature of schema
+ */
+void QETDiagramEditor::nomenclatureProject() {
+	//TODO: Test nomenclature CYRIL F.
+	nomenclature *nomencl= new nomenclature(currentProject()->project() ,this);
+	nomencl->saveToCSVFile();
+	
+	delete nomencl;
+}
+
+/**
+	Supprime le schema courant du projet courant
+*/
+void QETDiagramEditor::removeDiagramFromProject() {
+	if (ProjectView *current_project = currentProject()) {
+		if (DiagramView *current_diagram = current_project -> currentDiagram()) {
+			can_update_actions = false;
+			current_project -> removeDiagram(current_diagram);
+		}
+	}
+}
+
+/**
+	Gere l'ajout d'un schema dans un projet
+	@param dv DiagramView concerne
+*/
+void QETDiagramEditor::diagramWasAdded(DiagramView *dv) {
+	// quand on change qqc a l'interieur d'un schema, on met a jour les menus
+	undo_group.addStack(&(dv -> diagram() -> undoStack()));
+	connect(dv,              SIGNAL(selectionChanged()),         this,     SLOT(slot_updateComplexActions()));
+	connect(dv,              SIGNAL(modeChanged()),              this,     SLOT(slot_updateModeActions()));
+	connect(dv,              SIGNAL(textAdded(bool)),            add_text, SLOT(setChecked(bool)));
+	connect(dv,              SIGNAL(ImageAdded(bool)),           add_image, SLOT(setChecked(bool)));
+	connect(dv,				 SIGNAL(ImageAddedCanceled(bool)),   add_image, SLOT(setChecked(bool)));
+}
+
+/**
+	Gere le retrait d'un schema dans un projet avant que le retrait ne soit effectif
+	@param dv DiagramView concerne
+*/
+void QETDiagramEditor::diagramIsAboutToBeRemoved(DiagramView *dv) {
+	undo_group.removeStack(&(dv -> diagram() -> undoStack()));
+	can_update_actions = false;
+}
+
+/**
+	Gere le retrait d'un schema dans un projet apres que le retrait soit effectif
+	@param dv DiagramView concerne
+*/
+void QETDiagramEditor::diagramWasRemoved(DiagramView *dv) {
+	Q_UNUSED(dv);
+	can_update_actions = true;
+}
+
+/**
+	@param location Emplacement de l'element a retrouver dans le panel
+	d'elements.
+*/
+void QETDiagramEditor::findElementInPanel(const ElementsLocation &location) {
+	bool element_found = pa -> elementsPanel().scrollToElement(location);
+	if (!element_found) {
+		// l'element n'a pas ete trouve
+		
+		ElementsCollectionItem *element = QETApp::collectionItem(location);
+		if (element) {
+			// mais il semble exister tout de meme
+			
+			// peut-etre vient-il d'un projet ouvert dans un autre editeur ?
+			if (location.project() && !findProject(location.project())) {
+				statusBar() -> showMessage(
+					tr("Impossible de retrouver cet \351l\351ment dans le panel car il semble \351dit\351 dans une autre fen\352tre"),
+					10000
+				);
+			} else {
+				// il devrait etre affiche : on tente de recharger le panel
+				statusBar() -> showMessage(
+					tr("Impossible de retrouver cet \351l\351ment dans le panel... rechargement du panel..."),
+					10000
+				);
+				pa -> reloadAndFilter();
+				statusBar() -> clearMessage();
+				element_found = pa -> elementsPanel().scrollToElement(location);
+			}
+		}
+	}
+	
+	if (!element_found) {
+		statusBar() -> showMessage(
+			tr("Impossible de retrouver cet \351l\351ment dans le panel"),
+			10000
+		);
+	}
+}
+
+/**
+	Search the panel for the definition for the selected element in the current
+	diagram view.
+*/
+void QETDiagramEditor::findSelectedElementInPanel() {
+	if (CustomElement *selected_element = currentCustomElement()) {
+		findElementInPanel(selected_element -> location());
+	}
+}
+
+/**
+	Lance l'editeur d'element pour l'element filename
+	@param location Emplacement de l'element a editer
+*/
+void QETDiagramEditor::editElementInEditor(const ElementsLocation &location) {
+	QETApp::instance() -> openElementLocations(QList<ElementsLocation>() << location);
+}
+
+/**
+	Launch an element editor to edit the selected element in the current
+	diagram view.
+*/
+void QETDiagramEditor::editSelectedElementInEditor() {
+	if (CustomElement *selected_element = currentCustomElement()) {
+		editElementInEditor(selected_element -> location());
+	}
+}
+
+/**
+	Show the error message contained in \a result.
+*/
+void QETDiagramEditor::showError(const QETResult &result) {
+	if (result.isOk()) return;
+	showError(result.errorMessage());
+}
+
+/**
+	Show the \a error message.
+*/
+void QETDiagramEditor::showError(const QString &error) {
+	if (error.isEmpty()) return;
+	QET::MessageBox::critical(this, tr("Erreur", "message box title"), error);
+}
+
+/**
+	@return Les proprietes par defaut pour le cartouche d'un schema
+*/
+TitleBlockProperties QETDiagramEditor::defaultTitleBlockProperties() {
+	// accede a la configuration de l'application
+	QSettings &settings = QETApp::settings();
+	
+	TitleBlockProperties def;
+	// lit le cartouche par defaut dans la configuration
+	def.fromSettings(settings, "diagrameditor/default");
+	
+	return(def);
+}
+
+/**
+	@return Les dimensions par defaut d'un schema
+*/
+BorderProperties QETDiagramEditor::defaultBorderProperties() {
+	// accede a la configuration de l'application
+	QSettings &settings = QETApp::settings();
+	
+	BorderProperties def;
+	// lit les dimensions par defaut dans la configuration
+	def.fromSettings(settings, "diagrameditor/default");
+	
+	return(def);
+}
+
+/**
+	@return Les proprietes par defaut d'un conducteur
+*/
+ConductorProperties QETDiagramEditor::defaultConductorProperties() {
+	// accede a la configuration de l'application
+	QSettings &settings = QETApp::settings();
+	
+	ConductorProperties def;
+	// lit les caracteristiques des conducteurs par defaut dans la configuration
+	def.fromSettings(settings, "diagrameditor/defaultconductor");
+	
+	return(def);
+}
+
+/**
+	@return Les parametres d'export par defaut pour un schema
+*/
+ExportProperties QETDiagramEditor::defaultExportProperties() {
+	// accede a la configuration de l'application
+	QSettings &settings = QETApp::settings();
+	
+	ExportProperties def;
+	// lit les caracteristiques des conducteurs par defaut dans la configuration
+	def.fromSettings(settings, "export/default");
+	
+	return(def);
+}
+
+/**
+	@return Les parametres d'impression par defaut pour un schema
+*/
+ExportProperties QETDiagramEditor::defaultPrintProperties() {
+	// accede a la configuration de l'application
+	QSettings &settings = QETApp::settings();
+	
+	ExportProperties def;
+	// lit les caracteristiques des conducteurs par defaut dans la configuration
+	def.fromSettings(settings, "print/default");
+	
+	return(def);
+}

Added: trunk/sources/qetdiagrameditor.h
===================================================================
--- trunk/sources/qetdiagrameditor.h	                        (rev 0)
+++ trunk/sources/qetdiagrameditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,238 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_DIAGRAM_EDITOR_H
+#define QET_DIAGRAM_EDITOR_H
+#include <QtGui>
+#include "qetmainwindow.h"
+#include "borderproperties.h"
+#include "conductorproperties.h"
+#include "titleblockproperties.h"
+#include "exportproperties.h"
+class QETProject;
+class QETResult;
+class ProjectView;
+class CustomElement;
+class Diagram;
+class DiagramView;
+class Element;
+class ElementsPanelWidget;
+class ElementsLocation;
+class RecentFiles;
+/**
+	This class represents the main window of the QElectroTech diagram editor and,
+	ipso facto, the most important part of the QElectroTech user interface.
+*/
+class QETDiagramEditor : public QETMainWindow {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETDiagramEditor(const QStringList & = QStringList(), QWidget * = 0);
+	virtual ~QETDiagramEditor();
+	
+	private:
+	QETDiagramEditor(const QETDiagramEditor &);
+	
+	// methods
+	public:
+	void closeEvent(QCloseEvent *);
+	QList<ProjectView *> openedProjects() const;
+	void addProjectView(ProjectView *);
+	bool openAndAddProject(const QString &, bool = true, bool = true);
+	QList<DiagramView *> projectViews() const;
+	QList<QString> editedFiles() const;
+	ProjectView *viewForFile(const QString &) const;
+	static TitleBlockProperties     defaultTitleBlockProperties();
+	static BorderProperties    defaultBorderProperties();
+	static ConductorProperties defaultConductorProperties();
+	static ExportProperties    defaultExportProperties();
+	static ExportProperties    defaultPrintProperties();
+	
+	protected:
+	void actions();
+	virtual bool event(QEvent *);
+	
+	private:
+	bool addProject(QETProject *, bool = true);
+	ProjectView *currentProject() const;
+	DiagramView *currentDiagram() const;
+	Element *currentElement() const;
+	CustomElement * currentCustomElement() const;
+	ProjectView *findProject(DiagramView *) const;
+	ProjectView *findProject(Diagram *) const;
+	ProjectView *findProject(QETProject *) const;
+	ProjectView *findProject(const QString &) const;
+	QMdiSubWindow *subWindowForWidget(QWidget *) const;
+	
+	void menus();
+	void toolbar();
+	
+	public slots:
+	void printDialog();
+	void exportDialog();
+	void save();
+	void saveAs();
+	void saveCurrentDiagram();
+	bool newProject();
+	bool openProject();
+	bool openRecentFile(const QString &);
+	bool closeProject(ProjectView *);
+	bool closeProject(QETProject *);
+	bool closeCurrentProject();
+	void slot_cut();
+	void slot_copy();
+	void slot_paste();
+	void slot_zoomIn();
+	void slot_zoomOut();
+	void slot_zoomFit();
+	void slot_zoomContent();
+	void slot_zoomReset();
+	void slot_selectAll();
+	void slot_selectNothing();
+	void slot_selectInvert();
+	void slot_delete();
+	void slot_rotate();
+	void slot_rotateTexts();
+	void slot_setSelectionMode();
+	void slot_setVisualisationMode();
+	void slot_updateActions();
+	void slot_updateModeActions();
+	void slot_updateComplexActions();
+	void slot_updatePasteAction();
+	void slot_updateWindowsMenu();
+	void slot_addColumn();
+	void slot_removeColumn();
+	void slot_addRow();
+	void slot_removeRow();
+	void editSelectionProperties();
+	void slot_editConductor();
+	void slot_resetConductors();
+	void slot_addText();
+	void slot_addImage();
+	void slot_editSelection();
+	void setWindowedMode();
+	void setTabbedMode();
+	void readSettings();
+	void writeSettings();
+	void activateDiagram(Diagram *);
+	void activateProject(QETProject *);
+	void activateProject(ProjectView *);
+	void activateWidget(QWidget *);
+	void projectWasClosed(ProjectView *);
+	void editCurrentProjectProperties();
+	void editProjectProperties(ProjectView *);
+	void editProjectProperties(QETProject *);
+	void editCurrentDiagramProperties();
+	void editDiagramProperties(DiagramView *);
+	void editDiagramProperties(Diagram *);
+	void addDiagramToProject();
+	void addDiagramToProject(QETProject *);
+	void removeDiagram(Diagram *);
+	void removeDiagramFromProject();
+	void moveDiagramUp(Diagram *);
+	void moveDiagramDown(Diagram *);
+	void cleanCurrentProject();
+	void diagramNumProject();
+	void nomenclatureProject();
+	void diagramWasAdded(DiagramView *);
+	void diagramIsAboutToBeRemoved(DiagramView *);
+	void diagramWasRemoved(DiagramView *);
+	void findElementInPanel(const ElementsLocation &);
+	void findSelectedElementInPanel();
+	void editElementInEditor(const ElementsLocation &);
+	void editSelectedElementInEditor();
+	void showError(const QETResult &);
+	void showError(const QString &);
+	
+	// attributes
+	public:
+	// Actions reachable through menus within QElectroTech
+	QActionGroup *grp_visu_sel;  ///< Action group for visualisation vs edition mode
+	QActionGroup *grp_view_mode; ///< Action group for project
+	QAction *tabbed_view_mode;   ///< Display projects as tabs
+	QAction *windowed_view_mode; ///< Display projects as windows
+	QAction *mode_selection;     ///< Set edition mode
+	QAction *mode_visualise;     ///< Set visualisation mode
+	QAction *new_file;           ///< Create new project file
+	QAction *open_file;          ///< Open project file
+	QAction *close_file;         ///< Close current project file
+	QAction *save_file;          ///< Save current project
+	QAction *save_file_as;       ///< Save current project as a specific file
+	QAction *save_cur_diagram;   ///< Save current diagram of the current project only
+	QAction *import_diagram;     ///< Importe an existing diagram (not implemented)
+	QAction *export_diagram;     ///< Export diagrams of the current project as imagess
+	QAction *print;              ///< Print diagrams of the current project
+	QAction *quit_editor;        ///< Quit the diagram editor
+	QAction *undo;               ///< Cancel the latest action
+	QAction *redo;               ///< Redo the latest cancelled operation
+	QAction *cut;                ///< Cut selection to clipboard
+	QAction *copy;               ///< Copy selection to clipboard
+	QAction *paste;              ///< Paste clipboard content on the current diagram
+	QAction *select_all;         ///< Select all
+	QAction *select_nothing;     ///< Cancel selection
+	QAction *select_invert;      ///< Invest selection
+	QAction *delete_selection;   ///< Delete selection
+	QAction *rotate_selection;   ///< Rotate selected elements and text items by 90 degrees
+	QAction *rotate_texts;       ///< Direct selected text items to a specific angle
+	QAction *find_element;       ///< Find the selected element in the panel
+	QAction *selection_prop;     ///< Show a dialog describing the selection
+	QAction *conductor_reset;    ///< Reset paths of selected conductors
+	QAction *conductor_default;  ///< Show a dialog to edit default conductor properties
+	QAction *infos_diagram;      ///< Show a dialog to edit diagram properties
+	QAction *add_text;           ///< Tool to add an independent text item on diagrams
+	QAction *add_column;         ///< Increase diagram width by adding an extra column
+	QAction *remove_column;      ///< Decrease diagram width by removing the last column
+	QAction *add_row;            ///< Increase diagram height by adding an extra row
+	QAction *remove_row;         ///< Decrease diagram height by removing the last row
+	QAction *prj_edit_prop;      ///< Edit the properties of the current project.
+	QAction *prj_add_diagram;    ///< Add a diagram to the current project.
+	QAction *prj_del_diagram;    ///< Delete a diagram from the current project
+	QAction *prj_clean;          ///< Clean the content of the curent project by removing useless items
+	QAction *prj_diagramNum;     ///< Numerotation des schemas
+	QAction *prj_nomenclature;   ///< generate nomenclature
+	QAction *zoom_in;            ///< Zoom in
+	QAction *zoom_out;           ///< Zoom out
+	QAction *zoom_fit;           ///< Adjust zoom to fit the whole diagram, including potential elements outside its borders, in the view
+	QAction *zoom_content;       ///< Adjust zoom to fit all elements in the view, regardless of diagram borders
+	QAction *zoom_reset;         ///< Reset zoom to 1:1
+	QAction *tile_window;        ///< Show MDI subwindows as tile
+	QAction *cascade_window;     ///< Show MDI subwindows as cascade
+	QAction *prev_window;        ///< Switch to the previous document
+	QAction *next_window;        ///< Switch to the next document
+	QAction *add_image;          ///< Tool to add an independent image item on diagrams
+	QAction *edit_selection;	 ///< To edit selected item
+
+	private:
+	QMdiArea workspace;
+	QSignalMapper windowMapper;
+	/// Directory to use for file dialogs such as File > save
+	QDir open_dialog_dir;
+	/// Dock for the elements panel
+	QDockWidget *qdw_pa;
+	/// Dock for the undo list
+	QDockWidget *qdw_undo;
+	/// Elements panel
+	ElementsPanelWidget *pa;
+	QMenu *windows_menu;
+	QToolBar *main_bar;
+	QToolBar *view_bar;
+	QToolBar *diagram_bar;
+	QUndoGroup undo_group;
+	bool can_update_actions;
+};
+#endif

Added: trunk/sources/qetgraphicsitem/conductor.cpp
===================================================================
--- trunk/sources/qetgraphicsitem/conductor.cpp	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/conductor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1631 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <QtDebug>
+#include "conductor.h"
+#include "conductorsegment.h"
+#include "conductorsegmentprofile.h"
+#include "conductortextitem.h"
+#include "element.h"
+#include "diagram.h"
+#include "diagramcommands.h"
+#include "conductorautonumerotation.h"
+#define PR(x) qDebug() << #x " = " << x;
+
+bool Conductor::pen_and_brush_initialized = false;
+QPen Conductor::conductor_pen = QPen();
+QBrush Conductor::conductor_brush = QBrush();
+QBrush Conductor::square_brush = QBrush(Qt::darkGreen);
+/**
+	Constructeur
+	@param p1              Premiere Borne a laquelle le conducteur est lie
+	@param p2              Seconde Borne a laquelle le conducteur est lie
+	@param parent_diagram  QGraphicsScene a laquelle appartient le conducteur
+*/
+Conductor::Conductor(Terminal *p1, Terminal* p2, Diagram *parent_diagram) :
+	QObject(),
+	QGraphicsPathItem(0, parent_diagram),
+	terminal1(p1),
+	terminal2(p2),
+	destroyed_(false),
+	text_item(0),
+	segments(NULL),
+	moving_point(false),
+	moving_segment(false),
+	modified_path(false),
+	has_to_save_profile(false),
+	segments_squares_scale_(1.0),
+	must_highlight_(Conductor::None)
+{
+	//set Zvalue at 9 to be upper than the DiagramImageItem and bottom of element(10)
+	setZValue(9);
+	previous_z_value = zValue();
+
+	// ajout du conducteur a la liste de conducteurs de chacune des deux bornes
+	bool ajout_p1 = terminal1 -> addConductor(this);
+	bool ajout_p2 = terminal2 -> addConductor(this);
+	
+	// en cas d'echec de l'ajout (conducteur deja existant notamment)
+	if (!ajout_p1 || !ajout_p2) return;
+	
+	// attributs de dessin par defaut (communs a tous les conducteurs)
+	if (!pen_and_brush_initialized) {
+		conductor_pen.setJoinStyle(Qt::MiterJoin);
+		conductor_pen.setCapStyle(Qt::SquareCap);
+		conductor_pen.setColor(Qt::black);
+		conductor_pen.setStyle(Qt::SolidLine);
+		conductor_pen.setWidthF(1.0);
+		conductor_brush.setColor(Qt::white);
+		conductor_brush.setStyle(Qt::NoBrush);
+		pen_and_brush_initialized = true;
+	}
+	
+	// par defaut, les 4 profils sont des profils nuls = il faut utiliser priv_calculeConductor
+	conductor_profiles.insert(Qt::TopLeftCorner,     ConductorProfile());
+	conductor_profiles.insert(Qt::TopRightCorner,    ConductorProfile());
+	conductor_profiles.insert(Qt::BottomLeftCorner,  ConductorProfile());
+	conductor_profiles.insert(Qt::BottomRightCorner, ConductorProfile());
+
+	// calcul du rendu du conducteur
+	generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
+	setFlags(QGraphicsItem::ItemIsSelectable);
+	setAcceptsHoverEvents(true);
+	
+	// ajout du champ de texte editable
+	text_item = new ConductorTextItem(properties_.text, this);
+	calculateTextItemPosition();
+	connect(
+		text_item,
+		SIGNAL(diagramTextChanged(DiagramTextItem *, const QString &, const QString &)),
+		this,
+		SLOT(displayedTextChanged())
+	);
+}
+
+/**
+	Destructeur
+	Detruit le conducteur ainsi que ses segments. Il ne detruit pas les bornes
+	mais s'en detache
+*/
+Conductor::~Conductor() {
+	// se detache des bornes
+	if (!isDestroyed()) destroy();
+	
+	// supprime les segments
+	deleteSegments();
+}
+
+/**
+	Met a jour la representation graphique du conducteur en recalculant son
+	trace. Cette fonction est typiquement appelee lorsqu'une seule des bornes du
+	conducteur a change de position.
+	@param rect Rectangle a mettre a jour
+	@see QGraphicsPathItem::update()
+*/
+void Conductor::updatePath(const QRectF &rect) {
+	QPointF p1, p2;
+	p1 = terminal1 -> dockConductor();
+	p2 = terminal2 -> dockConductor();
+	if (segmentsCount() && !conductor_profiles[currentPathType()].isNull())
+		updateConductorPath(p1, terminal1 -> orientation(), p2, terminal2 -> orientation());
+	else
+		generateConductorPath(p1, terminal1 -> orientation(), p2, terminal2 -> orientation());
+	calculateTextItemPosition();
+	QGraphicsPathItem::update(rect);
+}
+
+/**
+	Genere le QPainterPath a partir de la liste des points
+*/
+void Conductor::segmentsToPath() {
+	// chemin qui sera dessine
+	QPainterPath path;
+	
+	// s'il n'y a pa des segments, on arrete la
+	if (segments == NULL) setPath(path);
+	
+	// demarre le chemin
+	path.moveTo(segments -> firstPoint());
+	
+	// parcourt les segments pour dessiner le chemin
+	ConductorSegment *segment = segments;
+	while(segment -> hasNextSegment()) {
+		path.lineTo(segment -> secondPoint());
+		segment = segment -> nextSegment();
+	}
+	
+	// termine le chemin
+	path.lineTo(segment -> secondPoint());
+	
+	// affecte le chemin au conducteur
+	setPath(path);
+}
+
+/**
+	Gere les updates
+	@param p1 Coordonnees du point d'amarrage de la borne 1
+	@param o1 Orientation de la borne 1
+	@param p2 Coordonnees du point d'amarrage de la borne 2
+	@param o2 Orientation de la borne 2
+*/
+void Conductor::updateConductorPath(const QPointF &p1, QET::Orientation o1, const QPointF &p2, QET::Orientation o2) {
+	Q_UNUSED(o1);
+	Q_UNUSED(o2);
+	
+	ConductorProfile &conductor_profile = conductor_profiles[currentPathType()];
+	
+	Q_ASSERT_X(conductor_profile.segmentsCount(QET::Both) > 1, "Conductor::priv_modifieConductor", "pas de points a modifier");
+	Q_ASSERT_X(!conductor_profile.isNull(),                 "Conductor::priv_modifieConductor", "pas de profil utilisable");
+	
+	// recupere les coordonnees fournies des bornes
+	QPointF new_p1 = mapFromScene(p1);
+	QPointF new_p2 = mapFromScene(p2);
+	QRectF new_rect = QRectF(new_p1, new_p2);
+	
+	// recupere la largeur et la hauteur du profil
+	qreal profile_width  = conductor_profile.width();
+	qreal profile_height = conductor_profile.height();
+	
+	// calcule les differences verticales et horizontales a appliquer
+	qreal h_diff = (qAbs(new_rect.width())  - qAbs(profile_width) ) * getSign(profile_width);
+	qreal v_diff = (qAbs(new_rect.height()) - qAbs(profile_height)) * getSign(profile_height);
+	
+	// applique les differences aux segments
+	QHash<ConductorSegmentProfile *, qreal> segments_lengths;
+	segments_lengths.unite(shareOffsetBetweenSegments(h_diff, conductor_profile.horizontalSegments()));
+	segments_lengths.unite(shareOffsetBetweenSegments(v_diff, conductor_profile.verticalSegments()));
+	
+	// en deduit egalement les coefficients d'inversion (-1 pour une inversion, +1 pour conserver le meme sens)
+	int horiz_coeff = getCoeff(new_rect.width(),  profile_width);
+	int verti_coeff = getCoeff(new_rect.height(), profile_height);
+	
+	// genere les nouveaux points
+	QList<QPointF> points;
+	points << new_p1;
+	int limit = conductor_profile.segments.count() - 1;
+	for (int i = 0 ; i < limit ; ++ i) {
+		// dernier point
+		QPointF previous_point = points.last();
+		
+		// profil de segment de conducteur en cours
+		ConductorSegmentProfile *csp = conductor_profile.segments.at(i);
+		
+		// coefficient et offset a utiliser pour ce point
+		qreal coeff = csp -> isHorizontal ? horiz_coeff : verti_coeff;
+		qreal offset_applied = segments_lengths[csp];
+		
+		// applique l'offset et le coeff au point
+		if (csp -> isHorizontal) {
+			points << QPointF (
+				previous_point.x() + (coeff * offset_applied),
+				previous_point.y()
+			);
+		} else {
+			points << QPointF (
+				previous_point.x(),
+				previous_point.y() + (coeff * offset_applied)
+			);
+		}
+	}
+	points << new_p2;
+	pointsToSegments(points);
+	segmentsToPath();
+}
+
+/**
+	@param offset Longueur a repartir entre les segments
+	@param segments_list Segments sur lesquels il faut repartir la longueur
+	@param precision seuil en-deca duquel on considere qu'il ne reste rien a repartir
+*/
+QHash<ConductorSegmentProfile *, qreal> Conductor::shareOffsetBetweenSegments(
+	const qreal &offset,
+	const QList<ConductorSegmentProfile *> &segments_list,
+	const qreal &precision
+) const {
+	// construit le QHash qui sera retourne
+	QHash<ConductorSegmentProfile *, qreal> segments_hash;
+	foreach(ConductorSegmentProfile *csp, segments_list) {
+		segments_hash.insert(csp, csp -> length);
+	}
+	
+	// memorise le signe de la longueur de chaque segement
+	QHash<ConductorSegmentProfile *, int> segments_signs;
+	foreach(ConductorSegmentProfile *csp, segments_hash.keys()) {
+		segments_signs.insert(csp, getSign(csp -> length));
+	}
+	
+	//qDebug() << "repartition d'un offset de" << offset << "px sur" << segments_list.count() << "segments";
+	
+	// repartit l'offset sur les segments
+	qreal remaining_offset = offset;
+	while (remaining_offset > precision || remaining_offset < -precision) {
+		// recupere le nombre de segments differents ayant une longueur non nulle
+		uint segments_count = 0;
+		foreach(ConductorSegmentProfile *csp, segments_hash.keys()) if (segments_hash[csp]) ++ segments_count;
+		//qDebug() << "  remaining_offset =" << remaining_offset;
+		qreal local_offset = remaining_offset / segments_count;
+		//qDebug() << "  repartition d'un offset local de" << local_offset << "px sur" << segments_count << "segments";
+		remaining_offset = 0.0;
+		foreach(ConductorSegmentProfile *csp, segments_hash.keys()) {
+			// ignore les segments de longueur nulle
+			if (!segments_hash[csp]) continue;
+			// applique l'offset au segment
+			//qreal segment_old_length = segments_hash[csp];
+			segments_hash[csp] += local_offset;
+			
+			// (la longueur du segment change de signe) <=> (le segment n'a pu absorbe tout l'offset)
+			if (segments_signs[csp] != getSign(segments_hash[csp])) {
+				
+				// on remet le trop-plein dans la reserve d'offset
+				remaining_offset += qAbs(segments_hash[csp]) * getSign(local_offset);
+				//qDebug() << "    trop-plein de" << qAbs(segments_hash[csp]) * getSign(local_offset) << "remaining_offset =" << remaining_offset;
+				segments_hash[csp] = 0.0;
+			} else {
+				//qDebug() << "    offset local de" << local_offset << "accepte";
+			}
+		}
+	}
+	
+	return(segments_hash);
+}
+
+/**
+	Calcule un trajet "par defaut" pour le conducteur
+	@param p1 Coordonnees du point d'amarrage de la borne 1
+	@param o1 Orientation de la borne 1
+	@param p2 Coordonnees du point d'amarrage de la borne 2
+	@param o2 Orientation de la borne 2
+*/
+void Conductor::generateConductorPath(const QPointF &p1, QET::Orientation o1, const QPointF &p2, QET::Orientation o2) {
+	QPointF sp1, sp2, depart, newp1, newp2, arrivee, depart0, arrivee0;
+	QET::Orientation ori_depart, ori_arrivee;
+	
+	// s'assure qu'il n'y a ni points
+	QList<QPointF> points;
+	
+	// mappe les points par rapport a la scene
+	sp1 = mapFromScene(p1);
+	sp2 = mapFromScene(p2);
+	
+	// prolonge les bornes
+	newp1 = extendTerminal(sp1, o1);
+	newp2 = extendTerminal(sp2, o2);
+	
+	// distingue le depart de l'arrivee : le trajet se fait toujours de gauche a droite (apres prolongation)
+	if (newp1.x() <= newp2.x()) {
+		depart      = newp1;
+		arrivee     = newp2;
+		depart0     = sp1;
+		arrivee0    = sp2;
+		ori_depart  = o1;
+		ori_arrivee = o2;
+	} else {
+		depart      = newp2;
+		arrivee     = newp1;
+		depart0     = sp2;
+		arrivee0    = sp1;
+		ori_depart  = o2;
+		ori_arrivee = o1;
+	}
+	
+	// debut du trajet
+	points << depart0;
+	
+	// prolongement de la borne de depart 
+	points << depart;
+	
+	// commence le vrai trajet
+	if (depart.y() < arrivee.y()) {
+		// trajet descendant
+		if ((ori_depart == QET::North && (ori_arrivee == QET::South || ori_arrivee == QET::West)) || (ori_depart == QET::East && ori_arrivee == QET::West)) {
+			// cas "3"
+			int ligne_inter_x = qRound(depart.x() + arrivee.x()) / 2;
+			while (ligne_inter_x % Diagram::xGrid) -- ligne_inter_x;
+			points << QPointF(ligne_inter_x, depart.y());
+			points << QPointF(ligne_inter_x, arrivee.y());
+		} else if ((ori_depart == QET::South && (ori_arrivee == QET::North || ori_arrivee == QET::East)) || (ori_depart == QET::West && ori_arrivee == QET::East)) {
+			// cas "4"
+			int ligne_inter_y = qRound(depart.y() + arrivee.y()) / 2;
+			while (ligne_inter_y % Diagram::yGrid) -- ligne_inter_y;
+			points << QPointF(depart.x(), ligne_inter_y);
+			points << QPointF(arrivee.x(), ligne_inter_y);
+		} else if ((ori_depart == QET::North || ori_depart == QET::East) && (ori_arrivee == QET::North || ori_arrivee == QET::East)) {
+			points << QPointF(arrivee.x(), depart.y()); // cas "2"
+		} else {
+			points << QPointF(depart.x(), arrivee.y()); // cas "1"
+		}
+	} else {
+		// trajet montant
+		if ((ori_depart == QET::West && (ori_arrivee == QET::East || ori_arrivee == QET::South)) || (ori_depart == QET::North && ori_arrivee == QET::South)) {
+			// cas "3"
+			int ligne_inter_y = qRound(depart.y() + arrivee.y()) / 2;
+			while (ligne_inter_y % Diagram::yGrid) -- ligne_inter_y;
+			points << QPointF(depart.x(), ligne_inter_y);
+			points << QPointF(arrivee.x(), ligne_inter_y);
+		} else if ((ori_depart == QET::East && (ori_arrivee == QET::West || ori_arrivee == QET::North)) || (ori_depart == QET::South && ori_arrivee == QET::North)) {
+			// cas "4"
+			int ligne_inter_x = qRound(depart.x() + arrivee.x()) / 2;
+			while (ligne_inter_x % Diagram::xGrid) -- ligne_inter_x;
+			points << QPointF(ligne_inter_x, depart.y());
+			points << QPointF(ligne_inter_x, arrivee.y());
+		} else if ((ori_depart == QET::West || ori_depart == QET::North) && (ori_arrivee == QET::West || ori_arrivee == QET::North)) {
+			points << QPointF(depart.x(), arrivee.y()); // cas "2"
+		} else {
+			points << QPointF(arrivee.x(), depart.y()); // cas "1"
+		}
+	}
+	
+	// fin du vrai trajet
+	points << arrivee;
+	
+	// prolongement de la borne d'arrivee
+	points << arrivee0;
+	
+	// inverse eventuellement l'ordre des points afin que le trajet soit exprime de la borne 1 vers la borne 2
+	if (newp1.x() > newp2.x()) {
+		QList<QPointF> points2;
+		for (int i = points.size() - 1 ; i >= 0 ; -- i) points2 << points.at(i);
+		points = points2;
+	}
+	
+	pointsToSegments(points);
+	segmentsToPath();
+}
+
+/**
+	Prolonge une borne.
+	@param terminal Le point correspondant a la borne
+	@param terminal_orientation L'orientation de la borne
+	@param ext_size la taille de la prolongation
+	@return le point correspondant a la borne apres prolongation
+*/
+QPointF Conductor::extendTerminal(const QPointF &terminal, QET::Orientation terminal_orientation, qreal ext_size) {
+	QPointF extended_terminal;
+	switch(terminal_orientation) {
+		case QET::North:
+			extended_terminal = QPointF(terminal.x(), terminal.y() - ext_size);
+			break;
+		case QET::East:
+			extended_terminal = QPointF(terminal.x() + ext_size, terminal.y());
+			break;
+		case QET::South:
+			extended_terminal = QPointF(terminal.x(), terminal.y() + ext_size);
+			break;
+		case QET::West:
+			extended_terminal = QPointF(terminal.x() - ext_size, terminal.y());
+			break;
+		default: extended_terminal = terminal;
+	}
+	return(extended_terminal);
+}
+
+/**
+	Dessine le conducteur sans antialiasing.
+	@param qp Le QPainter a utiliser pour dessiner le conducteur
+	@param options Les options de style pour le conducteur
+	@param qw Le QWidget sur lequel on dessine 
+*/
+void Conductor::paint(QPainter *qp, const QStyleOptionGraphicsItem *options, QWidget *qw) {
+	Q_UNUSED(qw);
+	qp -> save();
+	qp -> setRenderHint(QPainter::Antialiasing, false);
+	
+	// determine la couleur du conducteur
+	QColor final_conductor_color(properties_.color);
+	if (must_highlight_ == Normal) {
+		final_conductor_color = QColor::fromRgb(69, 137, 255, 255);
+	} else if (must_highlight_ == Alert) {
+		final_conductor_color =QColor::fromRgb(255, 69, 0, 255);
+	} else if (isSelected()) {
+		final_conductor_color = Qt::red;
+	} else {
+		if (Diagram *parent_diagram = diagram()) {
+			if (!parent_diagram -> drawColoredConductors()) {
+				final_conductor_color = Qt::black;
+			}
+		}
+	}
+	
+	// affectation du QPen et de la QBrush modifies au QPainter
+	qp -> setBrush(conductor_brush);
+	QPen final_conductor_pen = conductor_pen;
+	
+	// modification du QPen generique pour lui affecter la couleur et le style adequats
+	final_conductor_pen.setColor(final_conductor_color);
+	final_conductor_pen.setStyle(properties_.style);
+	final_conductor_pen.setJoinStyle(Qt::SvgMiterJoin); // meilleur rendu des pointilles
+	
+	// utilisation d'un trait "cosmetique" en-dessous d'un certain zoom
+	if (options && options -> levelOfDetail < 1.0) {
+		final_conductor_pen.setCosmetic(true);
+	}
+	
+	qp -> setPen(final_conductor_pen);
+	
+	// dessin du conducteur
+	qp -> drawPath(path());
+	if (properties_.type == ConductorProperties::Single) {
+		qp -> setBrush(final_conductor_color);
+		properties_.singleLineProperties.draw(
+			qp,
+			middleSegment() -> isHorizontal() ? QET::Horizontal : QET::Vertical,
+			QRectF(middleSegment() -> middle() - QPointF(12.0, 12.0), QSizeF(24.0, 24.0))
+		);
+		if (isSelected()) qp -> setBrush(Qt::NoBrush);
+	}
+	
+	// decalage ideal pour le rendu centre d'un carre / cercle de 2.0 px de cote / diametre
+	qreal pretty_offset = (options -> levelOfDetail == 1 ? 1.0 : 1.0);
+	
+	// dessin des points d'accroche du conducteur si celui-ci est selectionne
+	if (isSelected()) {
+		QList<QPointF> points = segmentsToPoints();
+		QPointF previous_point;
+		for (int i = 1 ; i < (points.size() -1) ; ++ i) {
+			QPointF point = points.at(i);
+				
+			// dessine le carre de saisie du segment
+			if (i > 1) {
+				qp -> fillRect(
+					QRectF(
+						((previous_point.x() + point.x()) / 2.0 ) - pretty_offset * segments_squares_scale_,
+						((previous_point.y() + point.y()) / 2.0 ) - pretty_offset * segments_squares_scale_,
+						2.0 * segments_squares_scale_,
+						2.0 * segments_squares_scale_
+					),
+					square_brush
+				);
+			}
+			previous_point = point;
+		}
+	}
+	
+	// dessine les eventuelles jonctions
+	QList<QPointF> junctions_list = junctions();
+	if (!junctions_list.isEmpty()) {
+		final_conductor_pen.setStyle(Qt::SolidLine);
+		QBrush junction_brush(final_conductor_color, Qt::SolidPattern);
+		qp -> setPen(final_conductor_pen);
+		qp -> setBrush(junction_brush);
+		qp -> setRenderHint(QPainter::Antialiasing, true);
+		foreach(QPointF point, junctions_list) {
+			qp -> drawEllipse(QRectF(point.x() - pretty_offset, point.y() - pretty_offset, 2.0, 2.0));
+		}
+	}
+	qp -> restore();
+}
+
+/**
+	Methode de preparation a la destruction du conducteur ; le conducteur se detache de ses deux bornes
+*/
+void Conductor::destroy() {
+	destroyed_ = true;
+	terminal1 -> removeConductor(this);
+	terminal2 -> removeConductor(this);
+}
+
+/// @return le Diagram auquel ce conducteur appartient, ou 0 si ce conducteur est independant
+Diagram *Conductor::diagram() const {
+	return(qobject_cast<Diagram *>(scene()));
+}
+
+/**
+	@return le champ de texte associe a ce conducteur
+*/
+ConductorTextItem *Conductor::textItem() const {
+	return(text_item);
+}
+
+/**
+	Methode de validation d'element XML
+	@param e Un element XML sense represente un Conducteur
+	@return true si l'element XML represente bien un Conducteur ; false sinon
+*/
+bool Conductor::valideXml(QDomElement &e){
+	// verifie le nom du tag
+	if (e.tagName() != "conductor") return(false);
+	
+	// verifie la presence des attributs minimaux
+	if (!e.hasAttribute("terminal1")) return(false);
+	if (!e.hasAttribute("terminal2")) return(false);
+	
+	bool conv_ok;
+	// parse l'abscisse
+	e.attribute("terminal1").toInt(&conv_ok);
+	if (!conv_ok) return(false);
+	
+	// parse l'ordonnee
+	e.attribute("terminal2").toInt(&conv_ok);
+	if (!conv_ok) return(false);
+	return(true);
+}
+
+/**
+	Gere les clics sur le conducteur.
+	@param e L'evenement decrivant le clic.
+*/
+void Conductor::mousePressEvent(QGraphicsSceneMouseEvent *e) {
+	// clic gauche
+	if (e -> buttons() & Qt::LeftButton) {
+		// recupere les coordonnees du clic
+		press_point = e -> pos();
+		
+		/*
+			parcourt les segments pour determiner si le clic a eu lieu
+			- sur l'extremite d'un segment
+			- sur le milieu d'un segment
+			- ailleurs
+		*/
+		ConductorSegment *segment = segments;
+		while (segment -> hasNextSegment()) {
+			if (hasClickedOn(press_point, segment -> secondPoint())) {
+				moving_point = true;
+				moving_segment = false;
+				moved_segment = segment;
+				break;
+			} else if (hasClickedOn(press_point, segment -> middle())) {
+				moving_point = false;
+				moving_segment = true;
+				moved_segment = segment;
+				break;
+			}
+			segment = segment -> nextSegment();
+		}
+		if (moving_segment || moving_point) {
+			// en cas de debut de modification de conducteur, on memorise la position du champ de texte
+			before_mov_text_pos_ = text_item -> pos();
+		}
+	}
+	QGraphicsPathItem::mousePressEvent(e);
+	if (e -> modifiers() & Qt::ControlModifier) {
+		setSelected(!isSelected());
+	}
+}
+
+/**
+	Gere les deplacements de souris sur le conducteur.
+	@param e L'evenement decrivant le deplacement de souris.
+*/
+void Conductor::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
+	// clic gauche
+	if (e -> buttons() & Qt::LeftButton) {
+		// position pointee par la souris
+		qreal mouse_x = e -> pos().x();
+		qreal mouse_y = e -> pos().y();
+		
+		bool snap_conductors_to_grid = e -> modifiers() ^ Qt::ShiftModifier;
+		if (snap_conductors_to_grid) {
+			mouse_x = qRound(mouse_x / (Diagram::xGrid * 1.0)) * Diagram::xGrid;
+			mouse_y = qRound(mouse_y / (Diagram::yGrid * 1.0)) * Diagram::yGrid;
+		}
+		
+		if (moving_point) {
+			// la modification par points revient bientot
+			/*
+			// position precedente du point
+			QPointF p = moved_segment -> secondPoint();
+			qreal p_x = p.x();
+			qreal p_y = p.y();
+			
+			// calcul du deplacement
+			moved_segment -> moveX(mouse_x - p_x());
+			moved_segment -> moveY(mouse_y - p_y());
+			
+			// application du deplacement
+			modified_path = true;
+			updatePoints();
+			segmentsToPath();
+			*/
+		} else if (moving_segment) {
+			// position precedente du point
+			QPointF p = moved_segment -> middle();
+			
+			// calcul du deplacement
+			moved_segment -> moveX(mouse_x - p.x());
+			moved_segment -> moveY(mouse_y - p.y());
+			
+			// application du deplacement
+			modified_path = true;
+			has_to_save_profile = true;
+			segmentsToPath();
+			calculateTextItemPosition();
+		}
+	}
+	QGraphicsPathItem::mouseMoveEvent(e);
+}
+
+/**
+	Gere les relachements de boutons de souris sur le conducteur 
+	@param e L'evenement decrivant le lacher de bouton.
+*/
+void Conductor::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
+	// clic gauche
+	moving_point = false;
+	moving_segment = false;
+	if (has_to_save_profile) {
+		saveProfile();
+		has_to_save_profile = false;
+	}
+	if (!(e -> modifiers() & Qt::ControlModifier)) {
+		QGraphicsPathItem::mouseReleaseEvent(e);
+	}
+	calculateTextItemPosition();
+}
+
+/**
+	Gere l'entree de la souris dans la zone du conducteur
+	@param e Le QGraphicsSceneHoverEvent decrivant l'evenement
+*/
+void Conductor::hoverEnterEvent(QGraphicsSceneHoverEvent *e) {
+	Q_UNUSED(e);
+	segments_squares_scale_ = 2.0;
+	if (isSelected()) {
+		update();
+	}
+}
+
+/**
+	Gere la sortie de la souris de la zone du conducteur
+	@param e Le QGraphicsSceneHoverEvent decrivant l'evenement
+*/
+void Conductor::hoverLeaveEvent(QGraphicsSceneHoverEvent *e) {
+	Q_UNUSED(e);
+	segments_squares_scale_ = 1.0;
+	if (isSelected()) {
+		update();
+	}
+}
+
+/**
+	Gere les mouvements de souris au dessus du conducteur
+	@param e Le QGraphicsSceneHoverEvent decrivant l'evenement
+*/
+void Conductor::hoverMoveEvent(QGraphicsSceneHoverEvent *e) {
+	/*
+	if (isSelected()) {
+		QPointF hover_point = mapFromScene(e -> pos());
+		ConductorSegment *segment = segments;
+		bool cursor_set = false;
+		while (segment -> hasNextSegment()) {
+			if (hasClickedOn(hover_point, segment -> secondPoint())) {
+				setCursor(Qt::CrossCursor);
+				cursor_set = true;
+			} else if (hasClickedOn(hover_point, segment -> middle())) {
+				setCursor(segment -> isVertical() ? Qt::SplitHCursor : Qt::SplitVCursor);
+				cursor_set = true;
+			}
+			segment = segment -> nextSegment();
+		}
+		if (!cursor_set) setCursor(Qt::ArrowCursor);
+	}
+	*/
+	QGraphicsPathItem::hoverMoveEvent(e);
+}
+
+/**
+	Gere les changements relatifs au conducteur
+	Reimplemente ici pour :
+	  * positionner le conducteur en avant-plan lorsqu'il est selectionne
+	@param change Type de changement
+	@param value  Valeur relative au changement
+*/
+QVariant Conductor::itemChange(GraphicsItemChange change, const QVariant &value) {
+	if (change == QGraphicsItem::ItemSelectedChange) {
+		if (value.toBool()) {
+			// le conducteur vient de se faire selectionner
+			previous_z_value = zValue();
+			setZValue(qAbs(previous_z_value) + 10000);
+		} else {
+			// le conducteur vient de se faire deselectionner
+			setZValue(previous_z_value);
+		}
+	} else if (change == QGraphicsItem::ItemSceneHasChanged) {
+		// permet de positionner correctement le texte du conducteur lors de son ajout a un schema
+		calculateTextItemPosition();
+	} else if (change == QGraphicsItem::ItemVisibleHasChanged) {
+		// permet de positionner correctement le texte du conducteur lors de son ajout a un schema
+		calculateTextItemPosition();
+	}
+	return(QGraphicsPathItem::itemChange(change, value));
+}
+
+/**
+	@return Le rectangle delimitant l'espace de dessin du conducteur
+*/
+QRectF Conductor::boundingRect() const {
+	QRectF retour = QGraphicsPathItem::boundingRect();
+	retour.adjust(-11.0, -11.0, 11.0, 11.0);
+	return(retour);
+}
+
+/**
+	@return La forme / zone "cliquable" du conducteur (epaisseur : 5.0px).
+	@see variableShape()
+*/
+QPainterPath Conductor::shape() const {
+	return(variableShape(5.0));
+}
+
+/**
+	@return la distance en dessous de laquelle on considere qu'un point est a
+	proximite du trajet du conducteur. La valeur est actuellement fixee a
+	60.0px.
+*/
+qreal Conductor::nearDistance() const {
+	return(60.0);
+}
+
+/**
+	@return la zone dans laquelle dont on considere que tous les points sont a
+	proximite du trajet du conducteur.
+	@see nearDistance()
+	@see variableShape()
+*/
+QPainterPath Conductor::nearShape() const {
+	return(variableShape(nearDistance()));
+}
+
+/**
+	@return la forme du conducteur
+	@param thickness la moitie de l'epaisseur voulue pour cette forme
+*/
+QPainterPath Conductor::variableShape(const qreal &thickness) const {
+	qreal my_thickness = qAbs(thickness);
+	
+	QList<QPointF> points = segmentsToPoints();
+	QPainterPath area;
+	QPointF previous_point;
+	QPointF *point1, *point2;
+	foreach(QPointF point, points) {
+		if (!previous_point.isNull()) {
+			if (point.x() == previous_point.x()) {
+				if (point.y() <= previous_point.y()) {
+					point1 = &point;
+					point2 = &previous_point;
+				} else {
+					point1 = &previous_point;
+					point2 = &point;
+				}
+			} else {
+				if (point.x() <= previous_point.x()) {
+					point1 = &point;
+					point2 = &previous_point;
+				} else {
+					point1 = &previous_point;
+					point2 = &point;
+				}
+			}
+			qreal p1_x = point1 -> x();
+			qreal p1_y = point1 -> y();
+			qreal p2_x = point2 -> x();
+			qreal p2_y = point2 -> y();
+			area.setFillRule(Qt::OddEvenFill);
+			area.addRect(p1_x - my_thickness, p1_y - my_thickness, my_thickness * 2.0 + p2_x - p1_x, my_thickness * 2.0  + p2_y - p1_y);
+		}
+		previous_point = point;
+		area.setFillRule(Qt::WindingFill);
+		area.addRect(point.x() - my_thickness, point.y() - my_thickness, my_thickness * 2.0, my_thickness * 2.0 );
+	}
+	return(area);
+}
+
+/**
+	@param point un point, exprime dans les coordonnees du conducteur
+	@return true si le point est a proximite du conducteur, c-a-d a moins de
+	60px du conducteur.
+*/
+bool Conductor::isNearConductor(const QPointF &point) {
+	return(variableShape(60.1).contains(point));
+}
+
+/**
+	Renvoie une valeur donnee apres l'avoir bornee entre deux autres valeurs,
+	en y ajoutant une marge interne.
+	@param tobound valeur a borner
+	@param bound1 borne 1
+	@param bound2 borne 2
+	@param space marge interne ajoutee
+	@return La valeur bornee
+*/
+qreal Conductor::conductor_bound(qreal tobound, qreal bound1, qreal bound2, qreal space) {
+	qDebug() << "will bound" << tobound << "between" << bound1 << "and" << bound2 ;
+	if (bound1 < bound2) {
+		return(qBound(bound1 + space, tobound, bound2 - space));
+	} else {
+		return(qBound(bound2 + space, tobound, bound1 - space));
+	}
+}
+
+/**
+	Renvoie une valeur donnee apres l'avoir bornee avant ou apres une valeur.
+	@param tobound valeur a borner
+	@param bound borne
+	@param positive true pour borner la valeur avant la borne, false sinon
+	@return La valeur bornee
+*/
+qreal Conductor::conductor_bound(qreal tobound, qreal bound, bool positive) {
+	qreal space = 5.0;
+	return(positive ? qMax(tobound, bound + space) : qMin(tobound, bound - space));
+}
+
+/**
+	@param type Type de Segments
+	@return Le nombre de segments composant le conducteur.
+*/
+uint Conductor::segmentsCount(QET::ConductorSegmentType type) const {
+	QList<ConductorSegment *> segments_list = segmentsList();
+	if (type == QET::Both) return(segments_list.count());
+	uint nb_seg = 0;
+	foreach(ConductorSegment *conductor_segment, segments_list) {
+		if (conductor_segment -> type() == type) ++ nb_seg;
+	}
+	return(nb_seg);
+}
+
+/**
+	Genere une liste de points a partir des segments de ce conducteur
+	@return La liste de points representant ce conducteur
+*/
+QList<QPointF> Conductor::segmentsToPoints() const {
+	// liste qui sera retournee
+	QList<QPointF> points_list;
+	
+	// on retourne la liste tout de suite s'il n'y a pas de segments
+	if (segments == NULL) return(points_list);
+	
+	// recupere le premier point
+	points_list << segments -> firstPoint();
+	
+	// parcourt les segments pour recuperer les autres points
+	ConductorSegment *segment = segments;
+	while(segment -> hasNextSegment()) {
+		points_list << segment -> secondPoint();
+		segment = segment -> nextSegment();
+	}
+	
+	// recupere le dernier point
+	points_list << segment -> secondPoint();
+	
+	//retourne la liste
+	return(points_list);
+}
+
+/**
+	Regenere les segments de ce conducteur a partir de la liste de points passee en parametre
+	@param points_list Liste de points a utiliser pour generer les segments
+*/
+void Conductor::pointsToSegments(QList<QPointF> points_list) {
+	// supprime les segments actuels
+	deleteSegments();
+	
+	// cree les segments a partir de la liste de points
+	ConductorSegment *last_segment = NULL;
+	for (int i = 0 ; i < points_list.size() - 1 ; ++ i) {
+		last_segment = new ConductorSegment(points_list.at(i), points_list.at(i + 1), last_segment);
+		if (!i) segments = last_segment;
+	}
+}
+
+/**
+	Permet de savoir si un point est tres proche d'un autre. Cela sert surtout
+	pour determiner si un clic a ete effectue pres d'un point donne.
+	@param press_point Point effectivement clique
+	@param point point cliquable
+	@return true si l'on peut considerer que le point a ete clique, false sinon
+*/
+bool Conductor::hasClickedOn(QPointF press_point, QPointF point) const {
+	return (
+		press_point.x() >= point.x() - 5.0 &&\
+		press_point.x() <  point.x() + 5.0 &&\
+		press_point.y() >= point.y() - 5.0 &&\
+		press_point.y() <  point.y() + 5.0
+	);
+}
+
+/**
+	Charge les caracteristiques du conducteur depuis un element XML.
+	@param e Un element XML
+	@return true si le chargement a reussi, false sinon
+*/
+bool Conductor::fromXml(QDomElement &e) {
+	// recupere la "configuration" du conducteur
+	properties_.fromXml(e);
+	readProperties();
+	qreal user_pos_x, user_pos_y;
+	if (
+		QET::attributeIsAReal(e, "userx", &user_pos_x) &&
+		QET::attributeIsAReal(e, "usery", &user_pos_y)
+	) {
+		text_item -> forceMovedByUser(true);
+		text_item -> setPos(user_pos_x, user_pos_y);
+	}
+	if (e.hasAttribute("rotation")) {
+		text_item -> setRotationAngle(e.attribute("rotation").toDouble());
+		text_item -> forceRotateByUser(true);
+	}
+	
+	// parcourt les elements XML "segment" et en extrait deux listes de longueurs
+	// les segments non valides sont ignores
+	QList<qreal> segments_x, segments_y;
+	for (QDomNode node = e.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		// on s'interesse aux elements XML "segment"
+		QDomElement current_segment = node.toElement();
+		if (current_segment.isNull() || current_segment.tagName() != "segment") continue;
+		
+		// le segment doit avoir une longueur
+		if (!current_segment.hasAttribute("length")) continue;
+		
+		// cette longueur doit etre un reel
+		bool ok;
+		qreal segment_length = current_segment.attribute("length").toDouble(&ok);
+		if (!ok) continue;
+		
+		if (current_segment.attribute("orientation") == "horizontal") {
+			segments_x << segment_length;
+			segments_y << 0.0;
+		} else {
+			segments_x << 0.0;
+			segments_y << segment_length;
+		}
+	}
+	
+	// s'il n'y a pas de segments, on renvoie true
+	if (!segments_x.size()) return(true);
+	// les longueurs recueillies doivent etre coherentes avec les positions des bornes
+	qreal width = 0.0, height = 0.0;
+	foreach (qreal t, segments_x) width  += t;
+	foreach (qreal t, segments_y) height += t;
+	QPointF t1 = terminal1 -> dockConductor();
+	QPointF t2 = terminal2 -> dockConductor();
+	qreal expected_width  = t2.x() - t1.x();
+	qreal expected_height = t2.y() - t1.y();
+	
+	// on considere que le trajet est incoherent a partir d'une unite de difference avec l'espacement entre les bornes
+	if (
+		qAbs(expected_width  - width)  > 1.0 ||
+		qAbs(expected_height - height) > 1.0
+	) {
+		qDebug() << "Conductor::fromXml : les segments du conducteur ne semblent pas coherents - utilisation d'un trajet automatique";
+		return(false);
+	}
+	
+	/* on recree les segments a partir des donnes XML */
+	// cree la liste de points
+	QList<QPointF> points_list;
+	points_list << t1;
+	for (int i = 0 ; i < segments_x.size() ; ++ i) {
+		points_list << QPointF(
+			points_list.last().x() + segments_x.at(i),
+			points_list.last().y() + segments_y.at(i)
+		);
+	}
+	
+	pointsToSegments(points_list);
+	
+	// initialise divers parametres lies a la modification des conducteurs
+	modified_path = true;
+	saveProfile(false);
+	
+	segmentsToPath();
+	return(true);
+}
+
+/**
+	Exporte les caracteristiques du conducteur sous forme d'une element XML.
+	@param d Le document XML a utiliser pour creer l'element XML
+	@param table_adr_id Hash stockant les correspondances entre les ids des
+	bornes dans le document XML et leur adresse en memoire
+	@return Un element XML representant le conducteur
+*/
+QDomElement Conductor::toXml(QDomDocument &d, QHash<Terminal *, int> &table_adr_id) const {
+	QDomElement e = d.createElement("conductor");
+	e.setAttribute("terminal1", table_adr_id.value(terminal1));
+	e.setAttribute("terminal2", table_adr_id.value(terminal2));
+	
+	// on n'exporte les segments du conducteur que si ceux-ci ont
+	// ete modifies par l'utilisateur
+	if (modified_path) {
+		// parcours et export des segments
+		QDomElement current_segment;
+		foreach(ConductorSegment *segment, segmentsList()) {
+			current_segment = d.createElement("segment");
+			current_segment.setAttribute("orientation", segment -> isHorizontal() ? "horizontal" : "vertical");
+			current_segment.setAttribute("length", QString("%1").arg(segment -> length()));
+			e.appendChild(current_segment);
+		}
+	}
+	
+	// exporte la "configuration" du conducteur
+	properties_.toXml(e);
+	if (text_item -> wasRotateByUser()) {
+		e.setAttribute("rotation", QString("%1").arg(text_item -> rotationAngle()));
+	}
+	if (text_item -> wasMovedByUser()) {
+		e.setAttribute("userx", QString("%1").arg(text_item -> pos().x()));
+		e.setAttribute("usery", QString("%1").arg(text_item -> pos().y()));
+	}
+	return(e);
+}
+
+/// @return les segments de ce conducteur
+const QList<ConductorSegment *> Conductor::segmentsList() const {
+	if (segments == NULL) return(QList<ConductorSegment *>());
+	
+	QList<ConductorSegment *> segments_vector;
+	ConductorSegment *segment = segments;
+	
+	while (segment -> hasNextSegment()) {
+		segments_vector << segment;
+		segment = segment -> nextSegment();
+	}
+	segments_vector << segment;
+	return(segments_vector);
+}
+
+/**
+	@return La longueur totale du conducteur
+*/
+qreal Conductor::length() {
+	qreal length = 0.0;
+	
+	ConductorSegment *s = segments;
+	while (s -> hasNextSegment()) {
+		length += qAbs(s -> length());
+		s = s -> nextSegment();
+	}
+	
+	return(length);
+}
+
+/**
+	@return Le segment qui contient le point au milieu du conducteur
+*/
+ConductorSegment *Conductor::middleSegment() {
+	if (segments == NULL) return(NULL);
+	
+	qreal half_length = length() / 2.0;
+	
+	ConductorSegment *s = segments;
+	qreal l = 0;
+	
+	while (s -> hasNextSegment()) {
+		l += qAbs(s -> length());
+		if (l >= half_length) break;
+		s = s -> nextSegment();
+	}
+	// s est le segment qui contient le point au milieu du conducteur
+	return(s);
+}
+
+/**
+	Positionne le texte du conducteur au milieu du segment qui contient le
+	point au milieu du conducteur
+	@see middleSegment()
+*/
+void Conductor::calculateTextItemPosition() {
+	if (!text_item) return;
+	
+	//position
+	if (text_item -> wasMovedByUser()) {
+		// le champ de texte a ete deplace par l'utilisateur :
+		// on verifie qu'il est encore a proximite du conducteur
+		QPointF text_item_pos = text_item -> pos();
+		QPainterPath near_shape = nearShape();
+		if (!near_shape.contains(text_item_pos)) {
+			text_item -> setPos(movePointIntoPolygon(text_item_pos, near_shape));
+		}
+	} else {
+		// positionnement automatique basique
+		text_item -> setPos(middleSegment() -> middle());
+		//rotation
+		if (!text_item -> wasRotateByUser()) {
+			middleSegment() -> isVertical()? text_item -> setRotationAngle(properties_.verti_rotate_text):
+											 text_item -> setRotationAngle(properties_.horiz_rotate_text);
+		}
+	}
+}
+
+/**
+	Sauvegarde le profil courant du conducteur pour l'utiliser ulterieurement
+	dans priv_modifieConductor.
+*/
+void Conductor::saveProfile(bool undo) {
+	Qt::Corner current_path_type = currentPathType();
+	ConductorProfile old_profile(conductor_profiles[current_path_type]);
+	conductor_profiles[current_path_type].fromConductor(this);
+	Diagram *dia = diagram();
+	if (undo && dia) {
+		ChangeConductorCommand *undo_object = new ChangeConductorCommand(
+			this,
+			old_profile,
+			conductor_profiles[current_path_type],
+			current_path_type
+		);
+		undo_object -> setConductorTextItemMove(before_mov_text_pos_, text_item -> pos());
+		dia -> undoStack().push(undo_object);
+	}
+}
+
+/**
+	@param value1 Premiere valeur
+	@param value2 Deuxieme valeur
+	@return 1 si les deux valeurs sont de meme signe, -1 sinon
+*/
+int Conductor::getCoeff(const qreal &value1, const qreal &value2) {
+	return(getSign(value1) * getSign(value2));
+}
+
+/**
+	@param value valeur
+	@return 1 si valeur est negatif, 1 s'il est positif ou nul
+*/
+int Conductor::getSign(const qreal &value) {
+	return(value < 0 ? -1 : 1);
+}
+
+/**
+	Applique un nouveau profil a ce conducteur
+	@param cp Profil a appliquer a ce conducteur
+	@param path_type Type de trajet pour lequel ce profil convient
+*/
+void Conductor::setProfile(const ConductorProfile &cp, Qt::Corner path_type) {
+	conductor_profiles[path_type] = cp;
+	// si le type de trajet correspond a l'actuel
+	if (currentPathType() == path_type) {
+		if (conductor_profiles[path_type].isNull()) {
+			generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
+			modified_path = false;
+		} else {
+			updateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
+			modified_path = true;
+		}
+		if (type() == ConductorProperties::Multi) {
+			calculateTextItemPosition();
+		}
+	}
+}
+
+/// @return le profil de ce conducteur
+ConductorProfile Conductor::profile(Qt::Corner path_type) const {
+	return(conductor_profiles[path_type]);
+}
+
+/// @return le texte du conducteur
+QString Conductor::text() const {
+	return(text_item -> toPlainText());
+}
+
+/**
+	@param t Nouveau texte du conducteur
+*/
+void Conductor::setText(const QString &t) {
+	text_item -> setPlainText(t);
+}
+
+/// @param p les proprietes de ce conducteur
+void Conductor::setProperties(const ConductorProperties &p) {
+	properties_ = p;
+	readProperties();
+}
+
+/// @return les proprietes de ce conducteur
+ConductorProperties Conductor::properties() const {
+	return(properties_);
+}
+
+/**
+	Relit les proprietes et les applique
+*/
+void Conductor::readProperties() {
+	// la couleur n'est vraiment applicable que lors du rendu du conducteur
+	setText(properties_.text);
+	calculateTextItemPosition();
+	text_item -> setVisible(properties_.type == ConductorProperties::Multi);
+}
+
+/**
+	S'assure que le texte du conducteur est a une position raisonnable
+	Cette methode ne fait rien si ce conducteur n'affiche pas son champ de
+	texte.
+*/
+void Conductor::adjustTextItemPosition() {
+	calculateTextItemPosition();
+}
+
+/**
+	@return true si le conducteur est mis en evidence
+*/
+Conductor::Highlight Conductor::highlight() const {
+	return(must_highlight_);
+}
+
+/**
+	@param hl true pour mettre le conducteur en evidence, false sinon
+*/
+void Conductor::setHighlighted(Conductor::Highlight hl) {
+	must_highlight_ = hl;
+	update();
+}
+
+/**
+ * @brief Conductor::autoText
+ *lance l'autoNumerotation sur ce conducteur
+ */
+void Conductor::autoText() {
+	ConductorAutoNumerotation can(this);
+	can.numerate();
+}
+
+/**
+	Met a jour les proprietes du conducteur apres modification du champ de texte affiche
+*/
+void Conductor::displayedTextChanged() {
+	// verifie que le texte a reellement change
+	if (text_item -> toPlainText() == properties_.text) return;
+	
+	if (Diagram *my_diagram = diagram()) {
+		int qmbreturn=0;
+		//if conductor isn't alone at this potential
+		//ask user to apply text on every conductors of this potential
+		if (relatedPotentialConductors().size() >= 1){
+			qmbreturn = QMessageBox::question(diagramEditor(), tr("Textes de conducteurs"),
+											  tr("Voulez-vous appliquer le nouveau texte \n"
+												 "\340 l'ensemble des conducteurs de ce potentiel ?"),
+											  QMessageBox::No| QMessageBox::Yes, QMessageBox::Yes);
+			if (qmbreturn == QMessageBox::Yes){
+				ConductorAutoNumerotation can(this);
+				can.applyText(text_item -> toPlainText());
+			}
+		}
+		if (qmbreturn == 0 || qmbreturn == QMessageBox::No) {
+			// initialise l'objet UndoCommand correspondant
+			ConductorProperties new_properties(properties_);
+			new_properties.text = text_item -> toPlainText();
+
+			ChangeConductorPropertiesCommand *ccpc = new ChangeConductorPropertiesCommand(this);
+			ccpc -> setOldSettings(properties_);
+			ccpc -> setNewSettings(new_properties);
+			my_diagram -> undoStack().push(ccpc);
+		}
+	}
+}
+
+/**
+	@return les conducteurs avec lesquels ce conducteur partage des bornes
+	communes
+*/
+QSet<Conductor *> Conductor::relatedConductors() const {
+	QList<Conductor *> other_conductors_list = terminal1 -> conductors();
+	other_conductors_list += terminal2 -> conductors();
+	QSet<Conductor *> other_conductors = other_conductors_list.toSet();
+	other_conductors.remove(const_cast<Conductor *>(this));
+	return(other_conductors);
+}
+
+/**
+ * @param t_list terminaux a ne pas inclure dans la recherche
+ * @return les conducteurs avec lesquels ce conducteur partage
+ *  le meme potentiel electrique a l'exception de lui même
+ */
+QSet<Conductor *> Conductor::relatedPotentialConductors(QList <Terminal *> *t_list) {
+	bool declar_t_list = false;
+	if (t_list == 0) {
+		declar_t_list = true;
+		t_list = new QList <Terminal *>;
+	}
+
+	QSet <Conductor *> other_conductors;
+	//renvoie tous les conducteurs du terminal 1
+	if (!t_list -> contains(terminal1)) {
+		t_list -> append(terminal1);
+		QList <Conductor *> other_conductors_list_t1 = terminal1 -> conductors();
+		other_conductors_list_t1.removeAll(this);
+		//recherche les conducteurs connecté au conducteur déjà trouvé
+		foreach (Conductor *c, other_conductors_list_t1) {
+			other_conductors += c -> relatedPotentialConductors(t_list);
+		}
+		other_conductors += other_conductors_list_t1.toSet();
+	}
+	//renvoie tous les conducteurs du terminal 2
+	if (!t_list -> contains(terminal2)) {
+		t_list -> append(terminal2);
+		QList <Conductor *> other_conductors_list_t2 = terminal2 -> conductors();
+		other_conductors_list_t2.removeAll(this);
+		//recherche les conducteurs connecté au conducteur déjà trouvé
+		foreach (Conductor *c, other_conductors_list_t2) {
+			other_conductors += c -> relatedPotentialConductors(t_list);
+		}
+		other_conductors += other_conductors_list_t2.toSet();
+	}
+	other_conductors.remove(const_cast<Conductor *>(this));
+
+	if (declar_t_list) delete t_list;
+	return(other_conductors);
+}
+
+/**
+ * @return l'editeur de schemas parent ou 0
+ */
+QETDiagramEditor* Conductor::diagramEditor() const {
+	QWidget *w = const_cast<QGraphicsView *>(diagram() -> views().at(0));
+	while (w -> parentWidget() && !w -> isWindow()) {
+		w = w -> parentWidget();
+	}
+	return(qobject_cast<QETDiagramEditor *>(w));
+}
+
+/**
+	@param a reel
+	@param b reel
+	@param c reel
+	@return true si a est entre b et c ou est egal a l'un des deux
+*/
+bool isBetween(qreal a, qreal b, qreal c) {
+	if (b <= c) {
+		return(a >= b && a <= c);
+	} else {
+		return(a <= b && a >= c);
+	}
+}
+
+/**
+	@param a point
+	@param b point
+	@param c point
+	@return true si le point a est contenu dans le rectangle delimite par les points b et c
+*/
+bool isContained(const QPointF &a, const QPointF &b, const QPointF &c) {
+	return(
+		isBetween(a.x(), b.x(), c.x()) &&
+		isBetween(a.y(), b.y(), c.y())
+	);
+}
+
+/**
+	@return la liste des positions des jonctions avec d'autres conducteurs
+*/
+QList<QPointF> Conductor::junctions() const {
+	QList<QPointF> junctions_list;
+	
+	// pour qu'il y ait des jonctions, il doit y avoir d'autres conducteurs et des bifurcations
+	QSet<Conductor *> other_conductors = relatedConductors();
+	QList<ConductorBend> bends_list = bends();
+	if (other_conductors.isEmpty() || bends_list.isEmpty()) {
+		return(junctions_list);
+	}
+	
+	QList<QPointF> points = segmentsToPoints();
+	for (int i = 1 ; i < (points.size() -1) ; ++ i) {
+		QPointF point = points.at(i);
+		
+		// determine si le point est une bifurcation ou non
+		bool is_bend = false;
+		Qt::Corner current_bend_type = Qt::TopLeftCorner;
+		foreach(ConductorBend cb, bends_list) {
+			if (cb.first == point) {
+				is_bend = true;
+				current_bend_type = cb.second;
+				break;
+			}
+		}
+		// si le point n'est pas une bifurcation, il ne peut etre une jonction (enfin pas au niveau de ce conducteur)
+		if (!is_bend) continue;
+		
+		bool is_junction = false;
+		QPointF scene_point = mapToScene(point);
+		foreach(Conductor *c, other_conductors) {
+			// exprime le point dans les coordonnees de l'autre conducteur
+			QPointF conductor_point = c -> mapFromScene(scene_point);
+			// recupere les segments de l'autre conducteur
+			QList<ConductorSegment *> c_segments = c -> segmentsList();
+			if (c_segments.isEmpty()) continue;
+			// parcoure les segments a la recherche d'un point commun
+			for (int j = 0 ; j < c_segments.count() ; ++ j) {
+				ConductorSegment *segment = c_segments[j];
+				// un point commun a ete trouve sur ce segment
+				if (isContained(conductor_point, segment -> firstPoint(), segment -> secondPoint())) {
+					is_junction = true;
+					// ce point commun ne doit pas etre une bifurcation identique a celle-ci
+					QList<ConductorBend> other_conductor_bends = c -> bends();
+					foreach(ConductorBend cb, other_conductor_bends) {
+						if (cb.first == conductor_point && cb.second == current_bend_type) {
+							is_junction = false;
+						}
+					}
+				}
+				if (is_junction) junctions_list << point;
+			}
+		}
+	}
+	return(junctions_list);
+}
+
+/**
+	@return la liste des bifurcations de ce conducteur ; ConductorBend est un
+	typedef pour une QPair\<QPointF, Qt::Corner\>. Le point indique la position
+	(en coordonnees locales) de la bifurcation tandis que le Corner indique le
+	type de bifurcation.
+*/
+QList<ConductorBend> Conductor::bends() const {
+	QList<ConductorBend> points;
+	if (!segments) return(points);
+	
+	// recupere la liste des segments de taille non nulle
+	QList<ConductorSegment *> visible_segments;
+	ConductorSegment *segment = segments;
+	while (segment -> hasNextSegment()) {
+		if (!segment -> isPoint()) visible_segments << segment;
+		segment = segment -> nextSegment();
+	}
+	if (!segment  -> isPoint()) visible_segments << segment;
+	
+	ConductorSegment *next_segment;
+	for (int i = 0 ; i < visible_segments.count() -1 ; ++ i) {
+		segment = visible_segments[i];
+		next_segment = visible_segments[i + 1];
+		if (!segment -> isPoint() && !next_segment -> isPoint()) {
+			// si les deux segments ne sont pas dans le meme sens, on a une bifurcation
+			if (next_segment -> type() != segment -> type()) {
+				Qt::Corner bend_type;
+				qreal sl = segment -> length();
+				qreal nsl = next_segment -> length();
+				
+				if (segment -> isHorizontal()) {
+					if (sl < 0 && nsl < 0) {
+						bend_type = Qt::BottomLeftCorner;
+					} else if (sl < 0 && nsl > 0) {
+						bend_type = Qt::TopLeftCorner;
+					} else if (sl > 0 && nsl < 0) {
+						bend_type = Qt::BottomRightCorner;
+					} else {
+						bend_type = Qt::TopRightCorner;
+					}
+				} else {
+					if (sl < 0 && nsl < 0) {
+						bend_type = Qt::TopRightCorner;
+					} else if (sl < 0 && nsl > 0) {
+						bend_type = Qt::TopLeftCorner;
+					} else if (sl > 0 && nsl < 0) {
+						bend_type = Qt::BottomRightCorner;
+					} else {
+						bend_type = Qt::BottomLeftCorner;
+					}
+				}
+				points << qMakePair(segment -> secondPoint(), bend_type);
+			}
+		}
+	}
+	return(points);
+}
+
+/**
+	@param p Point, en coordonnees locales
+	@return true si le point p appartient au trajet du conducteur
+*/
+bool Conductor::containsPoint(const QPointF &p) const {
+	if (!segments) return(false);
+	ConductorSegment *segment = segments;
+	while (segment -> hasNextSegment()) {
+		QRectF rect(segment -> firstPoint(), segment -> secondPoint());
+		if (rect.contains(p)) return(true);
+		segment = segment -> nextSegment();
+	}
+	return(false);
+}
+
+/**
+	@param start Point de depart
+	@param end Point d'arrivee
+	@return le coin vers lequel se dirige le trajet de start vers end
+*/
+Qt::Corner Conductor::movementType(const QPointF &start, const QPointF &end) {
+	Qt::Corner result = Qt::BottomRightCorner;
+	if (start.x() <= end.x()) {
+		result = start.y() <= end.y() ? Qt::BottomRightCorner : Qt::TopRightCorner;
+	} else {
+		result = start.y() <= end.y() ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
+	}
+	return(result);
+}
+
+/// @return le type de trajet actuel de ce conducteur
+Qt::Corner Conductor::currentPathType() const {
+	return(movementType(terminal1 -> dockConductor(), terminal2 -> dockConductor()));
+}
+
+/// @return les profils de ce conducteur
+ConductorProfilesGroup Conductor::profiles() const {
+	return(conductor_profiles);
+}
+
+/**
+	@param cpg Les nouveaux profils de ce conducteur
+*/
+void Conductor::setProfiles(const ConductorProfilesGroup &cpg) {
+	conductor_profiles = cpg;
+	if (conductor_profiles[currentPathType()].isNull()) {
+		generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
+		modified_path = false;
+	} else {
+		updateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
+		modified_path = true;
+	}
+	if (type() == ConductorProperties::Multi) {
+		calculateTextItemPosition();
+	}
+}
+
+/// Supprime les segments
+void Conductor::deleteSegments() {
+	if (segments != NULL) {
+		while (segments -> hasNextSegment()) delete segments -> nextSegment();
+		delete segments;
+		segments = NULL;
+	}
+}
+
+/**
+	@param point Un point situe a l'exterieur du polygone
+	@param polygon Le polygone dans lequel on veut rapatrier le point
+	@return la position du point, une fois ramene dans le polygone, ou plus
+	exactement sur le bord du polygone
+*/
+QPointF Conductor::movePointIntoPolygon(const QPointF &point, const QPainterPath &polygon) {
+	// decompose le polygone en lignes et points
+	QList<QPolygonF> polygons = polygon.simplified().toSubpathPolygons();
+	QList<QLineF> lines;
+	QList<QPointF> points;
+	foreach(QPolygonF polygon, polygons) {
+		if (polygon.count() <= 1) continue;
+		
+		// on recense les lignes et les points
+		for (int i = 1 ; i < polygon.count() ; ++ i) {
+			lines << QLineF(polygon.at(i - 1), polygon.at(i));
+			points << polygon.at(i -1);
+		}
+	}
+	
+	// on fait des projetes orthogonaux du point sur les differents segments du
+	// polygone, en les triant par longueur croissante
+	QMap<qreal, QPointF> intersections;
+	foreach (QLineF line, lines) {
+		QPointF intersection_point;
+		if (QET::orthogonalProjection(point, line, &intersection_point)) {
+			intersections.insert(QLineF(intersection_point, point).length(), intersection_point);
+		}
+	}
+	if (intersections.count()) {
+		// on determine la plus courte longueur pour un projete orthogonal
+		QPointF the_point = intersections[intersections.keys().first()];
+		return(the_point);
+	} else {
+			// determine le coin du polygone le plus proche du point exterieur
+			qreal minimum_length = -1;
+			int point_index = -1;
+			for (int i = 0 ; i < points.count() ; ++ i) {
+				qreal length = qAbs(QLineF(points.at(i), point).length());
+				if (minimum_length < 0 || length < minimum_length) {
+					minimum_length = length;
+					point_index    = i;
+				}
+			}
+			// on connait desormais le coin le plus proche du texte
+		
+		// aucun projete orthogonal n'a donne quoi que ce soit, on met le texte sur un des coins du polygone
+		return(points.at(point_index));
+	}
+}

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

Added: trunk/sources/qetgraphicsitem/conductortextitem.cpp
===================================================================
--- trunk/sources/qetgraphicsitem/conductortextitem.cpp	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/conductortextitem.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,223 @@
+/*
+	Copyright 2006-2013 The QElectroTech Team
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "conductortextitem.h"
+#include "conductor.h"
+#include "diagramcommands.h"
+
+/**
+	Constructeur
+	@param parent_conductor  Conducteur auquel ce texte est rattache
+	@param parent_diagram    Schema auquel ce texte et son conducteur parent sont rattaches
+*/
+ConductorTextItem::ConductorTextItem(Conductor *parent_conductor, Diagram *parent_diagram) :
+	DiagramTextItem(parent_conductor, parent_diagram),
+	parent_conductor_(parent_conductor),
+	moved_by_user_(false),
+	rotate_by_user_(false),
+	first_move_(true)
+{
+	// par defaut, les DiagramTextItem sont Selectable et Movable
+	// cela nous convient, on ne touche pas a ces flags
+}
+
+/**
+	Constructeur
+	@param text Le texte affiche par le champ de texte
+	@param parent_conductor  Conducteur auquel ce texte est rattache
+	@param parent_diagram    Schema auquel ce texte et son conducteur parent sont rattaches
+*/
+ConductorTextItem::ConductorTextItem(const QString &text, Conductor *parent_conductor, Diagram *parent_diagram) :
+	DiagramTextItem(text, parent_conductor, parent_diagram),
+	parent_conductor_(parent_conductor),
+	moved_by_user_(false),
+	rotate_by_user_(false),
+	first_move_(true)
+{
+	// par defaut, les DiagramTextItem sont Selectable et Movable
+	// cela nous convient, on ne touche pas a ces flags
+}
+
+/**
+	Destructeur
+*/
+ConductorTextItem::~ConductorTextItem() {
+}
+
+/**
+	@return le conducteur parent de ce champ de texte, ou 0 si celui-ci n'en a
+	pas
+*/
+Conductor *ConductorTextItem::parentConductor() const {
+	return(parent_conductor_);
+}
+
+/**
+	Permet de lire le texte a mettre dans le champ a partir d'un element XML.
+	Cette methode se base sur la position du champ pour assigner ou non la
+	valeur a ce champ.
+	@param e L'element XML representant le champ de texte
+*/
+void ConductorTextItem::fromXml(const QDomElement &e) {
+	setPlainText(e.attribute("text"));
+	
+	qreal user_pos_x, user_pos_y;
+	if (
+		QET::attributeIsAReal(e, "userx", &user_pos_x) &&
+		QET::attributeIsAReal(e, "usery", &user_pos_y)
+	) {
+		setPos(user_pos_x, user_pos_y);
+	}
+	
+	setRotationAngle(e.attribute("rotation").toDouble());
+}
+
+/**
+	@param document Le document XML a utiliser
+	@return L'element XML representant ce champ de texte
+*/
+QDomElement ConductorTextItem::toXml(QDomDocument &document) const {
+	QDomElement result = document.createElement("input");
+	result.setAttribute("userx", QString("%1").arg(pos().x()));
+	result.setAttribute("usery", QString("%1").arg(pos().y()));
+	result.setAttribute("text", toPlainText());
+	if (rotationAngle()) {
+		result.setAttribute("rotation", QString("%1").arg(rotationAngle()));
+	}
+	return(result);
+}
+
+/**
+	@return true si ce champ de texte a ete explictement deplace par
+	l'utilisateur, false sinon
+*/
+bool ConductorTextItem::wasMovedByUser() const {
+	return(moved_by_user_);
+}
+
+/**
+ * @brief ConductorTextItem::wasRotateByUser
+ * @return true if text was explicit moved by user else false
+ */
+bool ConductorTextItem::wasRotateByUser() const {
+	return(rotate_by_user_);
+}
+
+/**
+	@param moved_by_user true pour que la position du texte soit consideree
+	comme ayant ete definie par l'utilisateur (et donc soit sauvegardee), false
+	pour remettre le texte a sa position originelle
+*/
+void ConductorTextItem::forceMovedByUser(bool moved_by_user) {
+	if (moved_by_user == moved_by_user_) return;
+	
+	moved_by_user_ = moved_by_user;
+	if (!moved_by_user && parent_conductor_) {
+		parent_conductor_ -> adjustTextItemPosition();
+	}
+	
+}
+
+/**
+ * @brief ConductorTextItem::forceRotateByUser
+ * @param rotate_by_user true pour que la rotation du texte soit consideree
+	comme ayant ete definie par l'utilisateur (et donc soit sauvegardee), false
+	pour remettre le texte a sont angle originelle
+ */
+void ConductorTextItem::forceRotateByUser(bool rotate_by_user) {
+	if (rotate_by_user == rotate_by_user_) return;
+
+	rotate_by_user_ = rotate_by_user;
+	if (!rotate_by_user && parent_conductor_) {
+		parent_conductor_ -> adjustTextItemPosition();
+	}
+}
+
+/**
+	Gere les clics de souris lies au champ de texte
+	@param e Objet decrivant l'evenement souris
+*/
+void ConductorTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
+	if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
+		before_mov_pos_ = pos();
+	}
+	first_move_ = true;
+	DiagramTextItem::mousePressEvent(e);
+}
+
+/**
+	Gere les mouvements de souris lies au champ de texte
+	@param e Objet decrivant l'evenement souris
+*/
+void ConductorTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
+	if (textInteractionFlags() & Qt::TextEditable) {
+		QGraphicsTextItem::mouseMoveEvent(e);
+	} else if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
+		if (first_move_) {
+			mouse_to_origin_movement_ = before_mov_pos_ - mapToParent(e -> buttonDownPos(Qt::LeftButton));
+		}
+		
+		QPointF intended_pos = mapToParent(e -> pos()) + mouse_to_origin_movement_;
+		// si ce texte est attache a un conducteur, alors ses mouvements seront
+		// limites a une certaine distance du trace de ce conducteur
+		if (parent_conductor_) {
+			if (parent_conductor_ -> isNearConductor(intended_pos)) {
+				setPos(intended_pos);
+				parent_conductor_ -> setHighlighted(Conductor::Normal);
+			} else {
+				parent_conductor_ -> setHighlighted(Conductor::Alert);
+			}
+		}
+		
+	} else e -> ignore();
+	
+	if (first_move_) {
+		first_move_ = false;
+	}
+}
+
+/**
+	Gere le relachement de souris
+	Cette methode cree un objet d'annulation pour le deplacement
+	@param e Objet decrivant l'evenement souris
+*/
+void ConductorTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
+	if (flags() & QGraphicsItem::ItemIsMovable) {
+		if (Diagram *diagram_ptr = diagram()) {
+			// on cree un objet d'annulation correspondant au deplacement qui s'acheve
+			QPointF applied_movement = pos() - before_mov_pos_;
+			
+			if (!applied_movement.isNull()) {
+				// on cree un objet d'annulation seulement pour ce champ de texte
+				MoveConductorsTextsCommand *undo_object = new MoveConductorsTextsCommand(diagram_ptr);
+				undo_object -> addTextMovement(this, before_mov_pos_, pos(), moved_by_user_);
+				
+				// on active le flag indiquant que ce champ de texte a ete explicitement repositionne par l'utilisateur
+				moved_by_user_ = true;
+				
+				diagram_ptr -> undoStack().push(undo_object);
+			}
+			
+			if (parent_conductor_) {
+				parent_conductor_ -> setHighlighted(Conductor::None);
+			}
+		}
+	}
+	if (!(e -> modifiers() & Qt::ControlModifier)) {
+		QGraphicsTextItem::mouseReleaseEvent(e);
+	}
+}

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

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

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

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

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

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

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

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

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

Added: trunk/sources/qetgraphicsitem/elementtextitem.cpp
===================================================================
--- trunk/sources/qetgraphicsitem/elementtextitem.cpp	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/elementtextitem.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,310 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "elementtextitem.h"
+#include "diagram.h"
+#include "diagramcommands.h"
+#include "element.h"
+
+/**
+	Constructeur
+	@param parent_element Le QGraphicsItem parent du champ de texte
+	@param parent_diagram Le schema auquel appartient le champ de texte
+*/
+ElementTextItem::ElementTextItem(Element *parent_element, Diagram *parent_diagram) :
+	DiagramTextItem(parent_element, parent_diagram),
+	parent_element_(parent_element),
+	follow_parent_rotations(false),
+	original_rotation_angle_(0.0),
+	first_move_(true)
+{
+	// par defaut, les DiagramTextItem sont Selectable et Movable
+	// cela nous convient, on ne touche pas a ces flags
+	
+	adjustItemPosition(1);
+	// ajuste la position du QGraphicsItem lorsque le QTextDocument change
+	connect(document(), SIGNAL(blockCountChanged(int)), this, SLOT(adjustItemPosition(int)));
+	connect(document(), SIGNAL(contentsChanged()),      this, SLOT(adjustItemPosition()));
+}
+
+/**
+	Constructeur
+	@param parent_element L'element parent du champ de texte
+	@param parent_diagram Le schema auquel appartient le champ de texte
+	@param text Le texte affiche par le champ de texte
+*/
+ElementTextItem::ElementTextItem(const QString &text, Element *parent_element, Diagram *parent_diagram) :
+	DiagramTextItem(text, parent_element, parent_diagram),
+	parent_element_(parent_element),
+	follow_parent_rotations(false),
+	original_rotation_angle_(0.0),
+	first_move_(true)
+{
+	// par defaut, les DiagramTextItem sont Selectable et Movable
+	// cela nous convient, on ne touche pas a ces flags
+	
+	adjustItemPosition(1);
+	// ajuste la position du QGraphicsItem lorsque le QTextDocument change
+	connect(document(), SIGNAL(blockCountChanged(int)), this, SLOT(adjustItemPosition(int)));
+	connect(document(), SIGNAL(contentsChanged()),      this, SLOT(adjustItemPosition()));
+}
+
+/// Destructeur
+ElementTextItem::~ElementTextItem() {
+}
+
+/**
+	@return L'element parent de ce champ de texte, ou 0 si celui-ci n'en a pas.
+*/
+Element *ElementTextItem::parentElement() const {
+	return(parent_element_);
+}
+
+/**
+	Modifie la position du champ de texte
+	@param pos La nouvelle position du champ de texte
+*/
+void ElementTextItem::setPos(const QPointF &pos) {
+	QGraphicsTextItem::setPos(pos);
+}
+
+/**
+	Modifie la position du champ de texte
+	@param x La nouvelle abscisse du champ de texte
+	@param y La nouvelle ordonnee du champ de texte
+*/
+void ElementTextItem::setPos(qreal x, qreal y) {
+	setPos(QPointF(x, y));
+}
+
+/**
+	@return La position (bidouillee) du champ de texte
+*/
+QPointF ElementTextItem::pos() const {
+	return(QGraphicsTextItem::pos());
+}
+
+/**
+	Permet de lire le texte a mettre dans le champ a partir d'un element XML.
+	Cette methode se base sur la position du champ pour assigner ou non la
+	valeur a ce champ.
+	@param e L'element XML representant le champ de texte
+*/
+void ElementTextItem::fromXml(const QDomElement &e) {
+	QPointF _pos = pos();
+	if (
+		qFuzzyCompare(qreal(e.attribute("x").toDouble()), _pos.x()) &&
+		qFuzzyCompare(qreal(e.attribute("y").toDouble()), _pos.y())
+	) {
+		setPlainText(e.attribute("text"));
+		
+		qreal user_pos_x, user_pos_y;
+		if (
+			QET::attributeIsAReal(e, "userx", &user_pos_x) &&
+			QET::attributeIsAReal(e, "usery", &user_pos_y)
+		) {
+			setPos(user_pos_x, user_pos_y);
+		}
+		
+		qreal xml_rotation_angle;
+		if (QET::attributeIsAReal(e, "userrotation", &xml_rotation_angle)) {
+			setRotationAngle(xml_rotation_angle);
+		}
+	}
+}
+
+/**
+	@param document Le document XML a utiliser
+	@return L'element XML representant ce champ de texte
+*/
+QDomElement ElementTextItem::toXml(QDomDocument &document) const {
+	QDomElement result = document.createElement("input");
+	
+	result.setAttribute("x", QString("%1").arg(originalPos().x()));
+	result.setAttribute("y", QString("%1").arg(originalPos().y()));
+	
+	if (pos() != originalPos()) {
+		result.setAttribute("userx", QString("%1").arg(pos().x()));
+		result.setAttribute("usery", QString("%1").arg(pos().y()));
+	}
+	
+	result.setAttribute("text", toPlainText());
+	
+	if (rotationAngle() != originalRotationAngle()) {
+		result.setAttribute("userrotation", QString("%1").arg(rotationAngle()));
+	}
+	
+	return(result);
+}
+
+/**
+	@param p Position originale / de reference pour ce champ
+	Cette position est utilisee lors de l'export en XML
+*/
+void ElementTextItem::setOriginalPos(const QPointF &p) {
+	original_position = p;
+}
+
+/**
+	@return la position originale / de reference pour ce champ
+*/
+QPointF ElementTextItem::originalPos() const {
+	return(original_position);
+}
+
+/**
+	Definit l'angle de rotation original de ce champ de texte
+	@param rotation_angle un angle de rotation
+*/
+void ElementTextItem::setOriginalRotationAngle(const qreal &rotation_angle) {
+	original_rotation_angle_ = QET::correctAngle(rotation_angle);
+}
+
+/**
+	@return l'angle de rotation original de ce champ de texte
+*/
+qreal ElementTextItem::originalRotationAngle() const {
+	return(original_rotation_angle_);
+}
+
+/**
+	Set the font used to render the text item to \a font.
+*/
+void ElementTextItem::setFont(const QFont &font) {
+	DiagramTextItem::setFont(font);
+	adjustItemPosition(1);
+}
+
+/**
+	Cette methode s'assure que la position de l'ElementTextItem est coherente
+	en ajustant :
+		* la transformation de base qui permet de considerer que l'origine
+	correspond au milieu du bord gauche du champ de texte
+		* l'origine utilisee lors des appels a setRotation et setScale
+	@param new_block_count Nombre de blocs dans l'ElementTextItem
+*/
+void ElementTextItem::adjustItemPosition(int new_block_count) {
+	Q_UNUSED(new_block_count);
+	qreal origin_offset = boundingRect().bottom() / 2.0;
+	
+	QTransform base_translation;
+	base_translation.translate(0.0, -origin_offset);
+	setTransform(base_translation, false);
+	setTransformOriginPoint(0.0, origin_offset);
+}
+
+/**
+	Effetue la rotation du texte en elle-meme
+	Pour les ElementTextItem, la rotation s'effectue autour du milieu du bord
+	gauche du champ de texte.
+	@param angle Angle de la rotation a effectuer
+*/
+void ElementTextItem::applyRotation(const qreal &angle) {
+	QGraphicsTextItem::setRotation(QGraphicsTextItem::rotation() + angle);
+}
+
+/**
+	Gere le clic sur le champ de texte
+	@param e Objet decrivant l'evenement souris
+*/
+void ElementTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
+	first_move_ = true;
+	if (e -> modifiers() & Qt::ControlModifier) {
+		setSelected(!isSelected());
+	}
+	DiagramTextItem::mousePressEvent(e);
+}
+
+/**
+	Gere les mouvements de souris lies au champ de texte
+	@param e Objet decrivant l'evenement souris
+*/
+void ElementTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
+	if (textInteractionFlags() & Qt::TextEditable) {
+		DiagramTextItem::mouseMoveEvent(e);
+	} else if ((flags() & QGraphicsItem::ItemIsMovable) && (e -> buttons() & Qt::LeftButton)) {
+		QPointF old_pos = pos();
+		/*
+			Utiliser e -> pos() directement aurait pour effet de positionner
+			l'origine du champ de texte a la position indiquee par le curseur,
+			ce qui n'est pas l'effet recherche
+			Au lieu de cela, on applique a la position actuelle le vecteur
+			definissant le mouvement effectue depuis la derniere position
+			cliquee avec le bouton gauche
+		*/
+		QPointF movement = e -> pos() - e -> buttonDownPos(Qt::LeftButton);
+		
+		/*
+			Les methodes pos() et setPos() travaillent toujours avec les
+			coordonnees de l'item parent (ou de la scene s'il n'y a pas d'item
+			parent). On n'oublie donc pas de mapper le mouvement fraichement
+			calcule sur l'item parent avant de l'appliquer.
+		*/
+		QPointF parent_movement = mapMovementToParent(movement);
+		setPos(pos() + parent_movement);
+		
+		Diagram *diagram_ptr = diagram();
+		if (diagram_ptr) {
+			if (first_move_) {
+				// on signale le debut d'un deplacement d'ElementTextItems au schema parent
+				int moved_texts_count = diagram_ptr -> beginMoveElementTexts(this);
+				
+				// s'il n'y a qu'un seul texte deplace, on met en valeur l'element parent
+				if (moved_texts_count == 1 && parent_element_) {
+					parent_element_ -> setHighlighted(true);
+					parent_element_ -> update();
+				}
+			}
+			
+			/*
+				Comme setPos() n'est pas oblige d'appliquer exactement la
+				valeur qu'on lui fournit, on calcule le mouvement reellement
+				applique.
+			*/
+			QPointF effective_movement = pos() - old_pos;
+			QPointF scene_effective_movement = mapMovementToScene(mapMovementFromParent(effective_movement));
+			
+			// on applique le mouvement subi aux autres textes a deplacer
+			diagram_ptr -> continueMoveElementTexts(scene_effective_movement);
+		}
+	} else e -> ignore();
+	
+	if (first_move_) {
+		first_move_ = false;
+	}
+}
+
+/**
+	Gere le relachement de souris
+	Cette methode cree un objet d'annulation pour le deplacement
+	@param e Objet decrivant l'evenement souris
+*/
+void ElementTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
+	if (Diagram *diagram_ptr = diagram()) {
+		// on arrete de mettre en valeur l'element parent
+		if (parent_element_) {
+			if (parent_element_ -> isHighlighted()) {
+				parent_element_ -> setHighlighted(false);
+			}
+		}
+		
+		diagram_ptr -> endMoveElementTexts();
+	}
+	if (!(e -> modifiers() & Qt::ControlModifier)) {
+		QGraphicsTextItem::mouseReleaseEvent(e);
+	}
+}

Added: trunk/sources/qetgraphicsitem/elementtextitem.h
===================================================================
--- trunk/sources/qetgraphicsitem/elementtextitem.h	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/elementtextitem.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,96 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENT_TEXT_ITEM_H
+#define ELEMENT_TEXT_ITEM_H
+#include "diagramtextitem.h"
+#include <QtXml>
+class Diagram;
+class Element;
+/**
+	This class represents a text item attached to an element. Users can change its
+	value, adjust its position (defined relatively to its parent element), and
+	direct it to any angle.
+*/
+class ElementTextItem : public DiagramTextItem {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	ElementTextItem(Element * = 0, Diagram * = 0);
+	ElementTextItem(const QString &, Element * = 0, Diagram * = 0);
+	virtual ~ElementTextItem();
+	
+	// attributes
+	public:
+	enum { Type = UserType + 1003 };
+	
+	private:
+	Element *parent_element_;
+	bool follow_parent_rotations;
+	QPointF original_position;
+	qreal original_rotation_angle_;
+	bool first_move_;
+	
+	// methods
+	public:
+	virtual int type() const { return Type; }
+	Element *parentElement() const;
+	/// @return the rectangle defining the bounds of this text item
+	virtual QRectF boundingRect() const { return(QGraphicsTextItem::boundingRect().adjusted(0.0, -1.1, 0.0, 0.0)); }
+	bool followParentRotations() const;
+	void setFollowParentRotations(bool);
+	void fromXml(const QDomElement &);
+	QDomElement toXml(QDomDocument &) const;
+	void setPos(const QPointF &);
+	void setPos(qreal, qreal);
+	virtual QPointF pos() const;
+	void setOriginalPos(const QPointF &);
+	QPointF originalPos() const;
+	void setOriginalRotationAngle(const qreal &);
+	qreal originalRotationAngle() const;
+	virtual void setFont(const QFont &);
+	
+	public slots:
+	void adjustItemPosition(int = 0);
+	
+	protected:
+	virtual void applyRotation(const qreal &);
+	virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
+	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
+	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
+};
+
+/**
+	Element text items can optionally be applied a counter-rotation when their
+	parent element is rotated, thus preserving their readability.
+	@return whether this text item follows the rotations of its parent element.
+*/
+inline bool ElementTextItem::followParentRotations() const {
+	return(follow_parent_rotations);
+}
+
+/**
+	Element text items can optionally be applied a counter-rotation when their
+	parent element is rotated, thus preserving their readability.
+	@param frp whether this text item should follow the rotations of its parent
+	element.
+*/
+inline void ElementTextItem::setFollowParentRotations(bool frp) {
+	follow_parent_rotations = frp;
+}
+
+#endif

Added: trunk/sources/qetgraphicsitem/fixedelement.cpp
===================================================================
--- trunk/sources/qetgraphicsitem/fixedelement.cpp	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/fixedelement.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,43 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "fixedelement.h"
+/**
+	Constructeur
+*/
+FixedElement::FixedElement(QGraphicsItem *parent, Diagram *scene) : Element(parent, scene) {
+}
+
+/**
+	Destructeur
+*/
+FixedElement::~FixedElement() {
+}
+
+/**
+	@return Le nombre minimal de bornes que l'element peut avoir
+*/
+int FixedElement::minTerminalsCount() const {
+	return(terminalsCount());
+}
+
+/**
+	@return Le nombre maximal de bornes que l'element peut avoir
+*/
+int FixedElement::maxTerminalsCount() const {
+	return(terminalsCount());
+}

Added: trunk/sources/qetgraphicsitem/fixedelement.h
===================================================================
--- trunk/sources/qetgraphicsitem/fixedelement.h	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/fixedelement.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,42 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ELEMENTFIXE_H
+#define ELEMENTFIXE_H
+#include "element.h"
+/**
+	This class represents an element having a fixed number of terminals.
+*/
+class FixedElement : public Element {
+	
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	FixedElement(QGraphicsItem * = 0, Diagram * = 0);
+	virtual ~FixedElement();
+	
+	// methods
+	public:
+	int minTerminalsCount() const;
+	int maxTerminalsCount() const;
+	virtual int terminalsCount() const = 0;
+	virtual void paint(QPainter *, const QStyleOptionGraphicsItem *) = 0;
+	virtual QString typeId() const = 0;
+	virtual QString name() const = 0;
+};
+#endif

Added: trunk/sources/qetgraphicsitem/ghostelement.cpp
===================================================================
--- trunk/sources/qetgraphicsitem/ghostelement.cpp	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/ghostelement.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,191 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "ghostelement.h"
+#include "qet.h"
+#include "terminal.h"
+#include "elementtextitem.h"
+#include "diagramcommands.h"
+
+/**
+	Constructeur
+	@param location Emplacement de la definition d'element a utiliser
+	@param qgi Le QGraphicsItem parent de cet element
+	@param d Le schema affichant cet element
+*/
+GhostElement::GhostElement(
+	const ElementsLocation &location,
+	QGraphicsItem *qgi,
+	Diagram *d
+) :
+	CustomElement(location, qgi, d)
+{
+	QString tooltip_string = QString(
+		tr("<u>\311l\351ment manquant\240:</u> %1")
+	).arg(location_.toString());
+	setToolTip(tooltip_string);
+}
+
+/**
+	Destructeur
+*/
+GhostElement::~GhostElement() {
+}
+
+
+/**
+	@param e L'element XML a analyser.
+	@param table_id_adr Reference vers la table de correspondance entre les IDs
+	du fichier XML et les adresses en memoire. Si l'import reussit, il faut y
+	ajouter les bons couples (id, adresse).
+	@return true si l'import a reussi, false sinon
+*/
+bool GhostElement::fromXml(QDomElement &e, QHash<int, Terminal *> &table_id_adr, bool handle_inputs_rotation) {
+	// instancie les bornes decrites dans l'element XML
+	terminalsFromXml(e, table_id_adr);
+	
+	// instancie les champs de texte decrits dans l'element XML
+	foreach(QDomElement qde, QET::findInDomElement(e, "inputs", "input")) {
+		qde.setAttribute("size", 9); // arbitraire
+		if (ElementTextItem *new_input = CustomElement::parseInput(qde)) {
+			new_input -> fromXml(qde);
+		}
+		qde.removeAttribute("size");
+	}
+	
+	/*
+		maintenant que l'element fantome connait toutes les bornes et tous les
+		champs de texte, on peut determiner une taille appropriee
+	*/
+	QRect final_bounding_rect = minimalBoundingRect().united(childrenBoundingRect()).toAlignedRect();
+	setSize(final_bounding_rect.width(), final_bounding_rect.height());
+	setHotspot(QPoint() - final_bounding_rect.topLeft());
+	setInternalConnections(true);
+	
+	// on peut desormais confectionner le rendu de l'element
+	generateDrawings();
+	
+	// position, selection
+	setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
+	setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
+	
+	// orientation
+	bool conv_ok;
+	int read_ori = e.attribute("orientation").toInt(&conv_ok);
+	if (!conv_ok || read_ori < 0 || read_ori > 3) read_ori = 0;
+	if (handle_inputs_rotation) {
+		rotateBy(90*read_ori);
+	} else {
+		applyRotation(90*read_ori);
+	}
+	return(true);
+}
+
+/**
+	@return le bounding rect minimum, utilise si l'element fantome n'a ni champ
+	de texte ni borne.
+*/
+QRectF GhostElement::minimalBoundingRect() const {
+	return(
+		QRectF(
+			QPointF(-10.0, -10.0),
+			QSizeF(20.0, 20.0)
+		)
+	);
+}
+
+/**
+	Gere l'import des bornes
+	@param e L'element XML a analyser.
+	@param table_id_adr Reference vers la table de correspondance entre les IDs
+	du fichier XML et les adresses en memoire. Si l'import reussit, il faut y
+	ajouter les bons couples (id, adresse).
+	@return true si l'import a reussi, false sinon
+*/
+bool GhostElement::terminalsFromXml(QDomElement &e, QHash<int, Terminal *> &table_id_adr) {
+	// instancie les bornes decrites dans l'element XML
+	foreach(QDomElement qde, QET::findInDomElement(e, "terminals", "terminal")) {
+		if (!Terminal::valideXml(qde)) continue;
+		
+		// modifie certains attributs pour que l'analyse par la classe CustomElement reussisse
+		int previous_x_value   = qde.attribute("x").toInt();
+		int previous_y_value   = qde.attribute("y").toInt();
+		int previous_ori_value = qde.attribute("orientation").toInt();
+		
+		qreal x_add = 0.0, y_add = 0.0;
+		if (previous_ori_value == QET::North)      y_add = -Terminal::terminalSize;
+		else if (previous_ori_value == QET::East)  x_add = Terminal::terminalSize;
+		else if (previous_ori_value == QET::South) y_add = Terminal::terminalSize;
+		else if (previous_ori_value == QET::West)  x_add = -Terminal::terminalSize;
+		qde.setAttribute("x",           previous_x_value + x_add);
+		qde.setAttribute("y",           previous_y_value + y_add);
+		qde.setAttribute("orientation", QET::orientationToString(static_cast<QET::Orientation>(previous_ori_value)));
+		
+		if (Terminal *new_terminal = CustomElement::parseTerminal(qde)) {
+			table_id_adr.insert(qde.attribute("id").toInt(), new_terminal);
+		}
+		
+		// restaure les attributs modifies
+		qde.setAttribute("x",           previous_x_value);
+		qde.setAttribute("y",           previous_y_value);
+		qde.setAttribute("orientation", previous_ori_value);
+	}
+	return(true);
+}
+
+/**
+	Genere les rendus de l'element fantome : il s'agit d'un rectangle
+	representant grosso modo l'espace que devait prendre l'element initial.
+	En son centre est dessine un point d'interrogation. Une petite croix indique
+	le point de saisie de l'element.
+*/
+void GhostElement::generateDrawings() {
+	// style de dessin
+	QPen t(QBrush(Qt::black), 1.0);
+	
+	// rendu normal
+	QPainter qp;
+	qp.begin(&drawing);
+	qp.setPen(t);
+	qp.setRenderHint(QPainter::Antialiasing, false);
+	generateDrawing(&qp);
+	qp.end();
+	
+	// rendu low_zoom
+	QPainter low_zoom_qp;
+	low_zoom_qp.begin(&low_zoom_drawing);
+	t.setCosmetic(true);
+	low_zoom_qp.setRenderHint(QPainter::Antialiasing, false);
+	low_zoom_qp.setPen(t);
+	generateDrawing(&low_zoom_qp);
+	low_zoom_qp.end();
+}
+
+/**
+	Genere un rendu de l'element fantome
+	@see generateDrawings
+*/
+void GhostElement::generateDrawing(QPainter *painter) {
+	// une petite croix indique le point de saisie de l'element
+	painter -> drawLine(QLineF(-1.0, 0.0, 1.0, 0.0));
+	painter -> drawLine(QLineF(0.0, -1.0, 0.0, 1.0));
+	
+	// rectangle avec un point d'interrogation au centre
+	QRectF drawn_rect = boundingRect().adjusted(4.0, 4.0, -4.0, -4.0);
+	painter -> drawRect(drawn_rect);
+	painter -> drawText(drawn_rect, Qt::AlignHCenter | Qt::AlignVCenter, "?");
+}

Added: trunk/sources/qetgraphicsitem/ghostelement.h
===================================================================
--- trunk/sources/qetgraphicsitem/ghostelement.h	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/ghostelement.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,53 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef GHOST_ELEMENT_H
+#define GHOST_ELEMENT_H
+#include "customelement.h"
+class Diagram;
+class QGraphicsItem;
+class ElementsLocation;
+class Terminal;
+/**
+	The GhostElement class inherits CustomElement. A GhostElement aims at
+	visually replacing a CustomElement whose definition could not be loaded.
+	This way, instead of not loading an element, thus potentially losing its
+	position, its orientation, its child text items and conductors, one can
+	substitute a GhostElement. The GhostElement will extrapolate the position
+	of terminals and text items from the rest of the diagram. It is visually
+	rendered using a simple rectangle.
+*/
+class GhostElement : public CustomElement {
+	
+	Q_OBJECT
+	
+	// constructor, destructor
+	public:
+	GhostElement(const ElementsLocation &, QGraphicsItem * = 0, Diagram * = 0);
+	virtual ~GhostElement();
+	
+	// methods
+	public:
+	virtual bool fromXml(QDomElement &, QHash<int, Terminal *> &, bool = false);
+	
+	protected:
+	QRectF minimalBoundingRect() const;
+	bool terminalsFromXml(QDomElement &, QHash<int, Terminal *> &);
+	void generateDrawings();
+	void generateDrawing(QPainter *);
+};
+#endif

Added: trunk/sources/qetgraphicsitem/independenttextitem.cpp
===================================================================
--- trunk/sources/qetgraphicsitem/independenttextitem.cpp	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/independenttextitem.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,140 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "independenttextitem.h"
+#include "diagram.h"
+
+/**
+	Constructeur
+	@param parent_diagram Le schema auquel est rattache le champ de texte
+*/
+IndependentTextItem::IndependentTextItem(Diagram *parent_diagram) :
+	DiagramTextItem(0, parent_diagram),
+	first_move_(true)
+{
+}
+
+/**
+	Constructeur
+	@param text Le texte affiche par le champ de texte
+	@param parent_diagram Le schema auquel est rattache le champ de texte
+*/
+IndependentTextItem::IndependentTextItem(const QString &text, Diagram *parent_diagram) :
+	DiagramTextItem(text, 0, parent_diagram),
+	first_move_(true)
+{
+}
+
+/// Destructeur
+IndependentTextItem::~IndependentTextItem() {
+}
+
+/**
+	Permet de lire le texte a mettre dans le champ a partir d'un element XML.
+	Cette methode se base sur la position du champ pour assigner ou non la
+	valeur a ce champ.
+	@param e L'element XML representant le champ de texte
+*/
+void IndependentTextItem::fromXml(const QDomElement &e) {
+	setPos(e.attribute("x").toDouble(), e.attribute("y").toDouble());
+	setHtml(e.attribute("text"));
+	setRotationAngle(e.attribute("rotation").toDouble());
+}
+
+/**
+	@param document Le document XML a utiliser
+	@return L'element XML representant ce champ de texte
+*/
+QDomElement IndependentTextItem::toXml(QDomDocument &document) const {
+	QDomElement result = document.createElement("input");
+	result.setAttribute("x", QString("%1").arg(pos().x()));
+	result.setAttribute("y", QString("%1").arg(pos().y()));
+	result.setAttribute("text", toHtml());
+	if (rotationAngle()) {
+		result.setAttribute("rotation", QString("%1").arg(rotationAngle()));
+	}
+	return(result);
+}
+
+/**
+	Gere le clic sur le champ de texte
+	@param e Objet decrivant l'evenement souris
+*/
+void IndependentTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e) {
+	first_move_ = true;
+	if (e -> modifiers() & Qt::ControlModifier) {
+		setSelected(!isSelected());
+	}
+	DiagramTextItem::mousePressEvent(e);
+}
+
+/**
+	Gere les mouvements de souris lies au champ de texte
+	@param e Objet decrivant l'evenement souris
+*/
+void IndependentTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
+	if (textInteractionFlags() & Qt::TextEditable) {
+		DiagramTextItem::mouseMoveEvent(e);
+	} else if ((flags() & QGraphicsItem::ItemIsMovable) && isSelected() && (e -> buttons() & Qt::LeftButton)) {
+		// le champ de texte est en train d'etre deplace
+		Diagram *diagram_ptr = diagram();
+		if (diagram_ptr) {
+			if (first_move_) {
+				// il s'agit du premier mouvement du deplacement, on le signale
+				// au schema parent
+				diagram_ptr -> beginMoveElements(this);
+			}
+		}
+		
+		// on applique le mouvement impose par la souris
+		QPointF old_pos = pos();
+		if (first_move_) {
+			mouse_to_origin_movement_ = old_pos - e -> buttonDownScenePos(Qt::LeftButton);
+		}
+		QPointF expected_pos = e-> scenePos() + mouse_to_origin_movement_;
+		setPos(expected_pos); // setPos() will snap the expected position to the grid
+		
+		// on calcule le mouvement reellement applique par setPos()
+		QPointF effective_movement = pos() - old_pos;
+		if (diagram_ptr) {
+			// on signale le mouvement ainsi applique au schema parent, qui
+			// l'appliquera aux autres items selectionnes selon son bon vouloir
+			diagram_ptr -> continueMoveElements(effective_movement);
+		}
+	} else e -> ignore();
+	
+	if (first_move_) {
+		first_move_ = false;
+	}
+}
+
+/**
+	Gere le relachement de souris
+	Cette methode a ete reimplementee pour tenir a jour la liste des elements
+	et conducteurs a deplacer au niveau du schema.
+	@param e Objet decrivant l'evenement souris
+*/
+void IndependentTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
+	if (flags() & QGraphicsItem::ItemIsMovable) {
+		if (Diagram *diagram_ptr = diagram()) {
+			diagram_ptr -> endMoveElements();
+		}
+	}
+	if (!(e -> modifiers() & Qt::ControlModifier)) {
+		DiagramTextItem::mouseReleaseEvent(e);
+	}
+}

Added: trunk/sources/qetgraphicsitem/independenttextitem.h
===================================================================
--- trunk/sources/qetgraphicsitem/independenttextitem.h	                        (rev 0)
+++ trunk/sources/qetgraphicsitem/independenttextitem.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,58 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef INDEPENDENT_TEXT_ITEM_H
+#define INDEPENDENT_TEXT_ITEM_H
+#include <QtGui>
+#include "diagramtextitem.h"
+/**
+	This class represents an independent text field on a particular diagram.
+	It may be moved, edited, and rotated.
+*/
+class IndependentTextItem : public DiagramTextItem {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	IndependentTextItem(Diagram * = 0);
+	IndependentTextItem(const QString &, Diagram* = 0);
+	virtual ~IndependentTextItem();
+	
+	// attributes
+	public:
+	enum { Type = UserType + 1005 };
+	
+	// methods
+	public:
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into an
+		IndependentTextItem.
+		@return le type de QGraphicsItem
+	*/
+	virtual int type() const { return Type; }
+	virtual void fromXml(const QDomElement &);
+	virtual QDomElement toXml(QDomDocument &) const;
+	
+	protected:
+	virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
+	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
+	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
+	
+	private:
+	bool first_move_;
+	QPointF mouse_to_origin_movement_;
+};
+#endif

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

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

Added: trunk/sources/qeticons.cpp
===================================================================
--- trunk/sources/qeticons.cpp	                        (rev 0)
+++ trunk/sources/qeticons.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,391 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qeticons.h"
+#include <QApplication>
+
+// on redeclare ici les icones
+namespace QET {
+	namespace Icons {
+		QIcon Add;
+		QIcon Allowed;
+		QIcon ApplicationExit;
+		QIcon ArrowLeft;
+		QIcon ArrowLeftDouble;
+		QIcon ArrowRight;
+		QIcon ArrowRightDouble;
+		QIcon BringForward;
+		QIcon Cancel;
+		QIcon Conductor;
+		QIcon ConductorSettings;
+		QIcon Configure;
+		QIcon ConfigureToolbars;
+		QIcon CopyFile;
+		QIcon DefaultConductor;
+		QIcon DiagramAdd;
+		QIcon Diagram;
+		QIcon DiagramDelete;
+		QIcon DialogCancel;
+		QIcon DialogInformation;
+		QIcon DialogOk;
+		QIcon DocumentClose;
+		QIcon DocumentExport;
+		QIcon DocumentImport;
+		QIcon DocumentNew;
+		QIcon DocumentOpen;
+		QIcon DocumentOpenRecent;
+		QIcon DocumentPrint;
+		QIcon DocumentPrintFrame;
+		QIcon DocumentSave;
+		QIcon DocumentSaveAll;
+		QIcon DocumentSaveAs;
+		QIcon East;
+		QIcon EditClear;
+		QIcon EditClearLocationBar;
+		QIcon EditCopy;
+		QIcon EditCut;
+		QIcon EditDelete;
+		QIcon EditPaste;
+		QIcon EditRedo;
+		QIcon EditRename;
+		QIcon EditSelectAll;
+		QIcon EditTableCellMerge;
+		QIcon EditTableCellSplit;
+		QIcon EditTableDeleteColumn;
+		QIcon EditTableDeleteRow;
+		QIcon EditTableInsertColumnLeft;
+		QIcon EditTableInsertColumnRight;
+		QIcon EditTableInsertRowAbove;
+		QIcon EditTableInsertRowUnder;
+		QIcon EditText;
+		QIcon EditUndo;
+		QIcon Element;
+		QIcon ElementDelete;
+		QIcon ElementEdit;
+		QIcon ElementNew;
+		QIcon EndLineCircle;
+		QIcon EndLineDiamond;
+		QIcon EndLineNone;
+		QIcon EndLineSimple;
+		QIcon EndLineTriangle;
+		QIcon EPS;
+		QIcon Folder;
+		QIcon FolderDelete;
+		QIcon FolderEdit;
+		QIcon FolderNew;
+		QIcon Forbidden;
+		QIcon FullScreenEnter;
+		QIcon FullScreenExit;
+		QIcon GoDown;
+		QIcon GoUp;
+		QIcon Ground;
+		QIcon Hide;
+		QIcon Home;
+		QIcon HotSpot;
+		QIcon InsertImage;
+		QIcon Lower;
+		QIcon MoveFile;
+		QIcon Names;
+		QIcon Neutral;
+		QIcon NewDiagram;
+		QIcon North;
+		QIcon ObjectLocked;
+		QIcon ObjectRotateRight;
+		QIcon ObjectUnlocked;
+		QIcon Orientations;
+		QIcon PartArc;
+		QIcon PartCircle;
+		QIcon PartEllipse;
+		QIcon PartLine;
+		QIcon PartPolygon;
+		QIcon PartRectangle;
+		QIcon PartSelect;
+		QIcon PartText;
+		QIcon PartTextField;
+		QIcon PDF;
+		QIcon Phase;
+		QIcon PrintAllPages;
+		QIcon Printer;
+		QIcon PrintLandscape;
+		QIcon PrintPortrait;
+		QIcon PrintTwoPages;
+		QIcon Project;
+		QIcon ProjectFile;
+		QIcon QETIcon;
+		QIcon QETLogo;
+		QIcon QETOxygenLogo;
+		QIcon QtLogo;
+		QIcon Raise;
+		QIcon Remove;
+		QIcon Restore;
+		QIcon SendBackward;
+		QIcon Settings;
+		QIcon SinglePage;
+		QIcon South;
+		QIcon Start;
+		QIcon Terminal;
+		QIcon TitleBlock;
+		QIcon UserInformations;
+		QIcon ViewFitWidth;
+		QIcon ViewFitWindow;
+		QIcon ViewMove;
+		QIcon ViewRefresh;
+		QIcon West;
+		QIcon WindowNew;
+		QIcon XmlTextFile;
+		QIcon ZoomDraw;
+		QIcon ZoomFitBest;
+		QIcon ZoomIn;
+		QIcon ZoomOriginal;
+		QIcon ZoomOut;
+		QIcon adding_image;
+		QIcon resize_image;
+	}
+}
+
+/**
+	Initialise les icones de l'application QElectroTech
+*/
+void QET::Icons::initIcons() {
+	// we may need to mirror some icons for right-to-left languages
+	bool rtl = QApplication::isRightToLeft();
+	QTransform reverse = QTransform().scale(-1, 1);
+	
+	Add                 .addFile(":/ico/16x16/list-add.png");
+	Add                 .addFile(":/ico/22x22/list-add.png");
+	Allowed             .addFile(":/ico/16x16/user-online.png");
+	ApplicationExit     .addFile(":/ico/16x16/application-exit.png");
+	ApplicationExit     .addFile(":/ico/22x22/application-exit.png");
+	ArrowLeft           .addFile(":/ico/16x16/arrow-left.png");
+	ArrowLeft           .addFile(":/ico/22x22/arrow-left.png");
+	ArrowLeftDouble     .addFile(":/ico/16x16/arrow-left-double.png");
+	ArrowLeftDouble     .addFile(":/ico/22x22/arrow-left-double.png");
+	ArrowRight          .addFile(":/ico/16x16/arrow-right.png");
+	ArrowRight          .addFile(":/ico/22x22/arrow-right.png");
+	ArrowRightDouble    .addFile(":/ico/16x16/arrow-right-double.png");
+	ArrowRightDouble    .addFile(":/ico/22x22/arrow-right-double.png");
+	BringForward        .addFile(":/ico/22x22/bring_forward.png");
+	Cancel              .addFile(":/ico/16x16/item_cancel.png");
+	Conductor           .addFile(":/ico/22x22/conductor.png");
+	ConductorSettings   .addFile(":/ico/22x22/conductor2.png");
+	Configure           .addFile(":/ico/16x16/configure.png");
+	Configure           .addFile(":/ico/22x22/configure.png");
+	ConfigureToolbars   .addFile(":/ico/16x16/configure-toolbars.png");
+	ConfigureToolbars   .addFile(":/ico/22x22/configure-toolbars.png");
+	CopyFile            .addFile(":/ico/16x16/item_copy.png");
+	DefaultConductor    .addFile(":/ico/22x22/conductor3.png");
+	DiagramAdd          .addFile(":/ico/22x22/diagram_add.png");
+	Diagram             .addFile(":/ico/diagram.png");
+	DiagramDelete       .addFile(":/ico/22x22/diagram_del.png");
+	DialogCancel        .addFile(":/ico/16x16/dialog-cancel.png");
+	DialogCancel        .addFile(":/ico/22x22/dialog-cancel.png");
+	DialogInformation   .addFile(":/ico/22x22/dialog-information.png");
+	DialogOk            .addFile(":/ico/16x16/dialog-ok.png");
+	DialogOk            .addFile(":/ico/22x22/dialog-ok.png");
+	DocumentClose       .addFile(":/ico/16x16/document-close.png");
+	DocumentClose       .addFile(":/ico/22x22/document-close.png");
+	DocumentExport      .addFile(":/ico/16x16/document-export.png");
+	DocumentExport      .addFile(":/ico/22x22/document-export.png");
+	DocumentExport      .addFile(":/ico/128x128/document-export.png");
+	DocumentImport      .addFile(":/ico/16x16/document-import.png");
+	DocumentImport      .addFile(":/ico/22x22/document-import.png");
+	DocumentNew         .addFile(":/ico/16x16/document-new.png");
+	DocumentNew         .addFile(":/ico/22x22/document-new.png");
+	DocumentOpen        .addFile(":/ico/16x16/document-open.png");
+	DocumentOpen        .addFile(":/ico/22x22/document-open.png");
+	DocumentOpenRecent  .addFile(":/ico/16x16/document-open-recent.png");
+	DocumentOpenRecent  .addFile(":/ico/22x22/document-open-recent.png");
+	DocumentPrint       .addFile(":/ico/16x16/document-print.png");
+	DocumentPrint       .addFile(":/ico/22x22/document-print.png");
+	DocumentPrintFrame  .addFile(":/ico/16x16/document-print-frame.png");
+	DocumentPrintFrame  .addFile(":/ico/22x22/document-print-frame.png");
+	DocumentSave        .addFile(":/ico/16x16/document-save.png");
+	DocumentSave        .addFile(":/ico/22x22/document-save.png");
+	DocumentSaveAll     .addFile(":/ico/16x16/document-save-all.png");
+	DocumentSaveAll     .addFile(":/ico/22x22/document-save-all.png");
+	DocumentSaveAs      .addFile(":/ico/16x16/document-save-as.png");
+	DocumentSaveAs      .addFile(":/ico/22x22/document-save-as.png");
+	East                .addFile(":/ico/16x16/east.png");
+	EditClear           .addFile(":/ico/16x16/edit-clear.png");
+	EditClear           .addFile(":/ico/22x22/edit-clear.png");
+    EditText            .addFile(":/ico/22x22/names.png");
+	adding_image        .addFile(":/ico/22x22/insert-image.png");
+	
+	if (rtl) {
+		EditClearLocationBar.addPixmap(QPixmap(":/ico/16x16/edit-clear-locationbar-ltr.png").transformed(reverse));
+		EditClearLocationBar.addPixmap(QPixmap(":/ico/22x22/edit-clear-locationbar-ltr.png").transformed(reverse));
+	} else {
+		EditClearLocationBar.addFile(":/ico/16x16/edit-clear-locationbar-ltr.png");
+		EditClearLocationBar.addFile(":/ico/22x22/edit-clear-locationbar-ltr.png");
+	}
+	EditCopy            .addFile(":/ico/16x16/edit-copy.png");
+	EditCopy            .addFile(":/ico/22x22/edit-copy.png");
+	EditCut             .addFile(":/ico/16x16/edit-cut.png");
+	EditCut             .addFile(":/ico/22x22/edit-cut.png");
+	EditDelete          .addFile(":/ico/16x16/edit-delete.png");
+	EditDelete          .addFile(":/ico/22x22/edit-delete.png");
+	EditPaste           .addFile(":/ico/22x22/edit-paste.png");
+	EditPaste           .addFile(":/ico/16x16/edit-paste.png");
+	if (rtl) {
+		EditRedo.addPixmap(QPixmap(":/ico/16x16/edit-redo.png").transformed(reverse));
+		EditRedo.addPixmap(QPixmap(":/ico/22x22/edit-redo.png").transformed(reverse));
+	} else {
+		EditRedo            .addFile(":/ico/16x16/edit-redo.png");
+		EditRedo            .addFile(":/ico/22x22/edit-redo.png");
+	}
+	EditRename          .addFile(":/ico/16x16/edit-rename.png");
+	EditRename          .addFile(":/ico/22x22/edit-rename.png");
+	EditSelectAll       .addFile(":/ico/16x16/edit-select-all.png");
+	EditSelectAll       .addFile(":/ico/22x22/edit-select-all.png");
+	EditTableCellMerge        .addFile(":ico/16x16/edit-table-cell-merge.png");
+	EditTableCellMerge        .addFile(":ico/22x22/edit-table-cell-merge.png");
+	EditTableCellSplit        .addFile(":ico/16x16/edit-table-cell-split.png");
+	EditTableCellSplit        .addFile(":ico/22x22/edit-table-cell-split.png");
+	EditTableDeleteColumn     .addFile(":ico/16x16/edit-table-delete-column.png");
+	EditTableDeleteColumn     .addFile(":ico/22x22/edit-table-delete-column.png");
+	EditTableDeleteRow        .addFile(":ico/16x16/edit-table-delete-row.png");
+	EditTableDeleteRow        .addFile(":ico/22x22/edit-table-delete-row.png");
+	EditTableInsertColumnLeft .addFile(":ico/16x16/edit-table-insert-column-left.png");
+	EditTableInsertColumnLeft .addFile(":ico/22x22/edit-table-insert-column-left.png");
+	EditTableInsertColumnRight.addFile(":ico/16x16/edit-table-insert-column-right.png");
+	EditTableInsertColumnRight.addFile(":ico/22x22/edit-table-insert-column-right.png");
+	EditTableInsertRowAbove   .addFile(":ico/16x16/edit-table-insert-row-above.png");
+	EditTableInsertRowAbove   .addFile(":ico/22x22/edit-table-insert-row-above.png");
+	EditTableInsertRowUnder   .addFile(":ico/16x16/edit-table-insert-row-under.png");
+	EditTableInsertRowUnder   .addFile(":ico/22x22/edit-table-insert-row-under.png");
+	if (rtl) {
+		EditUndo.addPixmap(QPixmap(":/ico/16x16/edit-undo.png").transformed(reverse));
+		EditUndo.addPixmap(QPixmap(":/ico/22x22/edit-undo.png").transformed(reverse));
+	} else {
+		EditUndo            .addFile(":/ico/16x16/edit-undo.png");
+		EditUndo            .addFile(":/ico/22x22/edit-undo.png");
+	}
+	Element             .addFile(":/ico/oxygen-icons/16x16/mimetypes/application-x-qet-element.png");
+	Element             .addFile(":/ico/oxygen-icons/22x22/mimetypes/application-x-qet-element.png");
+	Element             .addFile(":/ico/oxygen-icons/32x32/mimetypes/application-x-qet-element.png");
+	ElementDelete       .addFile(":/ico/22x22/element-delete.png");
+	ElementEdit         .addFile(":/ico/22x22/element-edit.png");
+	ElementNew          .addFile(":/ico/22x22/element-new.png");
+	EndLineCircle       .addFile(":/ico/16x16/endline-circle.png");
+	EndLineDiamond      .addFile(":/ico/16x16/endline-diamond.png");
+	EndLineNone         .addFile(":/ico/16x16/endline-none.png");
+	EndLineSimple       .addFile(":/ico/16x16/endline-simple.png");
+	EndLineTriangle     .addFile(":/ico/16x16/endline-triangle.png");
+	EPS                 .addFile(":/ico/32x32/image-x-eps.png");
+	Folder              .addFile(":/ico/16x16/folder.png");
+	FolderDelete        .addFile(":/ico/22x22/folder-delete.png");
+	FolderEdit          .addFile(":/ico/22x22/folder-edit.png");
+	FolderNew           .addFile(":/ico/16x16/folder-new.png");
+	FolderNew           .addFile(":/ico/22x22/folder-new.png");
+	Forbidden           .addFile(":/ico/16x16/user-busy.png");
+	FullScreenEnter     .addFile(":/ico/16x16/view-fullscreen.png");
+	FullScreenEnter     .addFile(":/ico/22x22/view-fullscreen.png");
+	FullScreenExit      .addFile(":/ico/16x16/view-restore.png");
+	FullScreenExit      .addFile(":/ico/22x22/view-restore.png");
+	GoDown              .addFile(":/ico/16x16/go-down.png");
+	GoDown              .addFile(":/ico/22x22/go-down.png");
+	GoUp                .addFile(":/ico/16x16/go-up.png");
+	GoUp                .addFile(":/ico/22x22/go-up.png");
+	Ground              .addFile(":/ico/16x16/ground.png");
+	Hide                .addFile(":/ico/16x16/masquer.png");
+	Home                .addFile(":/ico/16x16/go-home.png");
+	Home                .addFile(":/ico/22x22/go-home.png");
+	HotSpot             .addFile(":/ico/22x22/hotspot.png");
+	InsertImage         .addFile(":/ico/22x22/insert-image.png");
+	Lower               .addFile(":/ico/22x22/lower.png");
+	MoveFile            .addFile(":/ico/16x16/item_move.png");
+	Names               .addFile(":/ico/22x22/names.png");
+	Neutral             .addFile(":/ico/16x16/neutral.png");
+	NewDiagram          .addFile(":/ico/128x128/diagram.png");
+	North               .addFile(":/ico/16x16/north.png");
+	ObjectLocked        .addFile(":/ico/22x22/object-locked.png");
+	ObjectRotateRight   .addFile(":/ico/22x22/object-rotate-right.png");
+	ObjectUnlocked      .addFile(":/ico/22x22/object-unlocked.png");
+	Orientations        .addFile(":/ico/16x16/orientations.png");
+	PartArc             .addFile(":/ico/22x22/arc.png");
+	PartCircle          .addFile(":/ico/16x16/circle.png");
+	PartEllipse         .addFile(":/ico/22x22/ellipse.png");
+	PartLine            .addFile(":/ico/22x22/line.png");
+	PartPolygon         .addFile(":/ico/22x22/polygon.png");
+	PartRectangle       .addFile(":/ico/22x22/rectangle.png");
+	PartSelect          .addFile(":/ico/22x22/select.png");
+	PartText            .addFile(":/ico/22x22/text.png");
+	PartTextField       .addFile(":/ico/22x22/textfield.png");
+	PDF                 .addFile(":/ico/32x32/application-pdf.png");
+	Phase               .addFile(":/ico/16x16/phase.png");
+	PrintAllPages       .addFile(":/ico/22x22/all_pages.png");
+	Printer             .addFile(":/ico/32x32/printer.png");
+	Printer             .addFile(":/ico/128x128/printer.png");
+	PrintLandscape      .addFile(":/ico/22x22/landscape.png");
+	PrintPortrait       .addFile(":/ico/22x22/portrait.png");
+	PrintTwoPages       .addFile(":/ico/22x22/two_pages.png");
+	ProjectFile         .addFile(":/ico/oxygen-icons/16x16/mimetypes/application-x-qet-project.png");
+	ProjectFile         .addFile(":/ico/oxygen-icons/22x22/mimetypes/application-x-qet-project.png");
+	ProjectFile         .addFile(":/ico/oxygen-icons/32x32/mimetypes/application-x-qet-project.png");
+	QETIcon             .addFile(":/ico/256x256/qelectrotech.png");
+	QETLogo             .addFile(":/ico/16x16/qet.png");
+	QETLogo             .addFile(":/ico/256x256/qet.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/128x128/apps/qelectrotech.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/16x16/apps/qelectrotech.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/22x22/apps/qelectrotech.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/256x256/apps/qelectrotech.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/32x32/apps/qelectrotech.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/48x48/apps/qelectrotech.png");
+	QETOxygenLogo       .addFile(":/ico/oxygen-icons/64x64/apps/qelectrotech.png");
+	QtLogo              .addFile(":/ico/32x32/qt.png");
+	Raise               .addFile(":/ico/22x22/raise.png");
+	Remove              .addFile(":/ico/16x16/list-remove.png");
+	Remove              .addFile(":/ico/22x22/list-remove.png");
+	resize_image        .addFile(":/ico/22x22/transform-scale.png");
+	Restore             .addFile(":/ico/22x22/restaurer.png");
+	SendBackward        .addFile(":/ico/22x22/send_backward.png");
+	Settings            .addFile(":/ico/128x128/settings.png");
+	SinglePage          .addFile(":/ico/22x22/single_page.png");
+	South               .addFile(":/ico/16x16/south.png");
+	Start               .addFile(":/ico/22x22/start.png");
+	Terminal            .addFile(":/ico/22x22/terminal.png");
+	TitleBlock          .addFile(":/ico/16x16/label.png");
+	TitleBlock          .addFile(":/ico/22x22/label.png");
+	UserInformations    .addFile(":/ico/16x16/preferences-desktop-user.png");
+	UserInformations    .addFile(":/ico/22x22/preferences-desktop-user.png");
+	ViewFitWidth        .addFile(":/ico/22x22/view_fit_width.png");
+	ViewFitWindow       .addFile(":/ico/22x22/view_fit_window.png");
+	ViewMove            .addFile(":/ico/22x22/move.png");
+	if (rtl) {
+		ViewRefresh.addPixmap(QPixmap(":/ico/16x16/view-refresh.png").transformed(reverse));
+		ViewRefresh.addPixmap(QPixmap(":/ico/22x22/view-refresh.png").transformed(reverse));
+	} else {
+		ViewRefresh         .addFile(":/ico/16x16/view-refresh.png");
+		ViewRefresh         .addFile(":/ico/22x22/view-refresh.png");
+	}
+	West                .addFile(":/ico/16x16/west.png");
+	WindowNew           .addFile(":/ico/16x16/window-new.png");
+	WindowNew           .addFile(":/ico/22x22/window-new.png");
+	XmlTextFile         .addFile(":/ico/16x16/text-xml.png");
+	XmlTextFile         .addFile(":/ico/22x22/text-xml.png");
+	XmlTextFile         .addFile(":/ico/32x32/text-xml.png");
+	ZoomDraw            .addFile(":/ico/16x16/zoom-draw.png");
+	ZoomDraw            .addFile(":/ico/22x22/zoom-draw.png");
+	ZoomFitBest         .addFile(":/ico/16x16/zoom-fit-best.png");
+	ZoomFitBest         .addFile(":/ico/22x22/zoom-fit-best.png");
+	ZoomIn              .addFile(":/ico/16x16/zoom-in.png");
+	ZoomIn              .addFile(":/ico/22x22/zoom-in.png");
+	ZoomOriginal        .addFile(":/ico/16x16/zoom-original.png");
+	ZoomOriginal        .addFile(":/ico/22x22/zoom-original.png");
+	ZoomOut             .addFile(":/ico/16x16/zoom-out.png");
+	ZoomOut             .addFile(":/ico/22x22/zoom-out.png");
+}

Added: trunk/sources/qeticons.h
===================================================================
--- trunk/sources/qeticons.h	                        (rev 0)
+++ trunk/sources/qeticons.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,168 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_ICONS_H
+#define QET_ICONS_H
+#include <QIcon>
+/**
+	This namespace is meant to delare icons used within the QElectroTech
+	application.
+*/
+namespace QET {
+	namespace Icons {
+		void initIcons();
+		
+		// using the extern keyword enables access to static objects from outside the
+		// namespace
+		extern QIcon Add;
+		extern QIcon Allowed;
+		extern QIcon ApplicationExit;
+		extern QIcon ArrowLeft;
+		extern QIcon ArrowLeftDouble;
+		extern QIcon ArrowRight;
+		extern QIcon ArrowRightDouble;
+		extern QIcon BringForward;
+		extern QIcon Cancel;
+		extern QIcon Conductor;
+		extern QIcon ConductorSettings;
+		extern QIcon Configure;
+		extern QIcon ConfigureToolbars;
+		extern QIcon CopyFile;
+		extern QIcon DefaultConductor;
+		extern QIcon DiagramAdd;
+		extern QIcon Diagram;
+		extern QIcon DiagramDelete;
+		extern QIcon DialogCancel;
+		extern QIcon DialogInformation;
+		extern QIcon DialogOk;
+		extern QIcon DocumentClose;
+		extern QIcon DocumentExport;
+		extern QIcon DocumentImport;
+		extern QIcon DocumentNew;
+		extern QIcon DocumentOpen;
+		extern QIcon DocumentOpenRecent;
+		extern QIcon DocumentPrint;
+		extern QIcon DocumentPrintFrame;
+		extern QIcon DocumentSave;
+		extern QIcon DocumentSaveAll;
+		extern QIcon DocumentSaveAs;
+		extern QIcon East;
+		extern QIcon EditClear;
+		extern QIcon EditClearLocationBar;
+		extern QIcon EditCopy;
+		extern QIcon EditCut;
+		extern QIcon EditDelete;
+		extern QIcon EditPaste;
+		extern QIcon EditRedo;
+		extern QIcon EditRename;
+		extern QIcon EditSelectAll;
+		extern QIcon EditTableCellMerge;
+		extern QIcon EditTableCellSplit;
+		extern QIcon EditTableDeleteColumn;
+		extern QIcon EditTableDeleteRow;
+		extern QIcon EditTableInsertColumnLeft;
+		extern QIcon EditTableInsertColumnRight;
+		extern QIcon EditTableInsertRowAbove;
+		extern QIcon EditTableInsertRowUnder;
+		extern QIcon EditText;
+		extern QIcon EditUndo;
+		extern QIcon Element;
+		extern QIcon ElementDelete;
+		extern QIcon ElementEdit;
+		extern QIcon ElementNew;
+		extern QIcon EndLineCircle;
+		extern QIcon EndLineDiamond;
+		extern QIcon EndLineNone;
+		extern QIcon EndLineSimple;
+		extern QIcon EndLineTriangle;
+		extern QIcon EPS;
+		extern QIcon Folder;
+		extern QIcon FolderDelete;
+		extern QIcon FolderEdit;
+		extern QIcon FolderNew;
+		extern QIcon Forbidden;
+		extern QIcon FullScreenEnter;
+		extern QIcon FullScreenExit;
+		extern QIcon GoDown;
+		extern QIcon GoUp;
+		extern QIcon Ground;
+		extern QIcon Hide;
+		extern QIcon Home;
+		extern QIcon HotSpot;
+		extern QIcon InsertImage;
+		extern QIcon Lower;
+		extern QIcon MoveFile;
+		extern QIcon Names;
+		extern QIcon Neutral;
+		extern QIcon NewDiagram;
+		extern QIcon NewDiagram;
+		extern QIcon NewDiagram;
+		extern QIcon North;
+		extern QIcon ObjectLocked;
+		extern QIcon ObjectRotateRight;
+		extern QIcon ObjectUnlocked;
+		extern QIcon Orientations;
+		extern QIcon PartArc;
+		extern QIcon PartCircle;
+		extern QIcon PartEllipse;
+		extern QIcon PartLine;
+		extern QIcon PartPolygon;
+		extern QIcon PartRectangle;
+		extern QIcon PartSelect;
+		extern QIcon PartText;
+		extern QIcon PartTextField;
+		extern QIcon PDF;
+		extern QIcon Phase;
+		extern QIcon PrintAllPages;
+		extern QIcon Printer;
+		extern QIcon PrintLandscape;
+		extern QIcon PrintPortrait;
+		extern QIcon PrintTwoPages;
+		extern QIcon Project;
+		extern QIcon ProjectFile;
+		extern QIcon QETIcon;
+		extern QIcon QETLogo;
+		extern QIcon QETOxygenLogo;
+		extern QIcon QtLogo;
+		extern QIcon Raise;
+		extern QIcon Remove;
+		extern QIcon Restore;
+		extern QIcon SendBackward;
+		extern QIcon Settings;
+		extern QIcon SinglePage;
+		extern QIcon South;
+		extern QIcon Start;
+		extern QIcon Terminal;
+		extern QIcon TitleBlock;
+		extern QIcon UserInformations;
+		extern QIcon ViewFitWidth;
+		extern QIcon ViewFitWindow;
+		extern QIcon ViewMove;
+		extern QIcon ViewRefresh;
+		extern QIcon West;
+		extern QIcon WindowNew;
+		extern QIcon XmlTextFile;
+		extern QIcon ZoomDraw;
+		extern QIcon ZoomFitBest;
+		extern QIcon ZoomIn;
+		extern QIcon ZoomOriginal;
+		extern QIcon ZoomOut;
+		extern QIcon adding_image;
+		extern QIcon resize_image;
+	}
+}
+#endif

Added: trunk/sources/qetmainwindow.cpp
===================================================================
--- trunk/sources/qetmainwindow.cpp	                        (rev 0)
+++ trunk/sources/qetmainwindow.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,194 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetmainwindow.h"
+#include "qeticons.h"
+#include "qetapp.h"
+
+/**
+	Constructor
+*/
+QETMainWindow::QETMainWindow(QWidget *widget, Qt::WindowFlags flags) :
+	QMainWindow(widget, flags),
+	display_toolbars_(0),
+	first_activation_(true)
+{
+	initCommonActions();
+	initCommonMenus();
+	
+	setAcceptDrops(true);
+}
+
+/**
+	Destructor
+*/
+QETMainWindow::~QETMainWindow() {
+}
+
+/**
+	Initialize common actions.
+*/
+void QETMainWindow::initCommonActions() {
+	QETApp *qet_app = QETApp::instance();
+	
+	configure_action_ = new QAction(QET::Icons::Configure, tr("&Configurer QElectroTech"), this);
+	configure_action_ -> setStatusTip(tr("Permet de r\351gler diff\351rents param\350tres de QElectroTech", "status bar tip"));
+	connect(configure_action_,  SIGNAL(triggered()), qet_app, SLOT(configureQET()));
+	
+	fullscreen_action_ = new QAction(this);
+	updateFullScreenAction();
+	connect(fullscreen_action_, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
+	
+	whatsthis_action_ = QWhatsThis::createAction(this);
+	
+	about_qet_ = new QAction(QET::Icons::QETLogo, tr("\300 &propos de QElectroTech"), this);
+	about_qet_ -> setStatusTip(tr("Affiche des informations sur QElectroTech", "status bar tip"));
+	connect(about_qet_,  SIGNAL(triggered()), qet_app, SLOT(aboutQET()));
+	
+	about_qt_ = new QAction(QET::Icons::QtLogo,  tr("\300 propos de &Qt"), this);
+	about_qt_ -> setStatusTip(tr("Affiche des informations sur la biblioth\350que Qt", "status bar tip"));
+	connect(about_qt_, SIGNAL(triggered()), qet_app, SLOT(aboutQt()));
+}
+
+/**
+	Initialize common menus.
+*/
+void QETMainWindow::initCommonMenus() {
+	settings_menu_ = new QMenu(tr("&Configuration", "window menu"));
+	settings_menu_ -> addAction(fullscreen_action_);
+	settings_menu_ -> addAction(configure_action_);
+	connect(settings_menu_, SIGNAL(aboutToShow()), this, SLOT(checkToolbarsmenu()));
+	
+	
+	help_menu_ = new QMenu(tr("&Aide", "window menu"));
+	help_menu_ -> addAction(whatsthis_action_);
+	help_menu_ -> addSeparator();
+	help_menu_ -> addAction(about_qet_);
+	help_menu_ -> addAction(about_qt_);
+	
+	insertMenu(0, settings_menu_);
+	insertMenu(0, help_menu_);
+}
+
+/**
+	Add \a menu before \a before. Unless \a customize is false, this method also
+	enables some common settings on the inserted menu.
+*/
+void QETMainWindow::insertMenu(QMenu *before, QMenu *menu, bool customize) {
+	if (!menu) return;
+	
+	QAction *before_action = actionForMenu(before);
+	QAction *menu_action = menuBar() -> insertMenu(before_action, menu);
+	menu_actions_.insert(menu, menu_action);
+	
+	if (customize) {
+		menu -> setTearOffEnabled(true);
+	}
+}
+
+/**
+	@return the action returned when inserting \a menu
+*/
+QAction *QETMainWindow::actionForMenu(QMenu *menu) {
+	return(menu_actions_.value(menu, 0));
+}
+
+/**
+	Toggle the window from/to full screen.
+*/
+void QETMainWindow::toggleFullScreen() {
+	setWindowState(windowState() ^ Qt::WindowFullScreen);
+}
+
+/**
+	Update the look of the full screen action according to the current state of
+	the window.
+*/
+void QETMainWindow::updateFullScreenAction() {
+	if (windowState() & Qt::WindowFullScreen) {
+		fullscreen_action_ -> setText(tr("Sortir du &mode plein \351cran"));
+		fullscreen_action_ -> setIcon(QET::Icons::FullScreenExit);
+		fullscreen_action_ -> setStatusTip(tr("Affiche QElectroTech en mode fen\352tr\351", "status bar tip"));
+	} else {
+		fullscreen_action_ -> setText(tr("Passer en &mode plein \351cran"));
+		fullscreen_action_ -> setIcon(QET::Icons::FullScreenEnter);
+		fullscreen_action_ -> setStatusTip(tr("Affiche QElectroTech en mode plein \351cran", "status bar tip"));
+	}
+	fullscreen_action_ -> setShortcut(QKeySequence(tr("Ctrl+Shift+F")));
+}
+
+/**
+	Check whether a sub menu dedicated to docks and toolbars can be inserted on
+	top of the settings menu.
+*/
+void QETMainWindow::checkToolbarsmenu() {
+	if (display_toolbars_) return;
+	display_toolbars_ = createPopupMenu();
+	if (display_toolbars_) {
+		display_toolbars_ -> setTearOffEnabled(true);
+		display_toolbars_ -> setTitle(tr("Afficher", "menu entry"));
+		display_toolbars_ -> setIcon(QET::Icons::ConfigureToolbars);
+		settings_menu_ -> insertMenu(fullscreen_action_, display_toolbars_);
+	}
+}
+
+/**
+	Handle the \a e event.
+*/
+bool QETMainWindow::event(QEvent *e) {
+	if (e -> type() == QEvent::WindowStateChange) {
+		updateFullScreenAction();
+	} else if (first_activation_ && e -> type() == QEvent::WindowActivate) {
+		firstActivation(e);
+		first_activation_ = false;
+	}
+	return(QMainWindow::event(e));
+}
+
+/**
+	Base implementation of firstActivation (does nothing).
+*/
+void QETMainWindow::firstActivation(QEvent *) {
+}
+
+
+/**
+	Accept or refuse drag'n drop events depending on the dropped mime type;
+	especially, accepts only URLs to local files that we could open.
+	@param e le QDragEnterEvent correspondant au drag'n drop tente
+*/
+void QETMainWindow::dragEnterEvent(QDragEnterEvent *e) {
+	if (e -> mimeData() -> hasUrls()) {
+		if (QETApp::handledFiles(e -> mimeData() -> urls()).count()) {
+			e -> acceptProposedAction();
+		}
+	}
+}
+
+/**
+	Handle drops accepted on main windows; more specifically, open dropped files
+	as long as they are handled by QElectrotech.
+	@param e the QDropEvent describing the current drag'n drop
+*/
+void QETMainWindow::dropEvent(QDropEvent *e) {
+	if (e -> mimeData() -> hasUrls()) {
+		QStringList filepaths = QETApp::handledFiles(e -> mimeData() -> urls());
+		if (filepaths.count()) {
+			QETApp::instance() -> openFiles(QETArguments(filepaths));
+		}
+	}
+}

Added: trunk/sources/qetmainwindow.h
===================================================================
--- trunk/sources/qetmainwindow.h	                        (rev 0)
+++ trunk/sources/qetmainwindow.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,66 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_MAIN_WINDOW_H
+#define QET_MAIN_WINDOW_H
+#include <QMainWindow>
+#include <QHash>
+/**
+	This is the base class for the main top-level windows within
+	QElectroTech.
+*/
+class QETMainWindow : public QMainWindow {
+	Q_OBJECT
+	
+	// constructor, destructor
+	public:
+	QETMainWindow(QWidget * = 0, Qt::WindowFlags = 0);
+	virtual ~QETMainWindow();
+	
+	// methods
+	protected:
+	void initCommonActions();
+	void initCommonMenus();
+	void insertMenu(QMenu *, QMenu *, bool = true);
+	QAction *actionForMenu(QMenu *);
+	
+	protected:
+	virtual bool event(QEvent *);
+	virtual void dragEnterEvent(QDragEnterEvent *e);
+	virtual void dropEvent(QDropEvent *e);
+	virtual void firstActivation(QEvent *);
+	
+	// slots
+	public slots:
+	void toggleFullScreen();
+	void updateFullScreenAction();
+	void checkToolbarsmenu();
+	
+	// attributes
+	protected:
+	QAction *configure_action_;              ///< Launch the QElectroTech configuration dialog
+	QAction *fullscreen_action_;             ///< Toggle full screen
+	QAction *whatsthis_action_;              ///< Toggle "What's this" mode
+	QAction *about_qet_;                     ///< Launch the "About QElectroTech" dialog
+	QAction *about_qt_;                      ///< launch the "About Qt" dialog
+	QMenu *settings_menu_;                   ///< Settings menu
+	QMenu *help_menu_;                       ///< Help menu
+	QMenu *display_toolbars_;                ///< Show/hide toolbars/docks
+	QHash<QMenu *, QAction *> menu_actions_; ///< Store actions retrieved when inserting menus
+	bool first_activation_;                  ///< Used to detect whether the window is activated for the first time
+};
+#endif

Added: trunk/sources/qetmessagebox.cpp
===================================================================
--- trunk/sources/qetmessagebox.cpp	                        (rev 0)
+++ trunk/sources/qetmessagebox.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,78 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetmessagebox.h"
+
+/**
+	@see Documentation Qt pour QMessageBox::critical
+*/
+QMessageBox::StandardButton QET::MessageBox::critical   (QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
+#ifdef Q_WS_MAC
+	QMessageBox message_box(QMessageBox::Critical, title, text, buttons, parent, Qt::Sheet);
+	message_box.setWindowModality(Qt::WindowModal);
+#else
+	QMessageBox message_box(QMessageBox::Critical, title, text, buttons, parent);
+#endif
+	message_box.setDefaultButton(defaultButton);
+	
+	return(static_cast<QMessageBox::StandardButton>(message_box.exec()));
+}
+
+/**
+	@see Documentation Qt pour QMessageBox::information
+*/
+QMessageBox::StandardButton QET::MessageBox::information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
+#ifdef Q_WS_MAC
+	QMessageBox message_box(QMessageBox::Information, title, text, buttons, parent, Qt::Sheet);
+	message_box.setWindowModality(Qt::WindowModal);
+#else
+	QMessageBox message_box(QMessageBox::Information, title, text, buttons, parent);
+#endif
+	message_box.setDefaultButton(defaultButton);
+	
+	return(static_cast<QMessageBox::StandardButton>(message_box.exec()));
+}
+
+/**
+	@see Documentation Qt pour QMessageBox::question
+*/
+QMessageBox::StandardButton QET::MessageBox::question   (QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
+#ifdef Q_WS_MAC
+	QMessageBox message_box(QMessageBox::Question, title, text, buttons, parent, Qt::Sheet);
+	message_box.setWindowModality(Qt::WindowModal);
+#else
+	QMessageBox message_box(QMessageBox::Question, title, text, buttons, parent);
+#endif
+	message_box.setDefaultButton(defaultButton);
+	
+	return(static_cast<QMessageBox::StandardButton>(message_box.exec()));
+}
+
+/**
+	@see Documentation Qt pour QMessageBox::warning
+*/
+QMessageBox::StandardButton QET::MessageBox::warning    (QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
+#ifdef Q_WS_MAC
+	QMessageBox message_box(QMessageBox::Warning, title, text, buttons, parent, Qt::Sheet);
+	message_box.setWindowModality(Qt::WindowModal);
+#else
+	QMessageBox message_box(QMessageBox::Warning, title, text, buttons, parent);
+#endif
+	message_box.setDefaultButton(defaultButton);
+	
+	return(static_cast<QMessageBox::StandardButton>(message_box.exec()));
+}

Added: trunk/sources/qetmessagebox.h
===================================================================
--- trunk/sources/qetmessagebox.h	                        (rev 0)
+++ trunk/sources/qetmessagebox.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,36 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_MESSAGE_BOX_H
+#define QET_MESSAGE_BOX_H
+#include <QMessageBox>
+namespace QET {
+	/**
+		This namespace defines static methods behaving in a very similar way to some
+		static methods from the QMessageBox Qt class. The behavior is expected to be
+		the same under X11 and Windows. However, under MacOS, when a parent widget is
+		provided, these methodes ensure the dialog box is "window modal" and has the
+		Qt:Sheet flag, thus enabling a better MacOS integration.
+	*/
+	namespace MessageBox {
+		QMessageBox::StandardButton critical   (QWidget *, const QString &, const QString &, QMessageBox::StandardButtons = QMessageBox::Ok, QMessageBox::StandardButton = QMessageBox::NoButton);
+		QMessageBox::StandardButton information(QWidget *, const QString &, const QString &, QMessageBox::StandardButtons = QMessageBox::Ok, QMessageBox::StandardButton = QMessageBox::NoButton);
+		QMessageBox::StandardButton question   (QWidget *, const QString &, const QString &, QMessageBox::StandardButtons = QMessageBox::Ok, QMessageBox::StandardButton = QMessageBox::NoButton);
+		QMessageBox::StandardButton warning    (QWidget *, const QString &, const QString &, QMessageBox::StandardButtons = QMessageBox::Ok, QMessageBox::StandardButton = QMessageBox::NoButton);
+	};
+};
+#endif

Added: trunk/sources/qetprintpreviewdialog.cpp
===================================================================
--- trunk/sources/qetprintpreviewdialog.cpp	                        (rev 0)
+++ trunk/sources/qetprintpreviewdialog.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,424 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetprintpreviewdialog.h"
+#include "diagramschooser.h"
+#include "exportproperties.h"
+#include "exportpropertieswidget.h"
+#include "qetdiagrameditor.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param project Projet a imprimer
+	@param printer Imprimante a utiliser pour
+	@param widget  Widget parent
+	@param f       Flags passes au constructeur de QDialog puis QWidget
+*/
+QETPrintPreviewDialog::QETPrintPreviewDialog(QETProject *project, QPrinter *printer, QWidget *widget, Qt::WindowFlags f) :
+	QDialog(widget, f),
+	project_(project),
+	printer_(printer)
+{
+	setWindowTitle(tr("QElectroTech : Aper\347u avant impression"));
+	build();
+	
+	connect(preview_,       SIGNAL(paintRequested(QPrinter *)), this,     SLOT(requestPaint(QPrinter *)));
+	connect(diagrams_list_, SIGNAL(selectionChanged()),         preview_, SLOT(updatePreview()));
+	connect(diagrams_list_, SIGNAL(selectionChanged()),         this,     SLOT(checkDiagramsCount()));
+	
+	setWindowState(windowState() |  Qt::WindowMaximized);
+}
+
+/**
+	Destructeur
+*/
+QETPrintPreviewDialog::~QETPrintPreviewDialog() {
+}
+
+/**
+	@return le widget permettant de choisir les schemas a imprimer.
+*/
+DiagramsChooser *QETPrintPreviewDialog::diagramsChooser() {
+	return(diagrams_list_);
+}
+
+/**
+	@return true si l'option "Adapter le schema a la page" est activee
+*/
+bool QETPrintPreviewDialog::fitDiagramsToPages() const {
+	return(fit_diagram_to_page_ -> isChecked());
+}
+
+/**
+	@return les options de rendu definies par l'utilisateur
+*/
+ExportProperties QETPrintPreviewDialog::exportProperties() const {
+	return(render_properties_ -> exportProperties());
+}
+
+/**
+	Passe a la premiere page
+*/
+void QETPrintPreviewDialog::firstPage() {
+	preview_ -> setCurrentPage(1);
+}
+
+/**
+	Passe a la page precedente
+*/
+void QETPrintPreviewDialog::previousPage() {
+	int preview_previous_page = preview_ -> currentPage() - 1;
+	preview_ -> setCurrentPage(qMax(preview_previous_page, 0));
+}
+
+/**
+	Passe a la page suivante
+*/
+void QETPrintPreviewDialog::nextPage() {
+	int preview_next_page = preview_ -> currentPage() + 1;
+	preview_ -> setCurrentPage(qMin(preview_next_page, preview_ -> numPages()));
+}
+
+/**
+	Passe a la derniere page
+*/
+void QETPrintPreviewDialog::lastPage() {
+	preview_ -> setCurrentPage(preview_ -> numPages());
+}
+
+/**
+	Copnfigure la mise en page
+*/
+void QETPrintPreviewDialog::pageSetup() {
+	QPageSetupDialog page_setup_dialog(printer_, this);
+	if (page_setup_dialog.exec() == QDialog::Accepted) {
+		preview_ -> updatePreview();
+		updateZoomList();
+	}
+}
+
+/**
+	Utilise ou non toute la page sans teni compte des marges
+	@param full_page true pour utiliser toute la page, false sinon
+*/
+void QETPrintPreviewDialog::useFullPage(bool full_page) {
+	printer_ -> setFullPage(full_page);
+	preview_ -> updatePreview();
+	updateZoomList();
+}
+
+/**
+	Fait tenir ou non chaque schema sur une page
+	@param fit_diagram true pour adapter chaque schema sur une page, false sinon
+*/
+void QETPrintPreviewDialog::fitDiagramToPage(bool fit_diagram) {
+	Q_UNUSED(fit_diagram);
+	preview_ -> updatePreview();
+	updateZoomList();
+}
+
+/**
+	Effectue l'action "zoom avant" sur l'apercu avant impression
+*/
+void QETPrintPreviewDialog::zoomIn() {
+	preview_ -> zoomIn(4.0/3.0);
+	updateZoomList();
+}
+
+/**
+	Effectue l'action "zoom arriere" sur l'apercu avant impression
+*/
+void QETPrintPreviewDialog::zoomOut() {
+	preview_ -> zoomOut(4.0/3.0);
+	updateZoomList();
+}
+
+/**
+	Selectionne tous les schemas
+*/
+void QETPrintPreviewDialog::selectAllDiagrams() {
+	diagrams_list_ -> setSelectedAllDiagrams(true);
+}
+
+/**
+	Deselectionne tous les schemas
+*/
+void QETPrintPreviewDialog::selectNoDiagram() {
+	diagrams_list_ -> setSelectedAllDiagrams(false);
+}
+
+/**
+	Met en place le dialogue
+*/
+void QETPrintPreviewDialog::build() {
+	preview_ = new QPrintPreviewWidget(printer_);
+	diagrams_label_       = new QLabel(tr("Sch\351mas \340 imprimer\240:"));
+	diagrams_list_        = new DiagramsChooser(project_);
+	diagrams_select_all_  = new QPushButton(tr("Tout cocher"));
+	diagrams_select_none_ = new QPushButton(tr("Tout d\351cocher"));
+	toggle_diagrams_list_ = new QAction(QET::Icons::Diagram,              tr("Cacher la liste des sch\351mas"),            this);
+	toggle_print_options_ = new QAction(QET::Icons::Configure,            tr("Cacher les options d'impression"),           this);
+	adjust_width_         = new QAction(QET::Icons::ViewFitWidth,         tr("Ajuster la largeur"),                        this);
+	adjust_page_          = new QAction(QET::Icons::ViewFitWindow,        tr("Ajuster la page"),                           this);
+	zoom_out_             = new QAction(QET::Icons::ZoomOut,              tr("Zoom arri\350re"),                           this);
+	zoom_box_             = new QComboBox(this);
+	zoom_in_              = new QAction(QET::Icons::ZoomIn,               tr("Zoom avant"),                                this);
+	landscape_            = new QAction(QET::Icons::PrintLandscape,       tr("Paysage"),                                   this);
+	portrait_             = new QAction(QET::Icons::PrintPortrait,        tr("Portrait"),                                  this);
+	first_page_           = new QAction(QET::Icons::ArrowLeftDouble,      tr("Premi\350re page"),                          this);
+	previous_page_        = new QAction(QET::Icons::ArrowLeft,            tr("Page pr\351c\351dente"),                     this);
+	next_page_            = new QAction(QET::Icons::ArrowRight,           tr("Page suivante"),                             this);
+	last_page_            = new QAction(QET::Icons::ArrowRightDouble,     tr("Derni\350re page"),                          this);
+	single_page_view_     = new QAction(QET::Icons::SinglePage,           tr("Afficher une seule page"),                   this);
+	facing_pages_view_    = new QAction(QET::Icons::PrintTwoPages,        tr("Afficher deux pages"),                       this);
+	all_pages_view_       = new QAction(QET::Icons::PrintAllPages,        tr("Afficher un aper\347u de toutes les pages"), this);
+	page_setup_           = new QAction(QET::Icons::DocumentPrintFrame,   tr("Mise en page"),                              this);
+	
+	toggle_diagrams_list_ -> setCheckable(true);
+	toggle_diagrams_list_ -> setChecked(true);
+	toggle_print_options_ -> setCheckable(true);
+	toggle_print_options_ -> setChecked(true);
+	
+#ifdef Q_OS_WIN32
+	/*
+		Sous Windows, le QPageSetupDialog utilise le dialogue natif ; ce
+		dernier ne peut gerer que les imprimantes physiques ("native
+		printers" ).
+		cf avertissement : QAbstractPageSetupDialog::QAbstractPageSetupDialog:
+		Page setup dialog cannot be used on non-native printers
+	*/
+	if (!(printer_ -> outputFileName().isEmpty())) {
+		page_setup_ -> setEnabled(false);
+		page_setup_ -> setText(tr("Mise en page (non disponible sous Windows pour l'impression PDF/PS)"));
+	}
+#endif
+	
+	toolbar_ = new QToolBar();
+	toolbar_ -> addAction(toggle_diagrams_list_);
+	toolbar_ -> addAction(toggle_print_options_);
+	toolbar_ -> addSeparator();
+	toolbar_ -> addAction(adjust_width_);
+	toolbar_ -> addAction(adjust_page_);
+	toolbar_ -> addAction(zoom_out_);
+	toolbar_ -> addWidget(zoom_box_);
+	toolbar_ -> addAction(zoom_in_);
+	toolbar_ -> addSeparator();
+	toolbar_ -> addAction(landscape_);
+	toolbar_ -> addAction(portrait_);
+	toolbar_ -> addSeparator();
+	toolbar_ -> addAction(first_page_);
+	toolbar_ -> addAction(previous_page_);
+	toolbar_ -> addAction(next_page_);
+	toolbar_ -> addAction(last_page_);
+	toolbar_ -> addSeparator();
+	toolbar_ -> addAction(single_page_view_);
+	toolbar_ -> addAction(facing_pages_view_);
+	toolbar_ -> addAction(all_pages_view_);
+	toolbar_ -> addSeparator();
+	toolbar_ -> addAction(page_setup_);
+	
+	print_options_box_= new QGroupBox(tr("Options d'impression"));
+	use_full_page_ = new QCheckBox(tr("Utiliser toute la feuille"));
+	use_full_page_ -> setChecked(printer_ -> fullPage());
+	use_full_page_label_ = new QLabel(tr(
+		"Si cette option est coch\351e, les marges de la feuille seront "
+		"ignor\351es et toute sa surface sera utilis\351e pour l'impression. "
+		"Cela peut ne pas \352tre support\351 par votre imprimante."
+	));
+	use_full_page_label_ -> setWordWrap(true);
+	use_full_page_label_ -> setContentsMargins(20, 0, 0, 0);
+	fit_diagram_to_page_ = new QCheckBox(tr("Adapter le sch\351ma \340 la page"));
+	fit_diagram_to_page_label_ = new QLabel(tr(
+		"Si cette option est coch\351e, le sch\351ma sera agrandi ou "
+		"r\351tr\351ci de fa\347on \340 remplir toute la surface imprimable "
+		"d'une et une seule page."
+	));
+	fit_diagram_to_page_label_ -> setWordWrap(true);
+	fit_diagram_to_page_label_ -> setContentsMargins(20, 0, 0, 0);
+	fit_diagram_to_page_ -> setChecked(true);
+	
+	// recupere les parametres d'export definis dans la configuration de l'application
+	ExportProperties default_print_properties = QETDiagramEditor::defaultPrintProperties();
+	
+	render_properties_ = new ExportPropertiesWidget(default_print_properties);
+	render_properties_ -> setPrintingMode(true);
+	
+	buttons_ = new QDialogButtonBox();
+	buttons_ -> addButton(new QPushButton(QET::Icons::DocumentPrint, tr("Imprimer")), QDialogButtonBox::AcceptRole);
+	buttons_ -> addButton(QDialogButtonBox::Cancel);
+	
+	connect(diagrams_select_all_,  SIGNAL(released()),    this,     SLOT(selectAllDiagrams()));
+	connect(diagrams_select_none_, SIGNAL(released()),    this,     SLOT(selectNoDiagram()));
+	connect(toggle_diagrams_list_, SIGNAL(toggled(bool)), this,     SLOT(setDiagramsListVisible(bool)));
+	connect(toggle_print_options_, SIGNAL(toggled(bool)), this,     SLOT(setPrintOptionsVisible(bool)));
+	connect(adjust_width_,         SIGNAL(triggered()),   preview_, SLOT(fitToWidth()));
+	connect(adjust_page_,          SIGNAL(triggered()),   preview_, SLOT(fitInView()));
+	connect(zoom_out_,             SIGNAL(triggered()),   this,     SLOT(zoomOut()));
+	connect(zoom_in_,              SIGNAL(triggered()),   this,     SLOT(zoomIn()));
+	connect(landscape_,            SIGNAL(triggered()),   preview_, SLOT(setLandscapeOrientation()));
+	connect(portrait_,             SIGNAL(triggered()),   preview_, SLOT(setPortraitOrientation()));
+	connect(first_page_,           SIGNAL(triggered()),   this,     SLOT(firstPage()));
+	connect(previous_page_,        SIGNAL(triggered()),   this,     SLOT(previousPage()));
+	connect(next_page_,            SIGNAL(triggered()),   this,     SLOT(nextPage()));
+	connect(last_page_,            SIGNAL(triggered()),   this,     SLOT(lastPage()));
+	connect(single_page_view_,     SIGNAL(triggered()),   preview_, SLOT(setSinglePageViewMode()));
+	connect(facing_pages_view_,    SIGNAL(triggered()),   preview_, SLOT(setFacingPagesViewMode()));
+	connect(all_pages_view_,       SIGNAL(triggered()),   preview_, SLOT(setAllPagesViewMode()));
+	connect(page_setup_,           SIGNAL(triggered()),   this,     SLOT(pageSetup()));
+	
+	connect(use_full_page_,        SIGNAL(toggled(bool)), this, SLOT(useFullPage(bool)));
+	connect(fit_diagram_to_page_,  SIGNAL(toggled(bool)), this, SLOT(fitDiagramToPage(bool)));
+	
+	connect(render_properties_,    SIGNAL(optionChanged()), preview_, SLOT(updatePreview()));
+	
+	connect(preview_,  SIGNAL(previewChanged()),         this, SLOT(updateZoomList()));
+	connect(zoom_box_, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreviewZoom()));
+	
+	connect(buttons_, SIGNAL(accepted()), this, SLOT(accept()));
+	connect(buttons_, SIGNAL(rejected()), this, SLOT(reject()));
+	
+	hlayout0_ = new QHBoxLayout();
+	vlayout0_ = new QVBoxLayout();
+	vlayout1_ = new QVBoxLayout();
+	vlayout2_ = new QVBoxLayout();
+	
+	vlayout1_ -> addWidget(use_full_page_);
+	vlayout1_ -> addWidget(use_full_page_label_);
+	vlayout1_ -> addWidget(fit_diagram_to_page_);
+	vlayout1_ -> addWidget(fit_diagram_to_page_label_);
+	print_options_box_ -> setLayout(vlayout1_);
+	
+	vlayout2_ -> addWidget(diagrams_label_);
+	vlayout2_ -> addWidget(diagrams_list_);
+	vlayout2_ -> addWidget(diagrams_select_all_);
+	vlayout2_ -> addWidget(diagrams_select_none_);
+	
+	hlayout0_ -> addLayout(vlayout2_);
+	hlayout0_ -> addWidget(preview_);
+	
+	vlayout0_ -> addWidget(toolbar_);
+	vlayout0_ -> addLayout(hlayout0_);
+	vlayout0_ -> addWidget(render_properties_);
+	vlayout0_ -> addWidget(print_options_box_);
+	vlayout0_ -> addWidget(buttons_);
+	
+	setLayout(vlayout0_);
+	updateZoomList();
+}
+
+/**
+	Ce slot prive emet le signal paintRequested avec :
+	  * la liste des schemas a imprimer / selectionnes
+	  * un booleen indiquant s'il faut adapter les schemas aux pages ou non
+	  * l'imprimante a utiliser
+*/
+void QETPrintPreviewDialog::requestPaint(QPrinter *printer) {
+	emit(
+		paintRequested(
+			diagrams_list_ -> selectedDiagrams(),
+			fit_diagram_to_page_ -> isChecked(),
+			render_properties_ -> exportProperties(),
+			printer
+		)
+	);
+}
+
+/**
+	Ce slot prive verifie que le nombre de schemas a imprimer est bien superieur
+	a 0 et active ou desactive le bouton "Imprimer" en consequence.
+*/
+void QETPrintPreviewDialog::checkDiagramsCount() {
+	int diagrams_count = diagrams_list_ -> selectedDiagrams().count();
+	
+	// desactive le premier bouton de la liste (= le bouton "Imprimer")
+	QList<QAbstractButton *> buttons = buttons_ -> buttons();
+	if (buttons.count()) buttons[0] -> setEnabled(diagrams_count);
+}
+
+/**
+	Ce slot prive affiche ou cache la liste des schemas
+	@param display true pour affiche la liste des schemas, false pour la cacher
+*/
+void QETPrintPreviewDialog::setDiagramsListVisible(bool display) {
+	diagrams_label_ -> setVisible(display);
+	diagrams_list_  -> setVisible(display);
+	diagrams_select_all_ -> setVisible(display);
+	diagrams_select_none_ -> setVisible(display);
+	
+	if (display) {
+		toggle_diagrams_list_ -> setText(tr("Cacher la liste des sch\351mas"));
+	} else {
+		toggle_diagrams_list_ -> setText(tr("Afficher la liste des sch\351mas"));
+	}
+}
+
+/**
+	Ce slot prive affiche ou cache les options d'impression
+	@param display true pour affiche les options d'impression, false pour les
+	cacher
+*/
+void QETPrintPreviewDialog::setPrintOptionsVisible(bool display) {
+	print_options_box_ -> setVisible(display);
+	render_properties_ -> setVisible(display);
+	
+	if (display) {
+		toggle_print_options_ -> setText(tr("Cacher les options d'impression"));
+	} else {
+		toggle_print_options_ -> setText(tr("Afficher les options d'impression"));
+	}
+}
+
+/**
+	Met a jour la liste des zooms disponibles
+*/
+void QETPrintPreviewDialog::updateZoomList() {
+	// recupere le zooom courant
+	qreal current_zoom = preview_ -> zoomFactor();
+	bool current_zoom_is_not_null = bool(int(current_zoom * 100.0));
+	
+	// liste des zooms par defaut
+	QList<qreal> zooms_real;
+	zooms_real << 0.25 << 0.5 << 0.75 << 1.0 << 1.5 << 2.0 << 4.0 << 8.0;
+	
+	// ajout du zoom en cours
+	if (current_zoom_is_not_null && (!zooms_real.contains(current_zoom))) {
+		zooms_real << current_zoom;
+		qSort(zooms_real.begin(), zooms_real.end());
+	}
+	
+	// remplissage de la liste deroulante
+	int current_zoom_index = -1;
+	zoom_box_ -> blockSignals(true);
+	zoom_box_ -> clear();
+	foreach (qreal z, zooms_real) {
+		zoom_box_ -> addItem(QString(tr("%1 %")).arg(z * 100.0, 0, 'f', 2), z);
+		if (z == current_zoom) current_zoom_index = zoom_box_ -> count() - 1;
+	}
+	zoom_box_ -> setCurrentIndex(current_zoom_index);
+	zoom_box_ -> blockSignals(false);
+}
+
+/**
+	Change le zoom de l'apercu en fonctiopn du contenu du zoom selectionne
+*/
+void QETPrintPreviewDialog::updatePreviewZoom() {
+	preview_ -> setZoomFactor(
+		zoom_box_ -> itemData(zoom_box_ -> currentIndex()).toDouble()
+	);
+	updateZoomList();
+}

Added: trunk/sources/qetprintpreviewdialog.h
===================================================================
--- trunk/sources/qetprintpreviewdialog.h	                        (rev 0)
+++ trunk/sources/qetprintpreviewdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,114 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_PRINT_PREVIEW_DIALOG
+#define QET_PRINT_PREVIEW_DIALOG
+#include <QtGui>
+#include "exportproperties.h"
+class Diagram;
+class DiagramsChooser;
+class ExportPropertiesWidget;
+class QETProject;
+/**
+	This class provides a dialog for users to refine printing options for a
+	particular project by relying on a visual print preview.
+*/
+class QETPrintPreviewDialog : public QDialog {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETPrintPreviewDialog(QETProject *, QPrinter *, QWidget * = 0, Qt::WindowFlags = 0);
+	virtual ~QETPrintPreviewDialog();
+	private:
+	QETPrintPreviewDialog(const QETPrintPreviewDialog &);
+	
+	// methods
+	public:
+	DiagramsChooser *diagramsChooser();
+	bool fitDiagramsToPages() const;
+	ExportProperties exportProperties() const;
+	
+	// signaux
+	signals:
+	void paintRequested(const QList<Diagram *> &, bool, const ExportProperties, QPrinter *);
+	
+	public slots:
+	void firstPage();
+	void previousPage();
+	void nextPage();
+	void lastPage();
+	void pageSetup();
+	void useFullPage(bool);
+	void fitDiagramToPage(bool);
+	void zoomIn();
+	void zoomOut();
+	void selectAllDiagrams();
+	void selectNoDiagram();
+	
+	// attributes
+	private:
+	QETProject *project_;
+	QPrinter *printer_;
+	QHBoxLayout *hlayout0_;
+	QVBoxLayout *vlayout0_;
+	QVBoxLayout *vlayout1_;
+	QVBoxLayout *vlayout2_;
+	QToolBar *toolbar_;
+	QPrintPreviewWidget *preview_;
+	QLabel *diagrams_label_;
+	DiagramsChooser *diagrams_list_;
+	QPushButton *diagrams_select_all_;
+	QPushButton *diagrams_select_none_;
+	QAction *toggle_diagrams_list_;
+	QAction *toggle_print_options_;
+	QAction *adjust_width_;
+	QAction *adjust_page_;
+	QAction *zoom_in_;
+	QComboBox *zoom_box_;
+	QAction *zoom_out_;
+	QAction *landscape_;
+	QAction *portrait_;
+	QAction *first_page_;
+	QAction *previous_page_;
+	QAction *next_page_;
+	QAction *last_page_;
+	QAction *all_pages_view_;
+	QAction *facing_pages_view_;
+	QAction *single_page_view_;
+	QAction *page_setup_;
+	QDialogButtonBox *buttons_;
+	QGroupBox *print_options_box_;
+	QCheckBox *use_full_page_;
+	QLabel *use_full_page_label_;
+	QCheckBox *fit_diagram_to_page_;
+	QLabel *fit_diagram_to_page_label_;
+	ExportPropertiesWidget *render_properties_;
+	
+	// methods
+	private:
+	void build();
+	
+	private slots:
+	void requestPaint(QPrinter *);
+	void checkDiagramsCount();
+	void setDiagramsListVisible(bool);
+	void setPrintOptionsVisible(bool);
+	void updateZoomList();
+	void updatePreviewZoom();
+};
+#endif

Added: trunk/sources/qetproject.cpp
===================================================================
--- trunk/sources/qetproject.cpp	                        (rev 0)
+++ trunk/sources/qetproject.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1332 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetproject.h"
+#include "diagram.h"
+#include "elementdefinition.h"
+#include "xmlelementscollection.h"
+#include "elementscategory.h"
+#include "qetapp.h"
+#include "qetdiagrameditor.h"
+#include "qetresult.h"
+#include "integrationmoveelementshandler.h"
+#include "movetemplateshandler.h"
+#include "basicmoveelementshandler.h"
+#include "qetmessagebox.h"
+#include "titleblocktemplate.h"
+
+#include "ui/dialogwaiting.h"
+
+QString QETProject::integration_category_name = "import";
+
+/**
+	Constructeur par defaut - cree un schema contenant une collection
+	d'elements vide et un schema vide.
+	@param diagrams Nombre de nouveaux schemas a ajouter a ce nouveau projet
+	@param parent QObject parent
+*/
+QETProject::QETProject(int diagrams, QObject *parent) :
+	QObject(parent),
+	collection_(0),
+	project_qet_version_(-1),
+	modified_(false),
+	read_only_(false),
+	titleblocks_(this)
+{
+	// 0 a n schema(s) vide(s)
+	int diagrams_count = qMax(0, diagrams);
+	for (int i = 0 ; i < diagrams_count ; ++ i) {
+		addNewDiagram();
+	}
+	
+	// une collection d'elements vide
+	collection_ = new XmlElementsCollection();
+	collection_ -> setProtocol("embed");
+	collection_ -> setProject(this);
+	connect(collection_, SIGNAL(written()), this, SLOT(componentWritten()));
+	
+	// une categorie dediee aux elements integres automatiquement
+	ensureIntegrationCategoryExists();
+	setupTitleBlockTemplatesCollection();
+}
+
+/**
+	Construit un projet a partir du chemin d'un fichier.
+	@param path Chemin du fichier
+	@param parent QObject parent
+*/
+QETProject::QETProject(const QString &path, QObject *parent) :
+	QObject(parent),
+	collection_(0),
+	project_qet_version_(-1),
+	modified_(false),
+	read_only_(false),
+	titleblocks_(this)
+{
+	// ouvre le fichier
+	QFile project_file(path);
+	if (!project_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+		state_ = FileOpenFailed;
+		return;
+	}
+	setFilePath(path);
+	
+	// en extrait le contenu XML
+	bool xml_parsing = document_root_.setContent(&project_file);
+	if (!xml_parsing) {
+		state_ = XmlParsingFailed;
+		return;
+	}
+	
+	// et construit le projet
+	readProjectXml();
+	
+	setupTitleBlockTemplatesCollection();
+	
+	// passe le projet en lecture seule si le fichier l'est
+	QFileInfo project_file_info(path);
+	if (!project_file_info.isWritable()) {
+		setReadOnly(true);
+	}
+}
+
+/**
+	Construit un projet a partir d'un element XML representant le projet.
+	L'element XML fourni est copie et conserve dans la classe.
+*/
+QETProject::QETProject(const QDomElement &xml_element, QObject *parent) :
+	QObject(parent),
+	collection_(0),
+	project_qet_version_(-1),
+	modified_(false),
+	read_only_(false),
+	titleblocks_(this)
+{
+	// copie le contenu XML
+	document_root_.appendChild(document_root_.importNode(xml_element, true));
+	
+	// et construit le projet
+	readProjectXml();
+	
+	setupTitleBlockTemplatesCollection();
+}
+
+/**
+	Destructeur
+*/
+QETProject::~QETProject() {
+	// supprime les schemas
+	// qDebug() << "Suppression du projet" << ((void *)this);
+	
+	// supprime la collection
+	// qDebug() << "Suppression de la collection du projet" << ((void *)this);
+	if (collection_) {
+		delete collection_;
+	}
+	// qDebug() << "Collection du projet" << ((void *)this) << "supprimee";
+	
+	// qDebug() << diagrams_;
+	foreach (Diagram *diagram, diagrams_) {
+		diagrams_.removeAll(diagram);
+		delete diagram;
+	}
+	// qDebug() << diagrams_;
+}
+
+/**
+	Cette methode peut etre utilisee pour tester la bonne ouverture d'un projet
+	@return l'etat du projet
+	@see ProjectState
+*/
+QETProject::ProjectState QETProject::state() const {
+	return(state_);
+}
+
+/**
+	@return la liste des schemas de ce projet
+*/
+QList<Diagram *> QETProject::diagrams() const {
+	return(diagrams_);
+}
+
+/**
+	@param diagram Pointer to a Diagram object
+	@return the folio number of the given diagram object within the project,
+	or -1 if it is not part of this project.
+	Note: this returns 0 for the first diagram, not 1
+*/
+int QETProject::folioIndex(const Diagram *diagram) const {
+	// QList::indexOf returns -1 if no item matched.
+	return(diagrams_.indexOf(const_cast<Diagram *>(diagram)));
+}
+
+/**
+	@return la collection embarquee de ce projet
+*/
+ElementsCollection *QETProject::embeddedCollection() const {
+	return(collection_);
+}
+
+/**
+	@return the title block templates collection enbeedded within this project
+*/
+TitleBlockTemplatesProjectCollection *QETProject::embeddedTitleBlockTemplatesCollection() {
+	return(&titleblocks_);
+}
+
+/**
+	@return le chemin du fichier dans lequel ce projet est enregistre
+*/
+QString QETProject::filePath() {
+	return(file_path_);
+}
+
+/**
+	Change le chemin du fichier dans lequel ce projet est enregistre
+	@param filepath Nouveau chemin de fichier
+*/
+void QETProject::setFilePath(const QString &filepath) {
+	file_path_ = filepath;
+	
+	// le chemin a change : on reevalue la necessite du mode lecture seule
+	QFileInfo file_path_info(file_path_);
+	if (file_path_info.isWritable()) {
+		setReadOnly(false);
+	}
+	
+	emit(projectFilePathChanged(this, file_path_));
+	emit(projectInformationsChanged(this));
+}
+
+/**
+	@return le dossier contenant le fichier projet si celui-ci a ete
+	enregistre ; dans le cas contraire, cette methode retourne l'emplacement
+	du bureau de l'utilisateur.
+*/
+QString QETProject::currentDir() const {
+	QString current_directory;
+	if (file_path_.isEmpty()) {
+		current_directory = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
+	} else {
+		current_directory = QFileInfo(file_path_).absoluteDir().absolutePath();
+	}
+	return(current_directory);
+}
+
+/**
+	
+	@return une chaine de caractere du type "Projet titre du projet".
+	Si le projet n'a pas de titre, le nom du fichier est utilise.
+	Si le projet n'est pas associe a un fichier, cette methode retourne "Projet
+	sans titre".
+	De plus, si le projet est en lecture seule, le tag "[lecture seule]" est
+	ajoute.
+*/
+QString QETProject::pathNameTitle() const {
+	QString final_title;
+	
+	if (!project_title_.isEmpty()) {
+		final_title = QString(
+			tr(
+				"Projet \253\240%1\240\273",
+				"displayed title for a ProjectView - %1 is the project title"
+			)
+		).arg(project_title_);
+	} else if (!file_path_.isEmpty()) {
+		final_title = QString(
+			tr(
+				"Projet %1",
+				"displayed title for a title-less project - %1 is the file name"
+			)
+		).arg(QFileInfo(file_path_).completeBaseName());
+	} else {
+		final_title = QString(
+			tr(
+				"Projet sans titre",
+				"displayed title for a project-less, file-less project"
+			)
+		);
+	}
+	
+	if (isReadOnly()) {
+		final_title = QString(
+			tr(
+				"%1 [lecture seule]",
+				"displayed title for a read-only project - %1 is a displayable title"
+			)
+		).arg(final_title);
+	}
+	if (modified_) {
+		final_title = QString(
+			tr(
+				"%1 [modifi\351]",
+				"displayed title for a modified project - %1 is a displayable title"
+			)
+		).arg(final_title);
+	}
+	
+	return(final_title);
+}
+
+/**
+	@return le titre du projet
+*/
+QString QETProject::title() const {
+	return(project_title_);
+}
+
+/**
+	@return la version de QElectroTech declaree dans le fichier projet lorsque
+	celui-ci a ete ouvert ; si ce projet n'a jamais ete enregistre / ouvert
+	depuis un fichier, cette methode retourne -1.
+*/
+qreal QETProject::declaredQElectroTechVersion() {
+	return(project_qet_version_);
+}
+
+/**
+	@param title le nouveau titre du projet
+*/
+void QETProject::setTitle(const QString &title) {
+	// ne fait rien si le projet est en lecture seule
+	if (isReadOnly()) return;
+	
+	// ne fait rien si le titre du projet n'est pas change par l'appel de cette methode
+	if (project_title_ == title) return;
+	
+	project_title_ = title;
+	emit(projectTitleChanged(this, project_title_));
+	emit(projectInformationsChanged(this));
+	updateDiagramsFolioData();
+}
+
+/**
+	@return the list of the titleblock templates embedded within this project 
+*/
+QList<QString> QETProject::embeddedTitleBlockTemplates() {
+	return(titleblocks_.templates());
+}
+
+/**
+	@param template_name Name of the requested template
+	@return the requested template, or 0 if there is no valid template of this
+	name within the project
+*/
+const TitleBlockTemplate *QETProject::getTemplateByName(const QString &template_name) {
+	return(titleblocks_.getTemplate(template_name));
+}
+
+/**
+	@param template_name Name of the requested template
+	@return the XML description of the requested template, or a null QDomElement
+	if the project does not have such an titleblock template
+*/
+QDomElement QETProject::getTemplateXmlDescriptionByName(const QString &template_name) {
+	return(titleblocks_.getTemplateXmlDescription(template_name));
+}
+
+/**
+	This methods allows adding or modifying a template embedded within the
+	project.
+	@param template_name Name / Identifier of the template - will be used to
+	determine whether the given description will be added or will replace an
+	existing one.
+	@param xml_elmt An \<titleblocktemplate\> XML element describing the
+	template. Its "name" attribute must equal to template_name.
+	@return false if a problem occured, true otherwise
+*/
+bool QETProject::setTemplateXmlDescription(const QString &template_name, const QDomElement &xml_elmt) {
+	return(titleblocks_.setTemplateXmlDescription(template_name, xml_elmt));
+}
+
+/**
+	This methods allows removing a template embedded within the project.
+	@param template_name Name of the template to be removed
+*/
+void QETProject::removeTemplateByName(const QString &template_name) {
+	return(titleblocks_.removeTemplate(template_name));
+}
+
+/**
+	@return les dimensions par defaut utilisees lors de la creation d'un
+	nouveau schema dans ce projet.
+*/
+BorderProperties QETProject::defaultBorderProperties() const {
+	return(default_border_properties_);
+}
+
+/**
+	Permet de specifier les dimensions par defaut utilisees lors de la creation
+	d'un nouveau schema dans ce projet.
+	@param border dimensions d'un schema
+*/
+void QETProject::setDefaultBorderProperties(const BorderProperties &border) {
+	default_border_properties_ = border;
+}
+
+/**
+	@return le cartouche par defaut utilise lors de la creation d'un
+	nouveau schema dans ce projet.
+*/
+TitleBlockProperties QETProject::defaultTitleBlockProperties() const {
+	return(default_titleblock_properties_);
+}
+
+/**
+	Permet de specifier le cartouche par defaut utilise lors de la creation
+	d'un nouveau schema dans ce projet.
+	@param titleblock Cartouche d'un schema
+*/
+void QETProject::setDefaultTitleBlockProperties(const TitleBlockProperties &titleblock) {
+	default_titleblock_properties_ = titleblock;
+}
+
+/**
+	@return le type de conducteur par defaut utilise lors de la creation d'un
+	nouveau schema dans ce projet.
+*/
+ConductorProperties QETProject::defaultConductorProperties() const {
+	return(default_conductor_properties_);
+}
+
+/**
+	Permet de specifier e type de conducteur par defaut utilise lors de la
+	creation d'un nouveau schema dans ce projet.
+*/
+void QETProject::setDefaultConductorProperties(const ConductorProperties &conductor) {
+	default_conductor_properties_ = conductor;
+}
+
+/**
+	@return un document XML representant le projet 
+*/
+QDomDocument QETProject::toXml() {
+	// racine du projet
+	QDomDocument xml_doc;
+	QDomElement project_root = xml_doc.createElement("project");
+	project_root.setAttribute("version", QET::version);
+	project_root.setAttribute("title", project_title_);
+	xml_doc.appendChild(project_root);
+	
+	// titleblock templates, if any
+	if (titleblocks_.templates().count()) {
+		QDomElement titleblocktemplates_elmt = xml_doc.createElement("titleblocktemplates");
+		foreach (QString template_name, titleblocks_.templates()) {
+			QDomElement e = titleblocks_.getTemplateXmlDescription(template_name);
+			titleblocktemplates_elmt.appendChild(xml_doc.importNode(e, true));
+		}
+		project_root.appendChild(titleblocktemplates_elmt);
+	}
+	
+	// project-wide properties
+	QDomElement project_properties = xml_doc.createElement("properties");
+	writeProjectPropertiesXml(project_properties);
+	project_root.appendChild(project_properties);
+	
+	// proprietes pour les nouveaux schemas
+	QDomElement new_diagrams_properties = xml_doc.createElement("newdiagrams");
+	writeDefaultPropertiesXml(new_diagrams_properties);
+	project_root.appendChild(new_diagrams_properties);
+	
+	// schemas
+	
+	// qDebug() << "Export XML de" << diagrams_.count() << "schemas";
+	int order_num = 1;
+	foreach(Diagram *diagram, diagrams_) {
+		qDebug() << qPrintable(QString("QETProject::toXml() : exporting diagram \"%1\" [%2]").arg(diagram -> title()).arg(QET::pointerString(diagram)));
+		QDomNode appended_diagram = project_root.appendChild(diagram -> writeXml(xml_doc));
+		appended_diagram.toElement().setAttribute("order", order_num ++);
+	}
+	
+	// collection
+	project_root.appendChild(collection_ -> writeXml(xml_doc));
+	
+	return(xml_doc);
+}
+
+/**
+	Ferme le projet
+*/
+bool QETProject::close() {
+	return(true);
+}
+
+/**
+	Enregistre le projet vers un fichier.
+	@see filePath()
+	@see setFilePath()
+	@return true si l'enregistrement a reussi, false sinon
+*/
+QETResult QETProject::write() {
+	// this operation requires a filepath
+	if (file_path_.isEmpty()) {
+		return(QString("unable to save project to file: no filepath was specified"));
+	}
+	
+	// if the project was opened read-only and the file is still non-writable, do not save the project
+	if (isReadOnly() && !QFileInfo(file_path_).isWritable()) {
+		return(QString("the file %1 was opened read-only and thus will not be written").arg(file_path_));
+	}
+	
+	// realise l'export en XML du projet dans le document XML interne
+	document_root_.clear();
+	document_root_.appendChild(document_root_.importNode(toXml().documentElement(), true));
+	
+	QString error_message;
+	bool writing = QET::writeXmlFile(document_root_, file_path_, &error_message);
+	if (!writing) {
+		return(error_message);
+	}
+	
+	setModified(false);
+	return(QETResult());
+}
+
+/**
+	@return true si le projet est en mode readonly, false sinon
+*/
+bool QETProject::isReadOnly() const {
+	return(read_only_ && read_only_file_path_ == file_path_);
+}
+
+/**
+	@param read_only true pour passer le projet (schemas et collection)
+	en mode Read Only, false sinon.
+*/
+void QETProject::setReadOnly(bool read_only) {
+	if (read_only_ != read_only) {
+		// memorise le fichier pour lequel ce projet est en lecture seule
+		read_only_file_path_ = file_path_;
+		
+		// applique le nouveau mode aux schemas
+		foreach(Diagram *diagram, diagrams()) {
+			diagram -> setReadOnly(read_only);
+		}
+		
+		read_only_ = read_only;
+		emit(readOnlyChanged(this, read_only));
+	}
+}
+
+/**
+	@return true si le projet peut etre considere comme vide, c'est-a-dire :
+	  - soit avec une collection embarquee vide
+	  - soit avec uniquement des schemas consideres comme vides
+	  - soit avec un titre de projet
+*/
+bool QETProject::isEmpty() const {
+	// si le projet a un titre, on considere qu'il n'est pas vide
+	if (!project_title_.isEmpty()) return(false);
+	
+	// si la collection du projet n'est pas vide, alors le projet n'est pas vide
+	if (!collection_ -> isEmpty()) return(false);
+	
+	// compte le nombre de schemas non vides
+	int pertinent_diagrams = 0;
+	foreach(Diagram *diagram, diagrams_) {
+		if (!diagram -> isEmpty()) ++ pertinent_diagrams;
+	}
+	
+	return(pertinent_diagrams > 0);
+}
+
+/**
+	Cree une categorie dediee aux elements integres automatiquement dans le
+	projet si celle-ci n'existe pas deja.
+	@return true si tout s'est bien passe, false sinon
+*/
+bool QETProject::ensureIntegrationCategoryExists() {
+	ElementsCategory *root_cat = rootCategory();
+	if (!root_cat) return(false);
+	
+	if (root_cat -> category(integration_category_name)) return(true);
+	
+	ElementsCategory *integration_category = root_cat -> createCategory(integration_category_name);
+	if (!integration_category) return(false);
+	
+	integration_category -> setNames(namesListForIntegrationCategory());
+	return(true);
+}
+
+/**
+	@return la categorie dediee aux elements integres automatiquement dans le
+	projet ou 0 si celle-ci n'a pu etre creee.
+	@see ensureIntegrationCategoryExists()
+*/
+ElementsCategory *QETProject::integrationCategory() const {
+	ElementsCategory *root_cat = rootCategory();
+	if (!root_cat) return(0);
+	
+	return(root_cat -> category(integration_category_name));
+}
+
+/**
+	Integre un element dans le projet.
+	Cette methode delegue son travail a la methode
+	integrateElement(const QString &, MoveElementsHandler *, QString &)
+	en lui passant un MoveElementsHandler approprie.
+	@param elmt_location Emplacement de l'element a integrer
+	@param error_msg Reference vers une chaine de caractere qui contiendra
+	eventuellement un message d'erreur
+	@return L'emplacement de l'element apres integration, ou une chaine vide si
+	l'integration a echoue.
+*/
+QString QETProject::integrateElement(const QString &elmt_location, QString &error_msg) {
+	// handler dedie a l'integration d'element
+	IntegrationMoveElementsHandler *integ_handler = new IntegrationMoveElementsHandler(0);
+	QString integ_path = integrateElement(elmt_location, integ_handler, error_msg);
+	delete integ_handler;
+	
+	return(integ_path);
+}
+
+/**
+	Integre un element dans le projet.
+	Cette methode prend en parametre l'emplacement d'un element a integrer.
+	Chaque categorie mentionnee dans le chemin de cet element sera copiee de
+	maniere non recursive sous la categorie dediee a l'integration si elle
+	n'existe pas deja.
+	L'element sera ensuite copiee dans cette copie de la hierarchie d'origine.
+	En cas de probleme, error_message sera modifiee de facon a contenir un
+	message decrivant l'erreur rencontree.
+	@param elmt_path Emplacement de l'element a integrer
+	@param handler Gestionnaire a utiliser pour gerer les copies d'elements et categories
+	@param error_message Reference vers une chaine de caractere qui contiendra
+	eventuellement un message d'erreur
+	@return L'emplacement de l'element apres integration, ou une chaine vide si
+	l'integration a echoue.
+*/
+QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandler *handler, QString &error_message) {
+	// on s'assure que le projet a une categorie dediee aux elements importes automatiquement
+	if (!ensureIntegrationCategoryExists()) {
+		error_message = tr("Impossible de cr\351er la cat\351gorie pour l'int\351gration des \351l\351ments");
+		return(QString());
+	}
+	
+	// accede a la categorie d'integration
+	ElementsCategory *integ_cat = integrationCategory();
+	
+	// accede a l'element a integrer
+	ElementsCollectionItem *integ_item = QETApp::collectionItem(ElementsLocation::locationFromString(elmt_path));
+	ElementDefinition *integ_elmt = integ_item ? integ_item -> toElement() : 0;
+	if (!integ_item || !integ_elmt) {
+		error_message = tr("Impossible d'acc\351der \340 l'\351l\351ment \340 int\351grer");
+		return(QString());
+	}
+	
+	// recopie l'arborescence de l'element de facon non recursive
+	QList<ElementsCategory *> integ_par_cat = integ_elmt -> parentCategories();
+	ElementsCategory *target_cat = integ_cat;
+	foreach(ElementsCategory *par_cat, integ_par_cat) {
+		if (par_cat -> isRootCategory()) continue;
+		
+		if (ElementsCategory *existing_cat = target_cat -> category(par_cat -> pathName())) {
+			// la categorie cible existe deja : on continue la progression
+			target_cat = existing_cat;
+		} else {
+			// la categorie cible n'existe pas : on la cree par recopie
+			ElementsCollectionItem *result_cat = par_cat -> copy(target_cat, handler, false);
+			if (!result_cat || !result_cat -> isCategory()) {
+				error_message = QString(tr("Un probl\350me s'est produit pendant la copie de la cat\351gorie %1")).arg(par_cat -> location().toString());
+				return(QString());
+			}
+			target_cat = result_cat -> toCategory();
+		}
+	}
+	
+	// recopie l'element
+	ElementsLocation result;
+	if (ElementDefinition *existing_elmt = target_cat -> element(integ_item -> pathName())) {
+		
+		// l'element existe deja - on demande au handler ce que l'on doit faire
+		QET::Action action = handler -> elementAlreadyExists(integ_elmt, existing_elmt);
+		
+		if (action == QET::Ignore) {
+			// il faut conserver et utiliser l'element deja integre
+			result = existing_elmt -> location();
+		} else if (action == QET::Erase) {
+			// il faut ecraser l'element deja integre
+			BasicMoveElementsHandler *erase_handler = new BasicMoveElementsHandler();
+			result = copyElementWithHandler(integ_elmt, target_cat, erase_handler, error_message);
+			delete erase_handler;
+		} else if (action == QET::Rename) {
+			// il faut faire cohabiter les deux elements en renommant le nouveau 
+			QString integ_element_name = handler -> nameForRenamingOperation();
+			BasicMoveElementsHandler *rename_handler = new BasicMoveElementsHandler();
+			rename_handler -> setActionIfItemAlreadyExists(QET::Rename);
+			rename_handler -> setNameForRenamingOperation(integ_element_name);
+			result = copyElementWithHandler(integ_elmt, target_cat, rename_handler, error_message);
+			delete rename_handler;
+		} else {
+			// il faut annuler la pose de l'element
+			result = ElementsLocation();
+		}
+	} else {
+		// integre l'element normalement
+		result = copyElementWithHandler(integ_elmt, target_cat, handler, error_message);
+	}
+	
+	if (!result.isNull()) emit(elementIntegrated(this, result));
+	return(result.toString());
+}
+
+/**
+	Integrate a title block template into this project.
+	@param src_tbt The location of the title block template to be integrated into this project
+	@param handler 
+	@return the name of the template after integration, or an empty QString if a problem occured.
+*/
+QString QETProject::integrateTitleBlockTemplate(const TitleBlockTemplateLocation &src_tbt, MoveTitleBlockTemplatesHandler *handler) {
+	TitleBlockTemplateLocation dst_tbt(src_tbt.name(), &titleblocks_);
+	
+	// check whether a TBT having the same name already exists within this project
+	QString target_name = dst_tbt.name();
+	while (titleblocks_.templates().contains(target_name)) {
+		QET::Action action = handler -> templateAlreadyExists(src_tbt, dst_tbt);
+		if (action == QET::Retry) {
+			continue;
+		} else if (action == QET::Erase) {
+			break;
+		} else if (action == QET::Abort || action == QET::Ignore) {
+			return(QString());
+		} else if (action == QET::Rename) {
+			target_name = handler -> nameForRenamingOperation();
+		} else if (action == QET::Managed) {
+			return(target_name);
+		}
+	}
+	
+	bool integration = setTemplateXmlDescription(
+		target_name,
+		src_tbt.getTemplateXmlDescription()
+	);
+	if (!integration) {
+		handler -> errorWithATemplate(src_tbt, tr("Une erreur s'est produite durant l'int\351gration du mod\350le.", "error message"));
+		target_name = QString();
+	}
+	return(target_name);
+}
+
+/**
+	Permet de savoir si un element est utilise dans un projet
+	@param location Emplacement d'un element
+	@return true si l'element location est utilise sur au moins un des schemas
+	de ce projet, false sinon
+*/
+bool QETProject::usesElement(const ElementsLocation &location) {
+	foreach(Diagram *diagram, diagrams()) {
+		if (diagram -> usesElement(location)) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	@param location Location of a title block template
+	@return true if the provided template is used by at least one diagram
+	within this project, false otherwise
+*/
+bool QETProject::usesTitleBlockTemplate(const TitleBlockTemplateLocation &location) {
+	// a diagram can only use a title block template embedded wihtin its parent project
+	if (location.parentProject() != this) return(false);
+	
+	foreach (Diagram *diagram, diagrams()) {
+		if (diagram -> usesTitleBlockTemplate(location.name())) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	Delete all title block templates not used in the project
+*/
+void QETProject::cleanUnusedTitleBlocKTemplates() {
+	titleblocks_.deleteUnusedTitleBlocKTemplates();
+}
+
+/**
+	Supprime tous les elements inutilises dans le projet
+	@param handler Gestionnaire d'erreur
+*/
+void QETProject::cleanUnusedElements(MoveElementsHandler *handler) {
+	ElementsCategory *root_cat = rootCategory();
+	if (!root_cat) return;
+	
+	root_cat -> deleteUnusedElements(handler);
+}
+
+/**
+	Supprime tous les categories vides (= ne contenant aucun element ou que des
+	categories vides) dans le projet
+	@param handler Gestionnaire d'erreur
+*/
+void QETProject::cleanEmptyCategories(MoveElementsHandler *handler) {
+	ElementsCategory *root_cat = rootCategory();
+	if (!root_cat) return;
+	
+	root_cat -> deleteEmptyCategories(handler);
+}
+
+/**
+	Gere la reecriture du projet
+*/
+void QETProject::componentWritten() {
+	// reecrit tout le projet
+	write();
+}
+
+/**
+	Ajoute un nouveau schema au projet et emet le signal diagramAdded
+*/
+Diagram *QETProject::addNewDiagram() {
+	// ne fait rien si le projet est en lecture seule
+	if (isReadOnly()) return(0);
+	
+	// cree un nouveau schema
+	Diagram *diagram = new Diagram();
+	
+	// lui transmet les parametres par defaut
+	diagram -> border_and_titleblock.importBorder(defaultBorderProperties());
+	diagram -> border_and_titleblock.importTitleBlock(defaultTitleBlockProperties());
+	diagram -> defaultConductorProperties = defaultConductorProperties();
+	
+	addDiagram(diagram);
+	emit(diagramAdded(this, diagram));
+	return(diagram);
+}
+
+/**
+	Enleve un schema du projet et emet le signal diagramRemoved
+	@param diagram le schema a enlever
+*/
+void QETProject::removeDiagram(Diagram *diagram) {
+	// ne fait rien si le projet est en lecture seule
+	if (isReadOnly()) return;
+	
+	if (!diagram || !diagrams_.contains(diagram)) return;
+	
+	if (diagrams_.removeAll(diagram)) {
+		emit(diagramRemoved(this, diagram));
+		delete diagram;
+	}
+	
+	updateDiagramsFolioData();
+}
+
+/**
+	Gere le fait que l'ordre des schemas ait change
+	@param old_index ancien indice du schema deplace
+	@param new_index nouvel indice du schema deplace
+	Si l'ancien ou le nouvel index est negatif ou superieur au nombre de schemas
+	dans le projet, cette methode ne fait rien.
+	Les index vont de 0 a "nombre de schemas - 1"
+*/
+void QETProject::diagramOrderChanged(int old_index, int new_index) {
+	if (old_index < 0 || new_index < 0) return;
+	
+	int diagram_max_index = diagrams_.size() - 1;
+	if (old_index > diagram_max_index || new_index > diagram_max_index) return;
+	
+	diagrams_.move(old_index, new_index);
+	updateDiagramsFolioData();
+	setModified(true);
+	emit(projectDiagramsOrderChanged(this, old_index, new_index));
+}
+
+/**
+	Mark this project as modified and emit the projectModified() signal.
+*/
+void QETProject::setModified(bool modified) {
+	if (modified_ != modified) {
+		modified_ = modified;
+		emit(projectModified(this, modified_));
+		emit(projectInformationsChanged(this));
+	}
+}
+
+/**
+	Set up signals/slots connections related to the title block templates
+	collection.
+*/
+void QETProject::setupTitleBlockTemplatesCollection() {
+	connect(
+		&titleblocks_,
+		SIGNAL(changed(TitleBlockTemplatesCollection *, const QString &)),
+		this,
+		SLOT(updateDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *, const QString &))
+	);
+	connect(
+		&titleblocks_,
+		SIGNAL(aboutToRemove(TitleBlockTemplatesCollection *, const QString &)),
+		this,
+		SLOT(removeDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *, const QString &))
+	);
+}
+
+/**
+	@return un pointeur vers la categorie racine de la collection embarquee, ou
+	0 si celle-ci n'est pas accessible.
+*/
+ElementsCategory *QETProject::rootCategory() const {
+	if (!collection_) return(0);
+	
+	ElementsCategory *root_cat = collection_ -> rootCategory();
+	return(root_cat);
+}
+
+/**
+	(Re)lit le projet depuis sa description XML
+*/
+void QETProject::readProjectXml() {
+	QDomElement root_elmt = document_root_.documentElement();
+	state_ = ProjectParsingRunning;
+	
+	// la racine du document XML est sensee etre un element "project"
+	if (root_elmt.tagName() == "project") {
+		// mode d'ouverture normal
+		if (root_elmt.hasAttribute("version")) {
+			bool conv_ok;
+			project_qet_version_ = root_elmt.attribute("version").toDouble(&conv_ok);
+			if (conv_ok && QET::version.toDouble() < project_qet_version_) {
+				
+				int ret = QET::MessageBox::warning(
+					0,
+					tr("Avertissement", "message box title"),
+					tr(
+						"Ce document semble avoir \351t\351 enregistr\351 avec "
+						"une version ult\351rieure de QElectroTech. Il est "
+						"possible que l'ouverture de tout ou partie de ce "
+						"document \351choue.\n"
+						"Que d\351sirez vous faire ?",
+						"message box content"
+					),
+					QMessageBox::Open | QMessageBox::Cancel
+				);
+				
+				if (ret == QMessageBox::Cancel) {
+					state_ = FileOpenDiscard;
+					return;
+				}
+				
+			}
+		}
+		
+		setTitle(root_elmt.attribute("title"));
+	} else {
+		state_ = ProjectParsingFailed;
+	}
+	
+	// load the project-wide properties
+	readProjectPropertiesXml();
+	
+	// charge les proprietes par defaut pour les nouveaux schemas
+	readDefaultPropertiesXml();
+	
+	// load the embedded titleblock templates
+	readEmbeddedTemplatesXml();
+	
+	// charge la collection embarquee
+	readElementsCollectionXml();
+	
+	// charge les schemas
+	readDiagramsXml();
+	
+	state_ = Ok;
+}
+
+/**
+	Charge les schemas depuis la description XML du projet.
+	A noter qu'un projet peut parfaitement ne pas avoir de schema.
+*/
+void QETProject::readDiagramsXml() {
+	// map destinee a accueillir les schemas
+	QMultiMap<int, Diagram *> loaded_diagrams;
+	
+	//show DialogWaiting
+	DialogWaiting* dlgWaiting = new DialogWaiting();
+	dlgWaiting -> setModal(true);
+	dlgWaiting -> show();
+	dlgWaiting -> setTitle( tr("<b>Ouverture du projet en cours...</b>") );
+	
+	// recherche les schemas dans le projet
+	QDomNodeList diagram_nodes = document_root_.elementsByTagName("diagram");
+	dlgWaiting->setProgressBarRange(0, diagram_nodes.length());
+	for (uint i = 0 ; i < diagram_nodes.length() ; ++ i) {
+		dlgWaiting->setProgressBar(i+1);
+		if (diagram_nodes.at(i).isElement()) {
+			QDomElement diagram_xml_element = diagram_nodes.at(i).toElement();
+			Diagram *diagram = new Diagram();
+			diagram -> setProject(this);
+			bool diagram_loading = diagram -> initFromXml(diagram_xml_element);
+			if (diagram_loading) {
+				dlgWaiting->setDetail( diagram->title() );
+				// recupere l'attribut order du schema
+				int diagram_order = -1;
+				if (!QET::attributeIsAnInteger(diagram_xml_element, "order", &diagram_order)) diagram_order = 500000;
+				loaded_diagrams.insert(diagram_order, diagram);
+			} else {
+				delete diagram;
+			}
+		}
+	}
+	
+	// ajoute les schemas dans l'ordre indique par les attributs order
+	foreach(Diagram *diagram, loaded_diagrams.values()) {
+		addDiagram(diagram);
+	}
+	//delete dialog object
+	delete dlgWaiting;
+}
+
+/**
+	Loads the embedded template from the XML description of the project
+*/
+void QETProject::readEmbeddedTemplatesXml() {
+	titleblocks_.fromXml(document_root_.documentElement());
+}
+
+/**
+	Charge les schemas depuis la description XML du projet
+*/
+void QETProject::readElementsCollectionXml() {
+	// recupere la collection d'elements integreee au projet
+	QDomNodeList collection_roots = document_root_.elementsByTagName("collection");
+	QDomElement collection_root;
+	if (!collection_roots.isEmpty()) {
+		// seule la premiere collection trouvee est prise en compte
+		collection_root = collection_roots.at(0).toElement();
+	}
+	
+	if (collection_root.isNull()) {
+		// s'il n'y en a pas, cree une collection vide
+		collection_ = new XmlElementsCollection();
+	} else {
+		// sinon lit cette collection
+		collection_ = new XmlElementsCollection(collection_root);
+	}
+	collection_ -> setProtocol("embed");
+	collection_ -> setProject(this);
+	connect(collection_, SIGNAL(written()), this, SLOT(componentWritten()));
+}
+
+/**
+	Load project properties from the XML description of the project
+*/
+void QETProject::readProjectPropertiesXml() {
+	foreach (QDomElement e, QET::findInDomElement(document_root_.documentElement(), "properties")) {
+		project_properties_.fromXml(e);
+	}
+}
+
+/**
+	Export project properties under the \a xml_element XML element.
+*/
+void QETProject::writeProjectPropertiesXml(QDomElement &xml_element) {
+	project_properties_.toXml(xml_element);
+}
+
+/**
+	Charge les proprietes par defaut des nouveaux schemas depuis la description
+	XML du projet :
+	  * dimensions
+	  * contenu du cartouche
+	  * conducteurs par defaut
+*/
+void QETProject::readDefaultPropertiesXml() {
+	// repere l'element XML decrivant les proprietes des nouveaux schemas
+	QDomNodeList newdiagrams_nodes = document_root_.elementsByTagName("newdiagrams");
+	if (newdiagrams_nodes.isEmpty()) return;
+	
+	QDomElement newdiagrams_elmt = newdiagrams_nodes.at(0).toElement();
+	
+	// par defaut, les valeurs sont celles de la configuration QElectroTech
+	default_border_properties_    = QETDiagramEditor::defaultBorderProperties();
+	default_titleblock_properties_     = QETDiagramEditor::defaultTitleBlockProperties();
+	default_conductor_properties_ = QETDiagramEditor::defaultConductorProperties();
+	
+	// lecture des valeurs indiquees dans le projet
+	QDomElement border_elmt, titleblock_elmt, conductors_elmt;
+	
+	// recherche des elements XML concernant les dimensions, le cartouche et les conducteurs
+	for (QDomNode child = newdiagrams_elmt.firstChild() ; !child.isNull() ; child = child.nextSibling()) {
+		QDomElement child_elmt = child.toElement();
+		if (child_elmt.isNull()) continue;
+		if (child_elmt.tagName() == "border") {
+			border_elmt = child_elmt;
+		} else if (child_elmt.tagName() == "inset") {
+			titleblock_elmt = child_elmt;
+		} else if (child_elmt.tagName() == "conductors") {
+			conductors_elmt = child_elmt;
+		}
+	}
+	
+	// dimensions, cartouche, et conducteurs
+	if (!border_elmt.isNull())     default_border_properties_.fromXml(border_elmt);
+	if (!titleblock_elmt.isNull())      default_titleblock_properties_.fromXml(titleblock_elmt);
+	if (!conductors_elmt.isNull()) default_conductor_properties_.fromXml(conductors_elmt);
+}
+
+/**
+	Exporte les proprietes par defaut des nouveaux schemas dans l'element XML :
+	  * dimensions
+	  * contenu du cartouche
+	  * conducteurs par defaut
+	@param xml_element Element XML sous lequel seront exportes les proprietes
+	par defaut des nouveaux schemas 
+*/
+void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) {
+	QDomDocument xml_document = xml_element.ownerDocument();
+	
+	// exporte les dimensions
+	QDomElement border_elmt = xml_document.createElement("border");
+	default_border_properties_.toXml(border_elmt);
+	xml_element.appendChild(border_elmt);
+	
+	// exporte le contenu du cartouche
+	QDomElement titleblock_elmt = xml_document.createElement("inset");
+	default_titleblock_properties_.toXml(titleblock_elmt);
+	xml_element.appendChild(titleblock_elmt);
+	
+	// exporte le type de conducteur par defaut
+	QDomElement conductor_elmt = xml_document.createElement("conductors");
+	default_conductor_properties_.toXml(conductor_elmt);
+	xml_element.appendChild(conductor_elmt);
+}
+
+/**
+	Cette methode ajoute un schema donne au projet
+	@param diagram Schema a ajouter
+*/
+void QETProject::addDiagram(Diagram *diagram) {
+	if (!diagram) return;
+	
+	// s'assure que le schema connaisse son projet parent
+	diagram -> setProject(this);
+	
+	// si le schema est ecrit, alors il faut reecrire le fichier projet
+	connect(diagram, SIGNAL(written()), this, SLOT(componentWritten()));
+	connect(
+		&(diagram -> border_and_titleblock),
+		SIGNAL(needFolioData()),
+		this,
+		SLOT(updateDiagramsFolioData())
+	);
+	connect(
+		diagram, SIGNAL(usedTitleBlockTemplateChanged(const QString &)),
+		this, SLOT(usedTitleBlockTemplateChanged(const QString &))
+	);
+	
+	// ajoute le schema au projet
+	diagrams_ << diagram;
+	
+	updateDiagramsFolioData();
+}
+
+/**
+	@return La liste des noms a utiliser pour la categorie dediee aux elements
+	integres automatiquement dans le projet.
+*/
+NamesList QETProject::namesListForIntegrationCategory() {
+	NamesList names;
+	
+	const QChar russian_data[24] = { 0x0418, 0x043C, 0x043F, 0x043E, 0x0440, 0x0442, 0x0438, 0x0440, 0x043E, 0x0432, 0x0430, 0x043D, 0x043D, 0x044B, 0x0435, 0x0020, 0x044D, 0x043B, 0x0435, 0x043C, 0x0435, 0x043D, 0x0442, 0x044B };
+	const QChar greek_data[18] = { 0x0395, 0x03b9, 0x03c3, 0x03b7, 0x03b3, 0x03bc, 0x03ad, 0x03bd, 0x03b1, 0x0020, 0x03c3, 0x03c4, 0x03bf, 0x03b9, 0x03c7, 0x03b5, 0x03af, 0x03b1 };
+
+	names.addName("fr", "\311l\351ments import\351s");
+	names.addName("en", "Imported elements");
+	names.addName("es", "Elementos importados");
+	names.addName("ru", QString(russian_data, 24));
+	names.addName("cs", "Zaveden\351 prvky");
+	names.addName("pl", "Elementy importowane");
+	names.addName("it", "Elementi importati");
+	names.addName("el", QString(greek_data, 18));
+	
+	return(names);
+}
+
+/**
+	@return true if project options (title, project-wide properties, settings
+	for new diagrams, diagrams order...) were modified, false otherwise.
+*/
+bool QETProject::projectOptionsWereModified() {
+	// unlike similar methods, this method does not compare the content against
+	// expected values; instead, we just check whether we have been set as modified.
+	return(modified_);
+}
+
+/**
+	Cette methode sert a reperer un projet vide, c-a-d un projet identique a ce
+	que l'on obtient en faisant Fichier > Nouveau.
+	@return true si la collection d'elements embarquee a ete modifiee.
+	Concretement, cette methode retourne true si la collection embarquee
+	contient 0 element et 1 categorie vide qui s'avere etre la categorie dediee
+	aux elements integres automatiquement dans le projet.
+*/
+bool QETProject::embeddedCollectionWasModified() {
+	ElementsCategory *root_cat = rootCategory();
+	if (!root_cat) return(false);
+	
+	// la categorie racine doit comporter 0 element et 1 categorie
+	if (root_cat -> categories().count() != 1) return(true);
+	if (root_cat -> elements().count()   != 0) return(true);
+	
+	// la categorie d'integration doit exister
+	ElementsCategory *integ_cat = integrationCategory();
+	if (!integ_cat) return(true);
+	
+	// la categorie d'integration doit avoir les noms par defaut
+	if (integ_cat -> categoryNames() != namesListForIntegrationCategory()) {
+		return(true);
+	}
+	
+	// the integration category must be empty
+	if (integ_cat -> categories().count()) return(true);
+	if (integ_cat -> elements().count()) return(true);
+	
+	return(false);
+}
+
+/**
+	@return true if the embedded title block templates collection was modified,
+	false otherwise.
+*/
+bool QETProject::titleBlockTemplateCollectionWasModified() {
+	// we do not expect a new project to embed any title block template (this may
+	// change in the future though).
+	return(titleblocks_.templates().count());
+}
+
+/**
+	Cette methode sert a reperer un projet vide, c-a-d un projet identique a ce
+	que l'on obtient en faisant Fichier > Nouveau.
+	@return true si les schemas de ce projet ont ete modifies
+	Concretement, il doit y avoir exactement un schema, dont la pile
+	d'annulation est "clean".
+*/
+bool QETProject::diagramsWereModified() {
+	// il doit y avoir exactement un schema
+	if (diagrams_.count() != 1) return(true);
+	
+	// dont la pile d'annulation est "clean"
+	return(!(diagrams_[0] -> undoStack().isClean() && !diagrams_[0] -> wasWritten()));
+}
+
+/**
+	@return the project-wide properties made available to child diagrams.
+*/
+DiagramContext QETProject::projectProperties() {
+	return(project_properties_);
+}
+
+/**
+	Use \a context as project-wide properties made available to child diagrams.
+*/
+void QETProject::setProjectProperties(const DiagramContext &context) {
+	project_properties_ = context;
+	updateDiagramsFolioData();
+}
+
+/**
+	Cette methode sert a reperer un projet vide, c-a-d un projet identique a ce
+	que l'on obtient en faisant Fichier > Nouveau.
+	@return true si les schemas, la collection embarquee ou les proprietes de ce
+	projet ont ete modifies.
+	Concretement, le projet doit avoir un titre vide et ni ses schemas ni sa
+	collection embarquee ne doivent avoir ete modifies.
+	@see diagramsWereModified(), embeddedCollectionWasModified()
+*/
+bool QETProject::projectWasModified() {
+	if (projectOptionsWereModified()) return(true);
+	if (diagramsWereModified()) return(true);
+	if (embeddedCollectionWasModified()) return(true);
+	if (titleBlockTemplateCollectionWasModified()) return(true);
+	
+	return(false);
+}
+
+/**
+	Indique a chaque schema du projet quel est son numero de folio et combien de
+	folio le projet contient.
+*/
+void QETProject::updateDiagramsFolioData() {
+	int total_folio = diagrams_.count();
+	
+	DiagramContext project_wide_properties = project_properties_;
+	project_wide_properties.addValue("projecttitle", title());
+	
+	for (int i = 0 ; i < total_folio ; ++ i) {
+		diagrams_[i] -> border_and_titleblock.setFolioData(i + 1, total_folio, project_wide_properties);
+		diagrams_[i] -> update();
+	}
+}
+
+/**
+	Inform each diagram that the \a template_name title block changed.
+	@param collection Title block templates collection
+	@param template_name Name of the changed template
+*/
+void QETProject::updateDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *collection, const QString &template_name) {
+	Q_UNUSED(collection)
+	
+	foreach (Diagram *diagram, diagrams_) {
+		diagram -> titleBlockTemplateChanged(template_name);
+	}
+}
+
+/**
+	Inform each diagram that the \a template_name title block is about to be removed.
+	@param collection Title block templates collection
+	@param template_name Name of the removed template
+*/
+void QETProject::removeDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *collection, const QString &template_name) {
+	Q_UNUSED(collection)
+	
+	// warn diagrams that the given template is about to be removed
+	foreach (Diagram *diagram, diagrams_) {
+		diagram -> titleBlockTemplateRemoved(template_name);
+	}
+}
+
+/**
+	Handles the fact a digram changed the title block template it used
+	@param template_name Name of the template
+*/
+void QETProject::usedTitleBlockTemplateChanged(const QString &template_name) {
+	emit(diagramUsedTemplate(embeddedTitleBlockTemplatesCollection(), template_name));
+}
+
+/**
+	Copie l'element integ_elmt dans la categorie target_cat en utilisant le
+	gestionnaire handler ; en cas d'erreur, error_message est rempli.
+	@return l'emplacement de l'element cree
+*/
+ElementsLocation QETProject::copyElementWithHandler(
+	ElementDefinition *integ_elmt,
+	ElementsCategory *target_cat,
+	MoveElementsHandler *handler,
+	QString &error_message
+) {
+	ElementsCollectionItem *result_item = integ_elmt -> copy(target_cat, handler);
+	ElementDefinition *result_elmt = result_item ? result_item -> toElement() : 0;
+	if (!result_item || !result_elmt) {
+		error_message = QString(tr("Un probl\350me s'est produit pendant la copie de l'\351l\351ment %1")).arg(integ_elmt -> location().toString());
+		return(ElementsLocation());
+	}
+	return(result_elmt -> location());
+}

Added: trunk/sources/qetproject.h
===================================================================
--- trunk/sources/qetproject.h	                        (rev 0)
+++ trunk/sources/qetproject.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,198 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_PROJECT_H
+#define QET_PROJECT_H
+#include <QtCore>
+#include <QtXml>
+#include "nameslist.h"
+#include "elementslocation.h"
+#include "borderproperties.h"
+#include "conductorproperties.h"
+#include "titleblockproperties.h"
+#include "templatescollection.h"
+
+class Diagram;
+class ElementsCollection;
+class ElementsCategory;
+class ElementDefinition;
+class ElementsLocation;
+class QETResult;
+class TitleBlockTemplate;
+class XmlElementsCollection;
+class MoveElementsHandler;
+class MoveTitleBlockTemplatesHandler;
+
+/**
+	This class represents a QET project. Typically saved as a .qet file, it
+	consists in an XML document grouping 0 to n diagrams and embedding an elements
+	collection. This collection enables users to export diagrams on remote
+	machines without wondering whether required elements are available to them.
+*/
+class QETProject : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETProject(int = 1, QObject * = 0);
+	QETProject(const QString &, QObject * = 0);
+	QETProject(const QDomElement &, QObject * = 0);
+	virtual ~QETProject();
+	
+	private:
+	QETProject(const QETProject &);
+	
+	// enums
+	public:
+	/**
+		This enum lists possible states for a particular project.
+	*/
+	enum ProjectState {
+		Ok                    = 0, /// no error
+		FileOpenFailed        = 1, /// file opening failed
+		XmlParsingFailed      = 2, /// XML parsing failed
+		ProjectParsingRunning = 3, /// the XML content is currently being processed
+		ProjectParsingFailed  = 4, /// the parsing of the XML content failed
+		FileOpenDiscard       = 5  /// the user cancelled the file opening
+	};
+	
+	// methods
+	public:
+	ProjectState state() const;
+	QList<Diagram *> diagrams() const;
+	int folioIndex(const Diagram *) const;
+	ElementsCollection *embeddedCollection() const;
+	TitleBlockTemplatesProjectCollection *embeddedTitleBlockTemplatesCollection();
+	QString filePath();
+	void setFilePath(const QString &);
+	QString currentDir() const;
+	QString pathNameTitle() const;
+	QString title() const;
+	qreal declaredQElectroTechVersion();
+	void setTitle(const QString &);
+	QList<QString> embeddedTitleBlockTemplates();
+	const TitleBlockTemplate *getTemplateByName(const QString &template_name);
+	QDomElement getTemplateXmlDescriptionByName(const QString &);
+	bool setTemplateXmlDescription(const QString &, const QDomElement &);
+	void removeTemplateByName(const QString &);
+	BorderProperties defaultBorderProperties() const;
+	void setDefaultBorderProperties(const BorderProperties &);
+	TitleBlockProperties defaultTitleBlockProperties() const;
+	void setDefaultTitleBlockProperties(const TitleBlockProperties &);
+	ConductorProperties defaultConductorProperties() const;
+	void setDefaultConductorProperties(const ConductorProperties &);
+	QDomDocument toXml();
+	bool close();
+	QETResult write();
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	bool isEmpty() const;
+	bool ensureIntegrationCategoryExists();
+	ElementsCategory *integrationCategory() const;
+	QString integrateElement(const QString &, QString &);
+	QString integrateElement(const QString &, MoveElementsHandler *, QString &);
+	QString integrateTitleBlockTemplate(const TitleBlockTemplateLocation &, MoveTitleBlockTemplatesHandler *handler);
+	bool usesElement(const ElementsLocation &);
+	bool usesTitleBlockTemplate(const TitleBlockTemplateLocation &);
+	void cleanUnusedTitleBlocKTemplates();
+	void cleanUnusedElements(MoveElementsHandler *);
+	void cleanEmptyCategories(MoveElementsHandler *);
+	bool projectWasModified();
+	bool projectOptionsWereModified();
+	bool embeddedCollectionWasModified();
+	bool titleBlockTemplateCollectionWasModified();
+	bool diagramsWereModified();
+	DiagramContext projectProperties();
+	void setProjectProperties(const DiagramContext &);
+	
+	public slots:
+	void componentWritten();
+	Diagram *addNewDiagram();
+	void removeDiagram(Diagram *);
+	void diagramOrderChanged(int, int);
+	void setModified(bool);
+	
+	signals:
+	void projectFilePathChanged(QETProject *, const QString &);
+	void projectTitleChanged(QETProject *, const QString &);
+	void projectInformationsChanged(QETProject *);
+	void diagramAdded(QETProject *, Diagram *);
+	void diagramRemoved(QETProject *, Diagram *);
+	void projectModified(QETProject *, bool);
+	void projectDiagramsOrderChanged(QETProject *, int, int);
+	void elementIntegrated(QETProject *, const ElementsLocation &);
+	void diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &);
+	void readOnlyChanged(QETProject *, bool);
+	
+	private slots:
+	void updateDiagramsFolioData();
+	void updateDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *, const QString &);
+	void removeDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *, const QString &);
+	void usedTitleBlockTemplateChanged(const QString &);
+	
+	private:
+	void setupTitleBlockTemplatesCollection();
+	ElementsCategory *rootCategory() const;
+	void readProjectXml();
+	void readDiagramsXml();
+	void readElementsCollectionXml();
+	void readEmbeddedTemplatesXml();
+	void readProjectPropertiesXml();
+	void writeProjectPropertiesXml(QDomElement &);
+	void readDefaultPropertiesXml();
+	void writeDefaultPropertiesXml(QDomElement &);
+	void addDiagram(Diagram *);
+	NamesList namesListForIntegrationCategory();
+	ElementsLocation copyElementWithHandler(ElementDefinition *, ElementsCategory *, MoveElementsHandler *, QString &);
+	
+	// attributes
+	private:
+	/// File path this project is saved to
+	QString file_path_;
+	/// Current state of the project
+	ProjectState state_;
+	/// XML document representing the project
+	QDomDocument document_root_;
+	/// Diagrams carried by the project
+	QList<Diagram *> diagrams_;
+	/// Embedded elements collection
+	XmlElementsCollection *collection_;
+	/// Project title
+	QString project_title_;
+	/// QElectroTech version declared in the XML document at opening time
+	qreal project_qet_version_;
+	/// Whether options were modified
+	bool modified_;
+	/// Whether the project is read only
+	bool read_only_;
+	/// Filepath for which this project is considered read only
+	QString read_only_file_path_;
+	/// Name of the category used when automatically integrating elements within the embedded collection
+	static QString integration_category_name;
+	/// Default dimensions and properties for new diagrams created within the project
+	BorderProperties default_border_properties_;
+	/// Default conductor properties for new diagrams created within the project
+	ConductorProperties default_conductor_properties_;
+	/// Default title block properties for new diagrams created within the project
+	TitleBlockProperties default_titleblock_properties_;
+	/// Embedded title block templates collection
+	TitleBlockTemplatesProjectCollection titleblocks_;
+	/// project-wide variables that will be made available to child diagrams
+	DiagramContext project_properties_;
+};
+Q_DECLARE_METATYPE(QETProject *)
+#endif

Added: trunk/sources/qetregexpvalidator.cpp
===================================================================
--- trunk/sources/qetregexpvalidator.cpp	                        (rev 0)
+++ trunk/sources/qetregexpvalidator.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,50 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetregexpvalidator.h"
+
+/**
+	Constructeur
+	@param parent QObject parent
+*/
+QETRegExpValidator::QETRegExpValidator(QObject *parent) : QRegExpValidator(parent) {
+}
+
+/**
+	Constructeur
+	@param regexp Expression reguliere a valider
+	@param parent QObject parent
+*/
+QETRegExpValidator::QETRegExpValidator(const QRegExp &regexp, QObject *parent) : QRegExpValidator(regexp, parent) {
+}
+
+/**
+	Destructeur
+*/
+QETRegExpValidator::~QETRegExpValidator() {
+}
+
+/**
+	@see QRegExpValidator::validate
+	@see validationFailed()
+	Emet le signal validationFailed si la validation echoue
+*/
+QValidator::State QETRegExpValidator::validate(QString &input, int &pos) const {
+	QValidator::State result = QRegExpValidator::validate(input, pos);
+	if (result == QValidator::Invalid) emit(validationFailed());
+	return(result);
+}

Added: trunk/sources/qetregexpvalidator.h
===================================================================
--- trunk/sources/qetregexpvalidator.h	                        (rev 0)
+++ trunk/sources/qetregexpvalidator.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,43 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_REGEXP_VALIDATOR_H
+#define QET_REGEXP_VALIDATOR_H
+#include <QRegExpValidator>
+/**
+	This class acts like a QRegExpValidator except it emits a signal when the
+	input validation fails.
+*/
+class QETRegExpValidator : public QRegExpValidator {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETRegExpValidator(QObject *);
+	QETRegExpValidator(const QRegExp &, QObject *);
+	virtual ~QETRegExpValidator();
+	private:
+	QETRegExpValidator(const QETRegExpValidator &);
+	
+	// methods
+	public:
+	virtual QValidator::State validate(QString &, int &) const;
+	
+	signals:
+	void validationFailed() const;
+};
+#endif

Added: trunk/sources/qetresult.cpp
===================================================================
--- trunk/sources/qetresult.cpp	                        (rev 0)
+++ trunk/sources/qetresult.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,70 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetresult.h"
+
+/**
+	Construct a default "true" QET result without an error message.
+*/
+QETResult::QETResult() :
+	result_(true)
+{
+}
+
+/**
+	Construct a QET result embedding \a error_message and \a result (defaults
+	to false).
+*/
+QETResult::QETResult(const QString &error_message, bool result) :
+	result_(result),
+	error_message_(error_message)
+{
+}
+
+/**
+	Destructor
+*/
+QETResult::~QETResult() {
+}
+
+/**
+	@return the boolean value embedded within this result.
+*/
+bool QETResult::isOk() const {
+	return(result_);
+}
+
+/**
+	Embed \a result.
+*/
+void QETResult::setResult(bool result) {
+	result_ = result;
+}
+
+/**
+	@return the error message embedded within this result.
+*/
+QString QETResult::errorMessage() const {
+	return(error_message_);
+}
+
+/**
+	Embed \a error_message wihthin this result.
+*/
+void QETResult::setErrorMessage(const QString &error_message) {
+	error_message_ = error_message;
+}

Added: trunk/sources/qetresult.h
===================================================================
--- trunk/sources/qetresult.h	                        (rev 0)
+++ trunk/sources/qetresult.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,46 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_RESULT_H
+#define QET_RESULT_H
+#include <QString>
+
+/**
+	This class represents the result of a lambda operation. Technically, it is
+	a mere boolean+error message pair.
+*/
+class QETResult {
+	// Constructor, destructor
+	public:
+	QETResult();
+	QETResult(const QString &error_message, bool = false);
+	virtual ~QETResult();
+	
+	// methods
+	public:
+	bool isOk() const;
+	void setResult(bool);
+	QString errorMessage() const;
+	void setErrorMessage(const QString &);
+	
+	// attributes
+	private:
+	bool result_;           ///< Embedded boolean value
+	QString error_message_; ///< Embedded error message, typically used to explain what failed to users
+};
+
+#endif

Added: trunk/sources/qetsingleapplication.cpp
===================================================================
--- trunk/sources/qetsingleapplication.cpp	                        (rev 0)
+++ trunk/sources/qetsingleapplication.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,116 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTAvBILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qetsingleapplication.h"
+#include <QLocalSocket>
+
+const int QETSingleApplication::timeout_ = 10000;
+
+/**
+	Constructeur
+	@param argc Nombre d'arguments passes au programme par le systeme
+	@param argv Tableau des arguments passes au programme par le systeme
+	@param unique_key Cle unique
+*/
+QETSingleApplication::QETSingleApplication(int &argc, char **argv, const QString unique_key) :
+	QApplication(argc, argv),
+	unique_key_(unique_key)
+{
+	// verifie s'il y a un segment de memoire partage correspondant a la cle unique
+	shared_memory_.setKey(unique_key_);
+	if (shared_memory_.attach()) {
+		// oui : l'application est deja en cours d'execution
+		is_running_ = true;
+	} else {
+		// non : il s'agit du premier demarrage de l'application pour cette cle unique
+		is_running_ = false;
+		
+		// initialisation du segment de memoire partage
+		if (!shared_memory_.create(1)) {
+			qDebug() << "QETSingleApplication::QETSingleApplication() : Impossible de cr\351er l'instance unique" << qPrintable(unique_key_);
+			return;
+		}
+		
+		// initialisation d'un serveur local pour recevoir les messages des autres instances
+		local_server_ = new QLocalServer(this);
+		connect(local_server_, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
+		// la cle unique est egalement utilise pour le serveur
+		local_server_ -> listen(unique_key_);
+	}
+}
+
+/**
+	Destructeur
+*/
+QETSingleApplication::~QETSingleApplication() {
+}
+
+/**
+	Slot gerant la reception des messages.
+	Lorsque l'application recoit un message, ce slot emet le signal
+	messageAvailable avec le message recu.
+*/
+void QETSingleApplication::receiveMessage() {
+	QLocalSocket *local_socket = local_server_ -> nextPendingConnection();
+	if (!local_socket -> waitForReadyRead(timeout_)) {
+		qDebug() << "QETSingleApplication::receiveMessage() :" << qPrintable(local_socket -> errorString()) << "(" << qPrintable(unique_key_) << ")";
+		return;
+	}
+	QByteArray byteArray = local_socket -> readAll();
+	QString message = QString::fromUtf8(byteArray.constData());
+	emit(messageAvailable(message));
+	local_socket -> disconnectFromServer();
+}
+
+/**
+	@return true si l'application est deja en cours d'execution
+*/
+bool QETSingleApplication::isRunning() {
+	return(is_running_);
+}
+
+/**
+	Envoie un message a l'application. Si celle-ci n'est pas en cours
+	d'execution, cette methode ne fait rien.
+	@param message Message a transmettre a l'application
+	@return true si le message a ete tranmis, false sinon
+*/
+bool QETSingleApplication::sendMessage(const QString &message) {
+	// l'application doit etre en cours d'execution
+	if (!is_running_) {
+		return(false);
+	}
+	
+	// se connecte a l'application, avec gestion du timeout
+	QLocalSocket local_socket(this);
+	local_socket.connectToServer(unique_key_, QIODevice::WriteOnly);
+	if (!local_socket.waitForConnected(timeout_)) {
+		qDebug() << "QETSingleApplication::sendMessage() :" << qPrintable(local_socket.errorString()) << "(" << qPrintable(unique_key_) << ")";
+		return(false);
+	}
+	
+	// envoi du message, avec gestion du timeout
+	local_socket.write(message.toUtf8());
+	if (!local_socket.waitForBytesWritten(timeout_)) {
+		qDebug() << "QETSingleApplication::sendMessage() :" << qPrintable(local_socket.errorString()) << "(" << qPrintable(unique_key_) << ")";
+		return(false);
+	}
+	
+	// deconnexion
+	local_socket.disconnectFromServer();
+	return(true);
+}

Added: trunk/sources/qetsingleapplication.h
===================================================================
--- trunk/sources/qetsingleapplication.h	                        (rev 0)
+++ trunk/sources/qetsingleapplication.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,56 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_SINGLE_APPLICATION_H
+#define QET_SINGLE_APPLICATION_H
+#include <QApplication>
+#include <QSharedMemory>
+#include <QLocalServer>
+/**
+	This class represents a Qt Application executing only a single instance
+	depending on a unique string key.
+*/
+class QETSingleApplication : public QApplication {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	QETSingleApplication(int &, char **, const QString);
+	virtual ~QETSingleApplication();
+	
+	private:
+	QETSingleApplication(const QETSingleApplication &);
+	
+	// methods
+	public:
+	bool isRunning();
+	bool sendMessage(const QString &);
+	
+	public slots:
+	void receiveMessage();
+	
+	signals:
+	void messageAvailable(QString);
+	
+	// attributes
+	private:
+	bool is_running_;
+	QString unique_key_;
+	QSharedMemory shared_memory_;
+	QLocalServer *local_server_;
+	static const int timeout_;
+};
+#endif

Added: trunk/sources/qettabbar.cpp
===================================================================
--- trunk/sources/qettabbar.cpp	                        (rev 0)
+++ trunk/sources/qettabbar.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,334 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qettabbar.h"
+#include <QWheelEvent>
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+QETTabBar::QETTabBar(QWidget *parent) :
+	QTabBar(parent),
+	no_more_tabs_(true),
+	movable_tabs_(false),
+	moved_tab_(-1)
+{
+}
+
+/**
+	Destructeur
+*/
+QETTabBar::~QETTabBar() {
+}
+
+/**
+	Active l'onglet suivant si possible
+*/
+void QETTabBar::activateNextTab() {
+	int count_ = count();
+	if (count_ < 2) return;
+	
+	int current_index = currentIndex();
+	if (current_index == count_ - 1) {
+		setCurrentIndex(0);
+	} else {
+		setCurrentIndex(current_index + 1);
+	}
+}
+
+/**
+	Active l'onglet precedent si possible
+*/
+void QETTabBar::activatePreviousTab() {
+	int count_ = count();
+	if (count_ < 2) return;
+	
+	int current_index = currentIndex();
+	if (!current_index) {
+		setCurrentIndex(count_ - 1);
+	} else {
+		setCurrentIndex(current_index - 1);
+	}
+}
+
+/**
+	@param movable true pour que les onglets soient deplacables, false sinon
+*/
+void QETTabBar::setMovable(bool movable) {
+#if QT_VERSION < 0x040500
+	movable_tabs_ = movable;
+#else
+	QTabBar::setMovable(movable);
+#endif
+}
+
+/**
+	@return true si les onglets sont deplacables, false sinon
+*/
+bool QETTabBar::isMovable() const {
+#if QT_VERSION < 0x040500
+	return(movable_tabs_);
+#else
+	return(QTabBar::isMovable());
+#endif
+}
+
+/**
+	@return true si les onglets sont dessines de maniere verticale
+*/
+bool QETTabBar::isVertical() const {
+	int current_shape = shape();
+	return(
+		current_shape == QTabBar::RoundedEast    ||
+		current_shape == QTabBar::RoundedWest    ||
+		current_shape == QTabBar::TriangularEast ||
+		current_shape == QTabBar::TriangularWest
+	);
+}
+
+/**
+	@return true si les onglets sont dessines de maniere horizontale
+*/
+bool QETTabBar::isHorizontal() const {
+	int current_shape = shape();
+	return(
+		current_shape == QTabBar::RoundedNorth    ||
+		current_shape == QTabBar::RoundedSouth    ||
+		current_shape == QTabBar::TriangularNorth ||
+		current_shape == QTabBar::TriangularSouth
+	);
+}
+
+/**
+	Gere l'insertion d'un onglet
+	@param index indice de l'onglet insere
+*/
+void QETTabBar::tabInserted(int index) {
+	QTabBar::tabInserted(index);
+	if (no_more_tabs_) {
+		emit(firstTabInserted());
+	}
+	no_more_tabs_ = false;
+}
+
+/**
+	Gere le retrait d'un onglet
+	@param index indice de l'onglet enleve
+*/
+void QETTabBar::tabRemoved(int index) {
+	QTabBar::tabRemoved(index);
+	if (!count()) {
+		emit(lastTabRemoved());
+		no_more_tabs_ = true;
+	}
+}
+
+/**
+	Gere les evenements rollette sur cette barre d'onglets
+	@param event Evenement rollette
+*/
+void QETTabBar::wheelEvent(QWheelEvent *event) {
+	int num_degrees = event -> delta() / 8;
+	int num_steps = qAbs(num_degrees / 15);
+	
+	if (num_degrees <= 0) {
+		// passe a l'onglet suivant
+		for (int i = 0 ; i < num_steps ; ++ i) activateNextTab();
+	} else {
+		// passe a l'onglet precedent
+		for (int i = 0 ; i < num_steps ; ++ i) activatePreviousTab();
+	}
+	event -> accept();
+}
+
+/**
+	@param event Objet decrivant l'evenement souris
+*/
+void QETTabBar::mousePressEvent(QMouseEvent *event) {
+	QTabBar::mousePressEvent(event);
+	if (movable_tabs_) {
+		if (event -> button() == Qt::LeftButton) {
+			// retient l'onglet deplace et la position a laquelle le mouvement debute
+			moved_tab_ = tabForPressedPosition(event -> pos());
+			press_point_ = event -> pos();
+		}
+	}
+}
+
+/**
+	@param event Objet decrivant l'evenement souris
+*/
+void QETTabBar::mouseMoveEvent(QMouseEvent *event) {
+	QTabBar::mouseMoveEvent(event);
+	
+	// gere le deplacement d'onglets si celui-ci est active
+	if (movable_tabs_ && moved_tab_ != -1) {
+		// repere l'onglet de "destination"
+		int dest_tab = tabForMovedPosition(event -> pos());
+		
+		// verifie s'il faut deplacer l'onglet puis le deplace
+		if (mustMoveTab(moved_tab_, dest_tab, event -> pos())) {
+			moveTab(moved_tab_, dest_tab);
+			moved_tab_ = dest_tab;
+			event -> accept();
+		}
+	}
+}
+
+/**
+	@param event Objet decrivant l'evenement souris
+*/
+void QETTabBar::mouseReleaseEvent(QMouseEvent *event) {
+	QTabBar::mouseReleaseEvent(event);
+	moved_tab_ = -1;
+}
+
+/**
+	@param event Objet decrivant l'evenement souris
+*/
+void QETTabBar::mouseDoubleClickEvent(QMouseEvent *event) {
+	QTabBar::mouseDoubleClickEvent(event);
+	int clicked_tab = tabForPressedPosition(event -> pos());
+	emit(tabDoubleClicked(clicked_tab));
+}
+
+/**
+	@param src_tab Index de l'onglet de depart
+	@param dst_tab Index de l'onglet de destination
+	@param pos Position de la souris dans le cadre du deplacement de l'onglet
+	@return true s'il faut deplacer l'onglet src_tab a la place de l'onglet
+	dst_tab.
+	Cette methode 
+*/
+bool QETTabBar::mustMoveTab(int src_tab, int dst_tab, const QPoint &pos) const {
+	// les onglets sources et cibles doivent etre valides et differents
+	if (src_tab == -1 || dst_tab == -1) return(false);
+	if (src_tab == dst_tab) return(false);
+	
+	/*
+		A ce stade, le deplacement est possible mais selon la position du
+		pointeur de la souris, on peut assister a des deplacements prematures
+		d'onglets, rendant l'interface plus difficilement utilisable.
+		On s'assure donc que le curseur est assez "loin" pour eviter ces
+		problemes.
+	*/
+	// recupere les rectangles representant les onglets
+	QRect source_rect = tabRect(src_tab);
+	QRect target_rect = tabRect(dst_tab);
+	
+	if (isHorizontal()) {
+		if (layoutDirection() == Qt::LeftToRight && source_rect.x() < target_rect.x()) {
+			source_rect.moveRight(target_rect.right());
+		} else {
+			source_rect.moveLeft(target_rect.left());
+		}
+	} else {
+		if (source_rect.y() < target_rect.y()) {
+			source_rect.moveBottom(target_rect.bottom());
+		} else {
+			source_rect.moveTop(target_rect.top());
+		}
+	}
+	return(posMatchesTabRect(source_rect, pos));
+}
+
+/**
+	Deplace un onglet.
+	@param src_tab Index de l'onglet de depart
+	@param dst_tab Index de l'onglet de destination
+*/
+void QETTabBar::moveTab(int src_tab, int dst_tab) {
+#if QT_VERSION < 0x040500
+	// sauvegarde les caracteristiques de l'onglet deplace
+	QIcon    old_tab_icon      = tabIcon(src_tab);
+	QVariant old_tab_data      = tabData(src_tab);
+	QString  old_tab_text      = tabText(src_tab);
+	QColor   old_tab_textcolor = tabTextColor(src_tab);
+	QString  old_tab_tooltip   = tabToolTip(src_tab);
+	QString  old_tab_whatsthis = tabWhatsThis(src_tab);
+	
+	// si la QETTabBar est utilise dans un QTabWidget (ou une classe en
+	// derivant), elle lui delegue le deplacement de l'onglet
+	if (QTabWidget *qtabwidget = qobject_cast<QTabWidget *>(parent())) {
+		QWidget *old_tab_widget = qtabwidget -> widget(src_tab);
+		qtabwidget -> removeTab(src_tab);
+		qtabwidget -> insertTab(dst_tab, old_tab_widget, old_tab_text);
+		qtabwidget -> setCurrentIndex(dst_tab);
+	} else {
+		removeTab(src_tab);
+		insertTab(dst_tab, old_tab_text);
+		setCurrentIndex(dst_tab);
+	}
+	
+	// remet en place les caracteristiques de l'onglet deplace
+	setTabIcon     (dst_tab, old_tab_icon     );
+	setTabData     (dst_tab, old_tab_data     );
+	setTabTextColor(dst_tab, old_tab_textcolor);
+	setTabToolTip  (dst_tab, old_tab_tooltip  );
+	setTabWhatsThis(dst_tab, old_tab_whatsthis);
+	
+	// signale le deplacement de l'onglet
+	emit(tabMoved(src_tab, dst_tab));
+#else
+	QTabBar::moveTab(src_tab, dst_tab);
+#endif
+}
+
+/**
+	@param pos Position
+	@return l'index de l'onglet correspondant a la position pos, ou -1 si aucun
+	onglet ne correspond.
+*/
+int QETTabBar::tabForPressedPosition(const QPoint &pos) {
+	for (int tab_index = 0 ; tab_index < count() ; ++ tab_index) {
+		if (tabRect(tab_index).contains(pos)) return(tab_index);
+	}
+	return(-1);
+}
+
+/**
+	@param pos Position
+	@return l'index de l'onglet correspondant a la position pos lors d'un
+	deplacement d'onglet, ou -1 si aucun onglet ne correspond. Cette methode ne
+	prend en compte que l'abscisse ou que l'ordonnee de la position en fonction
+	de l'orientation des onglets.
+*/
+int QETTabBar::tabForMovedPosition(const QPoint &pos) {
+	for (int tab_index = 0 ; tab_index < count() ; ++ tab_index) {
+		if (posMatchesTabRect(tabRect(tab_index), pos)) return(tab_index);
+	}
+	return(-1);
+}
+
+/**
+	@param rect Un rectangle cense representer un onglet
+	@param pos Une position
+	@return true si la position pos correspond a ce rectangle.
+	Cette methode ne prend en compte que l'abscisse ou que
+	l'ordonnee de la position en fonction de l'orientation des onglets.
+*/
+bool QETTabBar::posMatchesTabRect(const QRect &rect, const QPoint &pos) const {
+	if (isVertical()) {
+		// les onglets sont disposes de maniere verticale : on prend en compte l'ordonnee
+		if (pos.y() >= rect.y() && pos.y() < rect.y() + rect.height()) return(true);
+	} else {
+		// les onglets sont disposes de maniere horizontale : on prend en compte l'abscisse
+		if (pos.x() >= rect.x() && pos.x() < rect.x() + rect.width()) return(true);
+	}
+	return(false);
+}

Added: trunk/sources/qettabbar.h
===================================================================
--- trunk/sources/qettabbar.h	                        (rev 0)
+++ trunk/sources/qettabbar.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,80 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_TAB_BAR_H
+#define QET_TAB_BAR_H
+#include <QtGui>
+#include <QTabBar>
+/**
+	This class provides a tab bar.
+	It is different from a QTabBar on the following points:
+	  * it emits a signal when the last tab is closed;
+	  * it emits a signal whe the first is inserted;
+	  * it allows switching tabs using the mouse wheel.
+*/
+class QETTabBar : public QTabBar {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETTabBar(QWidget * = 0);
+	virtual~QETTabBar();
+	
+	private:
+	QETTabBar(const QETTabBar &);
+	
+	// methods
+	public:
+	void activateNextTab();
+	void activatePreviousTab();
+	void setMovable(bool);
+	bool isMovable() const;
+	bool isVertical() const;
+	bool isHorizontal() const;
+	void moveTab(int, int);
+	
+	protected:
+	virtual void tabInserted(int);
+	virtual void tabRemoved(int);
+	virtual void wheelEvent(QWheelEvent *);
+	virtual void mousePressEvent(QMouseEvent *);
+	virtual void mouseMoveEvent(QMouseEvent *);
+	virtual void mouseReleaseEvent(QMouseEvent *);
+	virtual void mouseDoubleClickEvent(QMouseEvent *);
+	
+	signals:
+	void lastTabRemoved();
+	void firstTabInserted();
+#if QT_VERSION < 0x040500
+	void tabMoved(int, int);
+#endif
+	void tabDoubleClicked(int);
+	
+	private:
+	bool mustMoveTab(int, int, const QPoint &) const;
+	int tabForPressedPosition(const QPoint &);
+	int tabForMovedPosition(const QPoint &);
+	bool posMatchesTabRect(const QRect &, const QPoint &) const;
+	
+	// attributes
+	private:
+	bool no_more_tabs_;
+	bool movable_tabs_;
+	int  moved_tab_;
+	QPoint press_point_;
+};
+#endif

Added: trunk/sources/qettabwidget.cpp
===================================================================
--- trunk/sources/qettabwidget.cpp	                        (rev 0)
+++ trunk/sources/qettabwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,100 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "qettabwidget.h"
+#include <QCoreApplication>
+#include <QWheelEvent>
+#include "qettabbar.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent
+*/
+QETTabWidget::QETTabWidget(QWidget *parent) :
+	QTabWidget(parent)
+{
+	tab_bar_ = new QETTabBar(this);
+	setTabBar(tab_bar_);
+	
+	// re-emet les signaux emis par la barre d'onglets
+	connect(tab_bar_, SIGNAL(lastTabRemoved()),      this, SIGNAL(lastTabRemoved()));
+	connect(tab_bar_, SIGNAL(firstTabInserted()),    this, SIGNAL(firstTabInserted()));
+	connect(tab_bar_, SIGNAL(tabMoved(int, int)),    this, SIGNAL(tabMoved(int, int)));
+	connect(tab_bar_, SIGNAL(tabDoubleClicked(int)), this, SIGNAL(tabDoubleClicked(int)));
+}
+
+/**
+	Destructeur
+*/
+QETTabWidget::~QETTabWidget() {
+}
+
+/**
+	@param movable true pour que les onglets soient deplacables, false sinon
+*/
+void QETTabWidget::setMovable(bool movable) {
+#if QT_VERSION < 0x040500
+	tab_bar_ -> setMovable(movable);
+#else
+	QTabWidget::setMovable(movable);
+#endif
+}
+
+/**
+	@return true si les onglets sont deplacables, false sinon
+*/
+bool QETTabWidget::isMovable() const {
+#if QT_VERSION < 0x040500
+	return(tab_bar_ -> isMovable());
+#else
+	return(QTabWidget::isMovable());
+#endif
+}
+
+/**
+	Move the tab from index \a from to index \a to.
+*/
+void QETTabWidget::moveTab(int from, int to) {
+	tab_bar_ -> moveTab(from, to);
+	// workaround to a weird bug when moving the current tab
+	if (count() > 1) {
+		int current_index = tab_bar_ -> currentIndex();
+		// switch to any other index then back to the current one
+		tab_bar_ -> setCurrentIndex(current_index ? 0 : 1);
+		tab_bar_ -> setCurrentIndex(current_index);
+	}
+}
+
+QETTabBar *QETTabWidget::tabBar() const {
+	return(tab_bar_);
+}
+
+/**
+	Gere les evenements rollette sur cette barre d'onglets
+	@param event Evenement rollette
+*/
+void QETTabWidget::wheelEvent(QWheelEvent *event) {
+	QTabBar *tab_bar = tabBar();
+	// rectangle occupe par la barre d'onglets
+	QRect tab_bar_region(QPoint(0, 0), QSize(size().width(), tab_bar -> size().height()));
+	if (tab_bar_region.contains(event -> pos())) {
+		QCoreApplication::sendEvent(tab_bar, event);
+	} else {
+		event -> ignore();
+	}
+}

Added: trunk/sources/qettabwidget.h
===================================================================
--- trunk/sources/qettabwidget.h	                        (rev 0)
+++ trunk/sources/qettabwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,58 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QET_TAB_WIDGET_H
+#define QET_TAB_WIDGET_H
+#include <QTabWidget>
+class QETTabBar;
+/**
+	This class behaves like a QTabWidget except it uses a QETTAbBar to manage its
+	tabs. It also transmits the lastTabRemoved() and firstTabInserted() signals.
+	@see QETTabBar
+*/
+class QETTabWidget : public QTabWidget {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QETTabWidget(QWidget * = 0);
+	virtual~QETTabWidget();
+	
+	private:
+	QETTabWidget(const QETTabWidget &);
+	
+	// methods
+	public:
+	void setMovable(bool);
+	bool isMovable() const;
+	void moveTab(int, int);
+	QETTabBar *tabBar() const;
+	
+	protected:
+	void wheelEvent(QWheelEvent *);
+	
+	signals:
+	void lastTabRemoved();
+	void firstTabInserted();
+	void tabMoved(int, int);
+	void tabDoubleClicked(int);
+	
+	// attributes
+	private:
+	QETTabBar *tab_bar_;
+};
+#endif

Added: trunk/sources/qfilenameedit.cpp
===================================================================
--- trunk/sources/qfilenameedit.cpp	                        (rev 0)
+++ trunk/sources/qfilenameedit.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,100 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qfilenameedit.h"
+#include  "qetregexpvalidator.h"
+#include <QKeyEvent>
+#include <QRegExp>
+#include <QToolTip>
+
+/**
+	Constructeur
+	@param parent QWidget parent de ce champ de texte
+*/
+QFileNameEdit::QFileNameEdit(QWidget *parent) : QLineEdit(parent) {
+	init();
+}
+
+/**
+	Constructeur
+	@param contents Contenu initial du champ
+	@param parent QWidget parent de ce champ de texte
+*/
+QFileNameEdit::QFileNameEdit(const QString &contents, QWidget *parent) : QLineEdit(parent) {
+	init();
+	if (!contents.isEmpty() && regexp_.exactMatch(contents)) {
+		setText(contents);
+	}
+}
+
+/**
+	Destructeur
+*/
+QFileNameEdit::~QFileNameEdit() {
+}
+
+/**
+	@return true si le champ de texte est vide, false sinon
+*/
+bool QFileNameEdit::isEmpty() {
+	return(text().isEmpty());
+}
+
+/**
+	@return true si le champ de texte n'est pas vide et est valide
+*/
+bool QFileNameEdit::isValid() {
+	return(regexp_.exactMatch(text()));
+}
+
+/**
+	Construit l'objet
+*/
+void QFileNameEdit::init() {
+	regexp_ = QRegExp("^[0-9a-z_\\-\\.]+$", Qt::CaseSensitive);
+	validator_ = new QETRegExpValidator(regexp_, this);
+	setValidator(validator_);
+	tooltip_text_ = QString(
+		tr(
+			"Les caract\350res autoris\351s sont : \n"
+			" - les chiffres [0-9]\n"
+			" - les minuscules [a-z]\n"
+			" - le tiret [-], l'underscore [_] et le point [.]\n",
+			"tooltip content when editing a filename"
+		)
+	);
+	connect(validator_, SIGNAL(validationFailed()), this, SLOT(validationFailed()));
+}
+
+/**
+	Affiche l'info-bulle informant l'utilisateur des caracteres autorises.
+*/
+void QFileNameEdit::displayToolTip() {
+	QToolTip::showText(
+		mapToGlobal(QPoint(x() + width(), 0)),
+		tooltip_text_,
+		this,
+		QRect()
+	);
+}
+
+/**
+	Gere le fait que la validation du champ de texte ait echoue.
+*/
+void QFileNameEdit::validationFailed() {
+	displayToolTip();
+}

Added: trunk/sources/qfilenameedit.h
===================================================================
--- trunk/sources/qfilenameedit.h	                        (rev 0)
+++ trunk/sources/qfilenameedit.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,61 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef Q_FILENAME_EDIT_H
+#define Q_FILENAME_EDIT_H
+#include <QLineEdit>
+#include <QRegExp>
+#include <QString>
+class QETRegExpValidator;
+/**
+	This class represents a textfield dedicated to input a portable filename (not
+	a path).
+	It enables users to input a name matching the regular expression
+	^[0-9a-z_-\.]+$, thus avoiding problems with diacritics, non-printable,
+	non-ASCII or uppercase characters, which should improve the portability of
+	elements created by users.
+*/
+class QFileNameEdit : public QLineEdit {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QFileNameEdit(QWidget * = 0);
+	QFileNameEdit(const QString &, QWidget * = 0);
+	virtual ~QFileNameEdit();
+	private:
+	QFileNameEdit(const QFileNameEdit &);
+	
+	// methods
+	public:
+	bool isEmpty();
+	bool isValid();
+	
+	private:
+	void init();
+	void displayToolTip();
+	
+	private slots:
+	void validationFailed();
+	
+	// attributes
+	private:
+	QRegExp regexp_;
+	QETRegExpValidator *validator_;
+	QString tooltip_text_;
+};
+#endif

Added: trunk/sources/qgimanager.cpp
===================================================================
--- trunk/sources/qgimanager.cpp	                        (rev 0)
+++ trunk/sources/qgimanager.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,102 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qgimanager.h"
+
+/**
+	Constructeur
+	@param sc QGraphicsScene a utiliser pour gerer au mieux les QGraphicsItem
+*/
+QGIManager::QGIManager(QGraphicsScene *sc) :
+	scene(sc),
+	destroy_qgi_on_delete(true)
+{
+}
+
+/**
+	Destructeur
+	Lors de sa destruction, le QGI Manager detruit les QGraphicsItem restants
+	si ceux-ci n'appartiennent pas a la scene ; ce comportement peut etre
+	change avec la methode setDestroyQGIOnDelete
+	@see setDestroyQGIOnDelete
+*/
+QGIManager::~QGIManager(){
+	if (!destroy_qgi_on_delete) return;
+	foreach(QGraphicsItem *qgi, qgi_manager.keys()) {
+		if (!scene -> items().contains(qgi)) delete qgi;
+	}
+}
+
+/**
+	Demande au QGIManager de gerer un QGI
+	@param qgi QGraphicsItem a gerer
+*/
+void QGIManager::manage(QGraphicsItem *qgi) {
+	if (qgi_manager.contains(qgi)) ++ qgi_manager[qgi];
+	else qgi_manager.insert(qgi, 1);
+}
+
+/**
+	Indique au QGIManager qu'une reference vers un QGI a ete detruite
+	S'il n'y a plus de references vers ce QGI et que celui-ci n'est pas present
+	sur la scene de ce QGIManager, alors il sera detruit.
+	@param qgi QGraphicsItem a ne plus gerer
+*/
+void QGIManager::release(QGraphicsItem *qgi) {
+	if (!qgi_manager.contains(qgi)) return;
+	-- qgi_manager[qgi];
+	if (qgi_manager[qgi] <= 0 && !(scene -> items().contains(qgi))) {
+		delete qgi;
+		qgi_manager.remove(qgi);
+	}
+}
+
+/**
+	Demande au QGIManager de gerer plusieurs QGI
+	@param qgis QGraphicsItems a gerer
+*/
+void QGIManager::manage(const QList<QGraphicsItem *> &qgis) {
+	foreach(QGraphicsItem *qgi, qgis) manage(qgi);
+}
+
+/**
+	Indique au QGIManager que pour chaque QGI fourni, une reference vers celui-ci
+	a ete detruite.
+	S'il n'y a plus de references vers un QGI et que celui-ci n'est pas present
+	sur la scene de ce QGIManager, alors il sera detruit.
+	@param qgis QGraphicsItems a ne plus gerer
+*/
+void QGIManager::release(const QList<QGraphicsItem *> &qgis) {
+	foreach(QGraphicsItem *qgi, qgis) release(qgi);
+}
+
+/**
+	Indique au QGIManager de detruire les QGraphicsItem restants lors de sa
+	destruction si ceux-ci n'appartiennent pas a la scene
+*/
+void QGIManager::setDestroyQGIOnDelete(bool b) {
+	destroy_qgi_on_delete = b;
+}
+
+/**
+	Permet de savoir si ce QGIManager gere ou non un item donne
+	@param qgi QGraphicsItem dont il faut verifier la presence
+	@return true si l'item est gere, false sinon
+*/
+bool QGIManager::manages(QGraphicsItem *qgi) const {
+	return(qgi_manager.contains(qgi));
+}

Added: trunk/sources/qgimanager.h
===================================================================
--- trunk/sources/qgimanager.h	                        (rev 0)
+++ trunk/sources/qgimanager.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,50 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef QGI_MANAGER_H
+#define QGI_MANAGER_H
+#include <QtCore>
+#include <QGraphicsScene>
+#include <QGraphicsItem>
+/**
+	This class provides a QGraphicsItem manager, which can delete QGraphicsItem
+	as soon as there is no reference to them anymore.
+*/
+class QGIManager {
+	// constructors, destructors
+	public:
+	QGIManager(QGraphicsScene *);
+	virtual ~QGIManager();
+	private:
+	QGIManager(const QGIManager &);
+	
+	// attributes
+	private:
+	QGraphicsScene *scene;
+	QHash<QGraphicsItem *, int> qgi_manager;
+	bool destroy_qgi_on_delete;
+	
+	// methods
+	public:
+	void manage(QGraphicsItem *);
+	void release(QGraphicsItem *);
+	void manage(const QList<QGraphicsItem *> &);
+	void release(const QList<QGraphicsItem *> &);
+	void setDestroyQGIOnDelete(bool);
+	bool manages(QGraphicsItem *) const;
+};
+#endif

Added: trunk/sources/qtextorientationspinboxwidget.cpp
===================================================================
--- trunk/sources/qtextorientationspinboxwidget.cpp	                        (rev 0)
+++ trunk/sources/qtextorientationspinboxwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,134 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qtextorientationspinboxwidget.h"
+
+/**
+	Constructeur
+	@param parent QWidget parent de ce QTextOrientationSpinBoxWidget
+*/
+QTextOrientationSpinBoxWidget::QTextOrientationSpinBoxWidget(QWidget *parent) :
+	QWidget(parent)
+{
+	build();
+}
+
+/**
+	Destructeur
+*/
+QTextOrientationSpinBoxWidget::~QTextOrientationSpinBoxWidget() {
+}
+
+/**
+	@return un pointeur vers le QTextOrientationWidget
+*/
+QTextOrientationWidget *QTextOrientationSpinBoxWidget::orientationWidget() const {
+	return(orientation_widget_);
+}
+
+/**
+	@return un pointeur vers le QSpinBox
+*/
+QDoubleSpinBox *QTextOrientationSpinBoxWidget::spinBox() const {
+	return(spin_box_);
+}
+
+/**
+	@return l'orientation en cours
+*/
+double QTextOrientationSpinBoxWidget::orientation() const {
+	return(orientation_widget_ -> orientation());
+}
+
+/**
+	Synonyme pour orientation()
+	@return l'orientation en cours
+	@see orientation()
+*/
+double QTextOrientationSpinBoxWidget::value() const {
+	return(orientation());
+}
+
+/**
+	@return true si le widget est en mode "lecture seule", false sinon
+*/
+bool QTextOrientationSpinBoxWidget::isReadOnly() const {
+	return(orientation_widget_ -> isReadOnly());
+}
+
+/**
+	@param value Nouvelle valeur de l'orientation a afficher
+*/
+void QTextOrientationSpinBoxWidget::setOrientation(const double &value) {
+	orientation_widget_ -> setOrientation(value);
+	spin_box_           -> setValue(value);
+}
+
+/**
+	Synonyme pour setOrientation(value)
+	@param value Nouvelle valeur de l'orientation a afficher
+	@see setOrientation
+*/
+void QTextOrientationSpinBoxWidget::setValue(const double &value) {
+	setOrientation(value);
+}
+
+/**
+	@param ro true pour passer le widget en mode "lecture seule", false sinon
+*/
+void QTextOrientationSpinBoxWidget::setReadOnly(bool ro) {
+	orientation_widget_ -> setReadOnly(ro);
+	spin_box_           -> setReadOnly(ro);
+}
+
+/**
+	Construit le widget
+*/
+void QTextOrientationSpinBoxWidget::build() {
+	orientation_widget_ = new QTextOrientationWidget();
+	orientation_widget_ -> setMinimumSize(90.0, 90.0);
+	
+	spin_box_ = new QDoubleSpinBox();
+	spin_box_ -> setRange(-360.0, 360.0);
+	spin_box_ -> setSuffix("\260");
+	
+	// met en place les relations entre le SpinBox et le QTextOrientationWidget
+	connect(spin_box_,           SIGNAL(valueChanged(double)),       orientation_widget_, SLOT(setOrientation(double)));
+	connect(orientation_widget_, SIGNAL(orientationChanged(double)), spin_box_,           SLOT(setValue(double)));
+	
+	// cliquer sur un des carres du QTextOrientationWidget revient a finir une saisie dans le SpinBox
+	connect(orientation_widget_, SIGNAL(orientationChanged(double)), spin_box_, SIGNAL(editingFinished()));
+	
+	// lorsque l'utilisateur a change l'orientation, on emet un signal avec la valeur de la nouvelle orientation
+	connect(spin_box_, SIGNAL(editingFinished()), this, SLOT(emitChangeSignals()));
+	
+	// dispose les widgets : le QTextOrientationWidget a gauche, le SpinBox a droite
+	QHBoxLayout *main_layout = new QHBoxLayout();
+	main_layout -> addWidget(orientation_widget_);
+	main_layout -> addWidget(spin_box_);
+	main_layout -> addStretch();
+	setLayout(main_layout);
+}
+
+
+/**
+	Emet le signal orientationEditingFinished avec la valeur de l'orientation en cours
+*/
+void QTextOrientationSpinBoxWidget::emitChangeSignals() {
+	emit(editingFinished(orientation()));
+	emit(editingFinished());
+}

Added: trunk/sources/qtextorientationspinboxwidget.h
===================================================================
--- trunk/sources/qtextorientationspinboxwidget.h	                        (rev 0)
+++ trunk/sources/qtextorientationspinboxwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,68 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef Q_TEXT_ORIENTATION_SPINBOX_WIDGET_H
+#define Q_TEXT_ORIENTATION_SPINBOX_WIDGET_H
+#include <QtGui>
+#include "qtextorientationwidget.h"
+/**
+	This class provides a widget grouping a QTextOrientationWidget and QSpinBox
+	next to each other.
+	@see QTextOrientationWidget
+*/
+class QTextOrientationSpinBoxWidget : public QWidget {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QTextOrientationSpinBoxWidget(QWidget * = 0);
+	virtual ~QTextOrientationSpinBoxWidget();
+	private:
+	QTextOrientationSpinBoxWidget(const QTextOrientationSpinBoxWidget &);
+	
+	signals:
+	/**
+		Signals emitted when users have finished editing the orientation.
+	*/
+	void editingFinished(double);
+	void editingFinished();
+	
+	// methods
+	public:
+	QTextOrientationWidget *orientationWidget() const;
+	QDoubleSpinBox *spinBox() const;
+	double orientation() const;
+	double value() const;
+	bool isReadOnly() const;
+	
+	public slots:
+	void setOrientation(const double &);
+	void setValue(const double &);
+	void setReadOnly(bool);
+	
+	private:
+	void build();
+	
+	private slots:
+	void emitChangeSignals();
+	
+	// attributes
+	private:
+	QTextOrientationWidget *orientation_widget_;
+	QDoubleSpinBox *spin_box_;
+};
+#endif

Added: trunk/sources/qtextorientationwidget.cpp
===================================================================
--- trunk/sources/qtextorientationwidget.cpp	                        (rev 0)
+++ trunk/sources/qtextorientationwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,349 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qtextorientationwidget.h"
+
+/**
+	Constructeur
+	Par defaut, ce widget met en valeur les angles multiples de 45 degres
+	et presente un texte oriente a 0 degre, avec la police par defaut de
+	l'application. Le texte affiche est 
+	@param parent Widget parent
+*/
+QTextOrientationWidget::QTextOrientationWidget(QWidget *parent) :
+	QWidget(parent),
+	squares_interval_(45.0),
+	current_orientation_(0.0),
+	display_text_(true),
+	must_highlight_angle_(false),
+	read_only_(false)
+{
+	// chaines par defaut
+	text_size_hash_.insert(tr("Ex.",     "Short example string"),  -1);
+	text_size_hash_.insert(tr("Exemple", "Longer example string"), -1);
+	
+	// definit la politique de gestion de la taille de ce widget :
+	// on prefere la sizeHint()
+	QSizePolicy size_policy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+	// on souhaite conserver le rapport entre sa hauteur et sa largeur
+	size_policy.setHeightForWidth(true);
+	setSizePolicy(size_policy);
+	
+	// suivi de la souris : permet de recevoir les evenements relatifs aux
+	// mouvement de la souris sans que l'utilisateur n'ait a cliquer
+	setMouseTracking(true);
+}
+
+/**
+	Destructeur
+*/
+QTextOrientationWidget::~QTextOrientationWidget() {
+}
+
+/**
+	@param angle la nouvelle orientation / le nouvel angle selectionne(e)
+	0 degre correspond a un texte horizontal, de gauche a droite
+	90 degres correspondent a un texte vertical de haut en bas
+*/
+void QTextOrientationWidget::setOrientation(const double &angle) {
+	current_orientation_ = angle;
+	update();
+}
+
+/**
+	@return l'orientation / l'angle actuellement selectionne(e)
+	0 degre correspond a un texte horizontal, de gauche a droite
+	90 degres correspondent a un texte vertical de haut en bas
+*/
+double QTextOrientationWidget::orientation() const {
+	return(current_orientation_);
+}
+
+/**
+	Definit la police de caracteres a utiliser pour le texte affiche
+	@param font Une police de caracteres
+*/
+void QTextOrientationWidget::setFont(const QFont &font) {
+	text_font_ = font;
+	
+	// invalide le cache contenant les longueurs des textes a disposition
+	foreach(QString text, text_size_hash_.keys()) {
+		text_size_hash_[text] = -1;
+	}
+}
+
+/**
+	@return la police utilisee pour le texte affiche
+*/
+QFont QTextOrientationWidget::font() const {
+	return(text_font_);
+}
+
+/**
+	@param display_text true pour afficher un texte, false sinon
+*/
+void QTextOrientationWidget::setDisplayText(bool display_text) {
+	display_text_ = display_text;
+}
+
+/**
+	@return la police utilisee pour le texte affiche
+*/
+bool QTextOrientationWidget::textDisplayed() const {
+	return(display_text_);
+}
+
+/**
+	@param texts_list Une liste de chaines de caracteres utilisables par le
+	widget afin d'afficher un texte en guise d'exemple. Le widget choisit la
+	chaine la plus appropriee en fonction de sa taille.
+	Note : la liste fournie ne doit pas etre vide. Utilisez setDisplayText si
+	vous ne voulez plus afficher de texte.
+*/
+void QTextOrientationWidget::setUsableTexts(const QStringList &texts_list) {
+	if (texts_list.isEmpty()) return;
+	
+	// on oublie les anciennes chaines
+	foreach(QString text, text_size_hash_.keys()) {
+		// il faut oublier les anciennes chaines
+		if (!texts_list.contains(text)) {
+			text_size_hash_.remove(text);
+		}
+	}
+	
+	// on ajoute les nouvelles, sans les calculer (on met -1 en guise de longueur)
+	foreach(QString text, texts_list) {
+		if (!text_size_hash_.contains(text)) {
+			text_size_hash_[text] = -1;
+		}
+	}
+}
+
+/**
+	@return la liste des chaines dont le widget dispose pour afficher un texte
+*/
+QStringList QTextOrientationWidget::usableTexts() const {
+	return(text_size_hash_.keys());
+}
+
+/**
+	@return true si le widget est en mode "lecture seule", false sinon
+*/
+bool QTextOrientationWidget::isReadOnly() const {
+	return(read_only_);
+}
+
+/**
+	@param ro true pour passer le widget en mode "lecture seule", false sinon
+*/
+void QTextOrientationWidget::setReadOnly(bool ro) {
+	read_only_ = ro;
+}
+
+/**
+	@return la taille recommandee pour ce widget
+*/
+QSize QTextOrientationWidget::sizeHint() const {
+	return(QSize(50, 50));
+}
+
+/**
+	@param w une largeur donnee
+	@return la hauteur preferee pour une largeur donnee
+	Pour ce widget : retourne la largeur fournie afin de maintenir le widget carre
+*/
+int QTextOrientationWidget::heightForWidth(int w) const {
+	return(w);
+}
+
+/**
+	Effectue le rendu du widget
+	@param event Evenement decrivant la demande de rendu du widget
+*/
+void QTextOrientationWidget::paintEvent(QPaintEvent *event) {
+	Q_UNUSED(event);
+	
+	// rectangle de travail avec son centre et son rayon
+	QRect drawing_rectangle(QPoint(0, 0), size());
+	drawing_rectangle.adjust(5, 5, -5, -5);
+	
+	QPointF drawing_rectangle_center(drawing_rectangle.center());
+	qreal drawing_rectangle_radius = drawing_rectangle.width() / 2.0;
+	
+	QPainter p;
+	p.begin(this);
+	
+	p.setRenderHint(QPainter::Antialiasing,     true);
+	p.setRenderHint(QPainter::TextAntialiasing, true);
+	
+	// cercle gris a fond jaune
+	p.setPen(QPen(QBrush(QColor("#9FA8A8")), 2.0));
+	p.setBrush(QBrush(QColor("#ffffaa")));
+	p.drawEllipse(drawing_rectangle);
+	
+	// ligne rouge indiquant l'angle actuel
+	p.setPen(QPen(QBrush(Qt::red), 1.0));
+	p.translate(drawing_rectangle_center);
+	p.rotate(current_orientation_);
+	p.drawLine(QLineF(QPointF(), QPointF(drawing_rectangle_radius, 0.0)));
+	
+	// texte optionnel
+	if (display_text_) {
+		// determine le texte a afficher
+		QString chosen_text = getMostUsableStringForRadius(drawing_rectangle_radius);
+		if (!chosen_text.isEmpty()) {
+			p.resetTransform();
+			p.setPen(Qt::black);
+			p.setFont(text_font_);
+			p.translate(drawing_rectangle_center);
+			p.rotate(current_orientation_);
+			p.drawText(QPoint(), chosen_text);
+		}
+	}
+	
+	// carres verts a fond vert
+	qreal squares_size = size().width() / 15.0;
+	qreal square_offset = - squares_size / 2.0;
+	QRectF square_qrect = QRect(square_offset, square_offset, squares_size, squares_size);
+	p.setPen(Qt::NoPen);
+	p.setBrush(QBrush(QColor("#248A34")));
+	for (double drawing_angle = 0.0 ; drawing_angle < 360.0 ; drawing_angle += squares_interval_) {
+		if (must_highlight_angle_ && highlight_angle_ == drawing_angle && underMouse()) {
+			p.setBrush(QBrush(QColor("#43FF5F")));
+		}
+		p.resetTransform();
+		p.translate(drawing_rectangle_center);
+		p.rotate(drawing_angle);
+		p.translate(drawing_rectangle_radius - 1.0, 0.0);
+		p.rotate(-45.0);
+		p.drawRect(square_qrect);
+		if (must_highlight_angle_ && highlight_angle_ == drawing_angle) {
+			p.setBrush(QBrush(QColor("#248A34")));
+		}
+	}
+	
+	p.end();
+}
+
+/**
+	Gere les mouvements de la souris sur ce widget
+	@param event Evenement decrivant le mouvement de la souris
+*/
+void QTextOrientationWidget::mouseMoveEvent(QMouseEvent *event) {
+	if (read_only_) return;
+	
+	bool drawn_angle_hovered = positionIsASquare(event -> posF(), &highlight_angle_);
+	
+	if (must_highlight_angle_ != drawn_angle_hovered) {
+		must_highlight_angle_ = drawn_angle_hovered;
+		update();
+	}
+}
+
+/**
+	Gere les relachements de la souris sur ce widget
+	@param event Evenement decrivant le relachement de la souris
+*/
+void QTextOrientationWidget::mouseReleaseEvent(QMouseEvent *event) {
+	if (read_only_) return;
+	
+	double clicked_angle;
+	bool drawn_angle_clicked = positionIsASquare(event -> posF(), &clicked_angle);
+	
+	if (drawn_angle_clicked) {
+		setOrientation(clicked_angle);
+		emit(orientationChanged(clicked_angle));
+		must_highlight_angle_ = false;
+		update();
+	}
+}
+
+/**
+	@param radius Rayon du cercle qui limitera le rendu du texte
+	@return la chaine la plus appropriee en fonction de la taille du widget.
+*/
+QString QTextOrientationWidget::getMostUsableStringForRadius(const qreal &radius) {
+	// s'assure que l'on connait la longueur de chaque texte a disposition
+	generateTextSizeHash();
+	
+	// recupere les longueurs a disposition
+	QList<qreal> available_lengths = text_size_hash_.values();
+	// trie les longueurs par ordre croissant
+	qSort(available_lengths.begin(), available_lengths.end());
+	// recherche la position ou l'on insererait le rayon
+	QList<qreal>::const_iterator i = qUpperBound(available_lengths, radius);
+	
+	// la valeur precedent cette position est donc celle qui nous interesse
+	if (i == available_lengths.begin()) {
+		// nous sommes au debut de la liste - nous ne pouvons donc pas afficher de chaine
+		return(QString());
+	} else {
+		-- i;
+		qreal final_length = *i;
+		QString final_string = text_size_hash_.keys(final_length).first();
+		return(final_string);
+	}
+}
+
+/**
+	S'assure que le hash associant les textes utilisables a leur taille soit
+	correctement rempli.
+*/
+void QTextOrientationWidget::generateTextSizeHash() {
+	QFontMetrics font_metrics(text_font_);
+	foreach(QString text, text_size_hash_.keys()) {
+		if (text_size_hash_[text] == -1) {
+			text_size_hash_[text] = font_metrics.boundingRect(text).width();
+		}
+	}
+}
+
+/**
+	Determine si une position donnee correspond a un des carres representant un
+	angle pertinent.
+	@param pos Position donnee
+	@param angle_value_ptr Si different de 0, le double pointe par ce parametre
+	vaudra l'angle pertinent concerne
+*/
+bool QTextOrientationWidget::positionIsASquare(const QPointF &pos, double *angle_value_ptr) {
+	// rectangle de travail avec son centre et son rayon
+	QRect drawing_rectangle(QPoint(0, 0), size());
+	drawing_rectangle.adjust(5, 5, -5, -5);
+	
+	QPointF drawing_rectangle_center(drawing_rectangle.center());
+	qreal drawing_rectangle_radius = drawing_rectangle.width() / 2.0;
+	
+	qreal squares_size = size().width() / 15.0;
+	qreal square_offset = - squares_size / 2.0;
+	QRectF square_qrect = QRect(square_offset, square_offset, squares_size, squares_size);
+	
+	for (double drawing_angle = 0.0 ; drawing_angle < 360.0 ; drawing_angle += squares_interval_) {
+		QTransform transform = QTransform()
+			.translate(drawing_rectangle_center.x(), drawing_rectangle_center.y())
+			.rotate(drawing_angle)
+			.translate(drawing_rectangle_radius - 1.0, 0.0)
+			.rotate(-45.0);
+		
+		QRectF mapped_rectangle = transform.mapRect(square_qrect);
+		if (mapped_rectangle.contains(pos)) {
+			if (angle_value_ptr) *angle_value_ptr = drawing_angle;
+			return(true);
+		}
+	}
+	
+	return(false);
+}

Added: trunk/sources/qtextorientationwidget.h
===================================================================
--- trunk/sources/qtextorientationwidget.h	                        (rev 0)
+++ trunk/sources/qtextorientationwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,87 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef Q_TEXT_ORIENTATION_WIDGET_H
+#define Q_TEXT_ORIENTATION_WIDGET_H
+#include <QtGui>
+/**
+	This class provides a visual representation of a text orientation.
+*/
+class QTextOrientationWidget : public QWidget {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	QTextOrientationWidget(QWidget * = 0);
+	virtual ~QTextOrientationWidget();
+	private:
+	QTextOrientationWidget(const QTextOrientationWidget &);
+	QTextOrientationWidget &operator=(const QTextOrientationWidget &);
+	
+	// methods
+	public:
+	double orientation() const;
+	void setFont(const QFont &);
+	QFont font() const;
+	void setDisplayText(bool);
+	bool textDisplayed() const;
+	void setUsableTexts(const QStringList &);
+	QStringList usableTexts() const;
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	
+	public slots:
+	void setOrientation(const double &);
+	
+	protected:
+	virtual QSize sizeHint () const;
+	int heightForWidth(int) const;
+	virtual void paintEvent(QPaintEvent *);
+	void mouseMoveEvent(QMouseEvent *);
+	void mouseReleaseEvent(QMouseEvent *);
+	
+	signals:
+	/**
+		Signal emitted when users specify an orientation by clicking the widget.
+	*/
+	void orientationChanged(double);
+	
+	// attributes
+	private:
+	/// Interval between commonly used angles (represented by squares), in degrees
+	double squares_interval_;
+	/// current angle
+	double current_orientation_;
+	/// Whether to display an example text
+	bool display_text_;
+	/// Font used to render the example text
+	QFont text_font_;
+	/// Associate available example texts with their length (in pixels)
+	QHash<QString, qreal> text_size_hash_;
+	/// Specific angle to be highlighted
+	double highlight_angle_;
+	/// Whether to highlight a specific angle
+	bool must_highlight_angle_;
+	/// Whether this widget is read only
+	bool read_only_;
+	
+	private:
+	QString getMostUsableStringForRadius(const qreal &);
+	void generateTextSizeHash();
+	bool positionIsASquare(const QPointF &, double * = 0);
+};
+#endif

Added: trunk/sources/recentfiles.cpp
===================================================================
--- trunk/sources/recentfiles.cpp	                        (rev 0)
+++ trunk/sources/recentfiles.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,181 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "recentfiles.h"
+#include "qetapp.h"
+#include "qeticons.h"
+
+/**
+	Constructeur
+	@param identifier prefixe a utiliser pour recuperer les fichiers recents
+	dans la configuration de l'application
+	@param size Nombre de fichiers recents a retenir
+	@param parent QObject parent
+*/
+RecentFiles::RecentFiles(const QString &identifier, int size, QObject *parent) :
+	QObject(parent),
+	identifier_(identifier.isEmpty() ? "unnamed" : identifier),
+	size_(size > 0 ? size : 10),
+	menu_(0)
+{
+	mapper_ = new QSignalMapper(this);
+	connect(mapper_, SIGNAL(mapped(const QString &)), this, SLOT(handleMenuRequest(const QString &)));
+	
+	extractFilesFromSettings();
+	buildMenu();
+}
+
+/**
+	Destructeur
+	@todo determiner s'il faut detruire ou non le menu
+*/
+RecentFiles::~RecentFiles() {
+	delete menu_;
+}
+
+/**
+	@return le nombre de fichiers a retenir
+*/
+int RecentFiles::size() const {
+	return(size_);
+}
+
+/**
+	@return un menu listant les derniers fichiers ouverts
+*/
+QMenu *RecentFiles::menu() const {
+	return(menu_);
+}
+
+/**
+	@return l'icone affichee a cote de chaque fichier, ou une QIcon nulle si
+	aucune icone n'est utilisee.
+*/
+QIcon RecentFiles::iconForFiles() const {
+	return(files_icon_);
+}
+
+/**
+	Definit l'icone a afficher a cote de chaque fichier. Si une QIcon nulle
+	est fournie, aucune icone n'est utilisee.
+	@param icon Icone a afficher a cote de chaque fichier
+*/
+void RecentFiles::setIconForFiles(const QIcon &icon) {
+	files_icon_ = icon;
+	buildMenu();
+}
+
+/**
+	Oublie les fichiers recents
+*/
+void RecentFiles::clear() {
+	list_.clear();
+	buildMenu();
+}
+
+/**
+	Sauvegarde les fichiers recents dans la configuration
+*/
+void RecentFiles::save() {
+	saveFilesToSettings();
+}
+
+/**
+	Gere les actions sur le menu
+*/
+void RecentFiles::handleMenuRequest(const QString &filepath) {
+	emit(fileOpeningRequested(filepath));
+}
+
+/**
+	Gere le fait qu'un fichier ait ete ouvert
+	@param filepath Chemin du fichier ouvert
+*/
+void RecentFiles::fileWasOpened(const QString &filepath) {
+	insertFile(filepath);
+	buildMenu();
+}
+
+/**
+	Lit la liste des fichiers recents dans la configuration
+*/
+void RecentFiles::extractFilesFromSettings() {
+	// oublie la liste des fichiers recents
+	list_.clear();
+	
+	// recupere les derniers fichiers ouverts dans la configuration
+	for (int i = size_ ; i >= 1  ; -- i) {
+		QString key(identifier_ + "-recentfiles/file" + QString::number(i));
+		QString value(QETApp::settings().value(key, QString()).toString());
+		insertFile(value);
+	}
+}
+
+/**
+	Insere un fichier dans la liste des fichiers recents
+*/
+void RecentFiles::insertFile(const QString &filepath) {
+	// s'assure que le chemin soit exprime avec des separateurs conformes au systeme
+	QString filepath_ns = QDir::toNativeSeparators(filepath);
+	
+	// evite d'inserer un chemin de fichier vide ou en double
+	if (filepath_ns.isEmpty()) return;
+	list_.removeAll(filepath_ns);
+	
+	// insere le chemin de fichier
+	list_.push_front(filepath_ns);
+	
+	// s'assure que l'on ne retient pas plus de fichiers que necessaire
+	while (list_.count() > size_) list_.removeLast();
+}
+
+/**
+	Ecrit la liste des fichiers recents dans la configuration
+*/
+void RecentFiles::saveFilesToSettings() {
+	for (int i = 0 ; i < size_ && i < list_.count() ; ++ i) {
+		QString key(identifier_ + "-recentfiles/file" + QString::number(i + 1));
+		QETApp::settings().setValue(key, list_[i]);
+	}
+}
+
+/**
+	Construit le menu
+*/
+void RecentFiles::buildMenu() {
+	// reinitialise le menu
+	if (!menu_) {
+		menu_ = new QMenu(tr("&R\351cemment ouvert(s)"));
+		menu_ -> setIcon(QET::Icons::DocumentOpenRecent);
+	} else {
+		menu_ -> clear();
+	}
+	
+	// remplit le menu
+	foreach (QString filepath, list_) {
+		// creee une nouvelle action pour le fichier
+		QAction *action = new QAction(filepath, 0);
+		if (!files_icon_.isNull()) {
+			action -> setIcon(files_icon_);
+		}
+		menu_ -> addAction(action);
+		
+		// lie l'action et le mapper
+		mapper_ -> setMapping(action, filepath);
+		connect(action, SIGNAL(triggered()), mapper_, SLOT(map()));
+	}
+}

Added: trunk/sources/recentfiles.h
===================================================================
--- trunk/sources/recentfiles.h	                        (rev 0)
+++ trunk/sources/recentfiles.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,69 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef RECENT_FILES_H
+#define RECENT_FILES_H
+#include <QtCore>
+#include <QIcon>
+class QMenu;
+/**
+	This class provides a way to manage recently opened files.
+*/
+class RecentFiles : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	RecentFiles(const QString &, int = 10, QObject * = 0);
+	virtual ~RecentFiles();
+	private:
+	RecentFiles(const RecentFiles &);
+	
+	// methods
+	public:
+	int size() const;
+	QMenu *menu() const;
+	QIcon iconForFiles() const;
+	void setIconForFiles(const QIcon &);
+	
+	public slots:
+	void clear();
+	void save();
+	void fileWasOpened(const QString &);
+	
+	signals:
+	void fileOpeningRequested(const QString &);
+	
+	private:
+	void extractFilesFromSettings();
+	void insertFile(const QString &);
+	void saveFilesToSettings();
+	void buildMenu();
+	
+	private slots:
+	void handleMenuRequest(const QString &);
+	
+	// attributes
+	private:
+	QString identifier_;
+	int size_;
+	QList<QString> list_;
+	QMenu *menu_;
+	QSignalMapper *mapper_;
+	QIcon files_icon_;
+};
+#endif

Added: trunk/sources/richtext/README.txt
===================================================================
--- trunk/sources/richtext/README.txt	                        (rev 0)
+++ trunk/sources/richtext/README.txt	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1 @@
+Example running richtext through XSLT

Added: trunk/sources/richtext/addlinkdialog.ui
===================================================================
--- trunk/sources/richtext/addlinkdialog.ui	                        (rev 0)
+++ trunk/sources/richtext/addlinkdialog.ui	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,112 @@
+<ui version="4.0" >
+ <class>AddLinkDialog</class>
+ <widget class="QDialog" name="AddLinkDialog" >
+  <property name="windowTitle" >
+   <string>Insert Link</string>
+  </property>
+  <property name="sizeGripEnabled" >
+   <bool>false</bool>
+  </property>
+  <property name="modal" >
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" >
+   <item>
+    <layout class="QFormLayout" >
+     <item row="0" column="0" >
+      <widget class="QLabel" name="label" >
+       <property name="text" >
+        <string>Title:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" >
+      <widget class="QLineEdit" name="titleInput" >
+       <property name="minimumSize" >
+        <size>
+         <width>337</width>
+         <height>0</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0" >
+      <widget class="QLabel" name="label_2" >
+       <property name="text" >
+        <string>URL:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" >
+      <widget class="QLineEdit" name="urlInput" />
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer" >
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0" >
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="Line" name="line" >
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox" >
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons" >
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AddLinkDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AddLinkDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

Added: trunk/sources/richtext/richtexteditor.cpp
===================================================================
--- trunk/sources/richtext/richtexteditor.cpp	                        (rev 0)
+++ trunk/sources/richtext/richtexteditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,803 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@xxxxxxxxx)
+**
+** This file is part of the Qt Designer of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@xxxxxxxxx.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/**
+	Integration : QElectroTech Team
+	Changelog:
+	- 09/04/2013 : Start integration...Compilation and object creation are successful
+*/
+
+#include "richtexteditor_p.h"
+#include "ui_addlinkdialog.h"
+
+#include <QtDesigner/QDesignerFormEditorInterface>
+
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QPointer>
+#include <QtCore/QProcess>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QDir>
+
+#include <QtGui/QAction>
+#include <QtGui/QColorDialog>
+#include <QtGui/QComboBox>
+#include <QtGui/QFontDatabase>
+#include <QtGui/QTextCursor>
+#include <QtGui/QPainter>
+#include <QtGui/QIcon>
+#include <QtGui/QMenu>
+#include <QtGui/QMoveEvent>
+#include <QtGui/QTabWidget>
+#include <QtGui/QTextDocument>
+#include <QtGui/QTextBlock>
+#include <QtGui/QToolBar>
+#include <QtGui/QToolButton>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QPushButton>
+#include <QtGui/QDialogButtonBox>
+
+QT_BEGIN_NAMESPACE
+
+//static const char *RichTextDialogC = "RichTextDialog";
+//static const char *Geometry = "Geometry";
+
+namespace qdesigner_internal {
+
+class RichTextEditor : public QTextEdit
+{
+    Q_OBJECT
+public:
+    RichTextEditor(QWidget *parent = 0);
+    void setDefaultFont(const QFont &font);
+
+    QToolBar *createToolBar(QWidget *parent = 0);
+
+public slots:
+    void setFontBold(bool b);
+    void setFontPointSize(double);
+    void setText(const QString &text);
+    QString text(Qt::TextFormat format) const;
+
+signals:
+    void stateChanged();
+};
+
+class AddLinkDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    AddLinkDialog(RichTextEditor *editor, QWidget *parent = 0);
+    ~AddLinkDialog();
+
+    int showDialog();
+
+public slots:
+    void accept();
+
+private:
+    RichTextEditor *m_editor;
+    Ui::AddLinkDialog *m_ui;
+};
+
+AddLinkDialog::AddLinkDialog(RichTextEditor *editor, QWidget *parent) :
+    QDialog(parent),
+    m_ui(new Ui::AddLinkDialog)
+{
+    m_ui->setupUi(this);
+
+    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+    m_editor = editor;
+}
+
+AddLinkDialog::~AddLinkDialog()
+{
+    delete m_ui;
+}
+
+int AddLinkDialog::showDialog()
+{
+    // Set initial focus
+    const QTextCursor cursor = m_editor->textCursor();
+    if (cursor.hasSelection()) {
+        m_ui->titleInput->setText(cursor.selectedText());
+        m_ui->urlInput->setFocus();
+    } else {
+        m_ui->titleInput->setFocus();
+    }
+
+    return exec();
+}
+
+void AddLinkDialog::accept()
+{
+    const QString title = m_ui->titleInput->text();
+    const QString url = m_ui->urlInput->text();
+
+    if (!title.isEmpty()) {
+        QString html = QLatin1String("<a href=\"");
+        html += url;
+        html += QLatin1String("\">");
+        html += title;
+        html += QLatin1String("</a>");
+
+        m_editor->insertHtml(html);
+    }
+
+    m_ui->titleInput->clear();
+    m_ui->urlInput->clear();
+
+    QDialog::accept();
+}
+
+class HtmlTextEdit : public QTextEdit
+{
+    Q_OBJECT
+
+public:
+    HtmlTextEdit(QWidget *parent = 0)
+        : QTextEdit(parent)
+    {}
+
+    void contextMenuEvent(QContextMenuEvent *event);
+
+private slots:
+    void actionTriggered(QAction *action);
+};
+
+void HtmlTextEdit::contextMenuEvent(QContextMenuEvent *event)
+{
+    QMenu *menu = createStandardContextMenu();
+    QMenu *htmlMenu = new QMenu(tr("Insert HTML entity"), menu);
+
+    typedef struct {
+        const char *text;
+        const char *entity;
+    } Entry;
+
+    const Entry entries[] = {
+        { "&&amp; (&&)", "&amp;" },
+        { "&&nbsp;", "&nbsp;" },
+        { "&&lt; (<)", "&lt;" },
+        { "&&gt; (>)", "&gt;" },
+        { "&&copy; (Copyright)", "&copy;" },
+        { "&&reg; (Trade Mark)", "&reg;" },
+    };
+
+    for (int i = 0; i < 6; ++i) {
+        QAction *entityAction = new QAction(QLatin1String(entries[i].text),
+                                            htmlMenu);
+        entityAction->setData(QLatin1String(entries[i].entity));
+        htmlMenu->addAction(entityAction);
+    }
+
+    menu->addMenu(htmlMenu);
+    connect(htmlMenu, SIGNAL(triggered(QAction*)),
+                      SLOT(actionTriggered(QAction*)));
+    menu->exec(event->globalPos());
+    delete menu;
+}
+
+void HtmlTextEdit::actionTriggered(QAction *action)
+{
+    insertPlainText(action->data().toString());
+}
+
+class ColorAction : public QAction
+{
+    Q_OBJECT
+
+public:
+    ColorAction(QObject *parent);
+
+    const QColor& color() const { return m_color; }
+    void setColor(const QColor &color);
+
+signals:
+    void colorChanged(const QColor &color);
+
+private slots:
+    void chooseColor();
+
+private:
+    QColor m_color;
+};
+
+ColorAction::ColorAction(QObject *parent):
+    QAction(parent)
+{
+    setText(tr("Text Color"));
+    setColor(Qt::black);
+    connect(this, SIGNAL(triggered()), this, SLOT(chooseColor()));
+}
+
+void ColorAction::setColor(const QColor &color)
+{
+    if (color == m_color)
+        return;
+    m_color = color;
+    QPixmap pix(24, 24);
+    QPainter painter(&pix);
+    painter.setRenderHint(QPainter::Antialiasing, false);
+    painter.fillRect(pix.rect(), m_color);
+    painter.setPen(m_color.darker());
+    painter.drawRect(pix.rect().adjusted(0, 0, -1, -1));
+    setIcon(pix);
+}
+
+void ColorAction::chooseColor()
+{
+    const QColor col = QColorDialog::getColor(m_color, 0);
+    if (col.isValid() && col != m_color) {
+        setColor(col);
+        emit colorChanged(m_color);
+    }
+}
+
+class RichTextEditorToolBar : public QToolBar
+{
+    Q_OBJECT
+public:
+    RichTextEditorToolBar(RichTextEditor *editor,
+                          QWidget *parent = 0);
+
+public slots:
+    void updateActions();
+
+private slots:
+    void alignmentActionTriggered(QAction *action);
+    void sizeInputActivated(const QString &size);
+    void colorChanged(const QColor &color);
+    void setVAlignSuper(bool super);
+    void setVAlignSub(bool sub);
+    void insertLink();
+    void insertImage();
+    void runXmlPatterns();
+
+private:
+    QAction *m_bold_action;
+    QAction *m_italic_action;
+    QAction *m_underline_action;
+    QAction *m_valign_sup_action;
+    QAction *m_valign_sub_action;
+    QAction *m_align_left_action;
+    QAction *m_align_center_action;
+    QAction *m_align_right_action;
+    QAction *m_align_justify_action;
+    QAction *m_link_action;
+    QAction *m_image_action;
+    QAction *m_xmlPatterns;
+    ColorAction *m_color_action;
+    QComboBox *m_font_size_input;
+
+    QPointer<RichTextEditor> m_editor;
+};
+
+static QAction *createCheckableAction(const QIcon &icon, const QString &text,
+                                      QObject *receiver, const char *slot,
+                                      QObject *parent = 0)
+{
+    QAction *result = new QAction(parent);
+    result->setIcon(icon);
+    result->setText(text);
+    result->setCheckable(true);
+    result->setChecked(false);
+    if (slot)
+        QObject::connect(result, SIGNAL(triggered(bool)), receiver, slot);
+    return result;
+}
+
+RichTextEditorToolBar::RichTextEditorToolBar(RichTextEditor *editor,
+                                             QWidget *parent) :
+    QToolBar(parent),
+    m_link_action(new QAction(this)),
+    m_image_action(new QAction(this)),
+    m_color_action(new ColorAction(this)),
+    m_font_size_input(new QComboBox),
+    m_editor(editor)
+{
+
+    m_xmlPatterns = new QAction("Run XMLPatterns", this);
+    connect(m_xmlPatterns, SIGNAL(triggered()), this, SLOT(runXmlPatterns()));
+    addAction(m_xmlPatterns);
+	m_xmlPatterns -> setVisible( false );
+	
+    // Font size combo box
+    m_font_size_input->setEditable(false);
+    const QList<int> font_sizes = QFontDatabase::standardSizes();
+    foreach (int font_size, font_sizes)
+        m_font_size_input->addItem(QString::number(font_size));
+
+    connect(m_font_size_input, SIGNAL(activated(QString)),
+            this, SLOT(sizeInputActivated(QString)));
+    addWidget(m_font_size_input);
+
+    addSeparator();
+
+    // Bold, italic and underline buttons
+
+    m_bold_action = createCheckableAction(
+      QIcon(":/ico/32x32/format-text-bold.png"),
+			tr("Texte en gras"), editor, SLOT(setFontBold(bool)), this);
+    m_bold_action->setShortcut(tr("CTRL+B"));
+    addAction(m_bold_action);
+
+    m_italic_action = createCheckableAction(
+	QIcon(":/ico/32x32/format-text-italic.png"),
+			tr("Texte en italique"), editor, SLOT(setFontItalic(bool)), this);
+    m_italic_action->setShortcut(tr("CTRL+I"));
+    addAction(m_italic_action);
+
+    m_underline_action = createCheckableAction(
+	QIcon(":/ico/32x32/format-text-underline.png"),
+			tr("Texte soulig\351"), editor, SLOT(setFontUnderline(bool)), this);
+    m_underline_action->setShortcut(tr("CTRL+U"));
+    addAction(m_underline_action);
+
+    addSeparator();
+
+    // Left, center, right and justified alignment buttons
+
+    QActionGroup *alignment_group = new QActionGroup(this);
+    connect(alignment_group, SIGNAL(triggered(QAction*)),
+                             SLOT(alignmentActionTriggered(QAction*)));
+
+    m_align_left_action = createCheckableAction(
+            QIcon(),
+            tr("Left Align"), editor, 0, alignment_group);
+    addAction(m_align_left_action);
+
+    m_align_center_action = createCheckableAction(
+            QIcon(),
+            tr("Center"), editor, 0, alignment_group);
+    addAction(m_align_center_action);
+
+    m_align_right_action = createCheckableAction(
+            QIcon(),
+            tr("Right Align"), editor, 0, alignment_group);
+    addAction(m_align_right_action);
+
+    m_align_justify_action = createCheckableAction(
+            QIcon(),
+            tr("Justify"), editor, 0, alignment_group);
+    addAction(m_align_justify_action);
+
+	m_align_justify_action -> setVisible( false );
+	m_align_center_action  -> setVisible( false );
+	m_align_left_action   -> setVisible( false );
+	m_align_right_action   -> setVisible( false );
+    //addSeparator();
+
+    // Superscript and subscript buttons
+
+    m_valign_sup_action = createCheckableAction(
+            QIcon(),
+            tr("Superscript"),
+            this, SLOT(setVAlignSuper(bool)), this);
+    addAction(m_valign_sup_action);
+
+    m_valign_sub_action = createCheckableAction(
+            QIcon(),
+            tr("Subscript"),
+            this, SLOT(setVAlignSub(bool)), this);
+    addAction(m_valign_sub_action);
+	
+	m_valign_sup_action -> setVisible( false );
+	m_valign_sub_action -> setVisible( false );
+
+    //addSeparator();
+
+    // Insert hyperlink and image buttons
+
+	m_link_action->setText(tr("Ins\351rer un lien"));
+    connect(m_link_action, SIGNAL(triggered()), SLOT(insertLink()));
+    addAction(m_link_action);
+
+    m_image_action->setText(tr("Insert &Image"));
+    connect(m_image_action, SIGNAL(triggered()), SLOT(insertImage()));
+    addAction(m_image_action);
+
+	m_image_action -> setVisible( false );
+    addSeparator();
+
+	// Text color button
+	connect(m_color_action, SIGNAL(colorChanged(QColor)),
+            this, SLOT(colorChanged(QColor)));
+    addAction(m_color_action);
+
+    connect(editor, SIGNAL(textChanged()), this, SLOT(updateActions()));
+    connect(editor, SIGNAL(stateChanged()), this, SLOT(updateActions()));
+
+    updateActions();
+}
+
+void RichTextEditorToolBar::alignmentActionTriggered(QAction *action)
+{
+    Qt::Alignment new_alignment;
+
+    if (action == m_align_left_action) {
+        new_alignment = Qt::AlignLeft;
+    } else if (action == m_align_center_action) {
+        new_alignment = Qt::AlignCenter;
+    } else if (action == m_align_right_action) {
+        new_alignment = Qt::AlignRight;
+    } else {
+        new_alignment = Qt::AlignJustify;
+    }
+
+    m_editor->setAlignment(new_alignment);
+}
+
+QString runXSLT(const QString &t)
+{
+    QString pattern = QDir::tempPath();
+    if (!pattern.endsWith('/'))
+        pattern += '/';
+    pattern += "qt_tempXXXXXX.html";
+    QTemporaryFile tf(pattern);
+    if (!tf.open())
+        return QLatin1String("Open failure");
+    const QString tfName = tf.fileName();
+    tf.write(t.toUtf8());
+    tf.close();
+    QProcess p;
+    QStringList args;
+    args << "tohtml.xsl" << tfName;
+    p.start("xmlpatterns",args);
+    if (!p.waitForStarted() || !p.waitForFinished())
+        return QLatin1String("Run failure");
+    const QByteArray output = p.exitStatus() == QProcess::NormalExit &&
+                           p.exitCode() == 0 ?
+                           p.readAllStandardOutput() :
+                           p.readAllStandardError();
+    return QString::fromUtf8(output);
+}
+
+
+void RichTextEditorToolBar::runXmlPatterns()
+{
+    qWarning("%s", qPrintable(runXSLT(m_editor->text(Qt::RichText))));
+}
+
+void RichTextEditorToolBar::colorChanged(const QColor &color)
+{
+    m_editor->setTextColor(color);
+    m_editor->setFocus();
+}
+
+void RichTextEditorToolBar::sizeInputActivated(const QString &size)
+{
+    bool ok;
+    int i = size.toInt(&ok);
+    if (!ok)
+        return;
+
+    m_editor->setFontPointSize(i);
+    m_editor->setFocus();
+}
+
+void RichTextEditorToolBar::setVAlignSuper(bool super)
+{
+    const QTextCharFormat::VerticalAlignment align = super ?
+        QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal;
+
+    QTextCharFormat charFormat = m_editor->currentCharFormat();
+    charFormat.setVerticalAlignment(align);
+    m_editor->setCurrentCharFormat(charFormat);
+
+    m_valign_sub_action->setChecked(false);
+}
+
+void RichTextEditorToolBar::setVAlignSub(bool sub)
+{
+    const QTextCharFormat::VerticalAlignment align = sub ?
+        QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal;
+
+    QTextCharFormat charFormat = m_editor->currentCharFormat();
+    charFormat.setVerticalAlignment(align);
+    m_editor->setCurrentCharFormat(charFormat);
+
+    m_valign_sup_action->setChecked(false);
+}
+
+void RichTextEditorToolBar::insertLink()
+{
+    AddLinkDialog linkDialog(m_editor, this);
+    linkDialog.showDialog();
+    m_editor->setFocus();
+}
+
+void RichTextEditorToolBar::insertImage()
+{
+#ifdef hip
+    const QString path = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), QString(), this);
+    if (!path.isEmpty())
+        m_editor->insertHtml(QLatin1String("<img src=\"") + path + QLatin1String("\"/>"));
+#endif
+}
+
+void RichTextEditorToolBar::updateActions()
+{
+    if (m_editor == 0) {
+        setEnabled(false);
+        return;
+    }
+
+    const Qt::Alignment alignment = m_editor->alignment();
+    const QTextCursor cursor = m_editor->textCursor();
+    const QTextCharFormat charFormat = cursor.charFormat();
+    const QFont font = charFormat.font();
+    const QTextCharFormat::VerticalAlignment valign =
+        charFormat.verticalAlignment();
+    const bool superScript = valign == QTextCharFormat::AlignSuperScript;
+    const bool subScript = valign == QTextCharFormat::AlignSubScript;
+
+    if (alignment & Qt::AlignLeft) {
+        m_align_left_action->setChecked(true);
+    } else if (alignment & Qt::AlignRight) {
+        m_align_right_action->setChecked(true);
+    } else if (alignment & Qt::AlignHCenter) {
+        m_align_center_action->setChecked(true);
+    } else {
+        m_align_justify_action->setChecked(true);
+    }
+
+    m_bold_action->setChecked(font.bold());
+    m_italic_action->setChecked(font.italic());
+    m_underline_action->setChecked(font.underline());
+    m_valign_sup_action->setChecked(superScript);
+    m_valign_sub_action->setChecked(subScript);
+
+    const int size = font.pointSize();
+    const int idx = m_font_size_input->findText(QString::number(size));
+    if (idx != -1)
+        m_font_size_input->setCurrentIndex(idx);
+
+    m_color_action->setColor(m_editor->textColor());
+}
+
+RichTextEditor::RichTextEditor(QWidget *parent)
+    : QTextEdit(parent)
+{
+    connect(this, SIGNAL(currentCharFormatChanged(QTextCharFormat)),
+            this, SIGNAL(stateChanged()));
+    connect(this, SIGNAL(cursorPositionChanged()),
+            this, SIGNAL(stateChanged()));
+}
+
+QToolBar *RichTextEditor::createToolBar(QWidget *parent)
+{
+    return new RichTextEditorToolBar(this, parent);
+}
+
+void RichTextEditor::setFontBold(bool b)
+{
+    if (b)
+        setFontWeight(QFont::Bold);
+    else
+        setFontWeight(QFont::Normal);
+}
+
+void RichTextEditor::setFontPointSize(double d)
+{
+    QTextEdit::setFontPointSize(qreal(d));
+}
+
+void RichTextEditor::setText(const QString &text)
+{
+    if (Qt::mightBeRichText(text))
+        setHtml(text);
+    else
+        setPlainText(text);
+}
+
+void RichTextEditor::setDefaultFont(const QFont &font)
+{
+    document()->setDefaultFont(font);
+    if (font.pointSize() > 0)
+        setFontPointSize(font.pointSize());
+    else
+        setFontPointSize(QFontInfo(font).pointSize());
+    emit textChanged();
+}
+
+QString RichTextEditor::text(Qt::TextFormat format) const
+{
+    switch (format) {
+    case Qt::LogText:
+    case Qt::PlainText:
+        return toPlainText();
+    case Qt::RichText:
+        return toHtml();
+    case Qt::AutoText:
+        break;
+    }
+    const QString html = toHtml();
+    const QString plain = toPlainText();
+    QTextEdit tester;
+    tester.setPlainText(plain);
+    return tester.toHtml() == html ? plain : html;
+}
+
+RichTextEditorDialog::RichTextEditorDialog(QWidget *parent)  :
+    QDialog(parent),
+    m_editor(new RichTextEditor()),
+    m_text_edit(new HtmlTextEdit),
+    m_tab_widget(new QTabWidget),
+    m_state(Clean)
+{
+    setWindowTitle(tr("Edit text"));
+    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+    m_text_edit->setAcceptRichText(false);
+
+    connect(m_editor, SIGNAL(textChanged()), this, SLOT(richTextChanged()));
+    connect(m_text_edit, SIGNAL(textChanged()), this, SLOT(sourceChanged()));
+
+    // The toolbar needs to be created after the RichTextEditor
+    QToolBar *tool_bar = m_editor->createToolBar();
+    tool_bar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+
+    QWidget *rich_edit = new QWidget;
+    QVBoxLayout *rich_edit_layout = new QVBoxLayout(rich_edit);
+    rich_edit_layout->addWidget(tool_bar);
+    rich_edit_layout->addWidget(m_editor);
+
+    QWidget *plain_edit = new QWidget;
+    QVBoxLayout *plain_edit_layout = new QVBoxLayout(plain_edit);
+    plain_edit_layout->addWidget(m_text_edit);
+
+    m_tab_widget->setTabPosition(QTabWidget::South);
+    m_tab_widget->addTab(rich_edit, tr("Rich Text"));
+    m_tab_widget->addTab(plain_edit, tr("Source"));
+    connect(m_tab_widget, SIGNAL(currentChanged(int)),
+                          SLOT(tabIndexChanged(int)));
+
+    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
+    QPushButton *ok_button = buttonBox->button(QDialogButtonBox::Ok);
+    ok_button->setText(tr("&OK"));
+    ok_button->setDefault(true);
+    buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
+    connect(buttonBox, SIGNAL(accepted()), this, SLOT(on_buttonBox_accepted()));
+    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+    QVBoxLayout *layout = new QVBoxLayout(this);
+    layout->addWidget(m_tab_widget);
+    layout->addWidget(buttonBox);
+
+    m_editor->setFocus();
+
+}
+
+RichTextEditorDialog::~RichTextEditorDialog()
+{
+}
+
+/**
+ * @brief RichTextEditorDialog::on_buttonBox_accepted
+ */ 
+void RichTextEditorDialog::on_buttonBox_accepted() {
+	emit applyEditText( text(Qt::RichText) );
+	this->close();
+}
+
+
+int RichTextEditorDialog::showDialog()
+{
+    m_tab_widget->setCurrentIndex(0);
+    m_editor->selectAll();
+    m_editor->setFocus();
+
+    return exec();
+}
+
+void RichTextEditorDialog::setDefaultFont(const QFont &font)
+{
+    m_editor->setDefaultFont(font);
+}
+
+void RichTextEditorDialog::setText(const QString &text)
+{
+    m_editor->setText(text);
+    m_text_edit->setPlainText(text);
+    m_state = Clean;
+}
+
+QString RichTextEditorDialog::text(Qt::TextFormat format) const
+{
+    // In autotext mode, if the user has changed the source, use that
+    if (format == Qt::AutoText && (m_state == Clean || m_state == SourceChanged))
+        return m_text_edit->toPlainText();
+    // If the plain text HTML editor is selected, first copy its contents over
+    // to the rich text editor so that it is converted to Qt-HTML or actual
+    // plain text.
+    if (m_tab_widget->currentIndex() == SourceIndex && m_state == SourceChanged)
+        m_editor->setHtml(m_text_edit->toPlainText());
+    return m_editor->text(format);
+}
+
+void RichTextEditorDialog::tabIndexChanged(int newIndex)
+{
+    // Anything changed, is there a need for a conversion?
+    if (newIndex == SourceIndex && m_state != RichTextChanged)
+        return;
+    if (newIndex == RichTextIndex && m_state != SourceChanged)
+        return;
+    const State oldState = m_state;
+    // Remember the cursor position, since it is invalidated by setPlainText
+    QTextEdit *new_edit = (newIndex == SourceIndex) ? m_text_edit : m_editor;
+    const int position = new_edit->textCursor().position();
+
+    if (newIndex == SourceIndex) {
+        const QString html = m_editor->text(Qt::RichText);
+        qWarning("%s", qPrintable(runXSLT(html)));
+        m_text_edit->setPlainText(html);
+
+    } else
+        m_editor->setHtml(m_text_edit->toPlainText());
+
+    QTextCursor cursor = new_edit->textCursor();
+    cursor.movePosition(QTextCursor::End);
+    if (cursor.position() > position) {
+        cursor.setPosition(position);
+    }
+    new_edit->setTextCursor(cursor);
+    m_state = oldState; // Changed is triggered by setting the text
+}
+
+void RichTextEditorDialog::richTextChanged()
+{
+    m_state = RichTextChanged;
+}
+
+void RichTextEditorDialog::sourceChanged()
+{
+    m_state = SourceChanged;
+}
+
+} // namespace qdesigner_internal
+
+QT_END_NAMESPACE
+
+#include "richtexteditor.moc"

Added: trunk/sources/richtext/richtexteditor_p.h
===================================================================
--- trunk/sources/richtext/richtexteditor_p.h	                        (rev 0)
+++ trunk/sources/richtext/richtexteditor_p.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@xxxxxxxxx)
+**
+** This file is part of the Qt Designer of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@xxxxxxxxx.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists for the convenience
+// of Qt Designer.  This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef RICHTEXTEDITOR_H
+#define RICHTEXTEDITOR_H
+
+#include <QtGui/QTextEdit>
+#include <QtGui/QDialog>
+
+QT_BEGIN_NAMESPACE
+
+class QTabWidget;
+class QToolBar;
+
+class QDesignerFormEditorInterface;
+
+namespace qdesigner_internal {
+
+class RichTextEditor;
+
+class RichTextEditorDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit RichTextEditorDialog(QWidget *parent = 0);
+    ~RichTextEditorDialog();
+
+    int showDialog();
+    void setDefaultFont(const QFont &font);
+    void setText(const QString &text);
+    QString text(Qt::TextFormat format = Qt::AutoText) const;
+
+signals:
+	void applyEditText(const QString &);
+
+private slots:
+    void tabIndexChanged(int newIndex);
+    void richTextChanged();
+    void sourceChanged();
+	void on_buttonBox_accepted();
+	
+private:
+    enum TabIndex { RichTextIndex, SourceIndex };
+    enum State { Clean, RichTextChanged, SourceChanged };
+    RichTextEditor *m_editor;
+    QTextEdit      *m_text_edit;
+    QTabWidget     *m_tab_widget;
+    State m_state;
+    };
+
+} // namespace qdesigner_internal
+
+QT_END_NAMESPACE
+
+#endif // RITCHTEXTEDITOR_H

Added: trunk/sources/richtext/ui_addlinkdialog.h
===================================================================
--- trunk/sources/richtext/ui_addlinkdialog.h	                        (rev 0)
+++ trunk/sources/richtext/ui_addlinkdialog.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,117 @@
+/********************************************************************************
+** Form generated from reading UI file 'addlinkdialog.ui'
+**
+** Created: Thu 4. Apr 17:13:59 2013
+**      by: Qt User Interface Compiler version 4.8.4
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_ADDLINKDIALOG_H
+#define UI_ADDLINKDIALOG_H
+
+#include <QtCore/QVariant>
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QButtonGroup>
+#include <QtGui/QDialog>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QFormLayout>
+#include <QtGui/QFrame>
+#include <QtGui/QHeaderView>
+#include <QtGui/QLabel>
+#include <QtGui/QLineEdit>
+#include <QtGui/QSpacerItem>
+#include <QtGui/QVBoxLayout>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_AddLinkDialog
+{
+public:
+    QVBoxLayout *verticalLayout;
+    QFormLayout *formLayout;
+    QLabel *label;
+    QLineEdit *titleInput;
+    QLabel *label_2;
+    QLineEdit *urlInput;
+    QSpacerItem *verticalSpacer;
+    QFrame *line;
+    QDialogButtonBox *buttonBox;
+
+    void setupUi(QDialog *AddLinkDialog)
+    {
+        if (AddLinkDialog->objectName().isEmpty())
+            AddLinkDialog->setObjectName(QString::fromUtf8("AddLinkDialog"));
+        AddLinkDialog->setSizeGripEnabled(false);
+        AddLinkDialog->setModal(true);
+        verticalLayout = new QVBoxLayout(AddLinkDialog);
+        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
+        formLayout = new QFormLayout();
+        formLayout->setObjectName(QString::fromUtf8("formLayout"));
+        label = new QLabel(AddLinkDialog);
+        label->setObjectName(QString::fromUtf8("label"));
+
+        formLayout->setWidget(0, QFormLayout::LabelRole, label);
+
+        titleInput = new QLineEdit(AddLinkDialog);
+        titleInput->setObjectName(QString::fromUtf8("titleInput"));
+        titleInput->setMinimumSize(QSize(337, 0));
+
+        formLayout->setWidget(0, QFormLayout::FieldRole, titleInput);
+
+        label_2 = new QLabel(AddLinkDialog);
+        label_2->setObjectName(QString::fromUtf8("label_2"));
+
+        formLayout->setWidget(1, QFormLayout::LabelRole, label_2);
+
+        urlInput = new QLineEdit(AddLinkDialog);
+        urlInput->setObjectName(QString::fromUtf8("urlInput"));
+
+        formLayout->setWidget(1, QFormLayout::FieldRole, urlInput);
+
+
+        verticalLayout->addLayout(formLayout);
+
+        verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
+
+        verticalLayout->addItem(verticalSpacer);
+
+        line = new QFrame(AddLinkDialog);
+        line->setObjectName(QString::fromUtf8("line"));
+        line->setFrameShape(QFrame::HLine);
+        line->setFrameShadow(QFrame::Sunken);
+
+        verticalLayout->addWidget(line);
+
+        buttonBox = new QDialogButtonBox(AddLinkDialog);
+        buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+        buttonBox->setOrientation(Qt::Horizontal);
+        buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+        verticalLayout->addWidget(buttonBox);
+
+
+        retranslateUi(AddLinkDialog);
+        QObject::connect(buttonBox, SIGNAL(accepted()), AddLinkDialog, SLOT(accept()));
+        QObject::connect(buttonBox, SIGNAL(rejected()), AddLinkDialog, SLOT(reject()));
+
+        QMetaObject::connectSlotsByName(AddLinkDialog);
+    } // setupUi
+
+    void retranslateUi(QDialog *AddLinkDialog)
+    {
+        AddLinkDialog->setWindowTitle(QApplication::translate("AddLinkDialog", "Insert Link", 0, QApplication::UnicodeUTF8));
+        label->setText(QApplication::translate("AddLinkDialog", "Title:", 0, QApplication::UnicodeUTF8));
+        label_2->setText(QApplication::translate("AddLinkDialog", "URL:", 0, QApplication::UnicodeUTF8));
+    } // retranslateUi
+
+};
+
+namespace Ui {
+    class AddLinkDialog: public Ui_AddLinkDialog {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_ADDLINKDIALOG_H

Added: trunk/sources/terminal.cpp
===================================================================
--- trunk/sources/terminal.cpp	                        (rev 0)
+++ trunk/sources/terminal.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,532 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "terminal.h"
+#include "diagram.h"
+#include "qetgraphicsitem/element.h"
+#include "qetgraphicsitem/conductor.h"
+#include "diagramcommands.h"
+#include "qetapp.h"
+
+QColor Terminal::neutralColor      = QColor(Qt::blue);
+QColor Terminal::allowedColor      = QColor(Qt::darkGreen);
+QColor Terminal::warningColor      = QColor("#ff8000");
+QColor Terminal::forbiddenColor    = QColor(Qt::red);
+const qreal Terminal::terminalSize = 4.0;
+
+/**
+	Methode 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 Terminal::init(QPointF pf, QET::Orientation o, QString number) {
+	// definition du pount d'amarrage pour un conducteur
+	dock_conductor_  = pf;
+	
+	// definition de l'orientation de la borne (par defaut : sud)
+	if (o < QET::North || o > QET::West) ori_ = QET::South;
+	else ori_ = o;
+	
+	// calcul de la position du point d'amarrage a l'element
+	dock_elmt_ = dock_conductor_;
+	switch(ori_) {
+		case QET::North: dock_elmt_ += QPointF(0, Terminal::terminalSize);  break;
+		case QET::East : dock_elmt_ += QPointF(-Terminal::terminalSize, 0); break;
+		case QET::West : dock_elmt_ += QPointF(Terminal::terminalSize, 0);  break;
+		case QET::South:
+		default        : dock_elmt_ += QPointF(0, -Terminal::terminalSize);
+	}
+	// Number of terminal
+	number_terminal_ = number;
+	// par defaut : pas de conducteur
+	
+	// QRectF null
+	br_ = new QRectF();
+	previous_terminal_ = 0;
+	// divers
+	setAcceptsHoverEvents(true);
+	setAcceptedMouseButtons(Qt::LeftButton);
+	hovered_ = false;
+	setToolTip(QObject::tr("Borne", "tooltip"));
+}
+
+/**
+	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
+*/
+Terminal::Terminal(QPointF pf, QET::Orientation o, Element *e, Diagram *s) :
+	QGraphicsItem(e, s),
+	parent_element_(e),
+	hovered_color_(Terminal::neutralColor)
+{
+	init(pf, o, "_");
+}
+
+/**
+	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
+*/
+Terminal::Terminal(qreal pf_x, qreal pf_y, QET::Orientation o, Element *e, Diagram *s) :
+	QGraphicsItem(e, s),
+	parent_element_(e),
+	hovered_color_(Terminal::neutralColor)
+{
+	init(QPointF(pf_x, pf_y), o, "_");
+}
+
+/**
+	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 num number of terminal (ex 3 - 4 for NO)
+	@param e   Element auquel cette borne appartient
+	@param s   Scene sur laquelle figure cette borne
+*/
+Terminal::Terminal(QPointF pf, QET::Orientation o, QString num, Element *e, Diagram *s) :
+	QGraphicsItem(e, s),
+	parent_element_(e),
+	hovered_color_(Terminal::neutralColor)
+{
+	init(pf, o, num);
+}
+
+/**
+	Destructeur
+	La destruction de la borne entraine la destruction des conducteurs
+	associes.
+*/
+Terminal::~Terminal() {
+	foreach(Conductor *c, conductors_) delete c;
+	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 Terminal.
+*/
+QET::Orientation Terminal::orientation() const {
+	if (Element *elt = qgraphicsitem_cast<Element *>(parentItem())) {
+		// orientations actuelle et par defaut de l'element
+		int ori_cur = elt -> orientation();
+		if (ori_cur == 0) return(ori_);
+		else {
+			// calcul l'angle de rotation implique par l'orientation de l'element parent
+			// angle de rotation de la borne sur la scene, divise par 90
+			int angle = ori_cur + ori_;
+			while (angle >= 4) angle -= 4;
+			return((QET::Orientation)angle);
+		}
+	} else return(ori_);
+}
+
+
+/**
+ * @brief Terminal::setNumber
+ * @param number
+ */
+void Terminal::setNumber(QString number) {
+	number_terminal_ = number;
+}
+
+/**
+	Attribue un conductor a la borne
+	@param f Le conducteur a rattacher a cette borne
+*/
+bool Terminal::addConductor(Conductor *f) {
+	// pointeur 0 refuse
+	if (!f) return(false);
+	
+	// une seule des deux bornes du conducteur doit etre this
+	Q_ASSERT_X(((f -> terminal1 == this) ^ (f -> terminal2 == this)), "Terminal::addConductor", "Le conductor devrait etre relie exactement une fois a la terminal en cours");
+	
+	// determine l'autre borne a laquelle cette borne va etre relie grace au conducteur
+	Terminal *autre_terminal = (f -> terminal1 == this) ? f -> terminal2 : f -> terminal1;
+	
+	// verifie que la borne n'est pas deja reliee avec l'autre borne
+	bool deja_liees = false;
+	foreach (Conductor* conductor, conductors_) {
+		if (conductor -> terminal1 == autre_terminal || conductor -> terminal2 == autre_terminal) 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
+	conductors_.append(f);
+	return(true);
+}
+
+/**
+	Enleve un conducteur donne a la borne
+	@param f Conducteur a enlever
+*/
+void Terminal::removeConductor(Conductor *f) {
+	int index = conductors_.indexOf(f);
+	if (index == -1) return;
+	conductors_.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 Terminal::paint(QPainter *p, const QStyleOptionGraphicsItem *options, QWidget *widget) {
+	// en dessous d'un certain zoom, les bornes ne sont plus dessinees
+	if (options && options -> levelOfDetail < 0.5) return;
+	
+	p -> save();
+	
+#ifndef Q_WS_WIN
+	// corrige un bug de rendu ne se produisant que lors du rendu sur QGraphicsScene sous X11 au zoom par defaut
+	static bool must_correct_rendering_bug = QETApp::settings().value("correct-rendering", false).toBool();
+	if (must_correct_rendering_bug) {
+		Diagram *dia = diagram();
+		if (dia && options -> levelOfDetail == 1.0 && widget) {
+			// calcule la rotation qu'a subi l'element
+			qreal applied_rotation = 0.0;
+			if (Element *elt = qgraphicsitem_cast<Element *>(parentItem())) {
+				// orientations actuelle et par defaut de l'element
+				int ori_cur = elt -> orientation();
+				applied_rotation = QET::correctAngle(90.0 * ori_cur);
+			}
+			if (applied_rotation == 90.0) p -> translate(1.0, -1.0);
+			else if (applied_rotation == 180.0) p -> translate(-1.0, -1.0);
+			else if (applied_rotation == 270.0) p -> translate(-1.0, 1.0);
+		}
+	}
+#endif
+	
+	//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 c = mapFromParent(dock_conductor_);
+	QPointF e = mapFromParent(dock_elmt_);
+	
+	QPen t;
+	t.setWidthF(1.0);
+	
+	if (options && options -> levelOfDetail < 1.0) {
+		t.setCosmetic(true);
+	}
+	
+	// dessin de la borne en rouge
+	t.setColor(Qt::red);
+	p -> setPen(t);
+	p -> drawLine(c, e);
+	
+	// dessin du point d'amarrage au conducteur en bleu
+	t.setColor(hovered_color_);
+	p -> setPen(t);
+	p -> setBrush(hovered_color_);
+	if (hovered_) {
+		p -> setRenderHint(QPainter::Antialiasing, true);
+		p -> drawEllipse(QRectF(c.x() - 2.5, c.y() - 2.5, 5.0, 5.0));
+	} else p -> drawPoint(c);
+	
+	p -> restore();
+}
+
+/**
+	@return Le rectangle (en precision flottante) delimitant la borne et ses alentours.
+*/
+QRectF Terminal::boundingRect() const {
+	if (br_ -> isNull()) {
+		qreal dcx = dock_conductor_.x();
+		qreal dcy = dock_conductor_.y();
+		qreal dex = dock_elmt_.x();
+		qreal dey = dock_elmt_.y();
+		QPointF origin = (dcx <= dex && dcy <= dey ? dock_conductor_ : dock_elmt_);
+		origin += QPointF(-3.0, -3.0);
+		qreal w = qAbs((int)(dcx - dex)) + 7;
+		qreal h = qAbs((int)(dcy - dey)) + 7;
+		*br_ = QRectF(origin, QSizeF(w, h));
+	}
+	return(*br_);
+}
+
+/**
+	Gere l'entree de la souris sur la zone de la Borne.
+*/
+void Terminal::hoverEnterEvent(QGraphicsSceneHoverEvent *) {
+	hovered_ = true;
+	update();
+}
+
+/**
+	Gere les mouvements de la souris sur la zone de la Borne.
+*/
+void Terminal::hoverMoveEvent(QGraphicsSceneHoverEvent *) {
+}
+
+/**
+	Gere le fait que la souris sorte de la zone de la Borne.
+*/
+void Terminal::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 Terminal::mousePressEvent(QGraphicsSceneMouseEvent *e) {
+	if (Diagram *d = diagram()) {
+		d -> setConductorStart(mapToScene(QPointF(dock_conductor_)));
+		d -> setConductorStop(e -> scenePos());
+		d -> setConductor(true);
+		//setCursor(Qt::CrossCursor);
+	}
+}
+
+/**
+	Gere le fait qu'on bouge la souris sur la Borne.
+	@param e L'evenement souris correspondant
+*/
+void Terminal::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 (previous_terminal_) {
+		if (previous_terminal_ == this) hovered_ = true;
+		else previous_terminal_ -> hovered_ = false;
+		previous_terminal_ -> hovered_color_ = previous_terminal_ -> neutralColor;
+		previous_terminal_ -> update();
+	}
+	
+	
+	Diagram *d = diagram();
+	if (!d) return;
+	// si la scene est un Diagram, on actualise le poseur de conducteur
+	d -> setConductorStop(e -> scenePos());
+	
+	// on recupere la liste des qgi sous le pointeur
+	QList<QGraphicsItem *> qgis = d -> items(e -> scenePos());
+	
+	/* le qgi le plus haut
+	   = le poseur de conductor
+	   = 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()), "Terminal::mouseMoveEvent", "La liste d'items ne devrait pas etre vide");
+	
+	// s'il n'y rien d'autre que le poseur de conducteur dans la liste, on arrete la
+	if (qgis.size() <= 1) return;
+	
+	// sinon on prend le deuxieme element de la liste et on verifie s'il s'agit d'une borne
+	QGraphicsItem *qgi = qgis.at(1);
+	// si le qgi est une borne...
+	Terminal *other_terminal = qgraphicsitem_cast<Terminal *>(qgi);
+	if (!other_terminal) return;
+	previous_terminal_ = other_terminal;
+	
+	// s'il s'agit d'une borne, on lui applique l'effet hover approprie
+	if (!canBeLinkedTo(other_terminal)) {
+		other_terminal -> hovered_color_ = forbiddenColor;
+	} else if (other_terminal -> conductorsCount()) {
+		other_terminal -> hovered_color_ = warningColor;
+	} else {
+		other_terminal -> hovered_color_ = allowedColor;
+	}
+	
+	other_terminal -> hovered_ = true;
+	other_terminal -> update();
+}
+
+/**
+	Gere le fait qu'on relache la souris sur la Borne.
+	@param e L'evenement souris correspondant
+*/
+void Terminal::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
+	//setCursor(Qt::ArrowCursor);
+	previous_terminal_ = 0;
+	hovered_color_  = neutralColor;
+	// verifie que la scene est bien un Diagram
+	if (Diagram *d = diagram()) {
+		// on arrete de dessiner l'apercu du conducteur
+		d -> setConductor(false);
+		// on recupere l'element sous le pointeur lors du MouseReleaseEvent
+		QGraphicsItem *qgi = d -> 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
+		Terminal *other_terminal = qgraphicsitem_cast<Terminal *>(qgi);
+		if (!other_terminal) return;
+		// on remet la couleur de hover a sa valeur par defaut
+		other_terminal -> hovered_color_ = neutralColor;
+		other_terminal -> hovered_ = false;
+		// on s'arrete la s'il n'est pas possible de relier les bornes
+		if (!canBeLinkedTo(other_terminal)) return;
+		// autrement, on pose un conducteur
+		Conductor *new_conductor = new Conductor(this, other_terminal);
+		new_conductor -> setProperties(d -> defaultConductorProperties);
+		d -> undoStack().push(new AddConductorCommand(d, new_conductor));
+		new_conductor -> autoText();
+	}
+}
+
+/**
+	Met a jour l'eventuel conducteur relie a la borne.
+	@param newpos Position de l'element parent a prendre en compte
+*/
+void Terminal::updateConductor() {
+	if (!scene() || !parentItem()) return;
+	foreach (Conductor *conductor, conductors_) {
+		if (conductor -> isDestroyed()) continue;
+		conductor -> updatePath();
+	}
+}
+
+/**
+	@param other_terminal Autre borne
+	@return true si cette borne est reliee a other_terminal, false sion
+*/
+bool Terminal::isLinkedTo(Terminal *other_terminal) {
+	if (other_terminal == this) return(false);
+	
+	bool already_linked = false;
+	foreach (Conductor *c, conductors_) {
+		if (c -> terminal1 == other_terminal || c -> terminal2 == other_terminal) {
+			already_linked = true;
+			break;
+		}
+	}
+	return(already_linked);
+}
+
+/**
+	@param other_terminal Autre borne
+	@return true si cette borne peut etre reliee a other_terminal, false sion
+*/
+bool Terminal::canBeLinkedTo(Terminal *other_terminal) {
+	if (other_terminal == this) return(false);
+	
+	// l'autre borne appartient-elle au meme element ?
+	bool same_element = other_terminal -> parentElement() == parentElement();
+	// les connexions internes sont-elles autorisees ?
+	bool internal_connections_allowed = parentElement() -> internalConnections();
+	// les deux bornes sont-elles deja liees ?
+	bool already_linked = isLinkedTo(other_terminal);
+	// la liaison des deux bornes est-elle interdite ?
+	bool link_forbidden = (same_element && !internal_connections_allowed) || already_linked;
+	
+	return(!link_forbidden);
+}
+
+/**
+	@return La liste des conducteurs lies a cette borne
+*/
+QList<Conductor *> Terminal::conductors() const {
+	return(conductors_);
+}
+
+/**
+	Methode d'export en XML
+	@param doc Le Document XML a utiliser pour creer l'element XML
+	@return un QDomElement representant cette borne
+*/
+QDomElement Terminal::toXml(QDomDocument &doc) const {
+	QDomElement qdo = doc.createElement("terminal");
+	qdo.setAttribute("x", QString("%1").arg(dock_elmt_.x()));
+	qdo.setAttribute("y",  QString("%1").arg(dock_elmt_.y()));
+	qdo.setAttribute("orientation", ori_);
+	qdo.setAttribute("number", number_terminal_);
+	return(qdo);
+}
+
+/**
+	Permet de savoir si un element XML represente une borne
+	@param terminal Le QDomElement a analyser
+	@return true si le QDomElement passe en parametre est une borne, false sinon
+*/
+bool Terminal::valideXml(QDomElement &terminal) {
+	// verifie le nom du tag
+	if (terminal.tagName() != "terminal") return(false);
+	
+	// verifie la presence des attributs minimaux
+	if (!terminal.hasAttribute("x")) return(false);
+	if (!terminal.hasAttribute("y")) return(false);
+	if (!terminal.hasAttribute("orientation")) return(false);
+	if (!terminal.hasAttribute("number")) return(false);
+	
+	bool conv_ok;
+	// parse l'abscisse
+	terminal.attribute("x").toDouble(&conv_ok);
+	if (!conv_ok) return(false);
+	
+	// parse l'ordonnee
+	terminal.attribute("y").toDouble(&conv_ok);
+	if (!conv_ok) return(false);
+	
+	// parse l'id
+	terminal.attribute("id").toInt(&conv_ok);
+	if (!conv_ok) return(false);
+	
+	// parse l'orientation
+	int terminal_or = terminal.attribute("orientation").toInt(&conv_ok);
+	if (!conv_ok) return(false);
+	if (terminal_or != QET::North && terminal_or != QET::South && terminal_or != QET::East && terminal_or != QET::West) 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 terminal Le QDomElement a analyser
+	@return true si la borne "se reconnait" (memes coordonnes, meme orientation), false sinon
+*/
+bool Terminal::fromXml(QDomElement &terminal) {
+	return (
+		qFuzzyCompare(terminal.attribute("x").toDouble(), dock_elmt_.x()) &&
+		qFuzzyCompare(terminal.attribute("y").toDouble(), dock_elmt_.y()) &&
+		terminal.attribute("orientation").toInt() == ori_ &&
+		terminal.attribute("number") == number_terminal_
+	);
+}
+
+/**
+	@return le Diagram auquel cette borne appartient, ou 0 si cette borne est independant
+*/
+Diagram *Terminal::diagram() const {
+	return(qobject_cast<Diagram *>(scene()));
+}
+
+/**
+	@return L'element auquel cette borne est rattachee
+*/
+Element *Terminal::parentElement() const {
+	return(parent_element_);
+}
+

Added: trunk/sources/terminal.h
===================================================================
--- trunk/sources/terminal.h	                        (rev 0)
+++ trunk/sources/terminal.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,151 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TERMINAL_H
+#define TERMINAL_H
+#include <QtGui>
+#include <QtXml>
+#include "qet.h"
+class Conductor;
+class Diagram;
+class Element;
+/**
+	This class represents a terminal of an electrical element, i.e. a possible
+	plug point for conductors.
+*/
+class Terminal : public QGraphicsItem {
+	
+	// constructors, destructor
+	public:
+	Terminal(QPointF,      QET::Orientation, Element * = 0, Diagram * = 0);
+	Terminal(qreal, qreal, QET::Orientation, Element * = 0, Diagram * = 0);
+	Terminal(QPointF,      QET::Orientation, QString number, Element * = 0, Diagram * = 0);
+	virtual ~Terminal();
+	
+	private:
+	Terminal(const Terminal &);
+	
+	// methods
+	public:
+	/**
+		Enable the use of qgraphicsitem_cast to safely cast a QGraphicsItem into a
+		Terminal
+		@return the QGraphicsItem type
+	*/
+	virtual int type() const { return Type; }
+	
+	// implementation of QGraphicsItem pure virtual methods
+	void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+	QRectF boundingRect() const;
+	
+	// methods to manage conductors attached to the terminal
+	bool addConductor(Conductor *);
+	void removeConductor(Conductor *);
+	int conductorsCount() const;
+	Diagram *diagram() const;
+	Element *parentElement() const;
+	
+	QList<Conductor *> conductors() const;
+	QET::Orientation orientation() const;
+	QPointF dockConductor() const;
+	QString number() const;
+	void setNumber(QString number);
+	void updateConductor();
+	bool isLinkedTo(Terminal *);
+	bool canBeLinkedTo(Terminal *);
+	
+	// methods related to XML import/export
+	static bool valideXml(QDomElement  &);
+	bool fromXml (QDomElement &);
+	QDomElement toXml (QDomDocument &) const;
+	
+	protected:
+	// methods related to events management
+	void hoverEnterEvent  (QGraphicsSceneHoverEvent *);
+	void hoverMoveEvent   (QGraphicsSceneHoverEvent *);
+	void hoverLeaveEvent  (QGraphicsSceneHoverEvent *);
+	void mousePressEvent  (QGraphicsSceneMouseEvent *);
+	void mouseMoveEvent   (QGraphicsSceneMouseEvent *);
+	void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
+	
+	// attributes
+	public:
+	enum { Type = UserType + 1002 };
+	/// terminal length
+	static const qreal terminalSize;
+	
+	// Various static colors used for hover effects
+	/// default color
+	static QColor neutralColor;
+	/// color for legal actions
+	static QColor allowedColor;
+	/// color for allowed but fuzzy or not recommended  actions
+	static QColor warningColor;
+	/// color for forbidden actions
+	static QColor forbiddenColor;
+	
+	private:
+	/// Parent electrical element
+	Element *parent_element_;
+	/// docking point for conductors
+	QPointF dock_conductor_;
+	/// docking point for parent element
+	QPointF dock_elmt_;
+	/// terminal orientation
+	QET::Orientation ori_;
+	/// List of conductors attached to the terminal
+	QList<Conductor *> conductors_;
+	/// Pointer to a rectangle representing the terminal bounding rect;
+	/// used to calculate the bounding rect once only;
+	/// used a pointer because boundingRect() is supposed to be const.
+	QRectF *br_;
+	/// Last terminal seen through an attached conductor
+	Terminal *previous_terminal_;
+	/// Whether the mouse pointer is hovering the terminal
+	bool hovered_;
+	/// Color used for the hover effect
+	QColor hovered_color_;
+	/// Number of Terminal
+	QString number_terminal_;
+	
+	private:
+	void init(QPointF, QET::Orientation, QString number);
+};
+
+/**
+	@return the number of conductors attached to the terminal.
+*/
+inline int Terminal::conductorsCount() const {
+	return(conductors_.size());
+}
+
+/**
+	@return the position, relative to the scene, of the docking point for
+	conductors.
+*/
+inline QPointF Terminal::dockConductor() const {
+	return(mapToScene(dock_conductor_));
+}
+
+/**
+	@return the number of terminal.
+*/
+inline QString Terminal::number() const {
+	return(number_terminal_);
+}
+
+#endif

Added: trunk/sources/titleblock/dimension.cpp
===================================================================
--- trunk/sources/titleblock/dimension.cpp	                        (rev 0)
+++ trunk/sources/titleblock/dimension.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,58 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "dimension.h"
+
+/**
+	Constructor
+	@param v Numeric value for this dimension
+	@param t Kind of length, determining how to interpret the numeric value
+*/
+TitleBlockDimension::TitleBlockDimension(int v, QET::TitleBlockColumnLength t) :
+	type(t),
+	value(v)
+{
+}
+
+/**
+	@return a string describing this dimension in a human-readable format.
+*/
+QString TitleBlockDimension::toString() const {
+	QString dim_str;
+	if (type == QET::Absolute) {
+		dim_str = QObject::tr("%1px", "titleblock: absolute width");
+	} else if (type == QET::RelativeToTotalLength) {
+		dim_str = QObject::tr("%1%", "titleblock: width relative to total length");
+	} else if (type == QET::RelativeToRemainingLength) {
+		dim_str = QObject::tr("%1% du restant", "titleblock: width relative to remaining length");
+	}
+	return(dim_str.arg(value));
+}
+
+/**
+	@return a string describing this dimension in a short format.
+*/
+QString TitleBlockDimension::toShortString() const {
+	QString short_string;
+	if (type == QET::RelativeToTotalLength) {
+		short_string = "t";
+	} else if (type == QET::RelativeToRemainingLength) {
+		short_string = "r";
+	}
+	short_string += QString("%1%2;").arg(value).arg(type == QET::Absolute ? "px" : "%");
+	return(short_string);
+}

Added: trunk/sources/titleblock/dimension.h
===================================================================
--- trunk/sources/titleblock/dimension.h	                        (rev 0)
+++ trunk/sources/titleblock/dimension.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,36 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_DIMENSION_H
+#define TITLEBLOCK_SLASH_DIMENSION_H
+#include "qet.h"
+
+/**
+	This struct is a simple container associating a length with its type.
+	@see TitleBlockColumnLength 
+*/
+struct TitleBlockDimension {
+	// constructor
+	TitleBlockDimension(int, QET::TitleBlockColumnLength = QET::Absolute);
+	// methods
+	QString toString() const;
+	QString toShortString() const;
+	// attribute
+	QET::TitleBlockColumnLength type; ///< Kind of length
+	int value;                        ///< Numeric value
+};
+#endif

Added: trunk/sources/titleblock/dimensionwidget.cpp
===================================================================
--- trunk/sources/titleblock/dimensionwidget.cpp	                        (rev 0)
+++ trunk/sources/titleblock/dimensionwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,176 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "dimensionwidget.h"
+
+/**
+	Constructor
+	@param complete True for this dialog to show the radio buttons that allow
+	the user to specify whether the dimension is absolute, relative to the
+	total width or relative to the remaining width.
+	@param parent Parent QWidget
+*/
+TitleBlockDimensionWidget::TitleBlockDimensionWidget(bool complete, QWidget *parent) :
+	QDialog(parent),
+	complete_(complete),
+	read_only_(false)
+{
+	initWidgets();
+	initLayouts();
+}
+
+/**
+	Destructor
+*/
+TitleBlockDimensionWidget::~TitleBlockDimensionWidget() {
+}
+
+/**
+	@return true if this dialog shows the optional radio buttons
+*/
+bool TitleBlockDimensionWidget::isComplete() const {
+	return(complete_);
+}
+
+/**
+	@return a pointer to the label displayed right before the spinbox.
+	Useful to specify a custom text.
+*/
+QLabel *TitleBlockDimensionWidget::label() const {
+	return(spinbox_label_);
+}
+
+/**
+	@return a pointer to the spinbox
+	Useful to specify custom parameters, such as the minimum value
+*/
+QSpinBox *TitleBlockDimensionWidget::spinbox() const {
+	return(spinbox_);
+}
+
+/**
+	@return The dimension as currently shown by the dialog
+*/
+TitleBlockDimension TitleBlockDimensionWidget::value() const {
+	QET::TitleBlockColumnLength type = QET::Absolute;
+	if (complete_) {
+		type = static_cast<QET::TitleBlockColumnLength>(dimension_type_ -> checkedId());
+	}
+	return(TitleBlockDimension(spinbox_ -> value(), type));
+}
+
+/**
+	@param dim Dimension to be displayed and edited by this dialog
+*/
+void TitleBlockDimensionWidget::setValue(const TitleBlockDimension &dim) {
+	if (complete_) {
+		if (QAbstractButton *button = dimension_type_ -> button(dim.type)) {
+			button -> setChecked(true);
+		}
+	}
+	updateSpinBoxSuffix();
+	spinbox_ -> setValue(dim.value);
+}
+
+/**
+	@return Whether or not this widget should allow edition of the displayed
+	dimension.
+*/
+bool TitleBlockDimensionWidget::isReadOnly() const {
+	return(read_only_);
+}
+
+/**
+	@param read_only Whether or not this widget should allow edition of the
+	displayed dimension.
+*/
+void TitleBlockDimensionWidget::setReadOnly(bool read_only) {
+	if (read_only_ == read_only) return;
+	read_only_ = read_only;
+	
+	spinbox_ -> setReadOnly(read_only_);
+	if (complete_) {
+		absolute_button_  -> setEnabled(!read_only_);
+		relative_button_  -> setEnabled(!read_only_);
+		remaining_button_ -> setEnabled(!read_only_);
+	}
+}
+
+/**
+	Initialize the widgets composing the dialog.
+*/
+void TitleBlockDimensionWidget::initWidgets() {
+	// basic widgets: label + spinbox
+	spinbox_label_ = new QLabel(tr("Largeur :", "default dialog label"));
+	
+	spinbox_ = new QSpinBox();
+	spinbox_ -> setValue(50);
+	
+	// extra widgets, for the user to specify whether the value is absolute, relative, etc.
+	if (complete_) {
+		absolute_button_  = new QRadioButton(tr("Absolu",             "a traditional, absolute measure"));
+		relative_button_  = new QRadioButton(tr("Relatif au total",   "a percentage of the total width"));
+		remaining_button_ = new QRadioButton(tr("Relatif au restant", "a percentage of what remains from the total width"));
+		dimension_type_   = new QButtonGroup(this);
+		dimension_type_ -> addButton(absolute_button_,  QET::Absolute);
+		dimension_type_ -> addButton(relative_button_,  QET::RelativeToTotalLength);
+		dimension_type_ -> addButton(remaining_button_, QET::RelativeToRemainingLength);
+		absolute_button_ -> setChecked(true);
+		connect(dimension_type_, SIGNAL(buttonClicked(int)), this, SLOT(updateSpinBoxSuffix()));
+	}
+	
+	updateSpinBoxSuffix();
+	
+	// buttons, for the user to validate its input
+	buttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	connect(buttons_, SIGNAL(accepted()), this, SLOT(accept()));
+	connect(buttons_, SIGNAL(rejected()), this, SLOT(reject()));
+}
+
+/**
+	Initialize the layout of the dialog.
+*/
+void TitleBlockDimensionWidget::initLayouts() {
+	QHBoxLayout *hlayout0 = new QHBoxLayout();
+	hlayout0 -> addWidget(spinbox_label_);
+	hlayout0 -> addWidget(spinbox_);
+	QVBoxLayout *vlayout0 = new QVBoxLayout();
+	vlayout0 -> addLayout(hlayout0);
+	if (complete_) {
+		vlayout0 -> addWidget(absolute_button_);
+		vlayout0 -> addWidget(relative_button_);
+		vlayout0 -> addWidget(remaining_button_);
+	}
+	vlayout0 -> addWidget(buttons_);
+	setLayout(vlayout0);
+}
+
+/**
+	Ensure the suffix displayed by the spinbox matches the selected kind of length.
+*/
+void TitleBlockDimensionWidget::updateSpinBoxSuffix() {
+	if (complete_ && dimension_type_ -> checkedId() != QET::Absolute) {
+		spinbox_ -> setSuffix(tr("%", "spinbox suffix when changing the dimension of a row/column"));
+		spinbox_ -> setMinimum(1);
+		spinbox_ -> setMaximum(100);
+	} else {
+		spinbox_ -> setSuffix(tr("px", "spinbox suffix when changing the dimension of a row/column"));
+		spinbox_ -> setMinimum(5);
+		spinbox_ -> setMaximum(10000);
+	}
+	spinbox_ -> selectAll();
+}

Added: trunk/sources/titleblock/dimensionwidget.h
===================================================================
--- trunk/sources/titleblock/dimensionwidget.h	                        (rev 0)
+++ trunk/sources/titleblock/dimensionwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,66 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_DIMENSION_WIDGET_H
+#define TITLEBLOCK_SLASH_DIMENSION_WIDGET_H
+#include <QtGui>
+#include "dimension.h"
+
+/**
+	This class represents a dialog for the user to input a dimension: a row
+	height, a column width, etc.
+*/
+class TitleBlockDimensionWidget : public QDialog {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	TitleBlockDimensionWidget(bool, QWidget * parent = 0);
+	virtual ~TitleBlockDimensionWidget();
+	private:
+	TitleBlockDimensionWidget(const TitleBlockDimensionWidget &);
+	
+	// methods
+	public:
+	bool isComplete() const;
+	QLabel *label() const;
+	QSpinBox *spinbox() const;
+	TitleBlockDimension value() const;
+	void setValue(const TitleBlockDimension &);
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	
+	private:
+	void initWidgets();
+	void initLayouts();
+	
+	private slots:
+	void updateSpinBoxSuffix();
+	
+	// attributes
+	private:
+	bool complete_;                    ///< Whether or not this dialog is required to be complete, i.e. displaying also
+	QSpinBox *spinbox_;                ///< Spinbox displaying the length
+	QLabel *spinbox_label_;            ///< Label shown right before the spinbox
+	QRadioButton *absolute_button_;    ///< Radio button to indicate the length is absolute
+	QRadioButton *relative_button_;    ///< Radio button to indicate the length is relative to the total length
+	QRadioButton *remaining_button_;   ///< Radio button to indicate the length is relative to the remaining length
+	QButtonGroup *dimension_type_;     ///< QButtonGroup for the three radio buttons
+	QDialogButtonBox *buttons_;        ///< Buttons to validate the dialog
+	bool read_only_;                   ///< Whether or not this widget allow edition of the displayed dimension
+};
+#endif

Added: trunk/sources/titleblock/gridlayoutanimation.cpp
===================================================================
--- trunk/sources/titleblock/gridlayoutanimation.cpp	                        (rev 0)
+++ trunk/sources/titleblock/gridlayoutanimation.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,90 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "gridlayoutanimation.h"
+
+/**
+	Constructor
+	@param grid Grid to be animated
+	@param parent Parent QObject
+*/
+GridLayoutAnimation::GridLayoutAnimation(QGraphicsGridLayout *grid, QObject *parent) :
+	QVariantAnimation(parent),
+	grid_(grid)
+{
+}
+
+/**
+	Destructor
+*/
+GridLayoutAnimation::~GridLayoutAnimation() {
+}
+
+/**
+	@return the animated grid
+*/
+QGraphicsGridLayout *GridLayoutAnimation::grid() {
+	return(grid_);
+}
+
+/**
+	@param grid The grid to be animated
+*/
+void GridLayoutAnimation::setGrid(QGraphicsGridLayout *grid) {
+	grid_ = grid;
+}
+
+/**
+	@return the index of the row/column to be animated
+*/
+int GridLayoutAnimation::index() const {
+	return(index_);
+}
+
+/**
+	@param index the index of the row/column to be animated
+*/
+void GridLayoutAnimation::setIndex(int index) {
+	index_ = index;
+}
+
+/**
+	@return true if this object acts on a row, false if it acts on a column.
+*/
+bool GridLayoutAnimation::actsOnRows() const {
+	return(row_);
+}
+
+/**
+	@param acts_on_row true for this object to act on a row, false for it to
+	act on a column.
+*/
+void GridLayoutAnimation::setActsOnRows(bool acts_on_row) {
+	row_ = acts_on_row;
+}
+
+/**
+	Implementation of QVariantAnimation::updateCurrentValue().
+*/
+void GridLayoutAnimation::updateCurrentValue(const QVariant &value) {
+	if (!grid_) return;
+	if (row_) {
+		grid_ -> setRowFixedHeight(index_, value.toReal());
+	} else {
+		grid_ -> setColumnFixedWidth(index_, value.toReal());
+	}
+}

Added: trunk/sources/titleblock/gridlayoutanimation.h
===================================================================
--- trunk/sources/titleblock/gridlayoutanimation.h	                        (rev 0)
+++ trunk/sources/titleblock/gridlayoutanimation.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,50 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_GRID_LAYOUT_ANIMATION_H
+#define TITLEBLOCK_SLASH_GRID_LAYOUT_ANIMATION_H
+#include <QtGui>
+
+/**
+	This class allows animating a dimension change for a QGraphicsGridLayout
+	row or column.
+*/
+class GridLayoutAnimation : public QVariantAnimation {
+	// Constructors, destructor
+	public:
+	GridLayoutAnimation(QGraphicsGridLayout * = 0, QObject * = 0);
+	virtual ~GridLayoutAnimation();
+	
+	// methods
+	public:
+	QGraphicsGridLayout *grid();
+	void setGrid(QGraphicsGridLayout *);
+	int index() const;
+	void setIndex(int);
+	bool actsOnRows() const;
+	void setActsOnRows(bool);
+	
+	protected:
+	void updateCurrentValue(const QVariant &);
+	
+	// attributes
+	private:
+	QGraphicsGridLayout *grid_; ///< Grid this class will animate
+	bool row_;                  ///< Whether we should animate a row or a column
+	int index_;                 ///< Index of the row/column to be animated
+};
+#endif

Added: trunk/sources/titleblock/helpercell.cpp
===================================================================
--- trunk/sources/titleblock/helpercell.cpp	                        (rev 0)
+++ trunk/sources/titleblock/helpercell.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,153 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "helpercell.h"
+
+/**
+	Constructor
+	@param parent Parent QGraphicsItem
+*/
+HelperCell::HelperCell(QGraphicsItem *parent) :
+	QGraphicsObject(parent),
+	QGraphicsLayoutItem(),
+	background_color(Qt::white),
+	foreground_color(Qt::black),
+	label(),
+	orientation(Qt::Horizontal),
+	index(-1)
+{
+	setGraphicsItem(this);
+	setFlag(QGraphicsItem::ItemIsSelectable, false);
+}
+
+/**
+	Destructor
+*/
+HelperCell::~HelperCell() {
+}
+
+/**
+	Ensure geometry changes are handled for both QGraphicsObject and
+	QGraphicsLayoutItem.
+	@param g New geometry
+*/
+void HelperCell::setGeometry(const QRectF &g) {
+	prepareGeometryChange();
+	QGraphicsLayoutItem::setGeometry(g);
+	setPos(g.topLeft());
+}
+
+/**
+	@param which Size hint to be modified
+	@param constraint New value for the size hint
+	@return the size hint for \a which using the width or height of \a constraint
+*/
+QSizeF HelperCell::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const {
+	Q_UNUSED(which);
+	return(constraint);
+}
+
+/**
+	@return the bounding rect of this helper cell
+*/
+QRectF HelperCell::boundingRect() const {
+	return QRectF(QPointF(0,0), geometry().size());
+}
+
+/**
+	Handles the helper cell visual rendering
+	@param painter QPainter to be used for the rendering
+	@param option Rendering options
+	@param widget QWidget being painted, if any
+*/
+void HelperCell::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
+	Q_UNUSED(option);
+	Q_UNUSED(widget);
+	
+	QRectF drawing_rectangle(QPointF(0, 0), geometry().size());
+	
+	painter -> setPen(Qt::black);
+	painter -> setBrush(background_color);
+	painter -> drawRect(drawing_rectangle);
+	
+	painter -> setPen(foreground_color);
+	painter -> drawText(drawing_rectangle, Qt::AlignHCenter | Qt::AlignVCenter, label);
+}
+
+/**
+	@param type new type of this helper cell -- @see QET::TitleBlockColumnLength
+*/
+void HelperCell::setType(QET::TitleBlockColumnLength type) {
+	if (type == QET::Absolute) {
+		background_color = QColor("#C0FFFF");
+		foreground_color = Qt::black;
+	} else if (type == QET::RelativeToTotalLength) {
+		background_color = QColor("#FFA858");
+		foreground_color = Qt::black;
+	} else if (type == QET::RelativeToRemainingLength) {
+		background_color = QColor("#FFC0C0");
+		foreground_color = Qt::black;
+	}
+}
+
+/**
+	Set the list of actions displayed by the context menu of this helper cell.
+*/
+void HelperCell::setActions(const QList<QAction *> &actions) {
+	actions_ = actions;
+}
+
+/**
+	@return the list of actions displayed by the context menu of this helper cell.
+*/
+QList<QAction *> HelperCell::actions() const {
+	return actions_;
+}
+
+/**
+	@param text New label displayed by this helper cell
+	@param set_as_tooltip If true, the text is also used as tooltip.
+*/
+void HelperCell::setLabel(const QString &text, bool set_as_tooltip) {
+	label = text;
+	if (set_as_tooltip) {
+		setToolTip(text);
+	}
+}
+
+/**
+	Handle context menu events.
+	@param event Context menu event.
+*/
+void HelperCell::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
+	if (actions_.isEmpty()) return;
+	
+	QMenu context_menu;
+	foreach (QAction *action, actions_) {
+		context_menu.addAction(action);
+	}
+	emit(contextMenuTriggered(this));
+	context_menu.exec(event -> screenPos());
+}
+
+/**
+	Handle double click events.
+*/
+void HelperCell::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
+	
+	emit(doubleClicked(this));
+}

Added: trunk/sources/titleblock/helpercell.h
===================================================================
--- trunk/sources/titleblock/helpercell.h	                        (rev 0)
+++ trunk/sources/titleblock/helpercell.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,68 @@
+/*
+    Copyright 2006-2012 Xavier Guerrin
+    This file is part of QElectroTech.
+    
+    QElectroTech is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+    
+    QElectroTech is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_HELPER_CELL_H
+#define TITLEBLOCK_SLASH_HELPER_CELL_H
+#include <QtGui>
+#include "qet.h"
+
+/**
+	This class implements a helper widget for cells that indicate the length of
+	columns and rows.
+*/
+class HelperCell : public QGraphicsObject, public QGraphicsLayoutItem {
+	Q_OBJECT
+	Q_INTERFACES(QGraphicsLayoutItem)
+	
+	// constructor, destructor
+	public:
+	HelperCell(QGraphicsItem * = 0);
+	virtual ~HelperCell();
+	private:
+	HelperCell(const HelperCell &);
+	
+	// attributes
+	public:
+	QColor background_color;     ///< Background color when rendering this cell
+	QColor foreground_color;     ///< Text color when rendering this cell
+	QString label;               ///< Label displayed in this cell
+	Qt::Orientation orientation; ///< Orientation of this cell
+	int index;                   ///< Index of this cell
+	
+	// methods
+	public:
+	virtual void setGeometry(const QRectF &);
+	virtual QSizeF sizeHint(Qt::SizeHint, const QSizeF & = QSizeF()) const;
+	virtual QRectF boundingRect() const;
+	void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	virtual void setType(QET::TitleBlockColumnLength);
+	virtual void setActions(const QList<QAction *> &);
+	virtual QList<QAction *> actions() const;
+	virtual void setLabel(const QString &text, bool = true);
+	
+	protected:
+	void contextMenuEvent(QGraphicsSceneContextMenuEvent *);
+	void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
+	
+	signals:
+	void contextMenuTriggered(HelperCell *);
+	void doubleClicked(HelperCell *);
+	
+	private:
+	QList<QAction *> actions_; ///< List of actions displayed by the context menu
+};
+#endif

Added: trunk/sources/titleblock/integrationmovetemplateshandler.cpp
===================================================================
--- trunk/sources/titleblock/integrationmovetemplateshandler.cpp	                        (rev 0)
+++ trunk/sources/titleblock/integrationmovetemplateshandler.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,247 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "integrationmovetemplateshandler.h"
+#include "templatescollection.h"
+#include "qetmessagebox.h"
+
+/**
+	Constructor
+	@param parent Qwidget used as parent when showing the user dialog.
+*/
+IntegrationMoveTitleBlockTemplatesHandler::IntegrationMoveTitleBlockTemplatesHandler(QWidget *parent) :
+	MoveTitleBlockTemplatesHandler(parent),
+	parent_widget_(parent),
+	integ_dialog_(0)
+{
+}
+
+/**
+	Destructor
+*/
+IntegrationMoveTitleBlockTemplatesHandler::~IntegrationMoveTitleBlockTemplatesHandler() {
+}
+
+/**
+	@param src Source template
+	@param dst Target template
+	@return the action to be done if the target template already exists
+*/
+QET::Action IntegrationMoveTitleBlockTemplatesHandler::templateAlreadyExists(const TitleBlockTemplateLocation &src, const TitleBlockTemplateLocation &dst) {
+	QString no_parent_collection_error_message(tr("Impossible d'acc\351der \340 la cat\351gorie parente", "error message"));
+	QString cant_get_xml_description_error_message(tr("Impossible d'obtenir la description XML de ce mod\350le", "error message"));
+	
+	// we'll need the parent collection of both templates
+	TitleBlockTemplatesCollection *src_tbt_parent_collection = src.parentCollection();
+	if (!src_tbt_parent_collection) return(errorWithATemplate(src, no_parent_collection_error_message));
+	
+	TitleBlockTemplatesCollection *dst_tbt_parent_collection = dst.parentCollection();
+	if (!dst_tbt_parent_collection) return(errorWithATemplate(dst, no_parent_collection_error_message));
+	
+	
+	// first, we compare templates (actually we compare their XML code... sadly not the most efficient approach)
+	QDomElement src_xml_elmt = src.getTemplateXmlDescription();
+	if (src_xml_elmt.isNull()) return(errorWithATemplate(src, cant_get_xml_description_error_message));
+	QDomElement dst_xml_elmt = dst.getTemplateXmlDescription();
+	if (dst_xml_elmt.isNull()) return(errorWithATemplate(dst, cant_get_xml_description_error_message));
+	
+	QDomDocument src_tbt_document;
+	src_tbt_document.appendChild(src_tbt_document.importNode(src_xml_elmt, true));
+	QDomDocument dst_tbt_document;
+	dst_tbt_document.appendChild(dst_tbt_document.importNode(dst_xml_elmt, true));
+	
+	
+	if (src_tbt_document.toString(0) == dst_tbt_document.toString(0)) {
+		// the templates are the same, consider the integration is done
+		qDebug() << Q_FUNC_INFO << "Not integrating" << src.parentCollection() << "/" << src.name()<< "because it is already present in the project";
+		return(QET::Managed);
+	} else {
+		return(askUser(src, dst));
+	}
+}
+
+/**
+	Display an error message related to a specific title block template.
+	@param tbt Problematic title block template
+	@param message Error message.
+*/
+QET::Action IntegrationMoveTitleBlockTemplatesHandler::errorWithATemplate(const TitleBlockTemplateLocation &tbt, const QString &message) {
+	QString error_message = QString("Une erreur s'est produite avec le mod\350le %1\240: %2").arg(tbt.toString()).arg(message);
+	QET::MessageBox::critical(
+		parent_widget_,
+		tr("Erreur", "message box title"),
+		error_message,
+		QMessageBox::Ok,
+		QMessageBox::Ok
+	);
+	return(QET::Ignore);
+}
+
+/**
+	@return the name to be used when this object returns QET::Rename
+	@see QET::Action
+*/
+QString IntegrationMoveTitleBlockTemplatesHandler::nameForRenamingOperation() {
+	return(rename_);
+}
+
+/**
+	@return the current date with a filename-friendly format
+*/
+QString IntegrationMoveTitleBlockTemplatesHandler::dateString() const {
+	return(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
+}
+
+/**
+	@param tbt A title block template location
+	@return a name to be used in order to duplicate the title block template.
+	This name is based on the current date.
+*/
+QString IntegrationMoveTitleBlockTemplatesHandler::newNameForTemplate(const TitleBlockTemplateLocation &tbt) {
+	return(QString("%1-%2.elmt").arg(tbt.name()).arg(dateString()));
+}
+
+/**
+	Ask the use whether they wish to erase the already existing template, rename it or cancel the operation.
+	@param src Source title block template
+	@param dst Target title block template
+	@return the user answer
+*/
+QET::Action IntegrationMoveTitleBlockTemplatesHandler::askUser(const TitleBlockTemplateLocation &src, const TitleBlockTemplateLocation &dst) {
+	Q_UNUSED(src)
+	initDialog();
+	int result = integ_dialog_ -> exec();
+	if (result == QDialog::Accepted) {
+		if (use_existing_template_ -> isChecked()) {
+			return(QET::Managed);
+		} else if (erase_template_ -> isChecked()) {
+			return(QET::Erase);
+		} else {
+			rename_ = newNameForTemplate(dst);
+			return(QET::Rename);
+		}
+	} else {
+		return(QET::Abort);
+	}
+}
+
+/**
+	Initialize the user dialog.
+*/
+void IntegrationMoveTitleBlockTemplatesHandler::initDialog() {
+	if (integ_dialog_) return;
+	integ_dialog_ = new QDialog(parent_widget_);
+	integ_dialog_ -> setWindowTitle(tr("Int\351gration d'un mod\350le de cartouche"));
+	
+	dialog_label_ = new QLabel(
+		QString(
+			tr(
+				"Le mod\350le a d\351j\340 \351t\351 "
+				"int\351gr\351 dans le projet. Toutefois, la version que vous "
+				"tentez d'appliquer semble diff\351rente. Que souhaitez-vous "
+				"faire ?",
+				"dialog content - %1 is a title block template name"
+			)
+		)
+	);
+	
+	use_existing_template_ = new QRadioButton(
+		QString(
+			tr(
+				"Utiliser le mod\350le d\351j\340 int\351gr\351",
+				"dialog content"
+			)
+		)
+	);
+	
+	integrate_new_template_ = new QRadioButton(
+		QString(
+			tr(
+				"Int\351grer le mod\350le d\351pos\351",
+				"dialog content"
+			)
+		)
+	);
+	radioButtonleftMargin(integrate_new_template_);
+	
+	erase_template_ = new QRadioButton(
+		QString(
+			tr(
+				"\311craser le mod\350le d\351j\340 int\351gr\351",
+				"dialog content"
+			)
+		)
+	);
+	radioButtonleftMargin(erase_template_);
+	
+	integrate_both_ = new QRadioButton(
+		QString(
+			tr(
+				"Faire cohabiter les deux mod\350les",
+				"dialog content"
+			)
+		)
+	);
+	
+	button_group1_ = new QButtonGroup(this);
+	button_group1_ -> addButton(use_existing_template_);
+	button_group1_ -> addButton(integrate_new_template_);
+	button_group2_ = new QButtonGroup(this);
+	button_group2_ -> addButton(erase_template_);
+	button_group2_ -> addButton(integrate_both_);
+	
+	integrate_new_template_ -> setChecked(true);
+	integrate_both_ -> setChecked(true);
+	
+	buttons_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	
+	dialog_glayout = new QGridLayout();
+	dialog_glayout -> setColumnMinimumWidth(0, 20);
+	dialog_glayout -> addWidget(erase_template_,  0, 1);
+	dialog_glayout -> addWidget(integrate_both_, 1, 1);
+	
+	dialog_vlayout_ = new QVBoxLayout(integ_dialog_);
+	dialog_vlayout_ -> addWidget(dialog_label_);
+	dialog_vlayout_ -> addWidget(use_existing_template_);
+	dialog_vlayout_ -> addWidget(integrate_new_template_);
+	dialog_vlayout_ -> addLayout(dialog_glayout);
+	dialog_vlayout_ -> addWidget(buttons_);
+	
+	connect(use_existing_template_,  SIGNAL(toggled(bool)), this,          SLOT(correctRadioButtons()));
+	connect(integrate_new_template_, SIGNAL(toggled(bool)), this,          SLOT(correctRadioButtons()));
+	connect(buttons_,                SIGNAL(accepted()),    integ_dialog_, SLOT(accept()));
+	connect(buttons_,                SIGNAL(rejected()),    integ_dialog_, SLOT(reject()));
+}
+
+/**
+	Increase the left margin of a radiob utton.
+	@param button Radio button
+*/
+void IntegrationMoveTitleBlockTemplatesHandler::radioButtonleftMargin(QRadioButton *button) {
+	int a, b, c, d;
+	button -> getContentsMargins(&a, &b, &c, &d);
+	button -> setContentsMargins(a + 15, b, c, d);
+}
+
+/**
+	Ensure the dialog remains consistent.
+*/
+void IntegrationMoveTitleBlockTemplatesHandler::correctRadioButtons() {
+	erase_template_ -> setEnabled(integrate_new_template_ -> isChecked());
+	integrate_both_ -> setEnabled(integrate_new_template_ -> isChecked());
+}
+

Added: trunk/sources/titleblock/integrationmovetemplateshandler.h
===================================================================
--- trunk/sources/titleblock/integrationmovetemplateshandler.h	                        (rev 0)
+++ trunk/sources/titleblock/integrationmovetemplateshandler.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,73 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_INTEGRATION_MOVE_TEMPLATES_HANDLER_H
+#define TITLEBLOCK_SLASH_INTEGRATION_MOVE_TEMPLATES_HANDLER_H
+#include "movetemplateshandler.h"
+#include <QtGui>
+/**
+	This class implements the interface defined by
+	MoveTitleBlockTemplatesHandler to ease the integration of title block
+	templates from files-based collections into projects.
+*/
+class IntegrationMoveTitleBlockTemplatesHandler : public MoveTitleBlockTemplatesHandler {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	IntegrationMoveTitleBlockTemplatesHandler(QWidget * = 0);
+	virtual ~IntegrationMoveTitleBlockTemplatesHandler();
+	private:
+	IntegrationMoveTitleBlockTemplatesHandler(const IntegrationMoveTitleBlockTemplatesHandler &);
+	
+	// methods
+	public:
+	virtual QET::Action templateAlreadyExists(const TitleBlockTemplateLocation &src, const TitleBlockTemplateLocation &dst);
+	virtual QET::Action errorWithATemplate(const TitleBlockTemplateLocation &, const QString &);
+	virtual QString nameForRenamingOperation();
+	
+	private:
+	QString dateString() const;
+	QString newNameForTemplate(const TitleBlockTemplateLocation &);
+	QET::Action askUser(const TitleBlockTemplateLocation &, const TitleBlockTemplateLocation &);
+	void initDialog();
+	void radioButtonleftMargin(QRadioButton *);
+	
+	private slots:
+	void correctRadioButtons();
+	
+	// attributes
+	private:
+	QWidget *parent_widget_;              ///< Widget used as parent to display dialogs
+	QString rename_;                      ///< Name to be used when renaming a title block template
+	QDialog *integ_dialog_;               ///< Dialog in case of conflict when integrating a title block template
+	QLabel *dialog_label_;
+	QVBoxLayout *dialog_vlayout_;
+	QGridLayout *dialog_glayout;
+	QDialogButtonBox *buttons_;
+	QRadioButton *use_existing_template_;    ///< Radio button the user may click to use the existing template and stop the integration
+	QRadioButton *integrate_new_template_;   ///< Radio button the user may click to integrate the template
+	QRadioButton *erase_template_;           ///< Radio button the user may click for the integrated template to erase the existing one
+	/*
+		Radio button the user may click for the integrated template to be
+		automatically renamed in order to be stored along with the existing one.
+	*/
+	QRadioButton *integrate_both_;
+	QButtonGroup *button_group1_;
+	QButtonGroup *button_group2_;
+};
+#endif

Added: trunk/sources/titleblock/movetemplateshandler.h
===================================================================
--- trunk/sources/titleblock/movetemplateshandler.h	                        (rev 0)
+++ trunk/sources/titleblock/movetemplateshandler.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,46 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_MOVE_TEMPLATES_HANDLER_H
+#define TITLEBLOCK_SLASH_MOVE_TEMPLATES_HANDLER_H
+#include <QtCore>
+#include "qet.h"
+#include "templatelocation.h"
+
+/**
+	This class defines the minimal interface required to implement an object
+	able to handle a title block template move or copy.
+	It is a Strategy pattern that embeds the copy/move error handling instead
+	of the whole process.
+*/
+class MoveTitleBlockTemplatesHandler : public QObject {
+	Q_OBJECT
+	
+	// Constructors, destructor
+	public:
+	MoveTitleBlockTemplatesHandler(QObject * = 0) {}
+	virtual ~MoveTitleBlockTemplatesHandler() {}
+	private:
+	MoveTitleBlockTemplatesHandler(const MoveTitleBlockTemplatesHandler &);
+	
+	// methods
+	public:
+	virtual QET::Action templateAlreadyExists(const TitleBlockTemplateLocation &src, const TitleBlockTemplateLocation &dst) = 0;
+	virtual QET::Action errorWithATemplate(const TitleBlockTemplateLocation &, const QString &) = 0;
+	virtual QString nameForRenamingOperation() = 0;
+};
+#endif

Added: trunk/sources/titleblock/qettemplateeditor.cpp
===================================================================
--- trunk/sources/titleblock/qettemplateeditor.cpp	                        (rev 0)
+++ trunk/sources/titleblock/qettemplateeditor.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,951 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "qettemplateeditor.h"
+#include "qetmessagebox.h"
+#include "qeticons.h"
+#include "qetapp.h"
+#include "qetproject.h"
+#include "templatecellwidget.h"
+#include "templatecommands.h"
+#include "templateview.h"
+#include "templatelocationsaver.h"
+#include "templatelogomanager.h"
+#include "templatecellwidget.h"
+
+/**
+	@param parent parent QWidget of this window
+*/
+QETTitleBlockTemplateEditor::QETTitleBlockTemplateEditor(QWidget *parent) :
+	QETMainWindow(parent),
+	opened_from_file_(false),
+	read_only_(false),
+	duplicate_(false),
+	tb_template_(0),
+	logo_manager_(0)
+{
+	setWindowIcon(QET::Icons::QETLogo);
+	setAttribute(Qt::WA_DeleteOnClose);
+	
+	initWidgets();
+	initActions();
+	initMenus();
+	initToolbars();
+	readSettings();
+}
+
+/**
+	Destructor
+*/
+QETTitleBlockTemplateEditor::~QETTitleBlockTemplateEditor() {
+}
+
+/**
+	@return the location of the currently edited template
+*/
+TitleBlockTemplateLocation QETTitleBlockTemplateEditor::location() const {
+	return(location_);
+}
+
+/**
+	@return true if the provided filepath matches the currently edited template.
+	@param filepath path of a title block template on the filesystem
+*/
+bool QETTitleBlockTemplateEditor::isEditing(const QString &filepath) {
+	QString current_filepath;
+	if (opened_from_file_) {
+		current_filepath = filepath_;
+	} else {
+		current_filepath = QETApp::realPath(location_.toString());
+	}
+	
+	return(
+		QET::compareCanonicalFilePaths(
+			current_filepath,
+			filepath
+		)
+	);
+}
+
+/**
+	@param true for this editor to prompt the user for a new template name as
+	soon as the window appears in order to duplicate the edited one.
+*/
+void QETTitleBlockTemplateEditor::setOpenForDuplication(bool duplicate) {
+	duplicate_ = duplicate;
+}
+
+/**
+	@return true if this editor will prompt the user for a new template name as
+	soon as the window appears in order to duplicate the edited one.
+*/
+bool QETTitleBlockTemplateEditor::openForDuplication() const {
+	return(duplicate_);
+}
+
+/**
+	@return true if the currently edited template can be closed. A template can be
+	closed if it has not been modified. If the template has been modified, this
+	method asks the user what he wants to do.
+*/
+bool QETTitleBlockTemplateEditor::canClose() {
+	if (undo_stack_ -> isClean()) return(true);
+	// ask the user whether he wants to save the current template
+	QMessageBox::StandardButton answer = QET::MessageBox::question(
+		this,
+		tr("Enregistrer le mod\350le en cours ?", "dialog title"),
+		QString(
+			tr(
+				"Voulez-vous enregistrer le mod\350le %1 ?",
+				"dialog content - %1 is a title block template name"
+			)
+		).arg(location_.name()),
+		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+		QMessageBox::Cancel
+	);
+	bool result;
+	switch(answer) {
+		case QMessageBox::Cancel: result = false;  break; // the user hits Cancel or closes the dialog: abort the closing
+		case QMessageBox::Yes:    result = save(); break; // the user hits Yes: la reussite depend de l'enregistrement
+		default:                  result = true;          // the user hits no: the editor can be closed
+	}
+	return(result);
+}
+
+/**
+	@param event Object describing the received event.
+*/
+void QETTitleBlockTemplateEditor::firstActivation(QEvent *event) {
+	Q_UNUSED(event)
+	if (duplicate_ && !opened_from_file_ && location_.parentCollection()) {
+		// this editor is supposed to duplicate its current location
+		QTimer::singleShot(250, this, SLOT(duplicateCurrentLocation()));
+	}
+}
+
+/**
+	Handle the closing of the main window
+	@param qce The QCloseEvent event
+*/
+void QETTitleBlockTemplateEditor::closeEvent(QCloseEvent *qce) {
+	if (canClose()) {
+		writeSettings();
+		setAttribute(Qt::WA_DeleteOnClose);
+		qce -> accept();
+	} else qce -> ignore();
+}
+
+/**
+	Ask the user for a new template name in order to duplicate the currently
+	edited template.
+*/
+void QETTitleBlockTemplateEditor::duplicateCurrentLocation() {
+	// this method does not work for templates edited from the filesystem
+	if (opened_from_file_) return;
+	
+	QString proposed_name;
+	if (location_.name().isEmpty()) {
+		proposed_name = tr("nouveau_modele", "template name suggestion when duplicating the default one");
+	} else {
+		proposed_name = QString("%1_copy").arg(location_.name());
+	}
+	
+	bool accepted = false;
+	QString new_template_name = QInputDialog::getText(
+		this,
+		tr("Dupliquer un mod\350le de cartouche", "input dialog title"),
+		tr("Pour dupliquer ce mod\350le, entrez le nom voulu pour sa copie", "input dialog text"),
+		QLineEdit::Normal,
+		proposed_name,
+		&accepted
+	);
+	if (accepted) {
+		TitleBlockTemplateLocation new_template_location(new_template_name, location_.parentCollection());
+		saveAs(new_template_location);
+	}
+}
+
+/**
+	@param location Location of the tile block template to be edited.
+*/
+bool QETTitleBlockTemplateEditor::edit(const TitleBlockTemplateLocation &location) {
+	// the template name may be empty to create a new one
+	const TitleBlockTemplate *tb_template_orig;
+	if (location.name().isEmpty()) {
+		// loads the default title block template provided by the application
+		// it will be used as a start point to design the title block
+		tb_template_orig = QETApp::defaultTitleBlockTemplate();
+	} else {
+		tb_template_orig = location.getTemplate();
+	}
+	if (!tb_template_orig) {
+		/// TODO The TBT does not exist, manage error
+		return(false);
+	}
+	
+	opened_from_file_ = false;
+	location_ = location;
+	setReadOnly(location.isReadOnly());
+	editCopyOf(tb_template_orig);
+	return(true);
+}
+
+/**
+	Edit the given template.
+	@param project Parent project of the template to edit.
+	@param template_name Name of the template to edit within its parent project.
+	@return true if this editor was able to edit the given template, false otherwise
+*/
+bool QETTitleBlockTemplateEditor::edit(QETProject *project, const QString &template_name) {
+	// we require a project we will rattach templates to
+	if (!project) return(false);
+	
+	// the template name may be empty to create a new one
+	const TitleBlockTemplate *tb_template_orig;
+	if (template_name.isEmpty()) {
+		// loads the default title block template provided by the application
+		// it will be used as a start point to design the title block
+		tb_template_orig = QETApp::defaultTitleBlockTemplate();
+	} else {
+		tb_template_orig = project -> getTemplateByName(template_name);
+	}
+	
+	if (!tb_template_orig) {
+		/// TODO The TBT does not exist, manage error
+		return(false);
+	}
+	
+	opened_from_file_ = false;
+	location_.setParentCollection(project -> embeddedTitleBlockTemplatesCollection());
+	location_.setName(template_name);
+	setReadOnly(project -> isReadOnly());
+	return(editCopyOf(tb_template_orig));
+}
+
+/**
+	@param file_path Path of the template file to edit.
+	@return false if a problem occured while opening the template, true otherwise.
+*/
+bool QETTitleBlockTemplateEditor::edit(const QString &file_path) {
+	// get title block template object from the file, edit it
+	TitleBlockTemplate *tbt = new TitleBlockTemplate();
+	bool loading = tbt -> loadFromXmlFile(file_path);
+	if (!loading) {
+		/// TODO the file opening failed, warn the user?
+		return(false);
+	}
+	
+	bool editing = edit(tbt);
+	if (!editing) {
+		/// TODO the file editing failed, warn the user?
+		return(false);
+	}
+	
+	QFileInfo file_path_info(file_path);
+	filepath_ = file_path;
+	opened_from_file_ = true;
+	setReadOnly(!file_path_info.isWritable());
+	return(true);
+}
+
+/**
+	@param tbt Title block template to be edited
+	@return false if a problem occured while opening the template, true otherwise.
+*/
+bool QETTitleBlockTemplateEditor::editCopyOf(const TitleBlockTemplate *tbt) {
+	if (!tbt) return(false);
+	return(edit(tbt -> clone()));
+}
+
+/**
+	@param tbt Title block template to be directly edited
+	@return false if a problem occured while opening the template, true otherwise.
+*/
+bool QETTitleBlockTemplateEditor::edit(TitleBlockTemplate *tbt) {
+	if (!tbt) return(false);
+	tb_template_ = tbt;
+	template_edition_area_view_ -> setTitleBlockTemplate(tb_template_);
+	template_cell_editor_widget_ -> updateLogosComboBox(tb_template_);
+	updateEditorTitle();
+	return(true);
+}
+
+/**
+	Launches the logo manager widget, which allows the user to manage the
+	logos embedded within the edited template.
+*/
+void QETTitleBlockTemplateEditor::editLogos() {
+	if (tb_template_) {
+		if (!logo_manager_) {
+			initLogoManager();
+		}
+		
+		logo_manager_ -> layout() -> setContentsMargins(0, 0, 0, 0);
+		QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close);
+		
+		QVBoxLayout *vlayout0 = new QVBoxLayout();
+		vlayout0 -> addWidget(logo_manager_);
+		vlayout0 -> addWidget(buttons);
+		
+		QDialog d(this);
+		d.setWindowTitle(logo_manager_ -> windowTitle());
+		d.setLayout(vlayout0);
+		connect(buttons, SIGNAL(rejected()), &d, SLOT(reject()));
+		d.exec();
+		
+		// prevent the logo manager from being deleted along with the dialog
+		logo_manager_ -> setParent(this);
+	}
+}
+
+/**
+	Launch a new title block template editor.
+*/
+void QETTitleBlockTemplateEditor::newTemplate() {
+	QETTitleBlockTemplateEditor *qet_template_editor = new QETTitleBlockTemplateEditor();
+	qet_template_editor -> edit(TitleBlockTemplateLocation());
+	qet_template_editor -> show();
+}
+
+/**
+	Initialize the various actions.
+*/
+void QETTitleBlockTemplateEditor::initActions() {
+	new_            = new QAction(QET::Icons::DocumentNew,          tr("&Nouveau",                     "menu entry"), this);
+	open_           = new QAction(QET::Icons::DocumentOpen,         tr("&Ouvrir",                      "menu entry"), this);
+	open_from_file_ = new QAction(QET::Icons::DocumentOpen,         tr("Ouvrir depuis un fichier",     "menu entry"), this);
+	save_           = new QAction(QET::Icons::DocumentSave,         tr("&Enregistrer",                 "menu entry"), this);
+	save_as_        = new QAction(QET::Icons::DocumentSaveAs,       tr("Enregistrer sous",             "menu entry"), this);
+	save_as_file_   = new QAction(QET::Icons::DocumentSaveAs,       tr("Enregistrer vers un fichier",  "menu entry"), this);
+	quit_           = new QAction(QET::Icons::ApplicationExit,      tr("&Quitter",                     "menu entry"), this);
+	undo_           = undo_stack_ -> createUndoAction(this);
+	redo_           = undo_stack_ -> createRedoAction(this);
+	cut_            = new QAction(QET::Icons::EditCut,              tr("Co&uper", "menu entry"),                      this);
+	copy_           = new QAction(QET::Icons::EditCopy,             tr("Cop&ier", "menu entry"),                      this);
+	paste_          = new QAction(QET::Icons::EditPaste,            tr("C&oller", "menu entry"),                      this);
+	edit_logos_     = new QAction(QET::Icons::InsertImage,          tr("G\351rer les logos", "menu entry"),           this);
+	edit_info_      = new QAction(QET::Icons::UserInformations,     tr("\311diter les informations compl\351mentaires", "menu entry"), this);
+	zoom_in_        = new QAction(QET::Icons::ZoomIn,               tr("Zoom avant",                   "menu entry"), this);
+	zoom_out_       = new QAction(QET::Icons::ZoomOut,              tr("Zoom arri\350re",              "menu entry"), this);
+	zoom_fit_       = new QAction(QET::Icons::ZoomFitBest,          tr("Zoom adapt\351",               "menu entry"), this);
+	zoom_reset_     = new QAction(QET::Icons::ZoomOriginal,         tr("Pas de zoom",                  "menu entry"), this);
+	add_row_        = new QAction(QET::Icons::EditTableInsertRowAbove,    tr("Ajouter une &ligne",      "menu entry"), this);
+	add_col_        = new QAction(QET::Icons::EditTableInsertColumnRight, tr("Ajouter une &colonne",    "menu entry"), this);
+	merge_cells_    = new QAction(QET::Icons::EditTableCellMerge,   tr("&Fusionner les cellules",      "menu entry"), this);
+	split_cell_     = new QAction(QET::Icons::EditTableCellSplit,   tr("&S\351parer les cellules",     "menu entry"), this);
+	
+	undo_ -> setIcon(QET::Icons::EditUndo);
+	redo_ -> setIcon(QET::Icons::EditRedo);
+	
+	new_              -> setShortcut(QKeySequence::New);
+	open_             -> setShortcut(QKeySequence::Open);
+	open_from_file_   -> setShortcut(tr("Ctrl+Shift+O", "shortcut to open a template from a file"));
+	save_             -> setShortcut(QKeySequence::Save);
+	save_as_file_     -> setShortcut(tr("Ctrl+Shift+S", "shortcut to save a template to a file"));
+	quit_             -> setShortcut(QKeySequence(tr("Ctrl+Q", "shortcut to quit")));
+	undo_             -> setShortcut(QKeySequence::Undo);
+	redo_             -> setShortcut(QKeySequence::Redo);
+	cut_              -> setShortcut(QKeySequence::Cut);
+	copy_             -> setShortcut(QKeySequence::Copy);
+	paste_            -> setShortcut(QKeySequence::Paste);
+	edit_logos_       -> setShortcut(QKeySequence(tr("Ctrl+T", "shortcut to manage embedded logos")));
+	edit_info_        -> setShortcut(QKeySequence(tr("Ctrl+Y", "shortcut to edit extra information")));
+	merge_cells_      -> setShortcut(QKeySequence(tr("Ctrl+J", "shortcut to merge cells")));
+	split_cell_       -> setShortcut(QKeySequence(tr("Ctrl+K", "shortcut to split merged cell")));
+	zoom_in_          -> setShortcut(QKeySequence::ZoomIn);
+	zoom_out_         -> setShortcut(QKeySequence::ZoomOut);
+	zoom_fit_         -> setShortcut(QKeySequence(tr("Ctrl+9", "shortcut to enable fit zoom")));
+	zoom_reset_       -> setShortcut(QKeySequence(tr("Ctrl+0", "shortcut to reset zoom")));
+	
+	connect(new_,             SIGNAL(triggered()), this,     SLOT(newTemplate()));
+	connect(open_,            SIGNAL(triggered()), this,     SLOT(open()));
+	connect(open_from_file_,  SIGNAL(triggered()), this,     SLOT(openFromFile()));
+	connect(save_,            SIGNAL(triggered()), this,     SLOT(save()));
+	connect(save_as_,         SIGNAL(triggered()), this,     SLOT(saveAs()));
+	connect(save_as_file_,    SIGNAL(triggered()), this,     SLOT(saveAsFile()));
+	connect(quit_,            SIGNAL(triggered()), this,     SLOT(quit()));
+	connect(cut_,             SIGNAL(triggered()), template_edition_area_view_, SLOT(cut()));
+	connect(copy_,            SIGNAL(triggered()), template_edition_area_view_, SLOT(copy()));
+	connect(paste_,           SIGNAL(triggered()), template_edition_area_view_, SLOT(paste()));
+	connect(zoom_in_,         SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomIn()));
+	connect(zoom_out_,        SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomOut()));
+	connect(zoom_fit_,        SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomFit()));
+	connect(zoom_reset_,      SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomReset()));
+	connect(edit_logos_,      SIGNAL(triggered()), this, SLOT(editLogos()));
+	connect(edit_info_,       SIGNAL(triggered()), this, SLOT(editTemplateInformation()));
+	connect(add_row_,         SIGNAL(triggered()), template_edition_area_view_, SLOT(addRowAtEnd()));
+	connect(add_col_,         SIGNAL(triggered()), template_edition_area_view_, SLOT(addColumnAtEnd()));
+	connect(merge_cells_,     SIGNAL(triggered()), template_edition_area_view_, SLOT(mergeSelectedCells()));
+	connect(split_cell_,      SIGNAL(triggered()), template_edition_area_view_, SLOT(splitSelectedCell()));
+}
+
+/**
+	Initialize the various menus.
+*/
+void QETTitleBlockTemplateEditor::initMenus() {
+	file_menu_    = new QMenu(tr("&Fichier",        "menu title"), this);
+	edit_menu_    = new QMenu(tr("&\311dition",     "menu title"), this);
+	display_menu_ = new QMenu(tr("Afficha&ge",      "menu title"), this);
+	
+	file_menu_    -> addAction(new_);
+	file_menu_    -> addAction(open_);
+	file_menu_    -> addAction(open_from_file_);
+	file_menu_    -> addAction(save_);
+	file_menu_    -> addAction(save_as_);
+	file_menu_    -> addAction(save_as_file_);
+	file_menu_    -> addSeparator();
+	file_menu_    -> addAction(quit_);
+	
+	edit_menu_   -> addAction(undo_);
+	edit_menu_   -> addAction(redo_);
+	edit_menu_   -> addSeparator();
+	edit_menu_   -> addAction(cut_);
+	edit_menu_   -> addAction(copy_);
+	edit_menu_   -> addAction(paste_);
+	edit_menu_   -> addSeparator();
+	edit_menu_   -> addAction(add_row_);
+	edit_menu_   -> addAction(add_col_);
+	edit_menu_   -> addAction(merge_cells_);
+	edit_menu_   -> addAction(split_cell_);
+	edit_menu_   -> addSeparator();
+	edit_menu_   -> addAction(edit_logos_);
+	edit_menu_   -> addAction(edit_info_);
+	display_menu_ -> addAction(zoom_in_);
+	display_menu_ -> addAction(zoom_out_);
+	display_menu_ -> addAction(zoom_fit_);
+	display_menu_ -> addAction(zoom_reset_);
+	
+	insertMenu(settings_menu_, file_menu_);
+	insertMenu(settings_menu_, edit_menu_);
+	insertMenu(settings_menu_, display_menu_);
+}
+
+/**
+	Initalize toolbars.
+*/
+void QETTitleBlockTemplateEditor::initToolbars() {
+	QToolBar *main_toolbar = new QToolBar(tr("Outils", "toolbar title"), this);
+	main_toolbar -> setObjectName("tbt_main_toolbar");
+	main_toolbar -> addAction(new_);
+	main_toolbar -> addAction(open_);
+	main_toolbar -> addAction(save_);
+	main_toolbar -> addAction(save_as_);
+	addToolBar(Qt::TopToolBarArea, main_toolbar);
+	
+	QToolBar *edit_toolbar = new QToolBar(tr("\311dition", "toolbar title"), this);
+	edit_toolbar -> setObjectName("tbt_edit_toolbar");
+	edit_toolbar -> addAction(undo_);
+	edit_toolbar -> addAction(redo_);
+	edit_toolbar -> addSeparator();
+	edit_toolbar -> addAction(merge_cells_);
+	edit_toolbar -> addAction(split_cell_);
+	addToolBar(Qt::TopToolBarArea, edit_toolbar);
+	
+	QToolBar *display_toolbar = new QToolBar(tr("Affichage", "toolbar title"), this);
+	display_toolbar -> setObjectName("tbt_display_toolbar");
+	display_toolbar -> addAction(zoom_in_);
+	display_toolbar -> addAction(zoom_out_);
+	display_toolbar -> addAction(zoom_fit_);
+	display_toolbar -> addAction(zoom_reset_);
+	addToolBar(Qt::TopToolBarArea, display_toolbar);
+}
+
+/**
+	Initialize layouts and widgets
+*/
+void QETTitleBlockTemplateEditor::initWidgets() {
+	QSettings &settings = QETApp::settings();
+	
+	// undo list on the right
+	undo_stack_ = new QUndoStack(this);
+	undo_view_ = new QUndoView(undo_stack_);
+	undo_view_ -> setEmptyLabel(tr("Aucune modification", "label displayed in the undo list when empty"));
+	
+	undo_dock_widget_ = new QDockWidget(tr("Annulations", "dock title"));
+	undo_dock_widget_ -> setObjectName("tbt_undo_dock");
+	undo_dock_widget_ -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	undo_dock_widget_ -> setWidget(undo_view_);
+	undo_dock_widget_ -> setMinimumWidth(290);
+	addDockWidget(Qt::RightDockWidgetArea, undo_dock_widget_);
+	
+	// WYSIWYG editor as central widget
+	template_edition_area_scene_ = new QGraphicsScene(this);
+	template_edition_area_view_  = new TitleBlockTemplateView(template_edition_area_scene_);
+	bool conv_ok;
+	int conf_preview_width = settings.value("titleblocktemplateeditor/preview_width", -1).toInt(&conv_ok);
+	if (conv_ok && conf_preview_width != -1) {
+		template_edition_area_view_ -> setPreviewWidth(conf_preview_width);
+	}
+	setCentralWidget(template_edition_area_view_);
+	
+	// cell edition widget at the bottom
+	template_cell_editor_widget_ = new TitleBlockTemplateCellWidget(tb_template_);
+	template_cell_editor_dock_widget_ = new QDockWidget(tr("Propri\351t\351s de la cellule", "dock title"), this);
+	template_cell_editor_dock_widget_ -> setObjectName("tbt_celleditor_dock");
+	template_cell_editor_dock_widget_ -> setFeatures(QDockWidget::AllDockWidgetFeatures);
+	template_cell_editor_dock_widget_ -> setWidget(template_cell_editor_widget_);
+	template_cell_editor_dock_widget_ -> setMinimumWidth(180);
+	template_cell_editor_dock_widget_ -> setMinimumHeight(250);
+	addDockWidget(Qt::BottomDockWidgetArea, template_cell_editor_dock_widget_);
+	template_cell_editor_widget_ -> setVisible(false);
+	
+	connect(
+		template_edition_area_view_,
+		SIGNAL(selectedCellsChanged(QList<TitleBlockCell *>)),
+		this,
+		SLOT(selectedCellsChanged(QList<TitleBlockCell *>))
+	);
+	connect(template_cell_editor_widget_, SIGNAL(logoEditionRequested()), this, SLOT(editLogos()));
+	connect(
+		template_cell_editor_widget_,
+		SIGNAL(cellModified(ModifyTitleBlockCellCommand *)),
+		this,
+		SLOT(pushCellUndoCommand(ModifyTitleBlockCellCommand *))
+	);
+	connect(
+		template_edition_area_view_,
+		SIGNAL(gridModificationRequested(TitleBlockTemplateCommand *)),
+		this,
+		SLOT(pushGridUndoCommand(TitleBlockTemplateCommand *))
+	);
+	connect(
+		template_edition_area_view_,
+		SIGNAL(previewWidthChanged(int,int)),
+		this,
+		SLOT(savePreviewWidthToApplicationSettings(int, int))
+	);
+	connect(undo_stack_, SIGNAL(cleanChanged(bool)), this, SLOT(updateEditorTitle()));
+	connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(updateActions()));
+}
+
+/**
+	Initialize the logo manager
+*/
+void QETTitleBlockTemplateEditor::initLogoManager() {
+	logo_manager_ = new TitleBlockTemplateLogoManager(tb_template_, this);
+	logo_manager_ -> setReadOnly(read_only_);
+	connect(
+		logo_manager_,
+		SIGNAL(logosChanged(const TitleBlockTemplate *)),
+		template_cell_editor_widget_,
+		SLOT(updateLogosComboBox(const TitleBlockTemplate *))
+	);
+}
+
+/**
+	@return a string describing what is being edited, along with [Changed] or
+	[Read only] tags. Useful to compose the window title.
+*/
+QString QETTitleBlockTemplateEditor::currentlyEditedTitle() const {
+	QString titleblock_title;
+	if (opened_from_file_) {
+		titleblock_title = filepath_;
+	} else {
+		titleblock_title = location_.name();
+	}
+	
+	// if a (file)name has been added, also add a "[Changed]" tag if needed
+	if (!titleblock_title.isEmpty()) {
+		QString tag;
+		if (!undo_stack_ -> isClean()) {
+			tag = tr("[Modifi\351]", "window title tag");
+		}
+		if (read_only_) {
+			tag = tr("[Lecture seule]", "window title tag");
+		}
+		titleblock_title = QString(
+			tr(
+				"%1 %2",
+				"part of the window title - %1 is the filepath or template name, %2 is the [Changed] or [Read only] tag"
+			)
+		).arg(titleblock_title).arg(tag);
+	}
+	
+	return(titleblock_title);
+}
+
+/**
+	Load template editor-related parameters.
+*/
+void QETTitleBlockTemplateEditor::readSettings() {
+	QSettings &settings = QETApp::settings();
+	
+	// window size and position
+	QVariant geometry = settings.value("titleblocktemplateeditor/geometry");
+	if (geometry.isValid()) restoreGeometry(geometry.toByteArray());
+	
+	// window state (toolbars, docks...)
+	QVariant state = settings.value("titleblocktemplateeditor/state");
+	if (state.isValid()) restoreState(state.toByteArray());
+}
+
+/**
+	Save template editor-related parameters.
+*/
+void QETTitleBlockTemplateEditor::writeSettings() {
+	QSettings &settings = QETApp::settings();
+	settings.setValue("titleblocktemplateeditor/geometry", saveGeometry());
+	settings.setValue("titleblocktemplateeditor/state", saveState());
+}
+
+/**
+	Update various things when user changes the selected cells.
+	@param selected_cells List of selected cells.
+*/
+void QETTitleBlockTemplateEditor::selectedCellsChanged(QList<TitleBlockCell *> selected_cells) {
+	if (selected_cells.count() == 1) {
+		template_cell_editor_widget_ -> edit(selected_cells.at(0));
+		template_cell_editor_widget_ -> setVisible(true);
+	} else {
+		template_cell_editor_widget_ -> setVisible(false);
+	}
+	updateActions();
+}
+
+/**
+	Configure an undo Command before adding it to the undo stack.
+	@param command to be added to the undo stack
+*/
+void QETTitleBlockTemplateEditor::pushCellUndoCommand(ModifyTitleBlockCellCommand *command) {
+	command -> setView(template_edition_area_view_);
+	pushUndoCommand(command);
+}
+
+/**
+	Add an undo Command to the undo stack.
+	@param command QUndoCommand to be added to the undo stack
+*/
+void QETTitleBlockTemplateEditor::pushGridUndoCommand(TitleBlockTemplateCommand *command) {
+	pushUndoCommand(command);
+}
+
+/**
+	Add an undo Command to the undo stack.
+	@param command QUndoCommand to be added to the undo stack
+*/
+void QETTitleBlockTemplateEditor::pushUndoCommand(QUndoCommand *command) {
+	undo_stack_ -> push(command);
+}
+
+/**
+	Set the title of this editor.
+*/
+void QETTitleBlockTemplateEditor::updateEditorTitle() {
+	// base title
+	QString min_title(
+		tr(
+			"QElectroTech - \311diteur de mod\350le de cartouche",
+			"titleblock template editor: base window title"
+		)
+	);
+	
+	// get the currently edited template (file)name
+	QString titleblock_title = currentlyEditedTitle();
+	
+	// generate the final window title
+	QString title;
+	if (titleblock_title.isEmpty()) {
+		title = min_title;
+	} else {
+		title = QString(
+			tr(
+				"%1 - %2",
+				"window title: %1 is the base window title, %2 is a template name"
+			)
+		).arg(min_title).arg(titleblock_title);
+	}
+	setWindowTitle(title);
+}
+
+/**
+	Ensure the user interface remains consistent by enabling or disabling
+	adequate actions.
+*/
+void QETTitleBlockTemplateEditor::updateActions() {
+	save_ -> setEnabled(!read_only_);
+	
+	bool can_merge;
+	bool can_split;
+	int count;
+	if (!read_only_) {
+		template_edition_area_view_ -> analyzeSelectedCells(&can_merge, &can_split, &count);
+	}
+	cut_ -> setEnabled(!read_only_ && count);
+	copy_ -> setEnabled(count);
+	paste_ -> setEnabled(!read_only_ && count && template_edition_area_view_ -> mayPaste());
+	add_row_ -> setEnabled(!read_only_);
+	add_col_ -> setEnabled(!read_only_);
+	merge_cells_ -> setEnabled(!read_only_ && can_merge);
+	split_cell_ -> setEnabled(!read_only_ && can_split);
+}
+
+/**
+	Save the template under the provided location.
+	@see QETProject::setTemplateXmlDescription()
+	@param location Location where the title block template should be saved.
+*/
+bool QETTitleBlockTemplateEditor::saveAs(const TitleBlockTemplateLocation &location) {
+	TitleBlockTemplatesCollection *collection = location.parentCollection();
+	if (!collection) return(false);
+	
+	QDomDocument doc;
+	QDomElement elmt = doc.createElement("root");
+	tb_template_ -> saveToXmlElement(elmt);
+	elmt.setAttribute("name", location.name());
+	doc.appendChild(elmt);
+	
+	collection -> setTemplateXmlDescription(location.name(), elmt);
+	
+	opened_from_file_ = false;
+	location_ = location;
+	undo_stack_ -> setClean();
+	setReadOnly(false);
+	return(true);
+}
+
+/**
+	Save the template in the provided filepath.
+	@see TitleBlockTemplate::saveToXmlFile()
+	@param filepath location Location where the title block template should be saved.
+*/
+bool QETTitleBlockTemplateEditor::saveAs(const QString &filepath) {
+	bool saving = tb_template_ -> saveToXmlFile(filepath);
+	if (!saving) return(false);
+	
+	opened_from_file_ = true;
+	filepath_ = filepath;
+	undo_stack_ -> setClean();
+	setReadOnly(false);
+	return(true);
+}
+
+/**
+	Ask the user to choose a title block template from the known collections
+	then open it for edition.
+*/
+void QETTitleBlockTemplateEditor::open() {
+	TitleBlockTemplateLocation location = getTitleBlockTemplateLocationFromUser(
+		tr("Ouvrir un mod\350le", "File > open dialog window title"),
+		true
+	);
+	if (location.isValid()) {
+		QETApp::instance() -> openTitleBlockTemplate(location);
+	}
+}
+
+/**
+	Ask the user to choose a file supposed to contain a title block template,
+	then open it for edition.
+*/
+void QETTitleBlockTemplateEditor::openFromFile() {
+	// directory to show
+	QString initial_dir = filepath_.isEmpty() ? QETApp::customTitleBlockTemplatesDir() : QDir(filepath_).absolutePath();
+	
+	// ask the user to choose a filepath
+	QString user_filepath = QFileDialog::getOpenFileName(
+		this,
+		tr("Ouvrir un fichier", "dialog title"),
+		initial_dir,
+		tr(
+			"Mod\350les de cartouches QElectroTech (*%1);;"
+			"Fichiers XML (*.xml);;"
+			"Tous les fichiers (*)",
+			"filetypes allowed when opening a title block template file - %1 is the .titleblock extension"
+		).arg(QString(TITLEBLOCKS_FILE_EXTENSION))
+	);
+	
+	
+	if (!user_filepath.isEmpty()) QETApp::instance() -> openTitleBlockTemplate(user_filepath);
+}
+
+/**
+	Save the currently edited title block template back to its parent project.
+*/
+bool QETTitleBlockTemplateEditor::save() {
+	if (opened_from_file_) {
+		if (!filepath_.isEmpty()) {
+			QFileInfo file_path_info(filepath_);
+			if (file_path_info.isWritable()) {
+				return(saveAs(filepath_));
+			}
+		}
+		return(saveAsFile());
+	} else {
+		if (location_.isValid()) {
+			if (!location_.isReadOnly()) {
+				return(saveAs(location_));
+			}
+		}
+		return(saveAs());
+	}
+}
+
+/**
+	Ask the user where he wishes to save the currently edited template.
+*/
+bool QETTitleBlockTemplateEditor::saveAs() {
+	TitleBlockTemplateLocation location = getTitleBlockTemplateLocationFromUser(
+		tr("Enregistrer le mod\350le sous", "dialog window title"),
+		false
+	);
+	if (location.isValid()) {
+		return(saveAs(location));
+	}
+	return(false);
+}
+
+/**
+	Ask the user where on the filesystem he wishes to save the currently edited template.
+*/
+bool QETTitleBlockTemplateEditor::saveAsFile() {
+	// directory to show
+	QString initial_dir = filepath_.isEmpty() ? QETApp::customTitleBlockTemplatesDir() : QDir(filepath_).absolutePath();
+	
+	// ask the user to choose a target file
+	QString filepath = QFileDialog::getSaveFileName(
+		this,
+		tr("Enregistrer sous", "dialog title"),
+		initial_dir,
+		tr(
+			"Mod\350les de cartouches QElectroTech (*%1)",
+			"filetypes allowed when saving a title block template file - %1 is the .titleblock extension"
+		).arg(QString(TITLEBLOCKS_FILE_EXTENSION))
+	);
+	
+	// if no name was entered, return false
+	if (filepath.isEmpty()) return(false);
+	
+	// if the name does not end with ".titleblock", add it
+	if (!filepath.endsWith(".titleblock", Qt::CaseInsensitive)) filepath += ".titleblock";
+	
+	// attempts to save the file
+	bool saving = saveAs(filepath);
+	
+	// retourne un booleen representatif de la reussite de l'enregistrement
+	return(saving);
+}
+
+/**
+	@param read_only True to restrict this editor to visualization of the
+	currently edited template, false to allow full edition.
+*/
+void QETTitleBlockTemplateEditor::setReadOnly(bool read_only) {
+	if (read_only != read_only_) {
+		read_only_ = read_only;
+		if (logo_manager_) {
+			logo_manager_ -> setReadOnly(read_only_);
+		}
+		template_cell_editor_widget_ -> setReadOnly(read_only_);
+		template_edition_area_view_ -> setReadOnly(read_only_);
+	}
+	updateActions();
+	updateEditorTitle();
+}
+
+/**
+	Ask the user for a title block template location
+	@param title Title displayed by the dialog window
+	@param existing_only True for the user to be forced to choose an existing
+	template, false if he may specify the template name
+	@return The location chosen by the user, or an empty
+	TitleBlockTemplateLocation if the user cancelled the dialog
+*/
+TitleBlockTemplateLocation QETTitleBlockTemplateEditor::getTitleBlockTemplateLocationFromUser(const QString &title, bool existing_only) {
+	TitleBlockTemplateLocationChooser *widget;
+	if (existing_only) {
+		widget = new TitleBlockTemplateLocationChooser(location());
+	} else {
+		widget = new TitleBlockTemplateLocationSaver(location());
+	}
+	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	
+	QVBoxLayout *dialog_layout = new QVBoxLayout();
+	dialog_layout -> addWidget(widget);
+	dialog_layout -> addWidget(buttons);
+	
+	QDialog dialog;
+	dialog.setWindowTitle(title);
+	dialog.setLayout(dialog_layout);
+	
+	connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept()));
+	connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject()));
+	
+	if (dialog.exec() == QDialog::Accepted) {
+		return(widget -> location());
+	}
+	return TitleBlockTemplateLocation();
+}
+
+/**
+	Close the current editor.
+*/
+void QETTitleBlockTemplateEditor::quit() {
+	close();
+}
+
+/**
+	Save the new preview width to application settings
+	@param former_preview_width Unused, former preview width
+	@param new_preview_width New preview width
+*/
+void QETTitleBlockTemplateEditor::savePreviewWidthToApplicationSettings(int former_preview_width, int new_preview_width) {
+	Q_UNUSED(former_preview_width)
+	QETApp::settings().setValue("titleblocktemplateeditor/preview_width", new_preview_width);
+}
+
+/**
+	Edit extra information attached to the template.
+*/
+void QETTitleBlockTemplateEditor::editTemplateInformation() {
+	if (!tb_template_) return;
+	
+	QDialog dialog_author(this);
+	dialog_author.setModal(true);
+#ifdef Q_WS_MAC
+	dialog_author.setWindowFlags(Qt::Sheet);
+#endif
+	dialog_author.setMinimumSize(400, 260);
+	dialog_author.setWindowTitle(tr("\311diter les informations compl\351mentaires", "window title"));
+	QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog_author);
+	
+	// explanation label
+	QLabel *information_label = new QLabel(tr("Vous pouvez utiliser ce champ libre pour mentionner les auteurs du cartouche, sa licence, ou tout autre renseignement que vous jugerez utile."));
+	information_label -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
+	information_label -> setWordWrap(true);
+	dialog_layout -> addWidget(information_label);
+	
+	// add a QTextEdit to the dialog
+	QTextEdit *text_field = new QTextEdit();
+	text_field -> setAcceptRichText(false);
+	text_field -> setPlainText(tb_template_ -> information());
+	text_field -> setReadOnly(read_only_);
+	dialog_layout -> addWidget(text_field);
+	
+	// add two buttons to the dialog
+	QDialogButtonBox *dialog_buttons = new QDialogButtonBox(read_only_ ? QDialogButtonBox::Ok : QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	dialog_layout -> addWidget(dialog_buttons);
+	connect(dialog_buttons, SIGNAL(accepted()),    &dialog_author, SLOT(accept()));
+	connect(dialog_buttons, SIGNAL(rejected()),    &dialog_author, SLOT(reject()));
+	
+	// run the dialog
+	if (dialog_author.exec() == QDialog::Accepted && !read_only_) {
+		QString new_info = text_field -> toPlainText().remove(QChar(13)); // CR-less text
+		if (new_info != tb_template_ -> information()) {
+			pushUndoCommand(new ChangeTemplateInformationsCommand(tb_template_, tb_template_ -> information(), new_info));
+		}
+	}
+}

Added: trunk/sources/titleblock/qettemplateeditor.h
===================================================================
--- trunk/sources/titleblock/qettemplateeditor.h	                        (rev 0)
+++ trunk/sources/titleblock/qettemplateeditor.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,137 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_QET_TEMPLATE_EDITOR_H
+#define TITLEBLOCK_SLASH_QET_TEMPLATE_EDITOR_H
+#include <QtGui>
+#include "qetmainwindow.h"
+#include "qet.h"
+#include "templateview.h"
+#include "templatelocation.h"
+class ModifyTitleBlockCellCommand;
+class TitleBlockTemplateCommand;
+class TitleBlockTemplateCellWidget;
+class TitleBlockTemplateLogoManager;
+class QETProject;
+
+/**
+	This class implements the main window of QElectroTech's titleblock template
+	editor. This editor aims at allowing users to easily create their own title
+	block templates.
+*/
+class QETTitleBlockTemplateEditor : public QETMainWindow {
+	Q_OBJECT
+	
+	// constructor, destructor
+	public:
+	QETTitleBlockTemplateEditor(QWidget * = 0);
+	virtual ~QETTitleBlockTemplateEditor();
+	private:
+	QETTitleBlockTemplateEditor(const QETTitleBlockTemplateEditor &);
+	
+	// attributes
+	private:
+	/// menus TODO
+	QMenu *file_menu_, *edit_menu_, *display_menu_;
+	/// actions
+	QAction *new_, *open_, *open_from_file_, *save_, *save_as_, *save_as_file_, *quit_;
+	QAction *undo_, *redo_, *cut_, *copy_, *paste_, *edit_logos_, *edit_info_, *add_row_, *add_col_, *merge_cells_, *split_cell_;
+	QAction *zoom_in_, *zoom_out_, *zoom_fit_, *zoom_reset_;
+	/// Location of the currently edited template
+	TitleBlockTemplateLocation location_;
+	/// Filepath of the currently edited template, if opened from a file
+	QString filepath_;
+	/// Whether to consider the location or the filepath
+	bool opened_from_file_;
+	/// whether the currently edited template is considered read only
+	bool read_only_;
+	/**
+		Whether to ask the user a new template name when the window appears in order
+		to rename the edited template.
+	*/
+	bool duplicate_;
+	/// Used to track the first activation of the editor main window.
+	bool first_activation_;
+	/// Template Object edited
+	TitleBlockTemplate *tb_template_;
+	/// Template preview
+	QGraphicsScene *template_edition_area_scene_;
+	TitleBlockTemplateView *template_edition_area_view_;
+	/// Individual cell widget edition
+	QDockWidget *template_cell_editor_dock_widget_;
+	TitleBlockTemplateCellWidget *template_cell_editor_widget_;
+	/// Logo manager widget
+	TitleBlockTemplateLogoManager *logo_manager_;
+	/// Undo interface
+	QUndoStack *undo_stack_;
+	QUndoView *undo_view_;
+	QDockWidget *undo_dock_widget_;
+	
+	// methods
+	public:
+	TitleBlockTemplateLocation location() const;
+	bool isEditing(const QString &ilepath);
+	void setOpenForDuplication(bool);
+	bool openForDuplication() const;
+	
+	protected:
+	bool canClose();
+	virtual void firstActivation(QEvent *);
+	void closeEvent(QCloseEvent *);
+	
+	private:
+	void initActions();
+	void initMenus();
+	void initToolbars();
+	void initWidgets();
+	void initLogoManager();
+	QString currentlyEditedTitle() const;
+	
+	public slots:
+	void readSettings();
+	void writeSettings();
+	void selectedCellsChanged(QList<TitleBlockCell *>);
+	void duplicateCurrentLocation();
+	bool edit(const TitleBlockTemplateLocation &);
+	bool edit(QETProject *, const QString &);
+	bool edit(const QString &);
+	bool editCopyOf(const TitleBlockTemplate *);
+	bool edit(TitleBlockTemplate *);
+	void editLogos();
+	void newTemplate();
+	void open();
+	void openFromFile();
+	bool save();
+	bool saveAs();
+	bool saveAsFile();
+	void setReadOnly(bool);
+	void quit();
+	void savePreviewWidthToApplicationSettings(int, int);
+	void editTemplateInformation();
+	
+	private slots:
+	TitleBlockTemplateLocation getTitleBlockTemplateLocationFromUser(const QString & = QString(), bool existing_only = true);
+	void pushCellUndoCommand(ModifyTitleBlockCellCommand *);
+	void pushGridUndoCommand(TitleBlockTemplateCommand *);
+	void pushUndoCommand(QUndoCommand *);
+	void updateEditorTitle();
+	void updateActions();
+	bool saveAs(const TitleBlockTemplateLocation &);
+	bool saveAs(const QString &);
+};
+
+#endif

Added: trunk/sources/titleblock/splittedhelpercell.cpp
===================================================================
--- trunk/sources/titleblock/splittedhelpercell.cpp	                        (rev 0)
+++ trunk/sources/titleblock/splittedhelpercell.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,62 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "splittedhelpercell.h"
+
+/**
+	Constructor
+	@param parent Parent QGraphicsItem
+*/
+SplittedHelperCell::SplittedHelperCell(QGraphicsItem *parent) :
+	HelperCell(parent),
+	split_background_color(background_color),
+	split_foreground_color(foreground_color),
+	split_size(0)
+{
+}
+
+/**
+	Destructor
+*/
+SplittedHelperCell::~SplittedHelperCell() {
+}
+
+/**
+	Handles the splitted helper cell visual rendering
+	@param painter QPainter to be used for the rendering
+	@param option Rendering options
+	@param widget QWidget being painted, if any
+*/
+void SplittedHelperCell::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget) {
+	if (!split_size) {
+		HelperCell::paint(painter, option, widget);
+		return;
+	}
+	QRectF first_drawing_rectangle(QPointF(0, 0), geometry().adjusted(0, 0, -split_size, 0).size());
+	QRectF second_drawing_rectangle(first_drawing_rectangle.topRight(), QSize(split_size, first_drawing_rectangle.height()));
+	
+	painter -> setPen(Qt::black);
+	painter -> setBrush(background_color);
+	painter -> drawRect(first_drawing_rectangle);
+	painter -> setBrush(split_background_color);
+	painter -> drawRect(second_drawing_rectangle);
+	
+	painter -> setPen(foreground_color);
+	painter -> drawText(first_drawing_rectangle, Qt::AlignHCenter | Qt::AlignVCenter, label);
+	painter -> setPen(split_foreground_color);
+	painter -> drawText(second_drawing_rectangle, Qt::AlignHCenter | Qt::AlignVCenter, split_label);
+}

Added: trunk/sources/titleblock/splittedhelpercell.h
===================================================================
--- trunk/sources/titleblock/splittedhelpercell.h	                        (rev 0)
+++ trunk/sources/titleblock/splittedhelpercell.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,45 @@
+/*
+    Copyright 2006-2012 Xavier Guerrin
+    This file is part of QElectroTech.
+    
+    QElectroTech is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+    
+    QElectroTech is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_SPLITTED_HELPER_CELL_H
+#define TITLEBLOCK_SLASH_SPLITTED_HELPER_CELL_H
+#include "helpercell.h"
+
+/**
+	This class is a variant of HelperCell having the ability to display two
+	labels, with a split line between them.
+*/
+class SplittedHelperCell : public HelperCell {
+	Q_OBJECT
+	public:
+	SplittedHelperCell(QGraphicsItem * = 0);
+	virtual ~SplittedHelperCell();
+	private:
+	SplittedHelperCell(const SplittedHelperCell &);
+	
+	// methods
+	public:
+	void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	
+	// attributes
+	QColor split_background_color; ///< Background color on the split side
+	QColor split_foreground_color; ///< Text color on the split side
+	QString split_label;           ///< Text displayed on the split side
+	int split_size;                ///< Length of the split side
+};
+
+#endif

Added: trunk/sources/titleblock/templatecellsset.cpp
===================================================================
--- trunk/sources/titleblock/templatecellsset.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatecellsset.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,212 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatecellsset.h"
+#include "templatevisualcell.h"
+#include "templateview.h"
+#include "titleblockcell.h"
+
+/**
+	Constructor
+	@param parent_view View this set of cells are rattached to
+*/
+TitleBlockTemplateCellsSet::TitleBlockTemplateCellsSet(const TitleBlockTemplateView *parent_view) :
+	parent_view_(parent_view)
+{
+}
+
+/**
+	Copy constructor
+	@param copy TitleBlockTemplateCellsSet object to copy
+*/
+TitleBlockTemplateCellsSet::TitleBlockTemplateCellsSet(const TitleBlockTemplateCellsSet &copy) :
+	QList<TitleBlockTemplateVisualCell *>(copy),
+	parent_view_(copy.parent_view_)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateCellsSet::~TitleBlockTemplateCellsSet() {
+}
+
+/**
+	@return a QPainterPath composed of the rectangles from cells within this set
+*/
+QPainterPath TitleBlockTemplateCellsSet::painterPath() const {
+	QPainterPath cells_path;
+	foreach (TitleBlockTemplateVisualCell *cell, *this) {
+		cells_path.addRect(cell -> geometry());
+	}
+	return(cells_path);
+}
+
+/**
+	@return true if the cells within this set are composing a rectangle shape,
+	false otherwise.
+*/
+bool TitleBlockTemplateCellsSet::isRectangle() const {
+	if (!count()) return(false);
+	if (count() == 1) return(true);
+	
+	QPolygonF points = painterPath().simplified().toFillPolygon();
+	if (points.isClosed()) points.pop_back();
+	
+	return(points.count() == 4 || points.count() == 5);
+}
+
+/**
+	@return true if all cells within this set are selected
+*/
+bool TitleBlockTemplateCellsSet::allCellsAreSelected() const {
+	foreach (TitleBlockTemplateVisualCell *cell, *this) {
+		if (!cell -> isSelected()) {
+			return(false);
+		}
+	}
+	return(true);
+}
+
+/**
+	@return true if this set includes at least one cell which is spanned by a
+	cell not present in this set, false otherwise.
+*/
+bool TitleBlockTemplateCellsSet::hasExternalSpan() const {
+	// fetches all cells concerned by this set
+	QSet<TitleBlockCell *> all_cells = cells(true);
+	
+	// look for cells spanned by cells that do not belong to this set
+	foreach (TitleBlockCell *cell, all_cells) {
+		if (cell -> spanner_cell && !all_cells.contains(cell -> spanner_cell)) {
+			return(true);
+		}
+	}
+	return(false);
+}
+
+/**
+	@return the top left cell within this set, or 0 if this set is empty
+*/
+TitleBlockTemplateVisualCell *TitleBlockTemplateCellsSet::topLeftCell() const {
+	if (empty()) return(0);
+	if (count() == 1) return(first());
+	
+	// look for cells at the top
+	QMultiMap<int, TitleBlockTemplateVisualCell *> top_cells;
+	foreach (TitleBlockTemplateVisualCell *cell_view, *this) {
+		if (TitleBlockCell *cell = cell_view -> cell()) {
+			top_cells.insertMulti(cell -> num_row, cell_view);
+		}
+	}
+	QList<TitleBlockTemplateVisualCell *> candidates = top_cells.values(top_cells.keys().first());
+	if (candidates.count() == 1) return(candidates.first());
+	
+	// look for the cell at the left
+	int lowest_num_col = 100000;
+	TitleBlockTemplateVisualCell *candidate = 0;
+	foreach (TitleBlockTemplateVisualCell *cell_view, candidates) {
+		if (TitleBlockCell *cell = cell_view -> cell()) {
+			if (cell -> num_col < lowest_num_col) {
+				lowest_num_col = cell -> num_col;
+				candidate = cell_view;
+			}
+		}
+	}
+	return(candidate);
+}
+
+/**
+	@return the bottom right cell within this set, or 0 if this set is empty
+*/
+TitleBlockTemplateVisualCell *TitleBlockTemplateCellsSet::bottomRightCell() const {
+	if (empty()) return(0);
+	if (count() == 1) return(first());
+	
+	// look for cells at the bottom
+	QMultiMap<qreal, TitleBlockTemplateVisualCell *> bottom_cells;
+	foreach (TitleBlockTemplateVisualCell *cell_view, *this) {
+		bottom_cells.insertMulti(cell_view -> geometry().bottom(), cell_view);
+	}
+	QList<TitleBlockTemplateVisualCell *> candidates = bottom_cells.values(bottom_cells.keys().last());
+	if (candidates.count() == 1) return(candidates.first());
+	
+	// look for the cell at the right
+	qreal highest_right = -100000;
+	TitleBlockTemplateVisualCell *candidate = 0;
+	foreach (TitleBlockTemplateVisualCell *cell_view, candidates) {
+		qreal right = cell_view -> geometry().right();
+		if (right > highest_right) {
+			highest_right = right;
+			candidate = cell_view;
+		}
+	}
+	return(candidate);
+}
+
+/**
+	@return the merge area, i.e. the rectangle delimited by the top left cell
+	and the bottom right cell within this cells set.
+*/
+QRectF TitleBlockTemplateCellsSet::mergeAreaRect() const {
+	QRectF merge_area;
+	if (!parent_view_) return(merge_area);
+	
+	TitleBlockTemplateVisualCell *top_left_cell = topLeftCell();
+	if (!top_left_cell) return(merge_area);
+	TitleBlockTemplateVisualCell *bottom_right_cell = bottomRightCell();
+	if (!bottom_right_cell) return(merge_area);
+	
+	merge_area.setTopLeft(top_left_cell -> geometry().topLeft());
+	merge_area.setBottomRight(bottom_right_cell -> geometry().bottomRight());
+	return(merge_area);
+}
+
+/**
+	@param rect (Optional) The merge area to be considered; if a null QRectF is
+	provided, this method will use mergeAreaRect().
+	@return the cells contained in the merge area of this cells set
+*/
+TitleBlockTemplateCellsSet TitleBlockTemplateCellsSet::mergeArea(const QRectF &rect) const {
+	TitleBlockTemplateCellsSet merge_area(parent_view_);
+	if (!parent_view_) return(merge_area);
+	
+	QRectF merge_area_rect = rect.isNull() ? mergeAreaRect() : rect;
+	
+	merge_area = parent_view_ -> cells(merge_area_rect);
+	return(merge_area);
+}
+
+/**
+	@return the list of cells rendered by the current selection
+	@param include_spanned whether to include spanned cells or not
+*/
+QSet<TitleBlockCell *> TitleBlockTemplateCellsSet::cells(bool include_spanned) const {
+	QSet<TitleBlockCell *> set;
+	foreach (TitleBlockTemplateVisualCell *cell_view, *this) {
+		if (TitleBlockCell *cell = cell_view -> cell()) {
+			if (include_spanned) {
+				foreach (TitleBlockCell *cell, cell_view -> cells()) {
+					set << cell;
+				}
+			} else {
+				set << cell;
+			}
+		}
+	}
+	return(set);
+}

Added: trunk/sources/titleblock/templatecellsset.h
===================================================================
--- trunk/sources/titleblock/templatecellsset.h	                        (rev 0)
+++ trunk/sources/titleblock/templatecellsset.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,51 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_CELLS_SET_H
+#define TITLEBLOCK_SLASH_TEMPLATE_CELLS_SET_H
+#include <QtGui>
+class TitleBlockCell;
+class TitleBlockTemplateVisualCell;
+class TitleBlockTemplateView;
+/**
+	This class represents a set of cells (selected or not) when editing a
+	title block template.
+*/
+class TitleBlockTemplateCellsSet : public QList<TitleBlockTemplateVisualCell *> {
+	// Constructors, destructor
+	public:
+	TitleBlockTemplateCellsSet(const TitleBlockTemplateView *);
+	TitleBlockTemplateCellsSet(const TitleBlockTemplateCellsSet &);
+	virtual ~TitleBlockTemplateCellsSet();
+	
+	// methods
+	public:
+	QPainterPath painterPath() const;
+	bool isRectangle() const;
+	bool allCellsAreSelected() const;
+	bool hasExternalSpan() const;
+	TitleBlockTemplateVisualCell *topLeftCell() const;
+	TitleBlockTemplateVisualCell *bottomRightCell() const;
+	QRectF mergeAreaRect() const;
+	TitleBlockTemplateCellsSet mergeArea(const QRectF & = QRectF()) const;
+	QSet<TitleBlockCell *> cells(bool = true) const;
+	
+	// attributes
+	public:
+	const TitleBlockTemplateView *parent_view_; ///< the view displaying the cells
+};
+#endif

Added: trunk/sources/titleblock/templatecellwidget.cpp
===================================================================
--- trunk/sources/titleblock/templatecellwidget.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatecellwidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,464 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatecellwidget.h"
+#include "titleblockcell.h"
+#include "nameslist.h"
+#include "nameslistwidget.h"
+#include "titleblocktemplate.h"
+#include "templatecommands.h"
+#include "qeticons.h"
+
+/**
+	Constructor
+	@param parent Parent QWidget
+*/
+TitleBlockTemplateCellWidget::TitleBlockTemplateCellWidget(TitleBlockTemplate *parent_template, QWidget *parent) :
+	QWidget(parent),
+	read_only_(false)
+{
+	initWidgets();
+	updateLogosComboBox(parent_template);
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateCellWidget::~TitleBlockTemplateCellWidget() {
+}
+
+/**
+	Initialize layout and widgets.
+*/
+void TitleBlockTemplateCellWidget::initWidgets() {
+	// type combo box: always displayed
+	cell_type_label_ = new QLabel(tr("Type de cellule :"));
+	cell_type_input_ = new QComboBox();
+	cell_type_input_ -> addItem(tr("Vide"),  TitleBlockCell::EmptyCell);
+	cell_type_input_ -> addItem(tr("Texte"), TitleBlockCell::TextCell);
+	cell_type_input_ -> addItem(tr("Logo"),  TitleBlockCell::LogoCell);
+	
+	// name input: displayed for text and logo cells
+	name_label_ = new QLabel(tr("Nom :"));
+	name_input_ = new QLineEdit();
+	
+	// widgets specific to empty cells
+	empty_label_ = new QLabel(tr("Attention : les bordures des cellules vides n'apparaissent pas lors du rendu final sur le sch\351ma."));
+	
+	// widgets specific to logo cells
+	logo_label_ = new QLabel(tr("Logo"));
+	logo_input_ = new QComboBox();
+	logo_input_ -> addItem(tr("Aucun logo"));
+	add_logo_input_ = new QPushButton(QET::Icons::InsertImage, tr("G\351rer les logos"));
+	
+	// widgets specific to text cells
+	label_checkbox_ = new QCheckBox(tr("Afficher un label :"));
+	label_input_ = new QLineEdit();
+	label_input_ -> setReadOnly(true);
+	label_edit_ = new QPushButton(tr("Editer"));
+	value_label_ = new QLabel(tr("Texte :"));
+	value_input_ = new QLineEdit();
+	value_input_ -> setReadOnly(true);
+	value_edit_ = new QPushButton(tr("Editer"));
+	align_label_ = new QLabel(tr("Alignement :"));
+	horiz_align_label_ = new QLabel(tr("horizontal :"));
+	horiz_align_input_ = new QComboBox();
+	horiz_align_input_ -> addItem(tr("Gauche"),      Qt::AlignLeft);
+	horiz_align_input_ -> addItem(tr("Centr\351"),   Qt::AlignHCenter);
+	horiz_align_input_ -> addItem(tr("Droite"),      Qt::AlignRight);
+	horiz_align_indexes_.insert(Qt::AlignLeft,    0);
+	horiz_align_indexes_.insert(Qt::AlignHCenter, 1);
+	horiz_align_indexes_.insert(Qt::AlignRight,   2);
+	vert_align_label_= new QLabel(tr("vertical :"));
+	vert_align_input_ = new QComboBox();
+	vert_align_input_ -> addItem(tr("Haut"),   Qt::AlignTop);
+	vert_align_input_ -> addItem(tr("Milieu"), Qt::AlignVCenter);
+	vert_align_input_ -> addItem(tr("Bas"),    Qt::AlignBottom);
+	vert_align_indexes_.insert(Qt::AlignTop,     0);
+	vert_align_indexes_.insert(Qt::AlignVCenter, 1);
+	vert_align_indexes_.insert(Qt::AlignBottom,  2);
+	font_size_label_ = new QLabel(tr("Police :"));
+	font_size_input_ = new QSpinBox();
+	font_adjust_input_ = new QCheckBox(tr("Ajuster la taille de police si besoin"));
+	
+	// layout
+	QHBoxLayout *label_edition = new QHBoxLayout();
+	label_edition -> addWidget(label_input_);
+	label_edition -> addWidget(label_edit_);
+	
+	QHBoxLayout *value_edition = new QHBoxLayout();
+	value_edition -> addWidget(value_input_);
+	value_edition -> addWidget(value_edit_);
+	
+	cell_editor_type_and_name_layout_ = new QHBoxLayout();
+	cell_editor_type_and_name_layout_ -> addWidget(cell_type_label_);
+	cell_editor_type_and_name_layout_ -> addWidget(cell_type_input_);
+	cell_editor_type_and_name_layout_ -> addWidget(name_label_);
+	cell_editor_type_and_name_layout_ -> addWidget(name_input_);
+	cell_editor_type_and_name_layout_ -> addStretch(5);
+	
+	cell_editor_text_layout_ = new QGridLayout();
+	cell_editor_text_layout_ -> addWidget(label_checkbox_,     2, 0);
+	cell_editor_text_layout_ -> addLayout(label_edition,       2, 1);
+	cell_editor_text_layout_ -> addWidget(value_label_,        3, 0);
+	cell_editor_text_layout_ -> addLayout(value_edition,       3, 1);
+	cell_editor_text_layout_ -> addWidget(align_label_,        1, 2, 1, 2, Qt::AlignHCenter);
+	cell_editor_text_layout_ -> addWidget(horiz_align_label_,  2, 2);
+	cell_editor_text_layout_ -> addWidget(horiz_align_input_,  2, 3);
+	cell_editor_text_layout_ -> addWidget(vert_align_label_,   3, 2);
+	cell_editor_text_layout_ -> addWidget(vert_align_input_,   3, 3);
+	cell_editor_text_layout_ -> addWidget(font_size_label_,    4, 0);
+	cell_editor_text_layout_ -> addWidget(font_size_input_,    4, 1);
+	cell_editor_text_layout_ -> addWidget(font_adjust_input_,  4, 2, 1, 2, Qt::AlignLeft);
+	cell_editor_text_layout_ -> addWidget(logo_label_,         5, 0);
+	cell_editor_text_layout_ -> addWidget(logo_input_,         5, 1);
+	cell_editor_text_layout_ -> addWidget(add_logo_input_,     5, 2);
+	cell_editor_text_layout_ -> addWidget(empty_label_,        6, 0);
+	cell_editor_text_layout_ -> setColumnStretch(4, 4000);
+	cell_editor_layout_ = new QVBoxLayout();
+	cell_editor_layout_ -> addLayout(cell_editor_type_and_name_layout_);
+	cell_editor_layout_ -> addLayout(cell_editor_text_layout_);
+	cell_editor_layout_ -> addStretch();
+	setLayout(cell_editor_layout_);
+	
+	// trigger the logo manager
+	connect(add_logo_input_, SIGNAL(released()), this, SIGNAL(logoEditionRequested()));
+	
+	// handle cell modifications
+	connect(cell_type_input_,   SIGNAL(activated(int)),           this, SLOT(updateFormType(int)));
+	connect(cell_type_input_,   SIGNAL(activated(int)),           this, SLOT(editType()));
+	connect(name_input_,        SIGNAL(editingFinished()),        this, SLOT(editName()));
+	connect(label_checkbox_,    SIGNAL(clicked(bool)),            this, SLOT(editLabelDisplayed()));
+	connect(label_edit_,        SIGNAL(released()),               this, SLOT(editLabel()));
+	connect(value_edit_,        SIGNAL(released()),               this, SLOT(editValue()));
+	connect(horiz_align_input_, SIGNAL(activated(int)),           this, SLOT(editAlignment()));
+	connect(vert_align_input_,  SIGNAL(activated(int)),           this, SLOT(editAlignment()));
+	connect(font_size_input_,   SIGNAL(valueChanged(int)),        this, SLOT(editFontSize()));
+	connect(font_adjust_input_, SIGNAL(clicked(bool)),            this, SLOT(editAdjust()));
+	connect(logo_input_,        SIGNAL(activated(int)),           this, SLOT(editLogo()));
+	
+	updateFormType(TitleBlockCell::TextCell);
+}
+
+/**
+	Shows or hides various widgets depending on the selected cell type
+*/
+void TitleBlockTemplateCellWidget::updateFormType(int cell_type) {
+	if (cell_type_input_ -> currentIndex() != cell_type) {
+		cell_type_input_ -> setCurrentIndex(cell_type);
+	}
+	
+	name_label_        -> setVisible(cell_type);
+	name_input_        -> setVisible(cell_type);
+	
+	empty_label_       -> setVisible(cell_type == TitleBlockCell::EmptyCell);
+	
+	logo_label_        -> setVisible(cell_type == TitleBlockCell::LogoCell);
+	logo_input_        -> setVisible(cell_type == TitleBlockCell::LogoCell);
+	add_logo_input_    -> setVisible(cell_type == TitleBlockCell::LogoCell);
+	
+	label_checkbox_    -> setVisible(cell_type == TitleBlockCell::TextCell);
+	label_input_       -> setVisible(cell_type == TitleBlockCell::TextCell);
+	label_edit_        -> setVisible(cell_type == TitleBlockCell::TextCell);
+	value_label_       -> setVisible(cell_type == TitleBlockCell::TextCell);
+	value_input_       -> setVisible(cell_type == TitleBlockCell::TextCell);
+	value_edit_        -> setVisible(cell_type == TitleBlockCell::TextCell);
+	align_label_       -> setVisible(cell_type == TitleBlockCell::TextCell);
+	horiz_align_label_ -> setVisible(cell_type == TitleBlockCell::TextCell);
+	horiz_align_input_ -> setVisible(cell_type == TitleBlockCell::TextCell);
+	vert_align_label_  -> setVisible(cell_type == TitleBlockCell::TextCell);
+	vert_align_input_  -> setVisible(cell_type == TitleBlockCell::TextCell);
+	font_size_label_   -> setVisible(cell_type == TitleBlockCell::TextCell);
+	font_size_input_   -> setVisible(cell_type == TitleBlockCell::TextCell);
+	font_adjust_input_ -> setVisible(cell_type == TitleBlockCell::TextCell);
+}
+
+/**
+	Set the title block cell to be edited. The cell pointer is stored by this
+	class; however, modifications made by the user are packaged as
+	ModifyTitleBlockCellCommand objects and emitted through the
+	cellModified() signal.
+	@param cell Title block cell to be edited
+*/
+void TitleBlockTemplateCellWidget::edit(TitleBlockCell *cell) {
+	if (!cell) return;
+	edited_cell_ = cell;
+	int type = cell -> type();
+	updateFormType(type);
+	
+	name_input_        -> setText(cell -> value_name);
+	label_checkbox_    -> setChecked(cell -> display_label);
+	label_input_       -> setText(cell -> label.name());
+	value_input_       -> setText(cell -> value.name());
+	font_adjust_input_ -> setChecked(cell -> hadjust);
+	horiz_align_input_ -> setCurrentIndex(horiz_align_indexes_[cell -> horizontalAlign()]);
+	vert_align_input_  -> setCurrentIndex(vert_align_indexes_[cell -> verticalAlign()]);
+	
+	font_size_input_   -> blockSignals(true); // QSpinBox has no signal triggered for each non-programmatic change
+	font_size_input_   -> setValue(TitleBlockTemplate::fontForCell(*cell).pointSize());
+	font_size_input_   -> blockSignals(false);
+	
+	logo_input_        -> setCurrentIndex(logo_input_ -> findData(cell -> logo_reference));
+}
+
+/**
+	Emit a type modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editType() {
+	emitModification("type", cell_type_input_ -> itemData(cell_type_input_ -> currentIndex()));
+}
+
+/**
+	Emit a name modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editName() {
+	emitModification("name", name_input_ -> text());
+}
+
+/**
+	Emit a modification command stating whether the label should be displayed or not.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editLabelDisplayed() {
+	emitModification("displaylabel", label_checkbox_ -> isChecked());
+}
+
+/**
+	Emit a label modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editLabel() {
+	if (!edited_cell_) return;
+	editTranslatableValue(edited_cell_ -> label, "label", tr("Label de cette cellule"));
+	label_input_ -> setText(edited_cell_ -> label.name());
+}
+
+/**
+	Emit a value modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editValue() {
+	if (!edited_cell_) return;
+	editTranslatableValue(edited_cell_ -> value, "value", tr("Valeur de cette cellule"));
+	value_input_ -> setText(edited_cell_ -> value.name());
+}
+
+/**
+	Emit an alignment modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editAlignment() {
+	emitModification("alignment", alignment());
+}
+
+/**
+	Emit a font size modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editFontSize() {
+	emitModification("fontsize", font_size_input_ -> value());
+}
+
+/**
+	Emit a modification command stating whether the text should be adjusted if needed.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editAdjust() {
+	emitModification("horizontal_adjust", font_adjust_input_ -> isChecked());
+}
+
+/**
+	Emit a logo modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+void TitleBlockTemplateCellWidget::editLogo() {
+	emitModification("logo", logo_input_ -> currentText());
+}
+
+/**
+	Updates the list of available logos
+	@param parent_template The title block template which contains the currently edited cell
+*/
+void TitleBlockTemplateCellWidget::updateLogosComboBox(const TitleBlockTemplate *parent_template) {
+	// saves the current value before erasing all entries
+	QVariant current_value = logo_input_ -> itemData(logo_input_ -> currentIndex());
+	logo_input_ -> clear();
+	
+	// default choice (the parent template may have no logo yet)
+	logo_input_ -> addItem(
+		tr("Aucun logo", "text displayed in the combo box when a template has no logo"),
+		QVariant(QString(""))
+	);
+	logo_input_ -> setCurrentIndex(0);
+	
+	if (!parent_template) return;
+	foreach (QString logo, parent_template -> logos()) {
+		logo_input_ -> addItem(logo, QVariant(logo));
+	}
+	int current_value_index = logo_input_ -> findData(current_value);
+	if (current_value_index != -1) {
+		logo_input_ -> setCurrentIndex(current_value_index);
+	}
+}
+
+/**
+	@param read_only whether this edition widget should be read only
+*/
+void TitleBlockTemplateCellWidget::setReadOnly(bool read_only) {
+	if (read_only_ == read_only) return;
+	read_only_ = read_only;
+	
+	cell_type_input_ -> setEnabled(!read_only_);
+	logo_input_ -> setEnabled(!read_only_);
+	name_input_ -> setReadOnly(read_only_);
+	label_checkbox_ -> setEnabled(!read_only_);
+	label_edit_ -> setEnabled(!read_only_);
+	value_edit_ -> setEnabled(!read_only_);
+	horiz_align_input_ -> setEnabled(!read_only_);
+	vert_align_input_ -> setEnabled(!read_only_);
+	font_size_input_ -> setReadOnly(read_only_);
+	font_adjust_input_ -> setEnabled(!read_only_);
+}
+
+/**
+	Emit a horizontal alignment modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+int TitleBlockTemplateCellWidget::horizontalAlignment() const {
+	return(horiz_align_indexes_.key(horiz_align_input_ -> currentIndex()));
+}
+
+/**
+	Emit a vertical alignment modification command.
+	@see ModifyTitleBlockCellCommand
+*/
+int TitleBlockTemplateCellWidget::verticalAlignment() const {
+	return(vert_align_indexes_.key(vert_align_input_ -> currentIndex()));
+}
+
+/**
+	@return the currently selected alignment.
+*/
+int TitleBlockTemplateCellWidget::alignment() const {
+	return(horizontalAlignment() | verticalAlignment());
+}
+
+/**
+	@return whether this edition widget is read only
+*/
+bool TitleBlockTemplateCellWidget::isReadOnly() const {
+	return(read_only_);
+}
+
+/**
+	Allow the user to edit a translatable string (e.g. value or label).
+	If the user modified the string, this method emits a
+	ModifyTitleBlockCellCommand object through the cellModified() signal.
+	@param names Translatable string to be edited
+	@param attribute Name of the edited cell attribute
+	@param title Title of the dialog window
+*/
+void TitleBlockTemplateCellWidget::editTranslatableValue(NamesList &names, const QString &attribute, const QString &title) const {
+	NamesListWidget *names_widget = new NamesListWidget();
+	names_widget -> setNames(names);
+	QDialogButtonBox * buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	
+	QLabel *information = new QLabel(labelValueInformationString());
+	information -> setTextFormat(Qt::RichText);
+	information -> setWordWrap(true);
+	
+	QLabel *def_var_label = new QLabel(defaultVariablesString());
+	def_var_label -> setTextFormat(Qt::RichText);
+	
+	QVBoxLayout *editor_layout = new QVBoxLayout();
+	editor_layout -> addWidget(information);
+	editor_layout -> addWidget(names_widget);
+	editor_layout -> addWidget(def_var_label);
+	editor_layout -> addWidget(buttons);
+	
+	QDialog edit_dialog;
+	edit_dialog.setWindowTitle(title);
+	connect(buttons, SIGNAL(rejected()), &edit_dialog, SLOT(reject()));
+	connect(buttons, SIGNAL(accepted()), &edit_dialog, SLOT(accept()));
+	edit_dialog.setLayout(editor_layout);
+	if (edit_dialog.exec() == QDialog::Accepted) {
+		emitModification(attribute, qVariantFromValue(names_widget -> names()));
+		
+	}
+}
+
+/**
+	Create a ModifyTitleBlockCellCommand object to change \a attribute to \a new_value.
+	This object is then emitted through the cellModified() signal.
+	@see ModifyTitleBlockCellCommand
+	@param attribute Modified cell attribute
+	@param new_value New value for the modified cell attribute
+*/
+void TitleBlockTemplateCellWidget::emitModification(const QString &attribute, const QVariant &new_value) const {
+	if (!edited_cell_) return;
+	
+	// avoid creating a QUndoCommand object when no modification was actually done
+	if (edited_cell_ -> attribute(attribute) == new_value) return;
+	
+	ModifyTitleBlockCellCommand *command = new ModifyTitleBlockCellCommand(edited_cell_);
+	command -> addModification(attribute, new_value);
+	command -> setText(
+		tr("\311dition d'une cellule : %1", "label of and undo command when editing a cell")
+		.arg(TitleBlockCell::attributeName(attribute))
+	);
+	emit(cellModified(command));
+}
+
+/**
+	@return a string describing the various variables provided by default by
+	the application.
+*/
+QString TitleBlockTemplateCellWidget::defaultVariablesString() const {
+	QString def_var_string = tr(
+		"Par d\351faut, les variables suivantes sont disponibles :"
+		"<ul>"
+		"<li>%{author} : auteur du folio</li>"
+		"<li>%{date} : date du folio</li>"
+		"<li>%{title} : titre du folio</li>"
+		"<li>%{filename} : nom de fichier du projet</li>"
+		"<li>%{folio} : indications relatives au folio</li>"
+		"<li>%{folio-id} : position du folio dans le projet</li>"
+		"<li>%{folio-total} : nombre total de folios dans le projet</li>"
+		"</ul>"
+	);
+	return(def_var_string);
+}
+
+/**
+	@return a string describing what the user may enter as cell label / value.
+*/
+QString TitleBlockTemplateCellWidget::labelValueInformationString() const {
+	QString lab_val_inf_string = tr(
+		"Chaque cellule d'un cartouche affiche une valeur, optionnellement "
+		"pr\351c\351d\351e d'un label. Tous deux peuvent \352tre traduits en "
+		"plusieurs langues."
+		"<br/>"
+		"Comme ce que vous \351ditez actuellement est un "
+		"<em>mod\350le</em> de cartouche, ne saisissez pas directement des "
+		"donn\351es brutes : ins\351rez plut\364t des variables sous la forme "
+		"%{nom-de-variable}, qui seront ensuite remplac\351es par les valeurs "
+		"ad\351quates sur le folio."
+	);
+	return(lab_val_inf_string);
+}

Added: trunk/sources/titleblock/templatecellwidget.h
===================================================================
--- trunk/sources/titleblock/templatecellwidget.h	                        (rev 0)
+++ trunk/sources/titleblock/templatecellwidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,118 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_CELL_WIDGET_H
+#define TITLEBLOCK_SLASH_TEMPLATE_CELL_WIDGET_H
+#include <QtGui>
+#include "qet.h"
+class ModifyTitleBlockCellCommand;
+class TitleBlockTemplate;
+class TitleBlockCell;
+class NamesList;
+
+/**
+	This class implements an edition widget for cells that compose a title
+	block template.
+*/
+class TitleBlockTemplateCellWidget : public QWidget {
+	Q_OBJECT
+	
+	// constructor, destructor
+	public:
+	TitleBlockTemplateCellWidget(TitleBlockTemplate * = 0, QWidget * = 0);
+	virtual ~TitleBlockTemplateCellWidget();
+	private:
+	TitleBlockTemplateCellWidget(const TitleBlockTemplateCellWidget &);
+	
+	// attributes
+	private:
+	/// is the template read-only?
+	bool read_only_;
+	QLabel    *cell_type_label_;
+	QComboBox *cell_type_input_;
+	
+	QLabel *empty_label_;
+	
+	QLabel      *logo_label_;
+	QComboBox   *logo_input_;
+	QPushButton *add_logo_input_;
+	
+	QLabel        *name_label_;
+	QLineEdit     *name_input_;
+	QCheckBox     *label_checkbox_;
+	QLineEdit     *label_input_;
+	QPushButton   *label_edit_;
+	QLabel        *value_label_;
+	QLineEdit     *value_input_;
+	QPushButton   *value_edit_;
+	QLabel        *align_label_;
+	QLabel        *horiz_align_label_;
+	QComboBox     *horiz_align_input_;
+	QHash<int, int> horiz_align_indexes_;
+	QLabel        *vert_align_label_;
+	QComboBox     *vert_align_input_;
+	QHash<int, int> vert_align_indexes_;
+	QLabel        *font_size_label_;
+	QSpinBox      *font_size_input_;
+	QCheckBox     *font_adjust_input_;
+	QVBoxLayout   *cell_editor_layout_;
+	QHBoxLayout   *cell_editor_type_and_name_layout_;
+	QGridLayout   *cell_editor_text_layout_;
+	QHBoxLayout   *cell_editor_image_layout_;
+	
+	TitleBlockCell *edited_cell_;
+	
+	// methods
+	public:
+	int horizontalAlignment() const;
+	int verticalAlignment() const;
+	int alignment() const;
+	bool isReadOnly() const;
+	
+	protected:
+	void editTranslatableValue(NamesList &, const QString &, const QString &) const;
+	void emitModification(const QString &, const QVariant &) const;
+	QString defaultVariablesString() const;
+	QString labelValueInformationString() const;
+	
+	private:
+	void initWidgets();
+	
+	public slots:
+	void updateFormType(int);
+	void edit(TitleBlockCell *);
+	void editType();
+	void editName();
+	void editLabelDisplayed();
+	void editLabel();
+	void editValue();
+	void editAlignment();
+	void editFontSize();
+	void editAdjust();
+	void editLogo();
+	void updateLogosComboBox(const TitleBlockTemplate *);
+	void setReadOnly(bool);
+	
+	private slots:
+	
+	
+	signals:
+	void logoEditionRequested();
+	void cellModified(ModifyTitleBlockCellCommand *) const;
+};
+
+#endif

Added: trunk/sources/titleblock/templatecommands.cpp
===================================================================
--- trunk/sources/titleblock/templatecommands.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatecommands.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1061 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatecommands.h"
+#include "templatevisualcell.h"
+#include "templateview.h"
+#include "titleblockcell.h"
+#include "dimension.h"
+#define TITLEBLOCK_DEFAULT_ROW_HEIGHT TitleBlockDimension(25)
+#define TITLEBLOCK_DEFAULT_COL_WIDTH  TitleBlockDimension(50)
+
+/**
+	Constructor
+	@param cell Modified cell
+	@param parent Parent QUndoCommand
+*/
+ModifyTitleBlockCellCommand::ModifyTitleBlockCellCommand(TitleBlockCell *cell, QUndoCommand *parent) :
+	QUndoCommand(parent),
+	view_(0),
+	modified_cell_(cell)
+{
+}
+
+/**
+	Destructor
+*/
+ModifyTitleBlockCellCommand::~ModifyTitleBlockCellCommand() {
+}
+
+/**
+	@see QUndoCommand::id()
+	@return the ID of this command.
+*/
+int ModifyTitleBlockCellCommand::id() const {
+	return(MODIFY_TITLE_BLOCK_CELL_COMMAND_ID);
+}
+
+/**
+	@see QUndoCommand::mergeWith()
+	@param command Command to merge with.
+	@return true on success, false otherwise
+*/
+bool ModifyTitleBlockCellCommand::mergeWith(const QUndoCommand *command) {
+	const ModifyTitleBlockCellCommand *other = static_cast<const ModifyTitleBlockCellCommand *>(command);
+	if (other) {
+		if (other -> modified_cell_ == modified_cell_) {
+			if (other -> new_values_.keys() == new_values_.keys()) {
+				new_values_ = other -> new_values_;
+				return(true);
+			}
+		}
+	}
+	return(false);
+}
+
+/**
+	Undo the change.
+*/
+void ModifyTitleBlockCellCommand::undo() {
+	if (!modified_cell_) return;
+	foreach (QString attribute, old_values_.keys()) {
+		modified_cell_ -> setAttribute(attribute, old_values_[attribute]);
+	}
+	if (view_) view_ -> refresh();
+}
+
+/**
+	Redo the change.
+*/
+void ModifyTitleBlockCellCommand::redo() {
+	if (!modified_cell_) return;
+	foreach (QString attribute, new_values_.keys()) {
+		modified_cell_ -> setAttribute(attribute, new_values_[attribute]);
+	}
+	if (view_) view_ -> refresh();
+}
+
+/**
+	@return the cell modified by this command
+*/
+TitleBlockCell *ModifyTitleBlockCellCommand::cell() const {
+	return(modified_cell_);
+}
+
+/**
+	Set the cell modified by this command object
+	@param modified_cell the cell modified by this command
+*/
+void ModifyTitleBlockCellCommand::setCell(TitleBlockCell *modified_cell) {
+	modified_cell_ = modified_cell;
+}
+
+/**
+	@return the view to be updated after the cell modification
+*/
+TitleBlockTemplateView *ModifyTitleBlockCellCommand::view() const {
+	return(view_);
+}
+
+/**
+	Set the view to be updated after the cell modification
+	@param view the view to be updated after the cell modification
+*/
+void ModifyTitleBlockCellCommand::setView(TitleBlockTemplateView *view) {
+	view_ = view;
+}
+
+/**
+	Erase the known old/new values.
+*/
+void ModifyTitleBlockCellCommand::clear() {
+	old_values_.clear();
+	new_values_.clear();
+}
+
+/**
+	Register a new modification on a title block template cell; you may
+	indicate either the new value or the old one: this method will
+	systematically fetch the other one.
+	@param attribute Name of the modified attribute
+	@param value Old or new value of the modified attribute, depending on is_old_value
+	@param is_old_value (optional, defaults to false) Indicates whether the provided value is the old or the new one.
+*/
+void ModifyTitleBlockCellCommand::addModification(const QString &attribute, const QVariant &value, bool is_old_value) {
+	if (is_old_value) {
+		// the provided value is the old one; therefore, the one we fetch is the new one
+		old_values_[attribute] = value;
+		if (modified_cell_) {
+			new_values_[attribute] = modified_cell_ -> attribute(attribute);
+		}
+	} else {
+		// the provided value is the new one; therefore, we fetch the old one
+		if (modified_cell_) {
+			old_values_[attribute] = modified_cell_ -> attribute(attribute);
+		}
+		new_values_[attribute] = value;
+	}
+}
+
+/**
+	Constructor
+	@param tbtemplate Modified title block template
+	@param parent Parent QUndoCommand
+*/
+TitleBlockTemplateCommand::TitleBlockTemplateCommand(TitleBlockTemplate *tbtemplate, QUndoCommand *parent) :
+	QUndoCommand(parent),
+	tbtemplate_(tbtemplate),
+	view_(0)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateCommand::~TitleBlockTemplateCommand() {
+}
+
+/**
+	@return the modified title block template.
+*/
+TitleBlockTemplate *TitleBlockTemplateCommand::titleBlockTemplate() const {
+	return(tbtemplate_);
+}
+
+/**
+	Set the modified title block template.
+	@param tbtemplate New modified title block template.
+*/
+void TitleBlockTemplateCommand::setTitleBlockTemplate(TitleBlockTemplate *tbtemplate) {
+	tbtemplate_ = tbtemplate;
+}
+
+/**
+	@return the view to be updated after the template modification
+*/
+TitleBlockTemplateView *TitleBlockTemplateCommand::view() const {
+	return(view_);
+}
+
+/**
+	Set the view to be updated after the template modification
+	@param view the view to be updated after the template modification
+*/
+void TitleBlockTemplateCommand::setView(TitleBlockTemplateView *view) {
+	view_ = view;
+}
+
+/**
+	Refresh the view, if any.
+*/
+void TitleBlockTemplateCommand::refreshView() {
+	if (!view_) return;
+	view_ -> refresh();
+}
+
+/**
+	Refresh the view, including layout reloading, if any.
+*/
+void TitleBlockTemplateCommand::refreshLayout() {
+	if (!view_) return;
+	view_ -> updateLayout();
+}
+
+/**
+	This static method is a convenience to create a ModifyTemplateGridCommand
+	that adds a row to \a tbtemplate at \a index.
+	@param tbtemplate Modified title block template
+	@param index Index where the row should be inserted.
+	@return a ModifyTemplateGridCommand object, or 0 if something went wrong.
+*/
+ModifyTemplateGridCommand *ModifyTemplateGridCommand::addRow(TitleBlockTemplate *tbtemplate, int index) {
+	if (!tbtemplate) return(0);
+	
+	// create the command itself
+	ModifyTemplateGridCommand *add_row_command = new ModifyTemplateGridCommand(tbtemplate);
+	add_row_command -> setInsertion(true);
+	add_row_command -> setType(true);
+	add_row_command -> setCells(tbtemplate -> createRow());
+	add_row_command -> setDimension(TITLEBLOCK_DEFAULT_ROW_HEIGHT);
+	add_row_command -> setIndex(index);
+	
+	return(add_row_command);
+}
+
+/**
+	This static method is a convenience to create a ModifyTemplateGridCommand
+	that adds a column to \a tbtemplate at \a index.
+	@param tbtemplate Modified title block template.
+	@param index Index where the column should be inserted.
+	@return a ModifyTemplateGridCommand object, or 0 if something went wrong.
+*/
+ModifyTemplateGridCommand *ModifyTemplateGridCommand::addColumn(TitleBlockTemplate *tbtemplate, int index) {
+	if (!tbtemplate) return(0);
+	
+	// create the command itself
+	ModifyTemplateGridCommand *add_column_command = new ModifyTemplateGridCommand(tbtemplate);
+	add_column_command -> setInsertion(true);
+	add_column_command -> setType(false);
+	add_column_command -> setCells(tbtemplate -> createColumn());
+	add_column_command -> setDimension(TITLEBLOCK_DEFAULT_COL_WIDTH);
+	add_column_command -> setIndex(index);
+	
+	return(add_column_command);
+}
+
+/**
+	This static method is a convenience to create a ModifyTemplateGridCommand
+	that removes the row at \a index from \a tbtemplate.
+	@param tbtemplate Modified title block template.
+	@param index Index of the removed row.
+	@return a ModifyTemplateGridCommand object, or 0 if something went wrong.
+*/
+ModifyTemplateGridCommand *ModifyTemplateGridCommand::deleteRow(TitleBlockTemplate *tbtemplate, int index) {
+	if (!tbtemplate) return(0);
+	
+	// create the command itself
+	ModifyTemplateGridCommand *del_row_command = new ModifyTemplateGridCommand(tbtemplate);
+	del_row_command -> setInsertion(false);
+	del_row_command -> setType(true);
+	del_row_command -> setIndex(index);
+	
+	return(del_row_command);
+}
+
+/**
+	This static method is a convenience to create a ModifyTemplateGridCommand
+	that removes the column at \a index from \a tbtemplate.
+	@param tbtemplate Modified title block template.
+	@param index Index of the removed column.
+	@return a ModifyTemplateGridCommand object, or 0 if something went wrong.
+*/
+ModifyTemplateGridCommand *ModifyTemplateGridCommand::deleteColumn(TitleBlockTemplate *tbtemplate, int index) {
+	if (!tbtemplate) return(0);
+	
+	// create the command itself
+	ModifyTemplateGridCommand *del_column_command = new ModifyTemplateGridCommand(tbtemplate);
+	del_column_command -> setInsertion(false);
+	del_column_command -> setType(false);
+	del_column_command -> setIndex(index);
+	
+	return(del_column_command);
+}
+
+/**
+	Construct a default ModifyTemplateGridCommand, i.e. a command adding a 25px row at the bottom of the template.
+	@param tbtemplate Modified title block template
+	@param parent Parent QUndoCommand
+*/
+ModifyTemplateGridCommand::ModifyTemplateGridCommand(TitleBlockTemplate *tbtemplate, QUndoCommand *parent) :
+	TitleBlockTemplateCommand(tbtemplate, parent),
+	index_(-1),
+	type_(true),
+	dimension_(TITLEBLOCK_DEFAULT_ROW_HEIGHT),
+	insertion_(true)
+{
+	updateText();
+}
+
+/**
+	Destructor
+*/
+ModifyTemplateGridCommand::~ModifyTemplateGridCommand() {
+}
+
+/**
+	@return the index of the inserted/deleted row/column
+*/
+int ModifyTemplateGridCommand::index() const {
+	return(index_);
+}
+
+/**
+	Set the index of the inserted/deleted row/column.
+	@param index Index of the inserted/deleted row/column.
+*/
+void ModifyTemplateGridCommand::setIndex(int index) {
+	index_ = index;
+}
+
+/**
+	@return a list of pointers to cells composing the inserted/deleted row/column.
+*/
+QList<TitleBlockCell *> ModifyTemplateGridCommand::cells() const {
+	return(cells_);
+}
+
+/**
+	Set the cells composing the inserted/deleted row/column.
+	@param cells List of pointers to cells composing the inserted/deleted row/column.
+*/
+void ModifyTemplateGridCommand::setCells(const QList<TitleBlockCell *> &cells) {
+	cells_ = cells;
+}
+
+/**
+	@return the dimension of the inserted/deleted row/column.
+*/
+TitleBlockDimension ModifyTemplateGridCommand::dimension() const {
+	return dimension_;
+}
+
+/**
+	Set the dimension of the inserted/deleted row/column
+	@param dimension Dimension of the inserted/deleted row/column
+*/
+void ModifyTemplateGridCommand::setDimension(const TitleBlockDimension &dimension) {
+	dimension_ = dimension;
+}
+
+/**
+	@return true if this object is about inserting/deleting a row, false for a column.
+*/
+int ModifyTemplateGridCommand::type() const {
+	return(type_);
+}
+
+/**
+	Indicates whether this object inserts/deletes a row or a column.
+	@param type true if this object is about inserting/deleting a row, false for a column.
+*/
+void ModifyTemplateGridCommand::setType(bool type) {
+	type_ = type;
+	updateText();
+}
+
+/**
+	@return true if the row/column is inserted, false if it is deleted
+*/
+bool ModifyTemplateGridCommand::isInsertion() const {
+	return(insertion_);
+}
+
+/**
+	@param insertion true if the row/column is inserted, false if it is deleted
+*/
+void ModifyTemplateGridCommand::setInsertion(bool insertion) {
+	insertion_ = insertion;
+	updateText();
+}
+
+/**
+	Undo the change.
+*/
+void ModifyTemplateGridCommand::undo() {
+	apply(true);
+}
+
+/**
+	Redo the change.
+*/
+void ModifyTemplateGridCommand::redo() {
+	apply(false);
+}
+
+/**
+	Update the text describing what the command does.
+*/
+void ModifyTemplateGridCommand::updateText() {
+	if (type_) {
+		if (insertion_) {
+			setText(QObject::tr("Insertion d'une ligne", "label used in the title block template editor undo list"));
+		} else {
+			setText(QObject::tr("Suppression d'une ligne", "label used in the title block template editor undo list"));
+		}
+	} else {
+		if (insertion_) {
+			setText(QObject::tr("Insertion d'une colonne", "label used in the title block template editor undo list"));
+		} else {
+			setText(QObject::tr("Suppression d'une colonne", "label used in the title block template editor undo list"));
+		}
+	}
+}
+
+/*
+	This method takes care of the actual job when undoing / redoing a
+	row/column insertion/removal.
+	@param true to undo the change, false to apply it.
+*/
+void ModifyTemplateGridCommand::apply(bool undo) {
+	if (!tbtemplate_ || index_ == -1) return;
+	
+	if (insertion_ ^ undo) {
+		if (type_) {
+			tbtemplate_ -> insertRow(dimension_.value, cells_, index_);
+		} else {
+			tbtemplate_ -> insertColumn(dimension_, cells_, index_);
+		}
+	} else {
+		if (type_) {
+			dimension_.value = tbtemplate_ -> rowDimension(index_);
+			cells_ = tbtemplate_ -> takeRow(index_);
+		} else {
+			dimension_ = tbtemplate_ -> columnDimension(index_);
+			cells_ = tbtemplate_ -> takeColumn(index_);
+		}
+	}
+	
+	// update the view, if any
+	if (view_) {
+		view_ -> updateLayout();
+	}
+}
+
+/**
+	Construct a default ModifyTemplateDimension.
+	@param tbtemplate Modified title block template
+	@param parent Parent QUndoCommand
+*/
+ModifyTemplateDimension::ModifyTemplateDimension(TitleBlockTemplate *tbtemplate, QUndoCommand *parent) :
+	TitleBlockTemplateCommand(tbtemplate, parent),
+	index_(-1),
+	type_(true),
+	before_(TitleBlockDimension(-1)),
+	after_(TitleBlockDimension(-1))
+{
+}
+
+/**
+	Destructor
+*/
+ModifyTemplateDimension::~ModifyTemplateDimension() {
+}
+
+/**
+	@return the index of the resized row/column
+*/
+int ModifyTemplateDimension::index() const {
+	return(index_);
+}
+
+/**
+	Set the index of the resized row/column.
+	@param index Index of the resized row/column.
+*/
+void ModifyTemplateDimension::setIndex(int index) {
+	index_ = index;
+}
+
+/**
+	@return true if this object is about resizing a row, false for a column.
+*/
+int ModifyTemplateDimension::type() const {
+	return type_;
+}
+
+/**
+	Indicates whether this object resizes a row or a column.
+	@param type true if this object is about resizing a row, false for a column.
+*/
+void ModifyTemplateDimension::setType(bool type) {
+	type_ = type;
+	updateText();
+}
+
+/**
+	@return the dimension of the row/column before it is resized
+*/
+TitleBlockDimension ModifyTemplateDimension::dimensionBefore() const {
+	return(before_);
+}
+
+/**
+	@param dimension the dimension of the row/column before it is resized
+*/
+void ModifyTemplateDimension::setDimensionBefore(const TitleBlockDimension &dimension) {
+	before_ = dimension;
+}
+
+/**
+	@return the dimension of the row/column after it is resized
+*/
+TitleBlockDimension ModifyTemplateDimension::dimensionAfter() const {
+	return(after_);
+}
+
+/**
+	@param dimension the dimension of the row/column after it is resized
+*/
+void ModifyTemplateDimension::setDimensionAfter(const TitleBlockDimension &dimension) {
+	after_ = dimension;
+}
+
+/**
+	Restore the previous size of the row/column.
+*/
+void ModifyTemplateDimension::undo() {
+	apply(before_);
+}
+
+/**
+	Resize the row/column.
+*/
+void ModifyTemplateDimension::redo() {
+	apply(after_);
+}
+
+/**
+	Update the text describing what the command does.
+*/
+void ModifyTemplateDimension::updateText() {
+	if (type_) {
+		setText(QObject::tr("Modification d'une ligne", "label used in the title block template editor undo list"));
+	} else {
+		setText(QObject::tr("Modification d'une colonne", "label used in the title block template editor undo list"));
+	}
+}
+
+/**
+	Applies a given size to the row/column
+	@param dimension Size to apply
+*/
+void ModifyTemplateDimension::apply(const TitleBlockDimension &dimension) {
+	if (!tbtemplate_) return;
+	if (type_) {
+		tbtemplate_ -> setRowDimension(index_, dimension);
+	} else {
+		tbtemplate_ -> setColumnDimension(index_, dimension);
+	}
+	if (view_) {
+		if (type_) {
+			view_ -> rowsDimensionsChanged();
+		} else {
+			view_ -> columnsDimensionsChanged();
+		}
+	}
+}
+
+/**
+	Construct a command object that acts on \a tbtemplate in order to merge \a merged_cells.
+	Note: you should check the resulting object is valid using isValid().
+	@param merged_cells Cells to be merged together into a single one.
+	@param tbtemplate Modified title block template.
+	@param parent Parent QUndoCommand.
+*/
+MergeCellsCommand::MergeCellsCommand(const TitleBlockTemplateCellsSet &merged_cells, TitleBlockTemplate *tbtemplate, QUndoCommand *parent) :
+	TitleBlockTemplateCommand(tbtemplate, parent),
+	spanning_cell_(0),
+	row_span_after_(-1),
+	col_span_after_(-1)
+{
+	if (!canMerge(merged_cells, tbtemplate)) return;
+	
+	// the spanning cell is the top left cell
+	TitleBlockTemplateVisualCell *top_left_cell = merged_cells.topLeftCell();
+	if (!top_left_cell) return;
+	spanning_cell_ = top_left_cell -> cell();
+	if (!spanning_cell_) return;
+	
+	// store the spanner_cell attribute of each cell implied in the merge
+	foreach(TitleBlockCell *cell, merged_cells.cells()) {
+		spanner_cells_before_merge_.insert(cell, cell -> spanner_cell);
+	}
+	
+	// store the former values of the row_span and col_span attributes of the spanning cell
+	row_span_before_ = spanning_cell_ -> row_span;
+	col_span_before_ = spanning_cell_ -> col_span;
+	applied_row_span_before_ = spanning_cell_ -> applied_row_span;
+	applied_col_span_before_ = spanning_cell_ -> applied_col_span;
+	span_state_before_ = spanning_cell_ -> span_state;
+	
+	// calculate their new values after the merge operation
+	TitleBlockCell *bottom_right_cell = getBottomRightCell(merged_cells);
+	if (!bottom_right_cell) return;
+	row_span_after_ = bottom_right_cell -> num_row - spanning_cell_ -> num_row;
+	col_span_after_ = bottom_right_cell -> num_col - spanning_cell_ -> num_col;
+	
+	setText(
+		QString(
+			QObject::tr(
+				"Fusion de %1 cellules",
+				"label used in the title block template editor undo list; %1 is the number of merged cells"
+			)
+		).arg(merged_cells.count())
+	);
+}
+
+/**
+	Destructor
+*/
+MergeCellsCommand::~MergeCellsCommand() {
+}
+
+/**
+	@param merged_cells Cell sto be merged.
+	@param tbtemplate Modified title block template.
+	@return true if the merge is feasible, false otherwise
+*/
+bool MergeCellsCommand::canMerge(const TitleBlockTemplateCellsSet &merged_cells, TitleBlockTemplate *tbtemplate) {
+	Q_UNUSED(tbtemplate)
+	
+	// basic checks
+	if (!merged_cells.isRectangle()) return(false);
+	if (merged_cells.count() < 2) return(false);
+	
+	// the spanning cell is the top left cell
+	TitleBlockTemplateVisualCell *top_left_cell = merged_cells.topLeftCell();
+	if (!top_left_cell || !top_left_cell -> cell()) return(false);
+	
+	if (!getBottomRightCell(merged_cells)) return(false);
+	
+	return(true);
+}
+
+/**
+	@return true if this command object is valid and usable, false otherwise.
+*/
+bool MergeCellsCommand::isValid() const {
+	// we consider having a non-zero spanning cell and positive spans makes a MergeCellsCommand valid
+	return(spanning_cell_ && row_span_after_ != -1 && col_span_after_ != -1);
+}
+
+/**
+	Undo the merge operation.
+*/
+void MergeCellsCommand::undo() {
+	if (!isValid()) return;
+	
+	// restore the original spanning_cell attribute of all impacted cells
+	foreach (TitleBlockCell *cell, spanner_cells_before_merge_.keys()) {
+		cell -> spanner_cell = spanner_cells_before_merge_[cell];
+	}
+	
+	// restore the span-related attributes of the spanning cell
+	spanning_cell_ -> row_span = row_span_before_;
+	spanning_cell_ -> col_span = col_span_before_;
+	spanning_cell_ -> applied_row_span = applied_row_span_before_;
+	spanning_cell_ -> applied_col_span = applied_col_span_before_;
+	spanning_cell_ -> span_state = span_state_before_;
+	
+	if (view_) view_ -> updateLayout();
+}
+
+/**
+	Apply the merge operation
+*/
+void MergeCellsCommand::redo() {
+	if (!isValid()) return;
+	
+	// set the spanning_cell attributes of spanned cells to the spanning cell
+	foreach (TitleBlockCell *cell, spanner_cells_before_merge_.keys()) {
+		if (cell == spanning_cell_) continue;
+		cell -> spanner_cell = spanning_cell_;
+	}
+	
+	// set the new values of the row_span and col_span attributes
+	spanning_cell_ -> row_span = row_span_after_;
+	spanning_cell_ -> col_span = col_span_after_;
+	spanning_cell_ -> applied_row_span = row_span_after_;
+	spanning_cell_ -> applied_col_span = col_span_after_;
+	spanning_cell_ -> span_state = TitleBlockCell::Enabled;
+	
+	if (view_) view_ -> updateLayout();
+}
+
+/**
+	@param cells_set Set of title block template visual cells.
+	@return the bottom right logical cell within a set of visual cells.
+*/
+TitleBlockCell *MergeCellsCommand::getBottomRightCell(const TitleBlockTemplateCellsSet &cells_set) {
+	// first, we get the visual cell at the bottom right
+	TitleBlockTemplateVisualCell *bottom_right_cell = cells_set.bottomRightCell();
+	if (!bottom_right_cell) return(0);
+	
+	// next, we get its logical cells: the painted one and the spanned ones (if any)
+	QSet<TitleBlockCell *> logical_cells = bottom_right_cell -> cells();
+	if (logical_cells.isEmpty()) return(0);
+	if (logical_cells.count() == 1) return(logical_cells.toList().first());
+	
+	// we then look for the bottom right logical cell
+	int max_num_row = -1, max_num_col = -1;
+	TitleBlockCell *candidate = 0;
+	foreach(TitleBlockCell *cell, logical_cells) {
+		if (cell -> num_row > max_num_row) max_num_row = cell -> num_row;
+		if (cell -> num_col > max_num_col) max_num_col = cell -> num_col;
+		if (cell -> num_row == max_num_row && cell -> num_col == max_num_col) {
+			candidate = cell;
+		}
+	}
+	return(candidate);
+}
+
+/**
+	Construct a command object that acts on \a tbtemplate in order to split
+	\a splitted_cells.
+	Note: you should check the resulting object is valid using isValid().
+	@param splitted_cells Cell to be splitted.
+	@param tbtemplate Modified title block template.
+	@param parent Parent QUndoCommand.
+*/
+SplitCellsCommand::SplitCellsCommand(const TitleBlockTemplateCellsSet &splitted_cells, TitleBlockTemplate *tbtemplate, QUndoCommand *parent) :
+	TitleBlockTemplateCommand(tbtemplate, parent),
+	spanning_cell_(0),
+	row_span_before_(-1),
+	col_span_before_(-1)
+{
+	if (!canSplit(splitted_cells, tbtemplate)) return;
+	
+	// retrieve values necessary for the undo operation
+	spanning_cell_ = splitted_cells.first() -> cell();
+	spanned_cells_ = tbtemplate_ -> spannedCells(spanning_cell_);
+	row_span_before_ = spanning_cell_ -> row_span;
+	col_span_before_ = spanning_cell_ -> col_span;
+	applied_row_span_before_ = spanning_cell_ -> row_span;
+	applied_col_span_before_ = spanning_cell_ -> col_span;
+	span_state_before_ = spanning_cell_ -> span_state;
+	
+	setText(
+		QString(
+			QObject::tr(
+				"S\351paration d'une cellule en %1",
+				"label used in the title block template editor undo list; %1 is the number of cells after the split"
+			)
+		).arg(spanned_cells_.count() + 1)
+	);
+}
+
+/**
+	Destructor
+*/
+SplitCellsCommand::~SplitCellsCommand() {
+}
+
+/**
+	@param splitted_cells Cell to be splitted.
+	@param tbtemplate Modified title block template.
+	@return true if the split is feasible, false otherwise
+*/
+bool SplitCellsCommand::canSplit(const TitleBlockTemplateCellsSet &splitted_cells, TitleBlockTemplate *tbtemplate) {
+	Q_UNUSED(tbtemplate)
+	
+	// basic check: the command applies to a single visual cell only
+	if (splitted_cells.count() != 1) return(false);
+	
+	// fetch the spanning cell
+	TitleBlockCell *spanning_cell = splitted_cells.first() -> cell();
+	if (!spanning_cell) return(false);
+	
+	// ensure the cell spans over other cells and therefore can be splitted
+	if (!spanning_cell -> spans()) return(false);
+	
+	return(true);
+}
+
+/**
+	@return true if this command object is valid and usable, false otherwise.
+*/
+bool SplitCellsCommand::isValid() const {
+	// we consider having a non-zero spanning cell and at least one spanned cell makes a SplitCellsCommand valid
+	return(spanning_cell_ && spanned_cells_.count());
+}
+
+/**
+	Undo the split operation
+*/
+void SplitCellsCommand::undo() {
+	if (!isValid()) return;
+	
+	// the spanned cells are spanned again
+	foreach(TitleBlockCell *cell, spanned_cells_) {
+		cell -> spanner_cell = spanning_cell_;
+	}
+	
+	// the spanning cell span again
+	spanning_cell_ -> row_span = row_span_before_;
+	spanning_cell_ -> col_span = col_span_before_;
+	spanning_cell_ -> applied_row_span = applied_row_span_before_;
+	spanning_cell_ -> applied_col_span = applied_col_span_before_;
+	spanning_cell_ -> span_state = span_state_before_;
+	
+	if (view_) view_ -> updateLayout();
+}
+
+/**
+	Apply the split operation
+*/
+void SplitCellsCommand::redo() {
+	if (!isValid()) return;
+	
+	// the spanned cells are not spanned anymore
+	foreach(TitleBlockCell *cell, spanned_cells_) {
+		cell -> spanner_cell = 0;
+	}
+	
+	// the spanning cell does not span anymore
+	spanning_cell_ -> row_span = 0;
+	spanning_cell_ -> col_span = 0;
+	tbtemplate_ -> checkCellSpan(spanning_cell_);
+	
+	if (view_) view_ -> updateLayout();
+}
+
+
+/**
+	Constructor
+	@param tbt Changed title block template
+	@param old_info Former information
+	@param new_info New information
+	@param parent Parent QUndoCommand
+*/
+ChangeTemplateInformationsCommand::ChangeTemplateInformationsCommand(TitleBlockTemplate *tbt, const QString &old_info, const QString &new_info, QUndoCommand *parent) :
+	QUndoCommand(QObject::tr("modification des informations compl\351mentaires", "undo caption"), parent),
+	tbtemplate_(tbt),
+	old_information_(old_info),
+	new_information_(new_info)
+{
+}
+
+/**
+	Destructor
+*/
+ChangeTemplateInformationsCommand::~ChangeTemplateInformationsCommand() {
+}
+
+/**
+	Undo the information change
+*/
+void ChangeTemplateInformationsCommand::undo() {
+	if (!tbtemplate_) return;
+	tbtemplate_ -> setInformation(old_information_);
+}
+
+/**
+	Redo the information change
+*/
+void ChangeTemplateInformationsCommand::redo() {
+	tbtemplate_ -> setInformation(new_information_);
+}
+
+/**
+	Constructor
+*/
+CutTemplateCellsCommand::CutTemplateCellsCommand(TitleBlockTemplate *tb_template, QUndoCommand *parent) :
+	TitleBlockTemplateCommand(tb_template, parent)
+{
+}
+
+/**
+	Destructor
+*/
+CutTemplateCellsCommand::~CutTemplateCellsCommand() {
+}
+
+/**
+	Undo a cut operation
+*/
+void CutTemplateCellsCommand::undo() {
+	foreach (TitleBlockCell *cell, cut_cells_.keys()) {
+		cell -> cell_type = cut_cells_.value(cell);
+	}
+	refreshView();
+}
+
+/**
+	Redo a cut operation
+*/
+void CutTemplateCellsCommand::redo() {
+	foreach (TitleBlockCell *cell, cut_cells_.keys()) {
+		cell -> cell_type = TitleBlockCell::EmptyCell;
+	}
+	refreshView();
+}
+
+void CutTemplateCellsCommand::setCutCells(const QList<TitleBlockCell *> &cells) {
+	foreach (TitleBlockCell *cell, cells) {
+		cut_cells_.insert(cell, cell -> cell_type);
+	}
+	updateText();
+}
+
+/**
+	Update the label describing this command
+*/
+void CutTemplateCellsCommand::updateText() {
+	setText(QObject::tr("Couper %n cellule(s)", "undo caption", cut_cells_.count()));
+}
+
+
+/**
+	Constructor
+	@param tb_template Changed title block template
+	@param parent Parent command
+*/
+PasteTemplateCellsCommand::PasteTemplateCellsCommand(TitleBlockTemplate *tb_template, QUndoCommand *parent) :
+	TitleBlockTemplateCommand(tb_template, parent)
+{
+}
+
+/**
+	Destructor
+*/
+PasteTemplateCellsCommand::~PasteTemplateCellsCommand() {
+}
+
+/**
+	Update the label describing this command
+*/
+void PasteTemplateCellsCommand::updateText() {
+	setText(QObject::tr("Coller %n cellule(s)", "undo caption", erased_cells_.count()));
+}
+
+/**
+	Undo a paste action.
+*/
+void PasteTemplateCellsCommand::undo() {
+	bool span_management = erased_cells_.count() > 1;
+	foreach (TitleBlockCell *cell, erased_cells_.keys()) {
+		cell -> loadContentFromCell(erased_cells_.value(cell));
+	}
+	if (span_management) {
+		// restore all span parameters as they were before the paste operation.
+		tbtemplate_ -> setAllSpans(spans_before_);
+		tbtemplate_ -> applyCellSpans();
+		refreshLayout();
+	} else {
+		refreshView();
+	}
+}
+
+/**
+	Redo a paste action.
+*/
+void PasteTemplateCellsCommand::redo() {
+	// we only play with spans when pasting more than one cell.
+	bool span_management = erased_cells_.count() > 1;
+	
+	if (span_management) {
+		// When pasting several cells, we may modify the span parameters of existing,
+		// non-erased cells. The easiest way to ensure everything can be restored at its
+		// initial state consists in saving the span parameters of every cell.
+		if (spans_before_.isEmpty()) {
+			spans_before_ = tbtemplate_ -> getAllSpans();
+		}
+	}
+	
+	// copy data from each pasted cell into each erased cell
+	foreach (TitleBlockCell *cell, erased_cells_.keys()) {
+		if (span_management) {
+			// the erased cell may be spanned by another cell
+			if (TitleBlockCell *spanning_cell = cell -> spanner_cell) {
+				// for the moment, we simply cancel the whole spanning
+				tbtemplate_ -> forgetSpanning(spanning_cell);
+			}
+		}
+		
+		// copy non-spans data
+		TitleBlockCell pasted_cell = pasted_cells_.value(cell);
+		cell -> loadContentFromCell(pasted_cell);
+		
+		if (span_management) {
+			// copy spans data
+			if ((pasted_cell.row_span != cell -> row_span) || (pasted_cell.col_span != cell -> col_span)) {
+				tbtemplate_ -> forgetSpanning(cell);
+				
+				// Note: the code below is similar to TitleBlockTemplate::checkCell() but is more aggressive (spans deletion).
+				// set the new/pasted span parameters
+				cell -> row_span = qBound(0, pasted_cell.row_span, tbtemplate_ -> rowsCount() - 1 - cell -> num_row);
+				cell -> col_span = qBound(0, pasted_cell.col_span, tbtemplate_ -> columnsCount() - 1 - cell -> num_col);
+				
+				if (cell -> row_span || cell -> col_span) {
+					// browse newly spanned cells...
+					foreach (TitleBlockCell *spanned_cell, tbtemplate_ -> spannedCells(cell, true)) {
+						// ... to ensure they are not already spanned by other cells
+						if (spanned_cell -> spanner_cell && spanned_cell -> spanner_cell != cell) {
+							// if so, simply cancel the whole spanning
+							tbtemplate_ -> forgetSpanning(spanned_cell -> spanner_cell);
+						}
+					}
+					
+					// set the spanner_cell attribute of newly spanned cells
+					tbtemplate_ -> applyCellSpan(cell);
+				}
+			}
+		}
+	}
+	if (span_management) {
+		refreshLayout();
+	} else {
+		refreshView();
+	}
+}
+
+/**
+	@param cell Pointer to the cell impacted by te paste operation
+	@param new_cell_content Content pasted to the cell
+*/
+void PasteTemplateCellsCommand::addPastedCell(TitleBlockCell *cell, const TitleBlockCell &new_cell_content) {
+	pasted_cells_.insert(cell, new_cell_content);
+}
+
+/**
+	@param cell Pointer to the cell impacted by te paste operation
+	@param former_cell_content Content of the cell before the paste operation
+*/
+void PasteTemplateCellsCommand::addErasedCell(TitleBlockCell *cell, const TitleBlockCell &former_cell_content) {
+	erased_cells_.insert(cell, former_cell_content);
+	updateText();
+}
+
+/**
+	This is a convenience function equivalent to:
+	addErasedCell(cell, before)
+	addPastedCell(cell, after)
+*/
+void PasteTemplateCellsCommand::addCell(TitleBlockCell *cell, const TitleBlockCell &before, const TitleBlockCell &after) {
+	addPastedCell(cell, after);
+	addErasedCell(cell, before);
+}

Added: trunk/sources/titleblock/templatecommands.h
===================================================================
--- trunk/sources/titleblock/templatecommands.h	                        (rev 0)
+++ trunk/sources/titleblock/templatecommands.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,317 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_COMMANDS_H
+#define TITLEBLOCK_SLASH_TEMPLATE_COMMANDS_H
+#define MODIFY_TITLE_BLOCK_CELL_COMMAND_ID 6378
+#include <QtCore>
+#include <QUndoCommand>
+#include "dimension.h"
+#include "templatecellsset.h"
+#include "titleblockcell.h"
+class TitleBlockTemplateView;
+class TitleBlockTemplate;
+
+/**
+	This class represents a set of modification applied to a title block
+	template cell.
+*/
+class ModifyTitleBlockCellCommand : public QUndoCommand {
+	// constructor, destructor
+	public:
+	ModifyTitleBlockCellCommand(TitleBlockCell *, QUndoCommand * = 0);
+	virtual ~ModifyTitleBlockCellCommand();
+	private:
+	ModifyTitleBlockCellCommand(const ModifyTitleBlockCellCommand &);
+	
+	// methods
+	public:
+	virtual int id() const;
+	virtual bool mergeWith(const QUndoCommand *);
+	virtual void undo();
+	virtual void redo();
+	TitleBlockCell *cell() const;
+	void setCell(TitleBlockCell *);
+	TitleBlockTemplateView *view() const;
+	void setView(TitleBlockTemplateView *);
+	void clear();
+	void addModification(const QString &, const QVariant &, bool = false);
+	
+	// attributes
+	private:
+	TitleBlockTemplateView *view_;        ///< This class may trigger a view update
+	TitleBlockCell *modified_cell_;       ///< modified cell
+	QHash<QString, QVariant> old_values_; ///< values before the cell is modified
+	QHash<QString, QVariant> new_values_; ///< values after the cell has been modified
+};
+
+/**
+	This class is a base class for any UndoCommand that needs to work with a
+	title block template.
+*/
+class TitleBlockTemplateCommand : public QUndoCommand {
+	// Constructors, destructor
+	public:
+	TitleBlockTemplateCommand(TitleBlockTemplate * = 0, QUndoCommand * = 0);
+	virtual ~TitleBlockTemplateCommand();
+	private:
+	TitleBlockTemplateCommand(const TitleBlockTemplateCommand &);
+	
+	// methods
+	public:
+	TitleBlockTemplate *titleBlockTemplate() const;
+	void setTitleBlockTemplate(TitleBlockTemplate *);
+	TitleBlockTemplateView *view() const;
+	void setView(TitleBlockTemplateView *);
+	void refreshView();
+	void refreshLayout();
+	
+	// attributes
+	protected:
+	TitleBlockTemplate *tbtemplate_; ///< Modified TitleBlock Template
+	TitleBlockTemplateView *view_;   ///< This class may trigger a view update
+};
+
+/**
+	This class represents the action of adding or deleting a row or column
+	within a title block template.
+*/
+class ModifyTemplateGridCommand : public TitleBlockTemplateCommand {
+	// static factory methods
+	public:
+	static ModifyTemplateGridCommand *addRow(TitleBlockTemplate *, int = -1);
+	static ModifyTemplateGridCommand *addColumn(TitleBlockTemplate *, int = -1);
+	static ModifyTemplateGridCommand *deleteRow(TitleBlockTemplate *, int = -1);
+	static ModifyTemplateGridCommand *deleteColumn(TitleBlockTemplate *, int = -1);
+	
+	// Constructors, destructor
+	public:
+	ModifyTemplateGridCommand(TitleBlockTemplate * = 0, QUndoCommand * = 0);
+	virtual ~ModifyTemplateGridCommand();
+	private:
+	ModifyTemplateGridCommand(const ModifyTemplateGridCommand &);
+	
+	// methods
+	public:
+	int index() const;
+	void setIndex(int);
+	QList<TitleBlockCell *> cells() const;
+	void setCells(const QList<TitleBlockCell *> &);
+	TitleBlockDimension dimension() const;
+	void setDimension(const TitleBlockDimension &);
+	int type() const;
+	void setType(bool);
+	bool isInsertion() const;
+	void setInsertion(bool);
+	virtual void undo();
+	virtual void redo();
+	
+	private:
+	void updateText();
+	void apply(bool = false);
+	
+	// attributes
+	private:
+	int index_;                     ///< Index of the inserted/deleted row/column
+	QList<TitleBlockCell *> cells_; ///< Cells composing the inserted/deleted row/column
+	bool type_;                     ///< true for a row, false for a column
+	TitleBlockDimension dimension_; ///< width/height of the column/row, which interpretation depends on type_
+	bool insertion_;                /// true if the row/column is inserted, false if it is deleted
+};
+
+/**
+	This class represents the action of changing the width/height of a
+	specific row/column within a title block template.
+*/
+class ModifyTemplateDimension : public TitleBlockTemplateCommand {
+	// Constructor, destructor
+	public:
+	ModifyTemplateDimension(TitleBlockTemplate * = 0, QUndoCommand * = 0);
+	virtual ~ModifyTemplateDimension();
+	private:
+	ModifyTemplateDimension(const ModifyTemplateDimension &);
+	
+	// methods
+	public:
+	int index() const;
+	void setIndex(int);
+	int type() const;
+	void setType(bool);
+	TitleBlockDimension dimensionBefore() const;
+	void setDimensionBefore(const TitleBlockDimension &);
+	TitleBlockDimension dimensionAfter() const;
+	void setDimensionAfter(const TitleBlockDimension &);
+	virtual void undo();
+	virtual void redo();
+	
+	private:
+	void updateText();
+	void apply(const TitleBlockDimension &);
+	
+	// attributes
+	private:
+	int index_;                   ///< Index of the resized row/column
+	bool type_;                   ///< true for a row, false for a column
+	TitleBlockDimension before_;  ///< Size of the row/column before it is changed
+	TitleBlockDimension after_;   ///< Size of the row/column after it is changed
+};
+
+
+/**
+	This class represents the action of merging 2 to n cells into a single one.
+*/
+class MergeCellsCommand : public TitleBlockTemplateCommand {
+	// Constructor, destructor
+	public:
+	MergeCellsCommand(const TitleBlockTemplateCellsSet &, TitleBlockTemplate * = 0, QUndoCommand * = 0);
+	virtual ~MergeCellsCommand();
+	
+	// methods
+	public:
+	static bool canMerge(const TitleBlockTemplateCellsSet &, TitleBlockTemplate *);
+	bool isValid() const;
+	virtual void undo();
+	virtual void redo();
+	private:
+	static TitleBlockCell *getBottomRightCell(const TitleBlockTemplateCellsSet &);
+	
+	// attributes
+	private:
+	/// the cell spanning over the other ones
+	TitleBlockCell *spanning_cell_;
+	/// hash associating spanned cells with their spanner_cell attribute
+	/// before the merge operation
+	QHash<TitleBlockCell *, TitleBlockCell *> spanner_cells_before_merge_;
+	int row_span_before_; ///< the row_span attribute of the spanning cell before the merge
+	int col_span_before_; ///< the col_span attribute of the spanning cell before the merge
+	int applied_row_span_before_; ///< the applied_row_span attribute of the spanning cell before the merge
+	int applied_col_span_before_; ///< the applied_col_span attribute of the spanning cell before the merge
+	int span_state_before_; ///< the span_state attribute of the spanning cell before the merge
+	int row_span_after_;  ///< the row_span attribute of the spanning cell after the merge
+	int col_span_after_;  ///< the col_span attribute of the spanning cell after the merge
+};
+
+/**
+	This class represents the action of splitting a visual cell into at least two logical cells
+*/
+class SplitCellsCommand : public TitleBlockTemplateCommand {
+	// Constructor, destructor
+	public:
+	SplitCellsCommand(const TitleBlockTemplateCellsSet &, TitleBlockTemplate * = 0, QUndoCommand * = 0);
+	virtual ~SplitCellsCommand();
+	
+	// methods
+	public:
+	static bool canSplit(const TitleBlockTemplateCellsSet &splitted_cells, TitleBlockTemplate *tbtemplate);
+	bool isValid() const;
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	TitleBlockCell *spanning_cell_;        ///< the cell spanning over the other ones
+	QSet<TitleBlockCell *> spanned_cells_; ///< the spanned cells
+	int row_span_before_;                  ///< the row_span attribute of the spanning cell before splitting
+	int col_span_before_;                  ///< the col_span attribute of the spanning cell before splitting
+	int applied_row_span_before_;          ///< the applied_row_span attribute of the spanning cell before splitting
+	int applied_col_span_before_;          ///< the applied_col_span attribute of the spanning cell before splitting
+	int span_state_before_;                ///< the span_state attribute of the spanning cell before splitting
+};
+
+/**
+	This class represents the action of changing extra information of a title
+	block template.
+*/
+class ChangeTemplateInformationsCommand : public QUndoCommand {
+	// constructors, destructor
+	public:
+	ChangeTemplateInformationsCommand(TitleBlockTemplate *, const QString &, const QString &, QUndoCommand * = 0);
+	virtual ~ChangeTemplateInformationsCommand();
+	private:
+	ChangeTemplateInformationsCommand(const ChangeTemplateInformationsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	
+	// attributes
+	private:
+	/// Changed title block template
+	TitleBlockTemplate *tbtemplate_;
+	/// Informations before they are modified
+	QString old_information_;
+	/// Informations after they were modified
+	QString new_information_;
+};
+
+/**
+	This class represents the action of cutting a cells set.
+*/
+class CutTemplateCellsCommand : public TitleBlockTemplateCommand {
+	// constructors, destructor
+	public:
+	CutTemplateCellsCommand(TitleBlockTemplate *, QUndoCommand * = 0);
+	virtual ~CutTemplateCellsCommand();
+	private:
+	CutTemplateCellsCommand(const CutTemplateCellsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void setCutCells(const QList<TitleBlockCell *> &);
+	protected:
+	virtual void updateText();
+	
+	// attributes
+	public:
+	/// Cut cells
+	QHash<TitleBlockCell *, TitleBlockCell::TemplateCellType> cut_cells_;
+};
+
+/**
+	This class represents the action of pasting a cells set.
+*/
+class PasteTemplateCellsCommand : public TitleBlockTemplateCommand {
+	// constructors, destructor
+	public:
+	PasteTemplateCellsCommand(TitleBlockTemplate *, QUndoCommand * = 0);
+	virtual ~PasteTemplateCellsCommand();
+	private:
+	PasteTemplateCellsCommand(const PasteTemplateCellsCommand &);
+	
+	// methods
+	public:
+	virtual void undo();
+	virtual void redo();
+	virtual void addPastedCell(TitleBlockCell *, const TitleBlockCell &);
+	virtual void addErasedCell(TitleBlockCell *, const TitleBlockCell &);
+	virtual void addCell(TitleBlockCell *, const TitleBlockCell &, const TitleBlockCell &);
+	protected:
+	virtual void updateText();
+	
+	// attributes
+	public:
+	/// Spans before operation
+	QHash<TitleBlockCell *, QPair<int, int> > spans_before_;
+	/// Pasted cells
+	QHash<TitleBlockCell *, TitleBlockCell> pasted_cells_;
+	/// Existing cells impacted by the paste operation
+	QHash<TitleBlockCell *, TitleBlockCell> erased_cells_;
+};
+#endif

Added: trunk/sources/titleblock/templatedeleter.cpp
===================================================================
--- trunk/sources/titleblock/templatedeleter.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatedeleter.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,72 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatedeleter.h"
+#include "qetmessagebox.h"
+#include "qetproject.h"
+/**
+	Constructor
+	@param tbt_location Location of the title block template to be deleted
+	@param parent Parent QWidget
+*/
+TitleBlockTemplateDeleter::TitleBlockTemplateDeleter(const TitleBlockTemplateLocation &tbt_location, QWidget *parent) :
+	QWidget(parent),
+	template_location_(tbt_location)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateDeleter::~TitleBlockTemplateDeleter() {
+}
+
+/**
+	Delete the title block template: check the provided location matches an
+	existing template, interactively require confirmationfrom the user before
+	actually proceeding to the deletion.
+	@return true if the deletion succeeded, false otherwise.
+*/
+bool TitleBlockTemplateDeleter::exec() {
+	if (!template_location_.isValid()) return(false);
+	
+	QString name = template_location_.name();
+	TitleBlockTemplatesCollection *collection = template_location_.parentCollection();
+	if (!collection) return(false);
+	
+	if (!collection -> templates().contains(name)) {
+		return(false);
+	}
+	
+	// require confirmation from the user
+	QMessageBox::StandardButton answer = QET::MessageBox::question(
+		this,
+		tr("Supprimer le mod\350le de cartouche ?", "message box title"),
+		QString(
+			tr(
+				"\312tes-vous s\373r de vouloir supprimer ce mod\350le de cartouche (%1) ?\n",
+				"message box content"
+			)
+		).arg(name),
+		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
+	);
+	if (answer != QMessageBox::Yes) return(false);
+	
+	// delete the title block template
+	collection -> removeTemplate(name);
+	return(true);
+}

Added: trunk/sources/titleblock/templatedeleter.h
===================================================================
--- trunk/sources/titleblock/templatedeleter.h	                        (rev 0)
+++ trunk/sources/titleblock/templatedeleter.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,43 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_DELETER_H
+#define TITLEBLOCK_SLASH_TEMPLATE_DELETER_H
+#include "templatelocation.h"
+#include <QtGui>
+/**
+	This class is an abstraction layer to delete an existing title block
+	template.
+*/
+class TitleBlockTemplateDeleter : public QWidget {
+	Q_OBJECT
+	// Constructors, destructor
+	public:
+	TitleBlockTemplateDeleter(const TitleBlockTemplateLocation &, QWidget * = 0);
+	virtual ~TitleBlockTemplateDeleter();
+	private:
+	TitleBlockTemplateDeleter(const TitleBlockTemplateDeleter &);
+	
+	// methods
+	public slots:
+	bool exec();
+	
+	// attributes
+	private:
+	TitleBlockTemplateLocation template_location_;
+};
+#endif

Added: trunk/sources/titleblock/templatelocation.cpp
===================================================================
--- trunk/sources/titleblock/templatelocation.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatelocation.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,171 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatelocation.h"
+#include "templatescollection.h"
+#include "qetapp.h"
+
+// make this class usable with QVariant
+int TitleBlockTemplateLocation::MetaTypeId = qRegisterMetaType<TitleBlockTemplateLocation>("TitleBlockTemplateLocation");
+
+/**
+	Constructor
+	@param collection Parent collection of the title block template
+	@param name Name of the title block template within its parent project or collection
+*/
+TitleBlockTemplateLocation::TitleBlockTemplateLocation(const QString &name, TitleBlockTemplatesCollection *collection) :
+	collection_(collection),
+	name_(name)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateLocation::~TitleBlockTemplateLocation() {
+}
+
+/**
+	@param loc_str String describing the location of a title block template.
+*/
+TitleBlockTemplateLocation TitleBlockTemplateLocation::locationFromString(const QString &loc_str) {
+	TitleBlockTemplateLocation loc;
+	loc.fromString(loc_str);
+	return(loc);
+}
+
+/**
+	@return the parent collection of the template, or 0 if none was defined
+*/
+TitleBlockTemplatesCollection *TitleBlockTemplateLocation::parentCollection() const {
+	return(collection_);
+}
+
+/**
+	@param project The new parent collection of the template, or 0 if none
+	applies.
+*/
+void TitleBlockTemplateLocation::setParentCollection(TitleBlockTemplatesCollection *collection) {
+	collection_ = collection;
+}
+
+/**
+	@return the name of this template within its parent project or collection.
+*/
+QString TitleBlockTemplateLocation::name() const {
+	return(name_);
+}
+
+/**
+	@param name The new name of this template.
+*/
+void TitleBlockTemplateLocation::setName(const QString &name) {
+	name_ = name;
+}
+
+/**
+	@return true if this location is null, false otherwise
+*/
+bool TitleBlockTemplateLocation::isValid() const {
+	return(!name_.isEmpty());
+}
+
+/**
+	@param loc_str String describing the location of a title block template.
+*/
+void TitleBlockTemplateLocation::fromString(const QString &loc_str) {
+	collection_ = QETApp::titleBlockTemplatesCollection(QUrl(loc_str).scheme());
+	
+	QRegExp name_from_url("^[^:]*:\\/\\/(.*)$");
+	if (name_from_url.exactMatch(loc_str)) {
+		name_ = name_from_url.capturedTexts().at(1);
+	} else {
+		name_ = QString();
+	}
+}
+
+/**
+	@return A string representation of the location
+*/
+QString TitleBlockTemplateLocation::toString() const {
+	return(protocol() + QString("://") + name_);
+}
+
+/**
+	This is a convenience method equivalent to
+	parentCollection() -> parentProject().
+*/
+QETProject *TitleBlockTemplateLocation::parentProject() const {
+	if (collection_) {
+		return(collection_ -> parentProject());
+	}
+	return(0);
+}
+
+/**
+	This is a convenience method equivalent to
+	parentCollection() -> protocol().
+*/
+QString TitleBlockTemplateLocation::protocol() const {
+	if (collection_) {
+		return(collection_ -> protocol());
+	}
+	return("unknown");
+}
+
+/**
+	This is a convenience method equivalent to
+	parentCollection() -> getTemplateXmlDescription
+*/
+QDomElement TitleBlockTemplateLocation::getTemplateXmlDescription() const {
+	if (!collection_ || name_.isEmpty()) return(QDomElement());
+	return(collection_ -> getTemplateXmlDescription(name_));
+}
+
+/**
+	This is a convenience method equivalent to
+	parentCollection() -> getTemplate(...).
+*/
+TitleBlockTemplate *TitleBlockTemplateLocation::getTemplate() const {
+	if (!collection_ || name_.isEmpty()) return(0);
+	return(collection_ -> getTemplate(name_));
+}
+
+/**
+	This is a convenience method equivalent to
+	parentCollection() -> isReadOnly(name())
+*/
+bool TitleBlockTemplateLocation::isReadOnly() const {
+	if (!collection_) return(false);
+	return(collection_ -> isReadOnly(name_));
+}
+
+/**
+	@param location other location that should be compared to this one
+	@return true if locations are equal, false otherwise
+*/
+bool TitleBlockTemplateLocation::operator==(const TitleBlockTemplateLocation &location) const {
+	return(location.collection_ == collection_ && location.name_ == name_);
+}
+
+/**
+	@param location A standard title block template location
+	@return a hash identifying this location
+*/
+uint qHash(const TitleBlockTemplateLocation &location) {
+	return(qHash(location.toString()));
+}

Added: trunk/sources/titleblock/templatelocation.h
===================================================================
--- trunk/sources/titleblock/templatelocation.h	                        (rev 0)
+++ trunk/sources/titleblock/templatelocation.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,65 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_LOCATION_H
+#define TITLEBLOCK_SLASH_TEMPLATE_LOCATION_H
+#include <QtCore>
+#include <QDomElement>
+class QETProject;
+class TitleBlockTemplate;
+class TitleBlockTemplatesCollection;
+
+/**
+	This class represents the location of a title block template.
+*/
+class TitleBlockTemplateLocation {
+	// constructor, destructor
+	public:
+	TitleBlockTemplateLocation(const QString & = QString(), TitleBlockTemplatesCollection * = 0);
+	virtual ~TitleBlockTemplateLocation();
+	
+	// static methods
+	public:
+	TitleBlockTemplateLocation locationFromString(const QString &);
+	
+	// methods
+	public:
+	TitleBlockTemplatesCollection *parentCollection() const;
+	void setParentCollection(TitleBlockTemplatesCollection *);
+	QString name() const;
+	void setName(const QString &);
+	bool isValid() const;
+	void fromString(const QString &);
+	QString toString() const;
+	QETProject *parentProject() const;
+	QString protocol() const;
+	QDomElement getTemplateXmlDescription() const;
+	TitleBlockTemplate *getTemplate() const;
+	bool isReadOnly() const;
+	bool operator==(const TitleBlockTemplateLocation &) const;
+	
+	// attributes
+	private:
+	TitleBlockTemplatesCollection *collection_; ///< Collection the template belongs to
+	QString name_;                              ///< Name of the template
+	
+	public:
+	static int MetaTypeId; ///< Id of the corresponding Qt meta type
+};
+Q_DECLARE_METATYPE(TitleBlockTemplateLocation)
+uint qHash(const TitleBlockTemplateLocation &);
+#endif

Added: trunk/sources/titleblock/templatelocationchooser.cpp
===================================================================
--- trunk/sources/titleblock/templatelocationchooser.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatelocationchooser.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,126 @@
+#include "templatelocationchooser.h"
+#include "qetapp.h"
+#include "qetproject.h"
+#include "templatescollection.h"
+
+/**
+	Constructor
+	@param location Initial location displayed by the widget
+	@param widget Parent QWidget
+*/
+TitleBlockTemplateLocationChooser::TitleBlockTemplateLocationChooser(
+	const TitleBlockTemplateLocation &location,
+	QWidget *parent
+) :
+	QWidget(parent)
+{
+	init();
+	setLocation(location);
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateLocationChooser::~TitleBlockTemplateLocationChooser() {
+}
+
+/**
+	@return the current location
+*/
+TitleBlockTemplateLocation TitleBlockTemplateLocationChooser::location() const {
+	return(TitleBlockTemplateLocation(name(), collection()));
+}
+
+/**
+	@return the currently selected collection
+*/
+TitleBlockTemplatesCollection *TitleBlockTemplateLocationChooser::collection() const {
+	return(collections_index_[collections_ -> currentIndex()]);
+}
+
+/**
+	@return the currently selected/entered name
+*/
+QString TitleBlockTemplateLocationChooser::name() const {
+	int template_index = templates_ -> currentIndex();
+	return(template_index != -1 ? templates_ -> currentText() : QString());
+}
+
+/**
+	Set the location displayed by this widget
+	@param location to be displayed by this widget
+*/
+void TitleBlockTemplateLocationChooser::setLocation(const TitleBlockTemplateLocation &location) {
+	int index = indexForCollection(location.parentCollection());
+	collections_ -> setCurrentIndex(index);
+	
+	if (!location.name().isEmpty()) {
+		int template_index = templates_ -> findText(location.name());
+		if (template_index != -1) {
+			templates_ -> setCurrentIndex(template_index);
+		} else {
+			templates_ -> setCurrentIndex(0);
+		}
+	}
+}
+
+/**
+	Initialize this widget.
+	@param location Initial location displayed by the widget
+*/
+void TitleBlockTemplateLocationChooser::init() {
+	collections_ = new QComboBox();
+	templates_ = new QComboBox();
+	
+	updateCollections();
+	connect(collections_, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTemplates()));
+	
+	form_layout_ = new QFormLayout();
+	form_layout_ -> addRow(tr("Collection parente",   "used in save as form"), collections_);
+	form_layout_ -> addRow(tr("Mod\350le existant",   "used in save as form"), templates_);
+	setLayout(form_layout_);
+}
+
+/**
+	@param coll A Title block templates collection which we want to know the index within the combo box of this dialog.
+	@return -1 if the collection is unknown to this dialog, or the index of \a coll
+*/
+int TitleBlockTemplateLocationChooser::indexForCollection(TitleBlockTemplatesCollection *coll) const {
+	QList<int> indexes = collections_index_.keys(coll);
+	if (indexes.count()) return(indexes.first());
+	return(-1);
+}
+
+/**
+	Update the collections list
+*/
+void TitleBlockTemplateLocationChooser::updateCollections() {
+	collections_ -> clear();
+	collections_index_.clear();
+	
+	int index = 0;
+	foreach(TitleBlockTemplatesCollection *collection, QETApp::availableTitleBlockTemplatesCollections()) {
+		collections_ -> addItem(collection -> title());
+		collections_index_.insert(index, collection);
+		++ index;
+	}
+	
+	updateTemplates();
+}
+
+/**
+	Update the templates list according to the selected collection.
+*/
+void TitleBlockTemplateLocationChooser::updateTemplates() {
+	TitleBlockTemplatesCollection *current_collection = collection();
+	if (!current_collection) return;
+	
+	templates_ -> clear();
+	
+	QStringList available_templates = current_collection -> templates();
+	if (available_templates.count()) {
+		foreach (QString template_name, available_templates) {
+			templates_ -> addItem(template_name, QVariant(true));
+		}
+	}
+}

Added: trunk/sources/titleblock/templatelocationchooser.h
===================================================================
--- trunk/sources/titleblock/templatelocationchooser.h	                        (rev 0)
+++ trunk/sources/titleblock/templatelocationchooser.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,61 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_LOCATION_CHOOSER_H
+#define TITLEBLOCK_SLASH_LOCATION_CHOOSER_H
+#include <QtGui>
+#include "templatelocation.h"
+class TitleBlockTemplateCollection;
+
+/**
+	This class is a widget that allows the user to choose a target title block
+	template.
+*/
+class TitleBlockTemplateLocationChooser : public QWidget {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	TitleBlockTemplateLocationChooser(const TitleBlockTemplateLocation &, QWidget * = 0);
+	~TitleBlockTemplateLocationChooser();
+	private:
+	TitleBlockTemplateLocationChooser(const TitleBlockTemplateLocationChooser &);
+	
+	// methods
+	public:
+	virtual TitleBlockTemplateLocation location() const;
+	virtual TitleBlockTemplatesCollection *collection() const;
+	virtual QString name() const;
+	virtual void setLocation(const TitleBlockTemplateLocation &);
+	
+	protected:
+	void init();
+	virtual int indexForCollection(TitleBlockTemplatesCollection *) const;
+	
+	// slots
+	protected slots:
+	virtual void updateCollections();
+	virtual void updateTemplates();
+	
+	// attributes
+	protected:
+	QFormLayout *form_layout_;
+	QComboBox *collections_;  ///< Collections combo box
+	/// Collections index within the combo box
+	QHash<int, TitleBlockTemplatesCollection *> collections_index_;
+	QComboBox *templates_;    ///< Existing templates combo box
+};
+#endif

Added: trunk/sources/titleblock/templatelocationsaver.cpp
===================================================================
--- trunk/sources/titleblock/templatelocationsaver.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatelocationsaver.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,87 @@
+#include "templatelocationsaver.h"
+#include "qetapp.h"
+#include "qetproject.h"
+#include "templatescollection.h"
+
+/**
+	Constructor
+	@param location Initial location displayed by the widget
+	@param widget Parent QWidget
+*/
+TitleBlockTemplateLocationSaver::TitleBlockTemplateLocationSaver(
+	const TitleBlockTemplateLocation &location,
+	QWidget *parent
+) :
+	TitleBlockTemplateLocationChooser(location, parent)
+{
+	init();
+	setLocation(location);
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateLocationSaver::~TitleBlockTemplateLocationSaver() {
+}
+
+/**
+	@return the currently selected/entered name
+*/
+QString TitleBlockTemplateLocationSaver::name() const {
+	int template_index = templates_ -> currentIndex();
+	return(template_index ? templates_ -> currentText() : new_name_ -> text());
+}
+
+/**
+	Set the location displayed by this widget
+	@param location to be displayed by this widget
+*/
+void TitleBlockTemplateLocationSaver::setLocation(const TitleBlockTemplateLocation &location) {
+	// hack: if no suitable index was found, set it to 1, which is supposed to be the user collection
+	int index = indexForCollection(location.parentCollection());
+	if (index == -1 && collections_ -> count() > 1) index = 1;
+	collections_ -> setCurrentIndex(index);
+	
+	if (!location.name().isEmpty()) {
+		int template_index = templates_ -> findText(location.name());
+		if (template_index != -1) {
+			templates_ -> setCurrentIndex(template_index);
+			return;
+		}
+	}
+	templates_ -> setCurrentIndex(0);
+}
+
+/**
+	Initialize this widget.
+	@param location Initial location displayed by the widget
+*/
+void TitleBlockTemplateLocationSaver::init() {
+	new_name_ = new QLineEdit();
+	connect(templates_, SIGNAL(currentIndexChanged(int)), this, SLOT(updateNewName()));
+	form_layout_ -> addRow(tr("ou nouveau nom",       "used in save as form"), new_name_);
+	updateTemplates();
+}
+
+/**
+	Update the templates list according to the selected collection.
+*/
+void TitleBlockTemplateLocationSaver::updateTemplates() {
+	TitleBlockTemplatesCollection *current_collection = collection();
+	if (!current_collection) return;
+	
+	TitleBlockTemplateLocationChooser::updateTemplates();
+	templates_ -> insertItem(0, tr("Nouveau mod\350le (entrez son nom)", "combox box entry"), QVariant(false));
+	templates_ -> insertSeparator(1);
+	
+	updateNewName();
+}
+
+/**
+	Enable or diable the "new name" text field depending of the selected
+	template.
+*/
+void TitleBlockTemplateLocationSaver::updateNewName() {
+	int template_index = templates_ -> currentIndex();
+	new_name_ -> setEnabled(!template_index);
+}

Added: trunk/sources/titleblock/templatelocationsaver.h
===================================================================
--- trunk/sources/titleblock/templatelocationsaver.h	                        (rev 0)
+++ trunk/sources/titleblock/templatelocationsaver.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,55 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_LOCATION_SAVER_H
+#define TITLEBLOCK_SLASH_LOCATION_SAVER_H
+#include <QtGui>
+#include "templatelocationchooser.h"
+class TitleBlockTemplateCollection;
+
+/**
+	This class is a widget that allows the user to choose a target title block
+	template, with the intention to save it. Therefore, compared to a
+	TitleBlockTemplateLocationChooser, it includes an extra field for the user to
+	set the name of the new template if needed.
+*/
+class TitleBlockTemplateLocationSaver : public TitleBlockTemplateLocationChooser {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	TitleBlockTemplateLocationSaver(const TitleBlockTemplateLocation &, QWidget * = 0);
+	~TitleBlockTemplateLocationSaver();
+	private:
+	TitleBlockTemplateLocationSaver(const TitleBlockTemplateLocationSaver &);
+	
+	// methods
+	virtual QString name() const;
+	virtual void setLocation(const TitleBlockTemplateLocation &);
+	
+	private:
+	void init();
+	
+	// slots
+	protected slots:
+	virtual void updateTemplates();
+	virtual void updateNewName();
+	
+	// attributes
+	protected:
+	QLineEdit *new_name_;     ///< New template name textfield
+};
+#endif

Added: trunk/sources/titleblock/templatelogomanager.cpp
===================================================================
--- trunk/sources/titleblock/templatelogomanager.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatelogomanager.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,381 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatelogomanager.h"
+#include "titleblocktemplate.h"
+#include "qeticons.h"
+
+/**
+	Constructor
+	@param managed_template Title block template this widget manages logos for.
+	@param parent Parent QWidget.
+*/
+TitleBlockTemplateLogoManager::TitleBlockTemplateLogoManager(TitleBlockTemplate *managed_template, QWidget *parent) :
+	QWidget(parent),
+	managed_template_(managed_template)
+{
+	initWidgets();
+	fillView();
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateLogoManager::~TitleBlockTemplateLogoManager() {
+}
+
+/**
+	@return the name of the currently selected logo, or a null QString if none
+	is selected.
+*/
+QString TitleBlockTemplateLogoManager::currentLogo() const {
+	if (!managed_template_) return QString();
+	
+	QListWidgetItem *current_item = logos_view_ -> currentItem();
+	if (!current_item) return QString();
+	
+	return(current_item -> text());
+}
+
+/**
+	@return Whether this logo manager should allow logo edition
+	(renaming, addition, deletion).
+*/
+bool TitleBlockTemplateLogoManager::isReadOnly() const {
+	return(read_only_);
+}
+
+/**
+	Emit the logosChanged() signal.
+*/
+void TitleBlockTemplateLogoManager::emitLogosChangedSignal() {
+	emit(logosChanged(const_cast<const TitleBlockTemplate *>(managed_template_)));
+}
+
+/**
+	Initialize widgets composing the Logo manager
+*/
+void TitleBlockTemplateLogoManager::initWidgets() {
+	open_dialog_dir_ = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
+	
+	setWindowTitle(tr("Gestionnaire de logos"));
+	setWindowIcon(QET::Icons::InsertImage);
+	setWindowFlags(Qt::Dialog);
+	logos_label_ = new QLabel(tr("Logos embarqu\351s dans ce mod\350le :"));
+	logos_view_ = new QListWidget();
+	logos_view_ -> setViewMode(QListView::IconMode);
+	logos_view_ -> setGridSize(iconsize() * 1.4);
+	logos_view_ -> setMinimumSize(iconsize() * 2.9);
+	logos_view_ -> setIconSize(iconsize());
+	logos_view_ -> setWrapping(true);
+	logos_view_ -> setMovement(QListView::Static);
+	logos_view_ -> setResizeMode(QListView::Adjust);
+	add_button_ = new QPushButton(QET::Icons::Add, tr("Ajouter un logo"));
+	export_button_ = new QPushButton(QET::Icons::DocumentExport, tr("Exporter ce logo"));
+	delete_button_ = new QPushButton(QET::Icons::Remove, tr("Supprimer ce logo"));
+	logo_box_ = new QGroupBox(tr("Propri\351t\351s"));
+	logo_name_label_ = new QLabel(tr("Nom :"));
+	logo_name_ = new QLineEdit();
+	rename_button_ = new QPushButton(QET::Icons::EditRename, tr("Renommer"));
+	logo_type_ = new QLabel(tr("Type :"));
+	buttons_ = new QDialogButtonBox(QDialogButtonBox::Ok);
+	
+	hlayout1_ = new QHBoxLayout();
+	hlayout1_ -> addWidget(logo_name_label_);
+	hlayout1_ -> addWidget(logo_name_);
+	hlayout1_ -> addWidget(rename_button_);
+	
+	hlayout0_ = new QHBoxLayout();
+	hlayout0_ -> addWidget(export_button_);
+	hlayout0_ -> addWidget(delete_button_);
+	
+	vlayout1_ = new QVBoxLayout();
+	vlayout1_ -> addLayout(hlayout1_);
+	vlayout1_ -> addWidget(logo_type_);
+	logo_box_ -> setLayout(vlayout1_);
+	
+	vlayout0_ = new QVBoxLayout();
+	vlayout0_ -> addWidget(logos_label_);
+	vlayout0_ -> addWidget(logos_view_);
+	vlayout0_ -> addWidget(add_button_);
+	vlayout0_ -> addLayout(hlayout0_);
+	vlayout0_ -> addWidget(logo_box_);
+	setLayout(vlayout0_);
+	
+	connect(
+		logos_view_,
+		SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
+		this,
+		SLOT(updateLogoInformations(QListWidgetItem *, QListWidgetItem *))
+	);
+	connect(add_button_, SIGNAL(released()), this, SLOT(addLogo()));
+	connect(export_button_, SIGNAL(released()), this, SLOT(exportLogo()));
+	connect(delete_button_, SIGNAL(released()), this, SLOT(removeLogo()));
+	connect(rename_button_, SIGNAL(released()), this, SLOT(renameLogo()));
+}
+
+/**
+	Update the logos display.
+*/
+void TitleBlockTemplateLogoManager::fillView() {
+	if (!managed_template_) return;
+	logos_view_ -> clear();
+	
+	foreach (QString logo_name, managed_template_ -> logos()) {
+		QIcon current_icon;
+		QPixmap current_logo = managed_template_ -> bitmapLogo(logo_name);
+		if (!current_logo.isNull()) {
+			current_icon = QIcon(current_logo);
+		} else {
+			QSvgRenderer *svg_logo = managed_template_ -> vectorLogo(logo_name);
+			if (svg_logo) {
+				QPixmap *svg_pixmap = new QPixmap(iconsize());
+				svg_pixmap -> fill();
+				QPainter p;
+				p.begin(svg_pixmap);
+				svg_logo -> render(&p);
+				p.end();
+				current_icon = QIcon(*svg_pixmap);
+			}
+		}
+		QListWidgetItem *qlwi = new QListWidgetItem(current_icon, logo_name);
+		qlwi -> setTextAlignment(Qt::AlignBottom | Qt::AlignHCenter);
+		logos_view_ -> insertItem(0, qlwi);
+	}
+	
+	QListWidgetItem *current_item = logos_view_ -> currentItem();
+	updateLogoInformations(current_item, 0);
+}
+
+/**
+	@return the icon size to display the logos embedded within the managed
+	template.
+*/
+QSize TitleBlockTemplateLogoManager::iconsize() const {
+	return(QSize(80, 80));
+}
+
+/**
+	When adding a logo, it may occur its name is already used by another
+	pre-existing logo. This method asks users whether they want to erase the
+	existing logo, change the initial name or simply cancel the operation.
+	@param initial_name Initial name of the logo to be added
+	@return Either a null QString if the user cancelled the operation, or the
+	name to be used when adding the logo.
+*/
+QString TitleBlockTemplateLogoManager::confirmLogoName(const QString &initial_name) {
+	QString name = initial_name;
+	QDialog *rename_dialog = 0;
+	QLabel *rd_label = 0;
+	QLineEdit *rd_input = 0;
+	while (managed_template_ -> logos().contains(name)) {
+		if (!rename_dialog) {
+			rename_dialog = new QDialog(this);
+			rename_dialog -> setWindowTitle(tr("Logo d\351j\340 existant"));
+			
+			rd_label = new QLabel();
+			rd_label -> setWordWrap(true);
+			rd_input = new QLineEdit();
+			QDialogButtonBox *rd_buttons = new QDialogButtonBox();
+			QPushButton *replace_button = rd_buttons -> addButton(tr("Remplacer"), QDialogButtonBox::YesRole);
+			QPushButton *rename_button  = rd_buttons -> addButton(tr("Renommer"),  QDialogButtonBox::NoRole);
+			QPushButton *cancel_button  = rd_buttons -> addButton(QDialogButtonBox::Cancel);
+			
+			QVBoxLayout *rd_vlayout0 = new QVBoxLayout();
+			rd_vlayout0 -> addWidget(rd_label);
+			rd_vlayout0 -> addWidget(rd_input);
+			rd_vlayout0 -> addWidget(rd_buttons);
+			rename_dialog -> setLayout(rd_vlayout0);
+			
+			QSignalMapper *signal_mapper = new QSignalMapper(rename_dialog);
+			signal_mapper -> setMapping(replace_button, QDialogButtonBox::YesRole);
+			signal_mapper -> setMapping(rename_button,  QDialogButtonBox::NoRole);
+			signal_mapper -> setMapping(cancel_button,  QDialogButtonBox::RejectRole);
+			connect(replace_button, SIGNAL(clicked()), signal_mapper, SLOT(map()));
+			connect(rename_button,  SIGNAL(clicked()), signal_mapper, SLOT(map()));
+			connect(cancel_button,  SIGNAL(clicked()), signal_mapper, SLOT(map()));
+			connect(signal_mapper, SIGNAL(mapped(int)), rename_dialog, SLOT(done(int)));
+		}
+		rd_label -> setText(
+			QString(tr(
+				"Il existe d\351j\340 un logo portant le nom \"%1\" au sein de "
+				"ce mod\350le de cartouche. Voulez-vous le remplacer ou "
+				"pr\351f\351rez-vous sp\351cifier un autre nom pour ce nouveau "
+				"logo ?"
+			)).arg(name)
+		);
+		rd_input -> setText(name);
+		int answer = rename_dialog -> exec();
+		if (answer == QDialogButtonBox::YesRole) {
+			// we can use the initial name
+			break;
+		} else if (answer == QDialogButtonBox::NoRole) {
+			// the user provided another name
+			name = rd_input -> text();
+			/// TODO prevent the user from entering an empty name
+		} else {
+			// the user cancelled the operation
+			return(QString());
+		}
+	};
+	return(name);
+}
+
+/**
+	Update the displayed informations relative to the currently selected logo.
+	@param current  Newly selected logo item
+	@param previous Previously selected logo item
+*/
+void TitleBlockTemplateLogoManager::updateLogoInformations(QListWidgetItem *current, QListWidgetItem *previous) {
+	Q_UNUSED(previous);
+	if (current) {
+		QString logo_name = current -> text();
+		logo_name_ -> setText(logo_name);
+		if (managed_template_) {
+			QString logo_type = managed_template_ -> logoType(logo_name);
+			logo_type_ -> setText(tr("Type : %1").arg(logo_type));
+		}
+	} else {
+		logo_name_ -> setText(QString());
+		logo_type_ -> setText(tr("Type :"));
+	}
+}
+
+/**
+	Ask the user for a filepath, and add it as a new logo in the managed
+	template.
+*/
+void TitleBlockTemplateLogoManager::addLogo() {
+	if (!managed_template_) return;
+	
+	QString filepath = QFileDialog::getOpenFileName(
+		this,
+		tr("Choisir une image / un logo"),
+		open_dialog_dir_.absolutePath(),
+		tr("Images vectorielles (*.svg);;Images bitmap (*.png *.jpg *.jpeg *.gif *.bmp *.xpm);;Tous les fichiers (*)")
+	);
+	if (filepath.isEmpty()) return;
+	
+	// that filepath needs to point to a valid, readable file
+	QFileInfo filepath_info(filepath);
+	if (!filepath_info.exists() || !filepath_info.isReadable()) {
+		QMessageBox::critical(this, tr("Erreur"), tr("Impossible d'ouvrir le fichier sp\351cifi\351"));
+		return;
+	}
+	
+	// ensure we can use the file name to add the logo
+	QString logo_name = confirmLogoName(filepath_info.fileName());
+	if (logo_name.isNull()) return;
+	
+	open_dialog_dir_ = QDir(filepath);
+	if (managed_template_ -> addLogoFromFile(filepath, logo_name)) {
+		fillView();
+		emitLogosChangedSignal();
+	}
+}
+
+/**
+	Export the currently selected logo
+*/
+void TitleBlockTemplateLogoManager::exportLogo() {
+	QString current_logo = currentLogo();
+	if (current_logo.isNull()) return;
+	
+	QString filepath = QFileDialog::getSaveFileName(
+		this,
+		tr("Choisir un fichier pour exporter ce logo"),
+		open_dialog_dir_.absolutePath() + "/" + current_logo,
+		tr("Tous les fichiers (*);;Images vectorielles (*.svg);;Images bitmap (*.png *.jpg *.jpeg *.gif *.bmp *.xpm)")
+	);
+	if (filepath.isEmpty()) return;
+	
+	bool save_logo = managed_template_ -> saveLogoToFile(current_logo, filepath);
+	if (!save_logo) {
+		QMessageBox::critical(this, tr("Erreur"), QString(tr("Impossible d'exporter vers le fichier sp\351cifi\351")));
+	} else {
+		open_dialog_dir_ = QDir(filepath);
+	}
+}
+
+/**
+	Delete the currently selected logo.
+*/
+void TitleBlockTemplateLogoManager::removeLogo() {
+	QString current_logo = currentLogo();
+	if (current_logo.isNull()) return;
+	
+	if (managed_template_ -> removeLogo(current_logo)) {
+		fillView();
+		emitLogosChangedSignal();
+	}
+}
+
+/**
+	Rename currently selected logo.
+*/
+void TitleBlockTemplateLogoManager::renameLogo() {
+	QString current_logo = currentLogo();
+	if (current_logo.isNull()) return;
+	
+	QString entered_name = logo_name_ -> text();
+	QString warning_title = tr("Renommer un logo");
+	if (entered_name == current_logo) {
+		QMessageBox::warning(
+			this,
+			warning_title,
+			tr("Vous devez saisir un nouveau nom.")
+		);
+		return;
+	}
+	
+	if (entered_name.trimmed().isEmpty()) {
+		QMessageBox::warning(
+			this,
+			warning_title,
+			tr("Le nouveau nom ne peut pas \352tre vide.")
+		);
+		return;
+	}
+	
+	if (managed_template_ -> logos().contains(entered_name)) {
+		QMessageBox::warning(
+			this,
+			warning_title,
+			tr("Le nom saisi est d\351j\340 utilis\351 par un autre logo.")
+		);
+		return;
+	}
+	
+	if (managed_template_ -> renameLogo(current_logo, entered_name)) {
+		fillView();
+		emitLogosChangedSignal();
+	}
+}
+
+/**
+	@param read_only Whether this logo manager should allow logo edition
+	(renaming, addition, deletion)
+*/
+void TitleBlockTemplateLogoManager::setReadOnly(bool read_only) {
+	if (read_only_ == read_only) return;
+	read_only_ = read_only;
+	
+	add_button_ -> setEnabled(!read_only_);
+	delete_button_ -> setEnabled(!read_only_);
+	rename_button_ -> setEnabled(!read_only_);
+	logo_name_ -> setReadOnly(read_only_);
+}

Added: trunk/sources/titleblock/templatelogomanager.h
===================================================================
--- trunk/sources/titleblock/templatelogomanager.h	                        (rev 0)
+++ trunk/sources/titleblock/templatelogomanager.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,78 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_LOGO_MANAGER
+#define TITLEBLOCK_SLASH_TEMPLATE_LOGO_MANAGER
+#include <QtGui>
+class TitleBlockTemplate;
+
+/**
+	This widget allows users to manage (list, add, edit, delete) logos embedded
+	within a title block template.
+*/
+class TitleBlockTemplateLogoManager : public QWidget {
+	Q_OBJECT
+	// Constructor, destructor
+	public:
+	TitleBlockTemplateLogoManager(TitleBlockTemplate *, QWidget * = 0);
+	virtual ~TitleBlockTemplateLogoManager();
+	
+	// methods
+	public:
+	QString currentLogo() const;
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	
+	signals:
+	void logosChanged(const TitleBlockTemplate *);
+	
+	protected:
+	void emitLogosChangedSignal();
+	
+	private:
+	void initWidgets();
+	void fillView();
+	QSize iconsize() const;
+	QString confirmLogoName(const QString &);
+	
+	private slots:
+	void updateLogoInformations(QListWidgetItem *, QListWidgetItem *);
+	void addLogo();
+	void exportLogo();
+	void removeLogo();
+	void renameLogo();
+	
+	// attributes
+	private:
+	TitleBlockTemplate *managed_template_; ///< title block template which this class manages logos
+	QVBoxLayout *vlayout0_, *vlayout1_;    ///< vertical layouts
+	QHBoxLayout *hlayout0_, *hlayout1_;    ///< horizontal layouts
+	QLabel *logos_label_;                  ///< simple displayed label
+	QListWidget *logos_view_;              ///< area showing the logos
+	QPushButton *add_button_;              ///< button to add a new logo
+	QPushButton *export_button_;           ///< button to export an embedded logo
+	QPushButton *delete_button_;           ///< button to delete an embedded logo
+	QGroupBox *logo_box_;                  ///< current logo properties box
+	QLabel *logo_name_label_;              ///< "name:" label
+	QLineEdit *logo_name_;                 ///< current logo name
+	QPushButton *rename_button_;           ///< button to rename the current logo
+	QLabel *logo_type_;                    ///< current logo type
+	QDialogButtonBox *buttons_;            ///< ok/cancel buttons
+	QDir open_dialog_dir_;                 ///< last opened directory
+	bool read_only_;                       ///< Whether this logo manager should allow logo edition (renaming, addition, deletion)
+};
+#endif

Added: trunk/sources/titleblock/templatescollection.cpp
===================================================================
--- trunk/sources/titleblock/templatescollection.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatescollection.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,528 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatescollection.h"
+#include "titleblocktemplate.h"
+#include "qetapp.h"
+#include "qetproject.h"
+
+/**
+	Constructor
+	@param parent Parent QObject
+*/
+TitleBlockTemplatesCollection::TitleBlockTemplatesCollection(QObject *parent) :
+	QObject(parent),
+	protocol_("unknown")
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplatesCollection::~TitleBlockTemplatesCollection() {
+}
+
+/**
+	@return the title of this collection
+*/
+QString TitleBlockTemplatesCollection::title() const {
+	return(title_);
+}
+
+/**
+	@oaram title New title for this collection
+*/
+void TitleBlockTemplatesCollection::setTitle(const QString &title) {
+	title_ = title;
+}
+
+/**
+	@return the protocol used by this collection ; examples: commontbt,
+	customtbt, embedtbt, ...
+*/
+QString TitleBlockTemplatesCollection::protocol() const {
+	return(protocol_);
+}
+
+/**
+	Define the protocol for this collection
+	@param protocol New protocol for this collection
+*/
+void TitleBlockTemplatesCollection::setProtocol(const QString &protocol) {
+	if (!protocol.isEmpty()) protocol_ = protocol;
+}
+
+/**
+	@return the project this collection is affiliated to, or 0 if this
+	collection is not related to any project.
+*/
+QETProject *TitleBlockTemplatesCollection::parentProject() {
+	return(0);
+}
+
+/**
+	@return the templates contained within this collection, as a list of location
+	objects.
+	@see templates()
+*/
+QList<TitleBlockTemplateLocation> TitleBlockTemplatesCollection::templatesLocations() {
+	QList<TitleBlockTemplateLocation> locations;
+	foreach (QString template_name, templates()) {
+		locations << location(template_name);
+	}
+	return(locations);
+}
+
+/**
+	Constructor
+	@param project Parent project
+	@param parent Parent QObject
+*/
+TitleBlockTemplatesProjectCollection::TitleBlockTemplatesProjectCollection(QETProject *project, QObject *parent) :
+	TitleBlockTemplatesCollection(parent),
+	project_(project)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplatesProjectCollection::~TitleBlockTemplatesProjectCollection() {
+}
+
+/**
+	@return a human readable title for this collection
+*/
+QString TitleBlockTemplatesProjectCollection::title() const {
+	if (!title_.isEmpty()) return(title_);
+	
+	// if the title attribute is empty, we generate a suitable one using the
+	// parent project
+	QString final_title;
+	if (project_) {
+		QString project_title = project_ -> title();
+		if (project_title.isEmpty()) {
+			final_title = QString(
+				tr(
+					"Cartouches du projet sans titre (id %1)",
+					"collection title when the parent project has an empty title -- %1 is the project internal id"
+				)
+			);
+			final_title = final_title.arg(QETApp::projectId(project_));
+		} else {
+			final_title = QString(
+				tr(
+					"Cartouches du projet \"%1\"",
+					"collection title when the project has a suitable title -- %1 is the project title"
+				)
+			);
+			final_title = final_title.arg(project_title);
+		}
+	}
+	return(final_title);
+}
+
+/**
+	@return the protocol used to mention this collection
+*/
+QString TitleBlockTemplatesProjectCollection::protocol() const {
+	if (project_) {
+		int project_id = QETApp::projectId(project_);
+		if (project_id != -1) {
+			return(QString("project%1+embedtbt").arg(project_id));
+		}
+	}
+	// fall back on the parent method
+	return(TitleBlockTemplatesCollection::protocol());
+}
+
+/**
+	@return the parent project of this project collection
+*/
+QETProject *TitleBlockTemplatesProjectCollection::parentProject() {
+	return(project_);
+}
+
+/**
+	@return the list of title block templates embedded within the project.
+*/
+QStringList TitleBlockTemplatesProjectCollection::templates() {
+	return(titleblock_templates_xml_.keys());
+}
+
+/**
+	@param template_name Name of the requested template
+	@return the requested template, or 0 if there is no valid template of this
+	name within the project
+*/
+TitleBlockTemplate *TitleBlockTemplatesProjectCollection::getTemplate(const QString &template_name){
+	// Do we have already loaded this template?
+	if (titleblock_templates_.contains(template_name)) {
+		return(titleblock_templates_[template_name]);
+	}
+	
+	// No? Do we even know of it?
+	if (!titleblock_templates_xml_.contains(template_name)) {
+		return(0);
+	}
+	
+	// Ok, we have its XML description, we have to generate a TitleBlockTemplate object
+	TitleBlockTemplate *titleblock_template = new TitleBlockTemplate(this);
+	if (titleblock_template -> loadFromXmlElement(titleblock_templates_xml_[template_name])) {
+		titleblock_templates_.insert(template_name, titleblock_template);
+		return(titleblock_template);
+	} else {
+		return(0);
+	}
+}
+
+/**
+	@param template_name Name of the requested template
+	@return the XML description of the requested template, or a null QDomElement
+	if the project does not have such an titleblock template
+*/
+QDomElement TitleBlockTemplatesProjectCollection::getTemplateXmlDescription(const QString &template_name)  {
+	if (titleblock_templates_xml_.contains(template_name)) {
+		return(titleblock_templates_xml_[template_name]);
+	}
+	return(QDomElement());
+}
+
+/**
+	This methods allows adding or modifying a template embedded within the
+	project. This method emits the signal changed() if
+	necessary.
+	@param template_name Name / Identifier of the template - will be used to
+	determine whether the given description will be added or will replace an
+	existing one.
+	@param xml_elmt An \<titleblocktemplate\> XML element describing the
+	template. Its "name" attribute must equal to template_name.
+	@return false if a problem occured, true otherwise
+*/
+bool TitleBlockTemplatesProjectCollection::setTemplateXmlDescription(const QString &template_name, const QDomElement &xml_elmt) {
+	// check basic stuff
+	if (xml_elmt.tagName() != "titleblocktemplate") {
+		return(false);
+	}
+	
+	// we *require* a project (at least for the moment...)
+	if (!project_) return(false);
+	
+	// we import the provided XML element in the project document
+	QDomElement import = xml_document_.importNode(xml_elmt, true).toElement();
+	
+	// ensure the name stored in the XML description remains consistent with the provided template name
+	import.setAttribute("name", template_name);
+	
+	// we either replace the previous description
+	if (titleblock_templates_xml_.contains(template_name)) {
+		QDomElement old_description = titleblock_templates_xml_[template_name];
+		if (!old_description.parentNode().isNull()) {
+			old_description.parentNode().replaceChild(import, old_description);
+		}
+	}
+	titleblock_templates_xml_.insert(template_name, import);
+	
+	if (titleblock_templates_.contains(template_name)) {
+		titleblock_templates_[template_name] -> loadFromXmlElement(titleblock_templates_xml_[template_name]);
+	}
+	emit(changed(this, template_name));
+	
+	return(true);
+}
+
+/**
+	This methods allows removing a template embedded within the project. This
+	method emits the signal changed() if necessary.
+	@param template_name Name of the template to be removed
+*/
+void TitleBlockTemplatesProjectCollection::removeTemplate(const QString &template_name) {
+	emit(aboutToRemove(this, template_name));
+	
+	// remove the template itself
+	titleblock_templates_xml_.remove(template_name);
+	titleblock_templates_.remove(template_name);
+	
+	// warn the rest of the world that the list of templates embedded within this project has changed
+	emit(changed(this, template_name));
+}
+
+/**
+	@param template_name Name of the requested template
+	@return the location object for the requested template
+*/
+TitleBlockTemplateLocation TitleBlockTemplatesProjectCollection::location(const QString &template_name) {
+	return(TitleBlockTemplateLocation(template_name, this));
+}
+
+/**
+	@return always false since a project collection is not stored on any
+	filesystem.
+*/
+bool TitleBlockTemplatesProjectCollection::hasFilePath() {
+	return(false);
+}
+
+/**
+	@return always an empty string since a project collection is not stored on
+	any filesystem.
+*/
+QString TitleBlockTemplatesProjectCollection::filePath() {
+	return(QString());
+}
+
+/**
+	@param template_name Either an empty QString to know whether the collection
+	itself is read only, or a specific template name.
+	@return true if the specified template is read only, false otherwise
+*/
+bool TitleBlockTemplatesProjectCollection::isReadOnly(const QString &template_name) const {
+	Q_UNUSED(template_name)
+	if (project_) {
+		return(project_ -> isReadOnly());
+	}
+	return(false);
+}
+
+/**
+	@param xml_element XML element to be parsed to load title block templates
+*/
+void TitleBlockTemplatesProjectCollection::fromXml(const QDomElement &xml_element) {
+	foreach (QDomElement e, QET::findInDomElement(xml_element, "titleblocktemplates", "titleblocktemplate")) {
+		// each titleblock template must have a name
+		if (!e.hasAttribute("name")) continue;
+		QString titleblock_template_name = e.attribute("name");
+		
+		// if several templates have the same name, we keep the first one encountered
+		if (titleblock_templates_xml_.contains(titleblock_template_name)) continue;
+		
+		// we simply store the XML element describing the titleblock template,
+		// without any further analysis for the moment
+		titleblock_templates_xml_.insert(titleblock_template_name, e);
+	}
+}
+
+/**
+	Delete all title block templates not used within the parent project
+*/
+void TitleBlockTemplatesProjectCollection::deleteUnusedTitleBlocKTemplates() {
+	if (!project_) return;
+	
+	foreach (QString template_name, templates()) {
+		if (!project_ -> usesTitleBlockTemplate(location(template_name))) {
+			removeTemplate(template_name);
+		}
+	}
+}
+
+/**
+	Constructor
+	@param path Path of the directory containing the collection
+	@param parent Parent QObject
+*/
+TitleBlockTemplatesFilesCollection::TitleBlockTemplatesFilesCollection(const QString &path, QObject *parent) :
+	TitleBlockTemplatesCollection(parent),
+	dir_(
+		path,
+		QString("*%1").arg(TITLEBLOCKS_FILE_EXTENSION),
+		QDir::SortFlags(QDir::Name | QDir::IgnoreCase),
+		QDir::Readable | QDir::Files
+	)
+{
+	if (dir_.exists()) {
+		watcher_.addPath(dir_.canonicalPath());
+	}
+	connect(&watcher_, SIGNAL(directoryChanged(const QString &)), this, SLOT(fileSystemChanged(const QString &)));
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplatesFilesCollection::~TitleBlockTemplatesFilesCollection() {
+}
+
+/**
+	@return the canonical path of the directory hosting this collection.
+*/
+QString TitleBlockTemplatesFilesCollection::path(const QString &template_name) const {
+	if (template_name.isEmpty()) {
+		return(dir_.canonicalPath());
+	} else {
+		return(dir_.absoluteFilePath(toFileName(template_name)));
+	}
+}
+
+/**
+	@return the list of templates contained in this collection
+*/
+QStringList TitleBlockTemplatesFilesCollection::templates() {
+	QStringList templates_names;
+	QRegExp replace_regexp(QString("%1$").arg(TITLEBLOCKS_FILE_EXTENSION));
+	foreach(QString name, dir_.entryList()) {
+		templates_names << name.replace(replace_regexp, "");
+	}
+	return(templates_names);
+}
+
+/**
+	@return the template which name is \a template_name, or 0 if the template
+	could not be loaded.
+*/
+TitleBlockTemplate *TitleBlockTemplatesFilesCollection::getTemplate(const QString &template_name) {
+	if (!templates().contains(template_name)) return(0);
+	
+	TitleBlockTemplate *tbtemplate = new TitleBlockTemplate();
+	QString tbt_file_path = path(template_name);
+	
+	bool loading = tbtemplate -> loadFromXmlFile(tbt_file_path);
+	if (!loading) {
+		delete tbtemplate;
+		return(0);
+	}
+	return(tbtemplate);
+}
+
+/**
+	@param template_name Name of a template (which has to already exist)
+	@return the XML description for the \a template_name template, or a null QDomElement if no such template exists.
+*/
+QDomElement TitleBlockTemplatesFilesCollection::getTemplateXmlDescription(const QString &template_name) {
+	QString xml_file_path = path(template_name);
+	
+	QFileInfo xml_file_info(xml_file_path);
+	if (!xml_file_info.exists() || !xml_file_info.isReadable()) {
+		return(QDomElement());
+	}
+	
+	QFile xml_file(xml_file_path);
+	if (!xml_file.open(QIODevice::ReadOnly)) {
+		return(QDomElement());
+	}
+	
+	QDomDocument *xml_document = new QDomDocument();
+	bool xml_parsing = xml_document -> setContent(&xml_file);
+	if (!xml_parsing) {
+		delete xml_document;
+		return(QDomElement());
+	}
+	return(xml_document -> documentElement());
+}
+
+/**
+	Set the XML description of the \a template_name template to \a xml_element.
+	@param template_name Name of a template (which does not have to already exist)
+	@param xml_element XML element describing the template
+*/
+bool TitleBlockTemplatesFilesCollection::setTemplateXmlDescription(const QString &template_name, const QDomElement &xml_element) {
+	if (template_name.isEmpty()) return(false);
+	
+	// prevent the watcher from emitting signals while we open and write to file
+	blockSignals(true);
+	
+	QDomDocument doc;
+	doc.appendChild(doc.importNode(xml_element, true));
+	
+	bool writing = QET::writeXmlFile(doc, path(template_name));
+	if (!writing) return(false);
+	
+	// emit a single signal for the change
+	blockSignals(false);
+	emit(changed(this, template_name));
+	return(true);
+}
+
+/**
+	Remove the \a template_name template.
+*/
+void TitleBlockTemplatesFilesCollection::removeTemplate(const QString &template_name) {
+	emit(aboutToRemove(this, template_name));
+	// prevent the watcher from emitting signals while we open and write to file
+	blockSignals(true);
+	
+	dir_.remove(toFileName(template_name));
+	
+	// emit a single signal for the removal
+	blockSignals(false);
+	emit(changed(this, template_name));
+}
+
+/**
+	@param template_name Name of a template supposed to be contained within
+	this collection.
+	@return 
+*/
+TitleBlockTemplateLocation TitleBlockTemplatesFilesCollection::location(const QString &template_name) {
+	return(TitleBlockTemplateLocation(template_name, this));
+}
+
+/**
+	@return always true since a files collection is always stored on a
+	filesystem.
+*/
+bool TitleBlockTemplatesFilesCollection::hasFilePath() {
+	return(true);
+}
+
+/**
+	@return The filesystem path where this files collection is actually stored.
+*/
+QString TitleBlockTemplatesFilesCollection::filePath() {
+	return(dir_.canonicalPath());
+}
+
+/**
+	@param template_name Either an empty QString to know whether the collection
+	itself is read only, or a specific template name.
+	@return true if the specified template is read only, false otherwise
+*/
+bool TitleBlockTemplatesFilesCollection::isReadOnly(const QString &template_name) const {
+	if (template_name.isEmpty()) {
+		QFileInfo info(dir_.canonicalPath());
+		return(!info.isWritable());
+	} else {
+		QFileInfo info(dir_.absoluteFilePath(toFileName(template_name)));
+		return(!info.isWritable());
+	}
+}
+
+/**
+	@param file_name A file name
+	@return the template name for \a file_name
+*/
+QString TitleBlockTemplatesFilesCollection::toTemplateName(const QString &file_name) {
+	static QRegExp replace_regexp(QString("%1$").arg(TITLEBLOCKS_FILE_EXTENSION));
+	QString template_name(file_name);
+	return(template_name.replace(replace_regexp, ""));
+}
+
+/**
+	@param template_name A template name
+	@return the file name for \a template_name
+*/
+QString TitleBlockTemplatesFilesCollection::toFileName(const QString &template_name) {
+	return(QString("%1%2").arg(template_name).arg(TITLEBLOCKS_FILE_EXTENSION));
+}
+
+/**
+	Handle the changes occuring on the file system.
+	@param str Path of the directory that changed.
+*/
+void TitleBlockTemplatesFilesCollection::fileSystemChanged(const QString &str) {
+	Q_UNUSED(str);
+	dir_.refresh();
+	emit(changed(this));
+}

Added: trunk/sources/titleblock/templatescollection.h
===================================================================
--- trunk/sources/titleblock/templatescollection.h	                        (rev 0)
+++ trunk/sources/titleblock/templatescollection.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,156 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATES_COLLECTION_H
+#define TITLEBLOCK_SLASH_TEMPLATES_COLLECTION_H
+#define TITLEBLOCKS_FILE_EXTENSION ".titleblock"
+#include <QtCore>
+#include <QtXml>
+#include "templatelocation.h"
+class TitleBlockTemplate;
+class QETProject;
+
+/**
+	This abstract class represents a generic collection of title block templates.
+	Unlike elements collections, title block templates collections do not provide
+	a tree structure. Instead, they provide a simple, flat list of available
+	templates.
+*/
+class TitleBlockTemplatesCollection : public QObject {
+	Q_OBJECT
+	
+	// Constructors, destructor
+	public:
+	TitleBlockTemplatesCollection(QObject *parent);
+	virtual ~TitleBlockTemplatesCollection();
+	private:
+	TitleBlockTemplatesCollection(const TitleBlockTemplatesCollection &);
+	
+	// methods
+	public:
+	virtual QStringList templates() = 0;
+	virtual TitleBlockTemplate *getTemplate(const QString &) = 0;
+	virtual QDomElement getTemplateXmlDescription(const QString &) = 0;
+	virtual bool setTemplateXmlDescription(const QString &, const QDomElement &) = 0;
+	virtual void removeTemplate(const QString &) = 0;
+	virtual TitleBlockTemplateLocation location(const QString & = QString()) = 0;
+	virtual bool hasFilePath() = 0;
+	virtual QString filePath() = 0;
+	virtual bool isReadOnly(const QString & = QString()) const = 0;
+	virtual QString title() const;
+	virtual void setTitle(const QString &);
+	virtual QString protocol() const;
+	virtual void setProtocol(const QString &);
+	virtual QETProject *parentProject();
+	virtual QList<TitleBlockTemplateLocation> templatesLocations();
+	
+	signals:
+	void changed(TitleBlockTemplatesCollection *, const QString & = QString());
+	void aboutToRemove(TitleBlockTemplatesCollection *, const QString &);
+	
+	// attributes
+	protected:
+	/// Human-readable title for this collection
+	QString title_;
+	/// Protocol used to designate this collection
+	QString protocol_;
+	/// Already parsed embedded titleblock templates
+	QHash<QString, TitleBlockTemplate *> titleblock_templates_;
+};
+
+/**
+	This class represents a simple abastraction layer for a collection of title
+	block templates embedded within a project.
+*/
+class TitleBlockTemplatesProjectCollection : public TitleBlockTemplatesCollection {
+	Q_OBJECT
+	
+	// Constructors, destructor
+	public:
+	TitleBlockTemplatesProjectCollection(QETProject *, QObject *parent = 0);
+	virtual ~TitleBlockTemplatesProjectCollection();
+	private:
+	TitleBlockTemplatesProjectCollection(const TitleBlockTemplatesProjectCollection &);
+	
+	// methods
+	public:
+	virtual QString title() const;
+	virtual QString protocol() const;
+	virtual QETProject *parentProject();
+	virtual QStringList templates();
+	virtual TitleBlockTemplate *getTemplate(const QString &);
+	virtual QDomElement getTemplateXmlDescription(const QString &);
+	virtual bool setTemplateXmlDescription(const QString &, const QDomElement &);
+	virtual void removeTemplate(const QString &);
+	virtual TitleBlockTemplateLocation location(const QString & = QString());
+	virtual bool hasFilePath();
+	virtual QString filePath();
+	virtual bool isReadOnly(const QString & = QString()) const;
+	virtual void fromXml(const QDomElement &);
+	virtual void deleteUnusedTitleBlocKTemplates();
+	
+	// attributes
+	private:
+	/// Parent project
+	QETProject *project_;
+	/// Parent QDomDocument for stored QDomElements
+	QDomDocument xml_document_;
+	/// XML descriptions of embedded titleblock templates
+	QHash<QString, QDomElement> titleblock_templates_xml_;
+};
+
+/**
+	This classe represents a simple abastraction layer for a file-based title
+	block templates directory. 
+*/
+class TitleBlockTemplatesFilesCollection : public TitleBlockTemplatesCollection {
+	Q_OBJECT
+	
+	// Constructors, destructor
+	public:
+	TitleBlockTemplatesFilesCollection(const QString &, QObject * = 0);
+	virtual ~TitleBlockTemplatesFilesCollection();
+	private:
+	TitleBlockTemplatesFilesCollection(const TitleBlockTemplatesFilesCollection &);
+	
+	// methods
+	public:
+	QString path(const QString & = QString()) const;
+	virtual QStringList templates();
+	virtual TitleBlockTemplate *getTemplate(const QString &);
+	virtual QDomElement getTemplateXmlDescription(const QString &);
+	virtual bool setTemplateXmlDescription(const QString &, const QDomElement &);
+	virtual void removeTemplate(const QString &);
+	virtual TitleBlockTemplateLocation location(const QString & = QString());
+	virtual bool hasFilePath();
+	virtual QString filePath();
+	virtual bool isReadOnly(const QString & = QString()) const;
+	
+	static QString toTemplateName(const QString &);
+	static QString toFileName(const QString &);
+	
+	private slots:
+	void fileSystemChanged(const QString &str);
+	
+	// attributes
+	private:
+	/// File System Watcher object to track the files changes made outside the application
+	QFileSystemWatcher watcher_;
+	/// Collection real directory
+	QDir dir_;
+};
+#endif

Added: trunk/sources/titleblock/templateview.cpp
===================================================================
--- trunk/sources/titleblock/templateview.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templateview.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1104 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templateview.h"
+#include "templatevisualcell.h"
+#include "gridlayoutanimation.h"
+#include "helpercell.h"
+#include "splittedhelpercell.h"
+#include "templatecommands.h"
+#include "templatecellsset.h"
+#include "dimensionwidget.h"
+#include "qeticons.h"
+#define ROW_OFFSET 2
+#define COL_OFFSET 1
+#define DEFAULT_PREVIEW_WIDTH 600
+#define DEFAULT_PREVIEW_HELPER_CELL_HEIGHT 15
+#define DEFAULT_COLS_HELPER_CELLS_HEIGHT   15
+#define DEFAULT_ROWS_HELPER_CELLS_WIDTH    50
+
+
+
+/**
+	Constructor
+	@param parent Parent QWidget.
+*/
+TitleBlockTemplateView::TitleBlockTemplateView(QWidget *parent) :
+	QGraphicsView(parent),
+	tbtemplate_(0),
+	tbgrid_(0),
+	form_(0),
+	preview_width_(DEFAULT_PREVIEW_WIDTH),
+	apply_columns_widths_count_(0),
+	apply_rows_heights_count_(0),
+	first_activation_(true),
+	read_only_(false)
+{
+	init();
+}
+
+/**
+	Constructor
+	@param parent Parent QWidget.
+*/
+TitleBlockTemplateView::TitleBlockTemplateView(QGraphicsScene *scene, QWidget *parent) :
+	QGraphicsView(scene, parent),
+	tbtemplate_(0),
+	tbgrid_(0),
+	preview_width_(DEFAULT_PREVIEW_WIDTH),
+	apply_columns_widths_count_(0),
+	apply_rows_heights_count_(0),
+	first_activation_(true),
+	read_only_(false)
+{
+	init();
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateView::~TitleBlockTemplateView() {
+}
+
+/**
+	@param tbtemplate Title block template to be rendered by this view.
+	If set to zero, the View will render nothing.
+*/
+void TitleBlockTemplateView::setTitleBlockTemplate(TitleBlockTemplate *tbtemplate) {
+	loadTemplate(tbtemplate);
+	zoomFit();
+}
+
+/**
+	@return The title block template object rendered by this view.
+*/
+TitleBlockTemplate *TitleBlockTemplateView::titleBlockTemplate() const {
+	return(tbtemplate_);
+}
+
+/**
+	Emits the selectedCellsChanged() signal with the currently selected cells.
+*/
+void TitleBlockTemplateView::selectionChanged() {
+	emit(selectedCellsChanged(selectedCells()));
+}
+
+/**
+	Zoom in by zoomFactor().
+	@see zoomFactor()
+*/
+void TitleBlockTemplateView::zoomIn() {
+	scale(zoomFactor(), zoomFactor());
+}
+
+/**
+	Zoom out by zoomFactor().
+	@see zoomFactor()
+*/
+void TitleBlockTemplateView::zoomOut() {
+	qreal zoom_factor = 1.0/zoomFactor();
+	scale(zoom_factor, zoom_factor);
+}
+
+/**
+	Fit the rendered title block template in this view.
+*/
+void TitleBlockTemplateView::zoomFit() {
+	adjustSceneRect();
+	fitInView(scene() -> sceneRect(), Qt::KeepAspectRatio);
+}
+
+/**
+	Reset the zoom level.
+*/
+void TitleBlockTemplateView::zoomReset() {
+	adjustSceneRect();
+	resetMatrix();
+}
+
+/**
+	Export currently selected cells to the clipboard before setting them as
+	empty.
+	@return the list of cells copied to the clipboard
+*/
+QList<TitleBlockCell *> TitleBlockTemplateView::cut() {
+	if (!tbtemplate_) return(QList<TitleBlockCell *>());
+	QList<TitleBlockCell *> copied_cells = copy();
+	
+	if (!copied_cells.isEmpty()) {
+		CutTemplateCellsCommand *cut_command = new CutTemplateCellsCommand(tbtemplate_);
+		cut_command -> setCutCells(copied_cells);
+		requestGridModification(cut_command);
+	}
+	return(copied_cells);
+}
+
+/**
+	Export currently selected cells to the clipboard.
+	@return the list of cells copied to the clipboard
+*/
+QList<TitleBlockCell *> TitleBlockTemplateView::copy() {
+	if (!tbtemplate_) return(QList<TitleBlockCell *>());
+	QList<TitleBlockCell *> copied_cells = selectedCells();
+	
+	QDomDocument xml_export;
+	QDomElement tbtpartial = xml_export.createElement("titleblocktemplate-partial");
+	xml_export.appendChild(tbtpartial);
+	foreach (TitleBlockCell *cell, copied_cells) {
+		tbtemplate_ -> exportCellToXml(cell, tbtpartial);
+		tbtpartial.setAttribute("row", cell -> num_row);
+		tbtpartial.setAttribute("col", cell -> num_col);
+		tbtpartial.setAttribute("row_span", cell -> row_span);
+		tbtpartial.setAttribute("col_span", cell -> col_span);
+	}
+	
+	QClipboard *clipboard = QApplication::clipboard();
+	clipboard -> setText(xml_export.toString());
+	
+	return(copied_cells);
+}
+
+/**
+	@return true if the content of the clipboard looks interesting
+*/
+bool TitleBlockTemplateView::mayPaste() {
+	// retrieve the clipboard content
+	QClipboard *clipboard = QApplication::clipboard();
+	return(clipboard -> text().contains("<titleblocktemplate-partial"));
+}
+
+/**
+	@return a list containing the pasted cells
+*/
+QList<TitleBlockCell> TitleBlockTemplateView::pastedCells() {
+	QList<TitleBlockCell> pasted_cells;
+	
+	// retrieve the clipboard content and parse it as XML
+	QClipboard *clipboard = QApplication::clipboard();
+	QDomDocument xml_import;
+	
+	if (!xml_import.setContent(clipboard -> text().trimmed())) {
+		return(pasted_cells);
+	}
+	
+	// ensure the XML document describes cells that can be pasted
+	if (xml_import.documentElement().tagName() != "titleblocktemplate-partial") {
+		return(pasted_cells);
+	}
+	
+	// load pasted cells
+	QDomElement paste_root = xml_import.documentElement();
+	for (QDomElement e = paste_root.firstChildElement() ; !e.isNull() ; e = e.nextSiblingElement()) {
+		if (e.tagName() == "empty" || e.tagName() == "field" || e.tagName() == "logo") {
+			TitleBlockCell cell;
+			cell.loadContentFromXml(e);
+			int row_num = -1, col_num = -1, row_span = -1, col_span = -1;
+			if (!QET::attributeIsAnInteger(e, "row", &row_num) || row_num < 0) {
+				continue;
+			}
+			if (!QET::attributeIsAnInteger(e, "col", &col_num) || col_num < 0) {
+				continue;
+			}
+			cell.num_row = row_num;
+			cell.num_col = col_num;
+			
+			// parse the rowspan and colspan attributes
+			if (QET::attributeIsAnInteger(e, "rowspan", &row_span) && row_span > 0) {
+				cell.row_span = row_span;
+			}
+			
+			if (QET::attributeIsAnInteger(e, "colspan", &col_span) && col_span > 0) {
+				cell.col_span = col_span;
+			}
+			pasted_cells << cell;
+		}
+	}
+	return(pasted_cells);
+}
+
+/**
+	Import the cells described in the clipboard.
+*/
+void TitleBlockTemplateView::paste() {
+	if (!tbtemplate_) return;
+	QList<TitleBlockCell> pasted_cells = pastedCells();
+	if (!pasted_cells.count()) return;
+	
+	// the top left cell among the selected ones will be used to position the pasted cells
+	TitleBlockTemplateVisualCell *selected_cell = selectedCellsSet().topLeftCell();
+	if (!selected_cell) return;
+	TitleBlockCell *erased_cell = selected_cell -> cell();
+	if (!erased_cell) return;
+	
+	// change num_row and num_col attributes of pasted cells so they get positionned relatively to selected_cell
+	normalizeCells(pasted_cells, erased_cell -> num_row, erased_cell -> num_col);
+	
+	PasteTemplateCellsCommand *paste_command = new PasteTemplateCellsCommand(tbtemplate_);
+	foreach (TitleBlockCell cell, pasted_cells) {
+		TitleBlockCell *erased_cell = tbtemplate_ -> cell(cell.num_row, cell.num_col);
+		if (!erased_cell) continue;
+		paste_command -> addCell(erased_cell, *erased_cell, cell);
+	}
+	
+	requestGridModification(paste_command);
+}
+
+/**
+	Add a column right after the last one.
+*/
+void TitleBlockTemplateView::addColumnAtEnd() {
+	if (read_only_) return;
+	requestGridModification(ModifyTemplateGridCommand::addColumn(tbtemplate_, tbtemplate_ -> columnsCount()));
+}
+
+/**
+	Add a row right after the last one.
+*/
+void TitleBlockTemplateView::addRowAtEnd() {
+	if (read_only_) return;
+	requestGridModification(ModifyTemplateGridCommand::addRow(tbtemplate_, tbtemplate_ -> rowsCount()));
+}
+
+/**
+	Add a column right before the last index selected when calling the context
+	menu.
+*/
+void TitleBlockTemplateView::addColumnBefore() {
+	if (read_only_) return;
+	int index = lastContextMenuCellIndex();
+	if (index == -1) return;
+	requestGridModification(ModifyTemplateGridCommand::addColumn(tbtemplate_, index));
+}
+
+/**
+	Add a row right before the last index selected when calling the context
+	menu.
+*/
+void TitleBlockTemplateView::addRowBefore() {
+	if (read_only_) return;
+	int index = lastContextMenuCellIndex();
+	if (index == -1) return;
+	requestGridModification(ModifyTemplateGridCommand::addRow(tbtemplate_, index));
+}
+
+/**
+	Add a column right after the last index selected when calling the context
+	menu.
+*/
+void TitleBlockTemplateView::addColumnAfter() {
+	if (read_only_) return;
+	int index = lastContextMenuCellIndex();
+	if (index == -1) return;
+	requestGridModification(ModifyTemplateGridCommand::addColumn(tbtemplate_, index + 1));
+}
+
+/**
+	Add a row right after the last index selected when calling the context
+	menu.
+*/
+void TitleBlockTemplateView::addRowAfter() {
+	if (read_only_) return;
+	int index = lastContextMenuCellIndex();
+	if (index == -1) return;
+	requestGridModification(ModifyTemplateGridCommand::addRow(tbtemplate_, index + 1));
+}
+
+/**
+	Edit the width of a column.
+	@param cell (optional) HelperCell of the column to be modified. If 0, this
+	method uses the last index selected when calling the context menu.
+*/
+void TitleBlockTemplateView::editColumn(HelperCell *cell) {
+	int index = cell ? cell -> index : lastContextMenuCellIndex();
+	if (index == -1) return;
+	
+	TitleBlockDimension dimension_before = tbtemplate_ -> columnDimension(index);
+	TitleBlockDimensionWidget dialog(true, this);
+	dialog.setReadOnly(read_only_);
+	dialog.setWindowTitle(tr("Changer la largeur de la colonne", "window title when changing a column with"));
+	dialog.label() -> setText(tr("Largeur :", "text before the spinbox to change a column width"));
+	dialog.setValue(dimension_before);
+	int user_answer = dialog.exec();
+	if (!read_only_ && user_answer == QDialog::Accepted) {
+		ModifyTemplateDimension *command = new ModifyTemplateDimension(tbtemplate_);
+		command -> setType(false);
+		command -> setIndex(index);
+		command -> setDimensionBefore(dimension_before);
+		command -> setDimensionAfter(dialog.value());
+		requestGridModification(command);
+	}
+}
+
+/**
+	Edit the height of a row.
+	@param cell (optional) HelperCell of the row to be modified. If 0, this
+	method uses the last index selected when calling the context menu.
+*/
+void TitleBlockTemplateView::editRow(HelperCell *cell) {
+	int index = cell ? cell -> index : lastContextMenuCellIndex();
+	if (index == -1) return;
+	
+	TitleBlockDimension dimension_before = TitleBlockDimension(tbtemplate_ -> rowDimension(index));
+	TitleBlockDimensionWidget dialog(false, this);
+	dialog.setReadOnly(read_only_);
+	dialog.setWindowTitle(tr("Changer la hauteur de la ligne", "window title when changing a row height"));
+	dialog.label() -> setText(tr("Hauteur :", "text before the spinbox to change a row height"));
+	dialog.setValue(dimension_before);
+	int user_answer = dialog.exec();
+	if (!read_only_ && user_answer == QDialog::Accepted) {
+		ModifyTemplateDimension *command = new ModifyTemplateDimension(tbtemplate_);
+		command -> setType(true);
+		command -> setIndex(index);
+		command -> setDimensionBefore(dimension_before);
+		command -> setDimensionAfter(dialog.value());
+		requestGridModification(command);
+	}
+}
+
+/**
+	Remove the column at the last index selected when calling the context menu.
+*/
+void TitleBlockTemplateView::deleteColumn() {
+	int index = lastContextMenuCellIndex();
+	if (index == -1) return;
+	requestGridModification(ModifyTemplateGridCommand::deleteColumn(tbtemplate_, index));
+}
+
+/**
+	Remove the row at the last index selected when calling the context menu.
+*/
+void TitleBlockTemplateView::deleteRow() {
+	int index = lastContextMenuCellIndex();
+	if (index == -1) return;
+	requestGridModification(ModifyTemplateGridCommand::deleteRow(tbtemplate_, index));
+}
+
+/**
+	Merge the selected cells.
+*/
+void TitleBlockTemplateView::mergeSelectedCells() {
+	// retrieve the selected cells
+	TitleBlockTemplateCellsSet selected_cells = selectedCellsSet();
+	
+	MergeCellsCommand *merge_command = new MergeCellsCommand(selected_cells, tbtemplate_);
+	if (merge_command -> isValid()) requestGridModification(merge_command);
+}
+
+/**
+	Split the selected cell.
+*/
+void TitleBlockTemplateView::splitSelectedCell() {
+	// retrieve the selected cells
+	TitleBlockTemplateCellsSet selected_cells = selectedCellsSet();
+	
+	SplitCellsCommand *split_command = new SplitCellsCommand(selected_cells, tbtemplate_);
+	if (split_command -> isValid()) requestGridModification(split_command);
+}
+
+/**
+	Reimplement the way the background is drawn to render the title block
+	template.
+*/
+void TitleBlockTemplateView::drawBackground(QPainter *painter, const QRectF &rect) {
+	QGraphicsView::drawBackground(painter, rect);
+	if (!tbtemplate_) return; // TODO shouldn't we draw a large uniform rect?
+}
+
+/**
+	@return the selected logical cells, not including the spanned ones.
+*/
+QList<TitleBlockCell *> TitleBlockTemplateView::selectedCells() const {
+	return(selectedCellsSet().cells(false).toList());
+}
+
+/**
+	@return the selected visual cells.
+*/
+TitleBlockTemplateCellsSet TitleBlockTemplateView::selectedCellsSet() const {
+	return(makeCellsSetFromGraphicsItems(scene() -> selectedItems()));
+}
+
+/**
+	@return the visual cells contained in the \a rect
+	@param rect Rectangle in the coordinates of the QGraphicsWidget
+	representing the title block template.
+*/
+TitleBlockTemplateCellsSet TitleBlockTemplateView::cells(const QRectF &rect) const {
+	QPolygonF mapped_rect(form_ -> mapToScene(rect));
+	QList<QGraphicsItem *> items = scene() -> items(mapped_rect, Qt::IntersectsItemShape);
+	return(makeCellsSetFromGraphicsItems(items));
+}
+
+/**
+	@param can_merge If non-zero, will be changed to reflect whether selected cells may be merged
+	@param can_merge If non-zero, will be changed to reflect whether selected cells may be splitted
+	@param count     If non-zero, will be changed to reflect the number of selected cells
+*/
+void TitleBlockTemplateView::analyzeSelectedCells(bool *can_merge, bool *can_split, int *count) {
+	if (!can_merge && !can_split) return;
+	
+	if (!tbtemplate_) {
+		if (can_merge) *can_merge = false;
+		if (can_split) *can_split = false;
+		return;
+	}
+	
+	// retrieve the selected cells
+	TitleBlockTemplateCellsSet selected_cells = selectedCellsSet();
+	
+	if (can_merge) {
+		*can_merge = MergeCellsCommand::canMerge(selected_cells, tbtemplate_);
+	}
+	if (can_split) {
+		*can_split = SplitCellsCommand::canSplit(selected_cells, tbtemplate_);
+	}
+	if (count) {
+		*count = selectedCellsSet().count();
+	}
+}
+
+/**
+	@return the current size of the rendered title block template
+*/
+QSizeF TitleBlockTemplateView::templateSize() const {
+	return(QSizeF(templateWidth(), templateHeight()));
+}
+
+/**
+	@return the current width of the rendered title block template
+*/
+qreal TitleBlockTemplateView::templateWidth() const {
+	if (!tbtemplate_) return(0);
+	
+	qreal width = DEFAULT_ROWS_HELPER_CELLS_WIDTH;
+	// the rendered width may exceed the initially planned preview width
+	width += qMax<int>(preview_width_, tbtemplate_ -> width(preview_width_));
+	
+	return(width);
+}
+
+/**
+	@return the current height of the rendered title block template
+*/
+qreal TitleBlockTemplateView::templateHeight() const {
+	if (!tbtemplate_) return(0);
+	
+	qreal height = DEFAULT_PREVIEW_HELPER_CELL_HEIGHT;
+	height += DEFAULT_COLS_HELPER_CELLS_HEIGHT;
+	height += tbtemplate_ -> height();
+	
+	return(height);
+}
+
+/**
+	Handles mouse wheel-related actions
+	@param e QWheelEvent describing the wheel event
+*/
+void TitleBlockTemplateView::wheelEvent(QWheelEvent *e) {
+	// si la touche Ctrl est enfoncee, on zoome / dezoome
+	if (e -> modifiers() & Qt::ControlModifier) {
+		if (e -> delta() > 0) { 
+			zoomIn();
+		} else {
+			zoomOut();
+		}
+	} else {
+		QAbstractScrollArea::wheelEvent(e);
+	}
+}
+
+/**
+	@return the zoom factor used by zoomIn() and zoomOut().
+*/
+qreal TitleBlockTemplateView::zoomFactor() const {
+	return(1.1);
+}
+
+/**
+	Initialize this view (actions, signals/slots connections, etc.)
+*/
+void TitleBlockTemplateView::init() {
+	add_column_before_    = new QAction(QET::Icons::EditTableInsertColumnLeft,  tr("Ajouter une colonne (avant)",              "context menu"), this);
+	add_row_before_       = new QAction(QET::Icons::EditTableInsertRowAbove,    tr("Ajouter une ligne (avant)",                "context menu"), this);
+	add_column_after_     = new QAction(QET::Icons::EditTableInsertColumnRight, tr("Ajouter une colonne (apr\350s)",           "context menu"), this);
+	add_row_after_        = new QAction(QET::Icons::EditTableInsertRowUnder,    tr("Ajouter une ligne (apr\350s)",             "context menu"), this);
+	edit_column_dim_      = new QAction(                                        tr("Modifier les dimensions de cette colonne", "context menu"), this);
+	edit_row_dim_         = new QAction(                                        tr("Modifier les dimensions de cette ligne",   "context menu"), this);
+	delete_column_        = new QAction(QET::Icons::EditTableDeleteColumn,      tr("Supprimer cette colonne",                  "context menu"), this);
+	delete_row_           = new QAction(QET::Icons::EditTableDeleteRow,         tr("Supprimer cette ligne",                    "context menu"), this);
+	change_preview_width_ = new QAction(                                        tr("Modifier la largeur de cet aper\347u",     "context menu"), this);
+	
+	connect(add_column_before_,    SIGNAL(triggered()), this, SLOT(addColumnBefore()));
+	connect(add_row_before_,       SIGNAL(triggered()), this, SLOT(addRowBefore()));
+	connect(add_column_after_,     SIGNAL(triggered()), this, SLOT(addColumnAfter()));
+	connect(add_row_after_,        SIGNAL(triggered()), this, SLOT(addRowAfter()));
+	connect(edit_column_dim_,      SIGNAL(triggered()), this, SLOT(editColumn()));
+	connect(edit_row_dim_,         SIGNAL(triggered()), this, SLOT(editRow()));
+	connect(delete_column_,        SIGNAL(triggered()), this, SLOT(deleteColumn()));
+	connect(delete_row_,           SIGNAL(triggered()), this, SLOT(deleteRow()));
+	connect(change_preview_width_, SIGNAL(triggered()), this, SLOT(changePreviewWidth()));
+	
+	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
+	setBackgroundBrush(QBrush(QColor(248, 255, 160)));
+	
+	connect(scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
+}
+
+/**
+	Apply the columns widths currently specified by the edited title block
+	template.
+	@param animate true to animate the change, false otherwise.
+*/
+void TitleBlockTemplateView::applyColumnsWidths(bool animate) {
+	// the first column is dedicated to helper cells showing the rows height
+	tbgrid_ -> setColumnFixedWidth(0, DEFAULT_ROWS_HELPER_CELLS_WIDTH);
+	tbgrid_ -> setColumnSpacing(0, 0);
+	
+	// we apply the other columns width based on the title block template data
+	QList<int> widths = tbtemplate_ -> columnsWidth(preview_width_);
+	int total_applied_width = 0;
+	for (int i = 0 ; i < widths.count() ; ++ i) {
+		int applied_width = qMax(0, widths.at(i));
+		tbgrid_ -> setColumnSpacing(COL_OFFSET + i, 0);
+		if (!animate) {
+			// no animation on first call
+			tbgrid_ -> setColumnFixedWidth(COL_OFFSET + i, widths.at(i));
+		} else {
+			GridLayoutAnimation *animation = new GridLayoutAnimation(tbgrid_, form_);
+			animation -> setIndex(COL_OFFSET + i);
+			animation -> setActsOnRows(false);
+			animation -> setStartValue(QVariant(tbgrid_ -> columnMinimumWidth(COL_OFFSET + i)));
+			animation -> setEndValue(QVariant(1.0 * applied_width));
+			animation -> setDuration(500);
+			connect(animation, SIGNAL(finished()), this, SLOT(updateColumnsHelperCells()));
+			animation -> start(QAbstractAnimation::DeleteWhenStopped);
+		}
+		total_applied_width += applied_width;
+	}
+	if (!animate) updateColumnsHelperCells();
+	++ apply_columns_widths_count_;
+	
+	// we systematically parameter some cells
+	total_width_helper_cell_ -> split_size = 0;
+	tbgrid_ -> addItem(total_width_helper_cell_, 0, COL_OFFSET, 1, widths.count());
+	removeItem(extra_cells_width_helper_cell_);
+	
+	if (total_applied_width < preview_width_) {
+		// preview_width is greater than the sum of cells widths
+		// we add an extra column with a helper cell
+		tbgrid_ -> addItem(extra_cells_width_helper_cell_, ROW_OFFSET - 1, COL_OFFSET + widths.count(), tbtemplate_ -> rowsCount() + 1, 1);
+		tbgrid_ -> addItem(total_width_helper_cell_, 0, COL_OFFSET, 1, widths.count() + 1);
+		tbgrid_ -> setColumnFixedWidth(COL_OFFSET + widths.count(), preview_width_ - total_applied_width);
+		extra_cells_width_helper_cell_ -> setLabel(
+			QString(
+				tr("[%1px]","content of the extra cell added when the total width of cells is less than the preview width")
+			).arg(preview_width_ - total_applied_width)
+		);
+	} else if (total_applied_width > preview_width_) {
+		// preview width is smaller than the sum of cells widths
+		// we draw an extra header within th "preview width" cell.
+		tbgrid_ -> addItem(total_width_helper_cell_, 0, COL_OFFSET, 1, widths.count());
+		total_width_helper_cell_ -> split_background_color = QColor(Qt::red);
+		total_width_helper_cell_ -> split_foreground_color = QColor(Qt::black);
+		total_width_helper_cell_ -> split_label = QString(
+			tr("[%1px]", "content of the extra helper cell added when the total width of cells is greather than the preview width")
+		).arg(total_applied_width - preview_width_);
+		total_width_helper_cell_ -> split_size = total_applied_width - preview_width_;
+	}
+	
+	updateDisplayedMinMaxWidth();
+}
+
+/**
+	Apply the rows heights currently specified by the edited title block
+	template.
+	@param animate true to animate the change, false otherwise.
+*/
+void TitleBlockTemplateView::applyRowsHeights(bool animate) {
+	// the first row is dedicated to a helper cell showing the total width
+	tbgrid_ -> setRowFixedHeight(0, DEFAULT_PREVIEW_HELPER_CELL_HEIGHT);
+	tbgrid_ -> setRowSpacing(0, 0);
+	// the second row is dedicated to helper cells showing the columns width
+	tbgrid_ -> setRowFixedHeight(1, DEFAULT_COLS_HELPER_CELLS_HEIGHT);
+	tbgrid_ -> setRowSpacing(1, 0);
+	
+	QList<int> heights = tbtemplate_ -> rowsHeights();
+	for (int i = 0 ; i < heights.count() ; ++ i) {
+		tbgrid_ -> setRowSpacing(ROW_OFFSET + i, 0);
+		if (!animate) {
+			// no animation on first call
+			tbgrid_ -> setRowFixedHeight(ROW_OFFSET + i, heights.at(i));
+		} else {
+			GridLayoutAnimation *animation = new GridLayoutAnimation(tbgrid_, form_);
+			animation -> setIndex(ROW_OFFSET + i);
+			animation -> setActsOnRows(true);
+			animation -> setStartValue(QVariant(tbgrid_ -> rowMinimumHeight(ROW_OFFSET + i)));
+			animation -> setEndValue(QVariant(1.0 * heights.at(i)));
+			animation -> setDuration(500);
+			connect(animation, SIGNAL(finished()), this, SLOT(updateRowsHelperCells()));
+			animation -> start(QAbstractAnimation::DeleteWhenStopped);
+		}
+		
+	}
+	if (!animate) updateRowsHelperCells();
+	++ apply_rows_heights_count_;
+}
+
+/**
+	Update the content (type and value) of rows helper cells.
+*/
+void TitleBlockTemplateView::updateRowsHelperCells() {
+	int row_count = tbtemplate_ -> rowsCount();
+	QList<int> heights = tbtemplate_ -> rowsHeights();
+	for (int i = 0 ; i < row_count ; ++ i) {
+		HelperCell *current_row_cell = static_cast<HelperCell *>(tbgrid_ -> itemAt(ROW_OFFSET + i, 0));
+		if (current_row_cell) {
+			current_row_cell -> setType(QET::Absolute); // rows always have absolute heights
+			current_row_cell -> setLabel(QString(tr("%1px", "format displayed in rows helper cells")).arg(heights.at(i)));
+		}
+	}
+}
+
+/**
+	Update the content (type and value) of columns helper cells.
+*/
+void TitleBlockTemplateView::updateColumnsHelperCells() {
+	int col_count = tbtemplate_ -> columnsCount();
+	for (int i = 0 ; i < col_count ; ++ i) {
+		TitleBlockDimension current_col_dim = tbtemplate_ -> columnDimension(i);
+		HelperCell *current_col_cell = static_cast<HelperCell *>(tbgrid_ -> itemAt(1, COL_OFFSET + i));
+		if (current_col_cell) {
+			current_col_cell -> setType(current_col_dim.type);
+			current_col_cell -> setLabel(current_col_dim.toString());
+		}
+	}
+}
+
+/**
+	Add the cells (both helper cells and regular visual cells) to the scene to
+	get a visual representation of the edited title block template.
+*/
+void TitleBlockTemplateView::addCells() {
+	int col_count = tbtemplate_ -> columnsCount();
+	int row_count = tbtemplate_ -> rowsCount();
+	
+	// we add a big cell to show the total width
+	total_width_helper_cell_ = new SplittedHelperCell();
+	total_width_helper_cell_ -> setType(QET::Absolute);
+	updateTotalWidthLabel();
+	total_width_helper_cell_ -> orientation = Qt::Horizontal;
+	total_width_helper_cell_ -> setActions(QList<QAction *>() << change_preview_width_);
+	connect(total_width_helper_cell_, SIGNAL(contextMenuTriggered(HelperCell *)), this, SLOT(updateLastContextMenuCell(HelperCell *)));
+	connect(total_width_helper_cell_, SIGNAL(doubleClicked(HelperCell*)),         this, SLOT(changePreviewWidth()));
+	tbgrid_ -> addItem(total_width_helper_cell_, 0, COL_OFFSET, 1, col_count);
+	
+	// we also initialize an extra helper cells that shows the preview width is
+	// too long for the current cells widths
+	extra_cells_width_helper_cell_ = new HelperCell();
+	extra_cells_width_helper_cell_ -> background_color = QColor(Qt::red);
+	
+	// we add one cell per column to show their respective width
+	for (int i = 0 ; i < col_count ; ++ i) {
+		TitleBlockDimension current_col_dim = tbtemplate_ -> columnDimension(i);
+		HelperCell *current_col_cell = new HelperCell();
+		current_col_cell -> setType(current_col_dim.type);
+		current_col_cell -> setLabel(current_col_dim.toString());
+		current_col_cell -> setActions(columnsActions());
+		current_col_cell -> orientation = Qt::Horizontal;
+		current_col_cell -> index = i;
+		connect(current_col_cell, SIGNAL(contextMenuTriggered(HelperCell *)), this, SLOT(updateLastContextMenuCell(HelperCell *)));
+		connect(current_col_cell, SIGNAL(doubleClicked(HelperCell*)),         this, SLOT(editColumn(HelperCell *)));
+		tbgrid_ -> addItem(current_col_cell, 1, COL_OFFSET + i, 1, 1);
+	}
+	
+	// we add one cell per row to show their respective height
+	QList<int> heights = tbtemplate_ -> rowsHeights();
+	for (int i = 0 ; i < row_count ; ++ i) {
+		HelperCell *current_row_cell = new HelperCell();
+		current_row_cell -> setType(QET::Absolute); // rows always have absolute heights
+		current_row_cell -> setLabel(QString(tr("%1px")).arg(heights.at(i)));
+		current_row_cell -> orientation = Qt::Vertical;
+		current_row_cell -> index = i;
+		current_row_cell -> setActions(rowsActions());
+		connect(current_row_cell, SIGNAL(contextMenuTriggered(HelperCell *)), this, SLOT(updateLastContextMenuCell(HelperCell *)));
+		connect(current_row_cell, SIGNAL(doubleClicked(HelperCell*)),         this, SLOT(editRow(HelperCell *)));
+		tbgrid_ -> addItem(current_row_cell, ROW_OFFSET + i, 0, 1, 1);
+	}
+	
+	// eventually we add the cells composing the titleblock template
+	for (int i = 0 ; i < col_count ; ++ i) {
+		for (int j = 0 ; j < row_count ; ++ j) {
+			TitleBlockCell *cell = tbtemplate_ -> cell(j, i);
+			if (cell -> spanner_cell) continue;
+			TitleBlockTemplateVisualCell *cell_item = new TitleBlockTemplateVisualCell();
+			cell_item -> setTemplateCell(tbtemplate_, cell);
+			
+			int row_span = 0, col_span = 0;
+			if (cell -> span_state != TitleBlockCell::Disabled) {
+				row_span = cell -> applied_row_span;
+				col_span = cell -> applied_col_span;
+			}
+			tbgrid_ -> addItem(cell_item, ROW_OFFSET + j, COL_OFFSET + i, row_span + 1, col_span + 1);
+		}
+	}
+}
+
+/**
+	Refresh the regular cells.
+*/
+void TitleBlockTemplateView::refresh() {
+	int col_count = tbtemplate_ -> columnsCount();
+	int row_count = tbtemplate_ -> rowsCount();
+	if (row_count < 1 || col_count < 1) return;
+	
+	for (int i = 0 ; i < col_count ; ++ i) {
+		for (int j = 0 ; j < row_count ; ++ j) {
+			if (QGraphicsLayoutItem *item = tbgrid_ -> itemAt(ROW_OFFSET + j, COL_OFFSET + i)) {
+				if (QGraphicsItem *qgi = dynamic_cast<QGraphicsItem *>(item)) {
+					qgi -> update();
+				}
+			}
+		}
+	}
+}
+
+/**
+	Ask the user a new width for the preview
+*/
+void TitleBlockTemplateView::changePreviewWidth() {
+	TitleBlockDimensionWidget dialog(false, this);
+	dialog.setWindowTitle(tr("Changer la largeur de l'aper\347u"));
+	dialog.label() -> setText(tr("Largeur de l'aper\347u :"));
+	dialog.setValue(TitleBlockDimension(preview_width_));
+	if (dialog.exec() == QDialog::Accepted) {
+		setPreviewWidth(dialog.value().value);
+	}
+}
+
+/**
+	Fill the layout with empty cells where needed.
+*/
+void TitleBlockTemplateView::fillWithEmptyCells() {
+	int col_count = tbtemplate_ -> columnsCount();
+	int row_count = tbtemplate_ -> rowsCount();
+	if (row_count < 1 || col_count < 1) return;
+	
+	for (int i = 0 ; i < col_count ; ++ i) {
+		for (int j = 0 ; j < row_count ; ++ j) {
+			if (tbgrid_ -> itemAt(ROW_OFFSET + j, COL_OFFSET + i)) continue;
+			TitleBlockTemplateVisualCell *cell_item = new TitleBlockTemplateVisualCell();
+			if (TitleBlockCell *target_cell = tbtemplate_ -> cell(j, i)) {
+				qDebug() << Q_FUNC_INFO << "target_cell" << target_cell;
+				cell_item -> setTemplateCell(tbtemplate_, target_cell);
+			}
+			tbgrid_ -> addItem(cell_item, ROW_OFFSET + j, COL_OFFSET + i);
+		}
+	}
+}
+
+/**
+	@param event Object describing the received event 
+*/
+bool TitleBlockTemplateView::event(QEvent *event) {
+	if (first_activation_ && event -> type() == QEvent::WindowActivate) {
+		QTimer::singleShot(250, this, SLOT(zoomFit()));
+		first_activation_ = false;
+	}
+	return(QGraphicsView::event(event));
+}
+
+/**
+	Given a cells list, change their position so the top left one is at row \a x and column \a y.
+	@param cells Cells list
+*/
+void TitleBlockTemplateView::normalizeCells(QList<TitleBlockCell> &cells, int x, int y) const {
+	if (!cells.count()) return;
+	
+	int min_row = cells.at(0).num_row;
+	int min_col = cells.at(0).num_col;
+	for (int i = 1 ; i < cells.count() ; ++ i) {
+		if (cells.at(i).num_row < min_row) min_row = cells.at(i).num_row;
+		if (cells.at(i).num_col < min_col) min_col = cells.at(i).num_col;
+	}
+	for (int i = 0 ; i < cells.count() ; ++ i) {
+		cells[i].num_row = cells[i].num_row - min_row + x;
+		cells[i].num_col = cells[i].num_col - min_col + y;
+	}
+}
+
+/**
+	Load the \a tbt title block template.
+	If a different template was previously loaded, it is deleted.
+	
+*/
+void TitleBlockTemplateView::loadTemplate(TitleBlockTemplate *tbt) {
+	if (tbgrid_) {
+		scene() -> removeItem(form_);
+		// also deletes TemplateCellPreview because, according to the
+		// documentation, QGraphicsGridLayout takes ownership of the items.
+		form_ -> deleteLater();
+	}
+	if (tbtemplate_ && tbtemplate_ != tbt) {
+		delete tbtemplate_;
+	}
+	
+	tbtemplate_ = tbt;
+	
+	// initialize a grid layout with no margin
+	tbgrid_ = new QGraphicsGridLayout();
+	tbgrid_ -> setContentsMargins(0, 0, 0, 0);
+	// add cells defined by the title block template in this layout
+	addCells();
+	// fill potential holes in the grid with empty cells
+	fillWithEmptyCells();
+	// apply rows and columns dimensions
+	applyColumnsWidths(false);
+	applyRowsHeights(false);
+	
+	// assign the layout to a basic QGraphicsWidget
+	form_ = new QGraphicsWidget();
+	// enforce the layout direction to avoid reversing the template rendering
+	form_ -> setLayoutDirection(Qt::LeftToRight);
+	form_ -> setLayout(tbgrid_);
+	scene() -> addItem(form_);
+	adjustSceneRect();
+}
+
+/**
+	@return the list of rows-specific actions.
+*/
+QList<QAction *> TitleBlockTemplateView::rowsActions() const {
+	return QList<QAction *>() << add_row_before_<< edit_row_dim_ << add_row_after_ << delete_row_;
+}
+
+/**
+	@return the list of columns-specific actions.
+*/
+QList<QAction *> TitleBlockTemplateView::columnsActions() const {
+	return QList<QAction *>() << add_column_before_ << edit_column_dim_ << add_column_after_ << delete_column_;
+}
+
+/**
+	Update the displayed layout. Call this function to refresh the display
+	after the rendered title block template has been "deeply" modified, e.g.
+	rows/columns have been added/modified or cells were merged/splitted.
+*/
+void TitleBlockTemplateView::updateLayout() {
+	// TODO we should try to update the grid instead of deleting-and-reloading it
+	loadTemplate(tbtemplate_);
+}
+
+/**
+	Update the displayed layout. Call this function when the dimensions of
+	rows changed.
+*/
+void TitleBlockTemplateView::rowsDimensionsChanged() {
+	applyRowsHeights();
+}
+
+/**
+	Update the displayed layout. Call this function when the dimensions of
+	columns changed.
+*/
+void TitleBlockTemplateView::columnsDimensionsChanged() {
+	applyColumnsWidths();
+}
+
+/**
+	Update the tooltip that displays the minimum and/or maximum width of the
+	template.
+*/
+void TitleBlockTemplateView::updateDisplayedMinMaxWidth() {
+	if (!tbtemplate_) return;
+	int min_width = tbtemplate_ -> minimumWidth();
+	int max_width = tbtemplate_ -> maximumWidth();
+	
+	QString min_max_width_sentence;
+	if (max_width != -1) {
+		min_max_width_sentence = QString(
+			tr(
+				"Longueur minimale\240: %1px\nLongueur maximale\240: %2px\n",
+				"tooltip showing the minimum and/or maximum width of the edited template"
+			)
+		).arg(min_width).arg(max_width);
+	} else {
+		min_max_width_sentence = QString(
+			tr(
+				"Longueur minimale\240: %1px\n",
+				"tooltip showing the minimum width of the edited template"
+			)
+		).arg(min_width);
+	}
+	
+	// the tooltip may also display the split label for readability purpose
+	if (total_width_helper_cell_ -> split_size) {
+		min_max_width_sentence += "---\n";
+		min_max_width_sentence += total_width_helper_cell_ -> split_label;
+	}
+	
+	total_width_helper_cell_ -> setToolTip(makePrettyToolTip(min_max_width_sentence));
+}
+
+/**
+	@param read_only whether this view should be read only.
+	
+*/
+void TitleBlockTemplateView::setReadOnly(bool read_only) {
+	if (read_only_ == read_only) return;
+	
+	read_only_ = read_only;
+	add_column_before_ -> setEnabled(!read_only_);
+	add_row_before_    -> setEnabled(!read_only_);
+	add_column_after_  -> setEnabled(!read_only_);
+	add_row_after_     -> setEnabled(!read_only_);
+	delete_column_     -> setEnabled(!read_only_);
+	delete_row_        -> setEnabled(!read_only_);
+}
+
+/**
+	Set the new preview width to width
+	@param width new preview width
+*/
+void TitleBlockTemplateView::setPreviewWidth(int width) {
+	if (preview_width_ == width) return;
+	int former_preview_width = preview_width_;
+	preview_width_ = width;
+	if (tbgrid_) {
+		applyColumnsWidths();
+		updateTotalWidthLabel();
+		//adjustSceneRect();
+		centerOn(form_);
+	}
+	emit(previewWidthChanged(former_preview_width, preview_width_));
+}
+
+/**
+	Update the label of the helper cell that indicates the preview width.
+*/
+void TitleBlockTemplateView::updateTotalWidthLabel() {
+	if (!total_width_helper_cell_) return;
+	total_width_helper_cell_ -> label = QString(
+		tr(
+			"Largeur totale pour cet aper\347u : %1px",
+			"displayed at the top of the preview when editing a title block template"
+		)
+	).arg(preview_width_);
+}
+
+/**
+	Emit the gridModificationRequested() signal with \a command after having set
+	its view component.
+	@see TitleBlockTemplateCommand::setView()
+	@param command A command object modifying the rendered title block template.
+*/
+void TitleBlockTemplateView::requestGridModification(TitleBlockTemplateCommand *command) {
+	if (!command) return;
+	command -> setView(this);
+	emit(gridModificationRequested(command));
+}
+
+/**
+	@return the last index selected when triggering the context menu.
+	@see updateLastContextMenuCell
+*/
+int TitleBlockTemplateView::lastContextMenuCellIndex() const {
+	if (last_context_menu_cell_) {
+		return(last_context_menu_cell_ -> index);
+	}
+	return(-1);
+}
+
+/**
+	@param item an item supposed to be contained in the grid layout.
+	@return the flat index if this item, or -1 if it could not be found.
+*/
+int TitleBlockTemplateView::indexOf(QGraphicsLayoutItem *item) {
+	for (int i = 0 ; i < tbgrid_ -> count() ; ++i) {
+		if (item == tbgrid_ -> itemAt(i)) return(i);
+	}
+	return(-1);
+}
+
+/**
+	Removes an item from the grid layout
+	@param item an item supposed to be contained in the grid layout.
+*/
+void TitleBlockTemplateView::removeItem(QGraphicsLayoutItem *item) {
+	int index = indexOf(item);
+	if (index != -1) {
+		tbgrid_ -> removeAt(index);
+		// trick: we also have to remove the item from the scene
+		if (QGraphicsScene *current_scene = scene()) {
+			if (QGraphicsItem *qgi = item -> graphicsItem()) {
+				current_scene -> removeItem(qgi);
+			}
+		}
+	}
+}
+
+/**
+	@param a list of QGraphicsItem
+	@return the corresponding TitleBlockTemplateCellsSet
+*/
+TitleBlockTemplateCellsSet TitleBlockTemplateView::makeCellsSetFromGraphicsItems(const QList<QGraphicsItem *> &items) const {
+	TitleBlockTemplateCellsSet set(this);
+	foreach (QGraphicsItem *item, items) {
+		if (TitleBlockTemplateVisualCell *cell_view = dynamic_cast<TitleBlockTemplateVisualCell *>(item)) {
+			if (cell_view -> cell() && cell_view -> cell() -> num_row != -1) {
+				set << cell_view;
+			}
+		}
+	}
+	return(set);
+}
+
+/*
+	@param a text string
+	@return an HTML string that can be passed to setToolTip()
+*/
+QString TitleBlockTemplateView::makePrettyToolTip(const QString &string) {
+	QString css_style = QString("white-space: pre;");
+	
+	QString final_tooltip_content = QString(
+		"<div style=\"%1\">%2</div>"
+	).arg(css_style).arg(string);
+	
+	return(final_tooltip_content);
+}
+
+/**
+	Stores \a last_context_menu_cell as being the last helper cell the context
+	menu was triggered on.
+*/
+void TitleBlockTemplateView::updateLastContextMenuCell(HelperCell *last_context_menu_cell) {
+	last_context_menu_cell_ = last_context_menu_cell;
+}
+
+/**
+	Adjusts the bounding rect of the scene.
+*/
+void TitleBlockTemplateView::adjustSceneRect() {
+	QRectF old_scene_rect = scene() -> sceneRect();
+	
+	// rectangle including everything on the scene
+	QRectF bounding_rect(QPointF(0, 0), templateSize());
+	scene() -> setSceneRect(bounding_rect);
+	
+	// met a jour la scene
+	scene() -> update(old_scene_rect.united(bounding_rect));
+}
+

Added: trunk/sources/titleblock/templateview.h
===================================================================
--- trunk/sources/titleblock/templateview.h	                        (rev 0)
+++ trunk/sources/titleblock/templateview.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,143 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_TEMPLATE_VIEW_H
+#define TITLEBLOCK_SLASH_TEMPLATE_VIEW_H
+#include <QGraphicsView>
+#include "titleblocktemplate.h"
+class HelperCell;
+class SplittedHelperCell;
+class TitleBlockTemplateCommand;
+class TitleBlockTemplateCellsSet;
+
+/**
+	This QGraphicsView subclass is used in the title block template editor to
+	offer a graphical preview of the template being edited, but also to handle
+	cell selection and various actions.
+*/
+class TitleBlockTemplateView : public QGraphicsView {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	TitleBlockTemplateView(QWidget * = 0);
+	TitleBlockTemplateView(QGraphicsScene *, QWidget * = 0);
+	virtual ~TitleBlockTemplateView();
+	private:
+	TitleBlockTemplateView(const TitleBlockTemplateView &);
+	
+	// methods and slots
+	public:
+	TitleBlockTemplate *titleBlockTemplate() const;
+	virtual QList<TitleBlockCell *> selectedCells() const;
+	virtual TitleBlockTemplateCellsSet selectedCellsSet() const;
+	virtual TitleBlockTemplateCellsSet cells(const QRectF &) const;
+	virtual void analyzeSelectedCells(bool *, bool *, int *);
+	virtual QSizeF templateSize() const;
+	virtual qreal templateWidth() const;
+	virtual qreal templateHeight() const;
+	
+	public slots:
+	void setTitleBlockTemplate(TitleBlockTemplate *);
+	void selectionChanged();
+	void zoomIn();
+	void zoomOut();
+	void zoomFit();
+	void zoomReset();
+	QList<TitleBlockCell *> cut();
+	QList<TitleBlockCell *> copy();
+	bool mayPaste();
+	QList<TitleBlockCell> pastedCells();
+	void paste();
+	void addColumnAtEnd();
+	void addRowAtEnd();
+	void addColumnBefore();
+	void addRowBefore();
+	void addColumnAfter();
+	void addRowAfter();
+	void editColumn(HelperCell * = 0);
+	void editRow(HelperCell * = 0);
+	void deleteColumn();
+	void deleteRow();
+	void mergeSelectedCells();
+	void splitSelectedCell();
+	void refresh();
+	void changePreviewWidth();
+	void setPreviewWidth(int);
+	void updateLayout();
+	void rowsDimensionsChanged();
+	void columnsDimensionsChanged();
+	void updateDisplayedMinMaxWidth();
+	void setReadOnly(bool);
+	
+	protected slots:
+	virtual void applyColumnsWidths(bool = true);
+	virtual void applyRowsHeights(bool = true);
+	virtual void updateRowsHelperCells();
+	virtual void updateColumnsHelperCells();
+	
+	protected:
+	virtual void drawBackground(QPainter *, const QRectF &);
+	virtual void addCells();
+	virtual void loadTemplate(TitleBlockTemplate *);
+	virtual void init();
+	virtual void wheelEvent(QWheelEvent *);
+	virtual qreal zoomFactor() const;
+	virtual void fillWithEmptyCells();
+	virtual bool event(QEvent *);
+	virtual void normalizeCells(QList<TitleBlockCell> &, int x = 0, int y = 0) const;
+	
+	signals:
+	void selectedCellsChanged(QList<TitleBlockCell *>);
+	void gridModificationRequested(TitleBlockTemplateCommand *);
+	void previewWidthChanged(int, int);
+	
+	private:
+	QList<QAction *> rowsActions() const;
+	QList<QAction *> columnsActions() const;
+	void updateTotalWidthLabel();
+	void requestGridModification(TitleBlockTemplateCommand *);
+	int lastContextMenuCellIndex() const;
+	int indexOf(QGraphicsLayoutItem *);
+	void removeItem(QGraphicsLayoutItem *);
+	TitleBlockTemplateCellsSet makeCellsSetFromGraphicsItems(const QList<QGraphicsItem *> &) const;
+	QString makePrettyToolTip(const QString &);
+	
+	private slots:
+	void updateLastContextMenuCell(HelperCell *);
+	void adjustSceneRect();
+	
+	// attributes
+	private:
+	TitleBlockTemplate *tbtemplate_;
+	QGraphicsGridLayout *tbgrid_;
+	QGraphicsWidget *form_;
+	int preview_width_;
+	SplittedHelperCell *total_width_helper_cell_;
+	HelperCell *extra_cells_width_helper_cell_;
+	QAction *add_column_before_, *add_row_before_;
+	QAction *add_column_after_, *add_row_after_;
+	QAction *edit_column_dim_, *edit_row_dim_;
+	QAction *delete_column_, *delete_row_;
+	QAction *change_preview_width_;
+	HelperCell *last_context_menu_cell_;
+	int apply_columns_widths_count_;
+	int apply_rows_heights_count_;
+	bool first_activation_;                ///< Boolean used to detect the first display of this widget
+	bool read_only_;                       ///< Boolean stating whether this view allows template edition
+};
+#endif

Added: trunk/sources/titleblock/templatevisualcell.cpp
===================================================================
--- trunk/sources/titleblock/templatevisualcell.cpp	                        (rev 0)
+++ trunk/sources/titleblock/templatevisualcell.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,136 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "templatevisualcell.h"
+#include "titleblocktemplate.h"
+#include "diagramcontext.h"
+
+/**
+	Constructor
+	@param parent Parent QGraphicsItem
+*/
+TitleBlockTemplateVisualCell::TitleBlockTemplateVisualCell(QGraphicsItem *parent) :
+	QGraphicsLayoutItem(),
+	QGraphicsItem(parent),
+	template_(0),
+	cell_(0)
+{
+	setGraphicsItem(this);
+	setFlag(QGraphicsItem::ItemIsSelectable, true);
+	
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateVisualCell::~TitleBlockTemplateVisualCell() {
+}
+
+/**
+	Ensure geometry changes are handled for both QGraphicsObject and
+	QGraphicsLayoutItem.
+	@param g New geometry
+*/
+void TitleBlockTemplateVisualCell::setGeometry(const QRectF &g) {
+	prepareGeometryChange();
+	QGraphicsLayoutItem::setGeometry(g);
+	setPos(g.topLeft());
+}
+
+/**
+	@param which Size hint to be modified
+	@param constraint New value for the size hint
+	@return the size hint for \a which using the width or height of \a constraint
+*/
+QSizeF TitleBlockTemplateVisualCell::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const {
+	Q_UNUSED(which);
+	return constraint;
+}
+
+/**
+	@return the bounding rect of this helper cell
+*/
+QRectF TitleBlockTemplateVisualCell::boundingRect() const {
+	return QRectF(QPointF(0,0), geometry().size());
+}
+
+/**
+	Handles the helper cell visual rendering
+	@param painter QPainter to be used for the rendering
+	@param option Rendering options
+	@param widget QWidget being painted, if any
+*/
+void TitleBlockTemplateVisualCell::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
+	Q_UNUSED(option);
+	Q_UNUSED(widget);
+	
+	QRectF drawing_rectangle(QPointF(0, 0), geometry().size() /*- QSizeF(1, 1)*/);
+	
+	if (template_ && cell_) {
+		template_ -> renderCell(*painter, *cell_, DiagramContext(), drawing_rectangle.toRect());
+	}
+	if (isSelected()) {
+		QBrush selection_brush = QApplication::palette().highlight();
+		QColor selection_color = selection_brush.color();
+		selection_color.setAlpha(127);
+		selection_brush.setColor(selection_color);
+		painter -> setPen(Qt::NoPen);
+		painter -> setBrush(selection_brush);
+		painter -> drawRect(drawing_rectangle/*.adjusted(1, 1, -1, -1)*/);
+	}
+}
+
+/**
+	Set the previewed title block cell.
+	@param tbt Parent title block template of the previewed cell
+	@param cell Previewed cell
+*/
+void TitleBlockTemplateVisualCell::setTemplateCell(TitleBlockTemplate *tbt, TitleBlockCell *cell) {
+	template_ = tbt;
+	cell_     = cell;
+}
+
+/**
+	@return the parent title block template of the previewed cell
+*/
+TitleBlockTemplate *TitleBlockTemplateVisualCell::titleBlockTemplate() const {
+	return(template_);
+}
+
+/**
+	@return the previewed title block cell
+*/
+TitleBlockCell *TitleBlockTemplateVisualCell::cell() const {
+	return(cell_);
+}
+
+/**
+	@return the title block cell previewed by this object, plus the cells it
+	spans over, if any
+*/
+QSet<TitleBlockCell *> TitleBlockTemplateVisualCell::cells() const {
+	QSet<TitleBlockCell *> set;
+	if (cell_) {
+		if (template_) {
+			set = template_ -> spannedCells(cell_);
+		}
+		
+		// the TitleBlockCell rendered by this object
+		set << cell_;
+	}
+	return(set);
+}

Added: trunk/sources/titleblock/templatevisualcell.h
===================================================================
--- trunk/sources/titleblock/templatevisualcell.h	                        (rev 0)
+++ trunk/sources/titleblock/templatevisualcell.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,55 @@
+/*
+    Copyright 2006-2012 Xavier Guerrin
+    This file is part of QElectroTech.
+    
+    QElectroTech is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+    
+    QElectroTech is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_SLASH_QET_TEMPLATE_VISUAL_CELL_H
+#define TITLEBLOCK_SLASH_QET_TEMPLATE_VISUAL_CELL_H
+#include <QtGui>
+#include "qet.h"
+class TitleBlockTemplate;
+#include "titleblockcell.h"
+
+/**
+	This class implements a preview widget for cells that compose a title
+	block template.
+*/
+class TitleBlockTemplateVisualCell : public QGraphicsLayoutItem, public QGraphicsItem {
+	// constructor, destructor
+	public:
+	TitleBlockTemplateVisualCell(QGraphicsItem * parent = 0);
+	virtual ~TitleBlockTemplateVisualCell();
+	private:
+	TitleBlockTemplateVisualCell(const TitleBlockTemplateVisualCell &);
+	
+	// methods
+	public:
+	virtual void setGeometry(const QRectF &);
+	virtual QSizeF sizeHint(Qt::SizeHint, const QSizeF & = QSizeF()) const;
+	virtual QRectF boundingRect() const;
+	void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
+	
+	public slots:
+	void setTemplateCell(TitleBlockTemplate *, TitleBlockCell *);
+	TitleBlockTemplate *titleBlockTemplate() const;
+	TitleBlockCell *cell() const;
+	QSet<TitleBlockCell *> cells() const;
+	
+	// attributes
+	private:
+	TitleBlockTemplate *template_; ///< Title block template of the previewed cell
+	TitleBlockCell *cell_;         ///< Previewed cell
+};
+#endif

Added: trunk/sources/titleblockcell.cpp
===================================================================
--- trunk/sources/titleblockcell.cpp	                        (rev 0)
+++ trunk/sources/titleblockcell.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,275 @@
+#include "titleblockcell.h"
+#include "titleblocktemplate.h"
+/**
+	Constructor
+*/
+TitleBlockCell::TitleBlockCell() {
+	cell_type = TitleBlockCell::EmptyCell;
+	num_row = num_col = -1;
+	row_span = col_span = 0;
+	applied_row_span = applied_col_span = 0;
+	span_state = TitleBlockCell::Enabled;
+	spanner_cell = 0;
+	display_label = true;
+	alignment = Qt::AlignCenter | Qt::AlignVCenter;
+	font_size = 9;
+	hadjust = false;
+	logo_reference = QString("");
+}
+
+/**
+	Destructor
+*/
+TitleBlockCell::~TitleBlockCell() {
+}
+
+/**
+	@return A string representing the titleblock cell
+*/
+QString TitleBlockCell::toString() const {
+	if (cell_type == TitleBlockCell::EmptyCell) return("TitleBlockCell{null}");
+	QString span_desc = (row_span > 0 || col_span > 0) ? QString("+%1,%2").arg(row_span).arg(col_span) : QET::pointerString(spanner_cell);
+	QString base_desc = QString("TitleBlockCell{ [%1, %2] %3 }").arg(num_row).arg(num_col).arg(span_desc);
+	return(base_desc);
+}
+
+/**
+	@return the type of this cell
+*/
+TitleBlockCell::TemplateCellType TitleBlockCell::type() const {
+	return(cell_type);
+}
+
+/**
+	@return the horizontal alignment of this cell
+*/
+int TitleBlockCell::horizontalAlign() const {
+	return(alignment & Qt::AlignHorizontal_Mask);
+}
+
+/**
+	@return the vertical alignment of this cell
+*/
+int TitleBlockCell::verticalAlign() const {
+	return(alignment & Qt::AlignVertical_Mask);
+}
+
+/**
+	Set the new value \a attr_value to the attribute named \a attribute.
+	@param attribute Name of the cell attribute which value is to be changed
+	@param attr_value New value of the changed attribute
+*/
+void TitleBlockCell::setAttribute(const QString &attribute, const QVariant &attr_value) {
+	if (attribute == "type") {
+		int new_type = attr_value.toInt();
+		if (new_type <= TitleBlockCell::LogoCell) {
+			cell_type = static_cast<TitleBlockCell::TemplateCellType>(new_type);
+		}
+	} else if (attribute == "name") {
+		value_name = attr_value.toString();
+	} else if (attribute == "logo") {
+		logo_reference = attr_value.toString();
+	} else if (attribute == "label") {
+		label = qvariant_cast<NamesList>(attr_value);
+	} else if (attribute == "displaylabel") {
+		display_label = attr_value.toBool();
+	} else if (attribute == "value") {
+		value = qvariant_cast<NamesList>(attr_value);
+	} else if (attribute == "alignment") {
+		alignment = attr_value.toInt();
+	} else if (attribute == "fontsize") {
+		font_size = attr_value.toInt();
+	} else if (attribute == "horizontal_adjust") {
+		hadjust = attr_value.toBool();
+	}
+}
+
+/**
+	@param attribute Name of the cell attribute which value is wanted
+	@return the value of the required attribute
+*/
+QVariant TitleBlockCell::attribute(const QString &attribute) {
+	if (attribute == "type") {
+		return(type());
+	} else if (attribute == "name") {
+		return(value_name);
+	} else if (attribute == "logo") {
+		return(logo_reference);
+	} else if (attribute == "label") {
+		return(qVariantFromValue(label));
+	} else if (attribute == "displaylabel") {
+		return(display_label);
+	} else if (attribute == "value") {
+		return(qVariantFromValue(value));
+	} else if (attribute == "alignment") {
+		return(alignment);
+	} else if (attribute == "fontsize") {
+		return(TitleBlockTemplate::fontForCell(*this).pointSizeF());
+	} else if (attribute == "horizontal_adjust") {
+		return(hadjust);
+	}
+	return(QVariant());
+}
+
+/**
+	@param attribute Name of the cell attribute which we want the human, translated name
+	@return the human, translated name for this attribute.
+*/
+QString TitleBlockCell::attributeName(const QString &attribute) {
+	if (attribute == "type") {
+		return(QObject::tr("type", "title block cell property human name"));
+	} else if (attribute == "name") {
+		return(QObject::tr("nom", "title block cell property human name"));
+	} else if (attribute == "logo") {
+		return(QObject::tr("logo", "title block cell property human name"));
+	} else if (attribute == "label") {
+		return(QObject::tr("label", "title block cell property human name"));
+	} else if (attribute == "displaylabel") {
+		return(QObject::tr("affichage du label", "title block cell property human name"));
+	} else if (attribute == "value") {
+		return(QObject::tr("valeur affich\351e", "title block cell property human name"));
+	} else if (attribute == "alignment") {
+		return(QObject::tr("alignement du texte", "title block cell property human name"));
+	} else if (attribute == "fontsize") {
+		return(QObject::tr("taille du texte", "title block cell property human name"));
+	} else if (attribute == "horizontal_adjust") {
+		return(QObject::tr("ajustement horizontal", "title block cell property human name"));
+	}
+	return(QString());
+}
+
+/**
+	@return true if this cell spans over other cells, false otherwise.
+*/
+bool TitleBlockCell::spans() const {
+	return(row_span || col_span);
+}
+
+/**
+	Copy the content of another cell.
+	@param other_cell Another cell
+*/
+void TitleBlockCell::loadContentFromCell(const TitleBlockCell &other_cell) {
+	value_name = other_cell.value_name;
+	cell_type = other_cell.cell_type;
+	logo_reference = other_cell.logo_reference;
+	value = other_cell.value;
+	label = other_cell.label;
+	display_label = other_cell.display_label;
+	font_size = other_cell.font_size;
+	alignment = other_cell.alignment;
+	hadjust = other_cell.hadjust;
+}
+
+/**
+	@param cell_element XML element from which cell content will be read
+*/
+void TitleBlockCell::loadContentFromXml(const QDomElement &cell_element) {
+	// common properties
+	if (cell_element.hasAttribute("name") && !cell_element.attribute("name").isEmpty()) {
+		value_name = cell_element.attribute("name");
+	}
+	
+	// specific properties
+	if (cell_element.tagName() == "logo") {
+		if (cell_element.hasAttribute("resource") && !cell_element.attribute("resource").isEmpty()) {
+			cell_type = TitleBlockCell::LogoCell;
+			logo_reference = cell_element.attribute("resource");
+		}
+	} else if (cell_element.tagName() == "field") {
+		cell_type = TitleBlockCell::TextCell;
+		
+		QHash<QString, QString> names_options;
+		names_options["TagName"] = "translation";
+		
+		names_options["ParentTagName"] = "value";
+		NamesList value_nameslist;
+		value_nameslist.fromXml(cell_element, names_options);
+		if (!value_nameslist.name().isEmpty()) {
+			value = value_nameslist;
+		}
+		
+		names_options["ParentTagName"] = "label";
+		NamesList label_nameslist;
+		label_nameslist.fromXml(cell_element, names_options);
+		if (!label_nameslist.name().isEmpty()) {
+			label = label_nameslist;
+		}
+		
+		if (cell_element.hasAttribute("displaylabel")) {
+			if (cell_element.attribute("displaylabel").compare("false", Qt::CaseInsensitive) == 0) {
+				display_label = false;
+			}
+		}
+		int fontsize;
+		if (QET::attributeIsAnInteger(cell_element, "fontsize", &fontsize)) {
+			font_size = fontsize;
+		} else {
+			font_size = -1;
+		}
+		
+		// horizontal and vertical alignments
+		alignment = 0;
+		
+		QString halignment = cell_element.attribute("align", "left");
+		if (halignment == "right") alignment |= Qt::AlignRight;
+		else if (halignment == "center") alignment |= Qt::AlignHCenter;
+		else alignment |= Qt::AlignLeft;
+		
+		QString valignment = cell_element.attribute("valign", "center");
+		if (valignment == "bottom") alignment |= Qt::AlignBottom;
+		else if (valignment == "top") alignment |= Qt::AlignTop;
+		else alignment |= Qt::AlignVCenter;
+		
+		// horizontal text adjustment
+		hadjust = cell_element.attribute("hadjust", "true") == "true";
+	}
+}
+
+/**
+	@param xml_element XML element to which cell content will be exported
+*/
+void TitleBlockCell::saveContentToXml(QDomElement &cell_elmt) {
+	cell_elmt.setAttribute("name", value_name);
+	
+	if (type() == TitleBlockCell::EmptyCell) {
+		cell_elmt.setTagName("empty");
+	} else if (type() == TitleBlockCell::LogoCell) {
+		cell_elmt.setTagName("logo");
+		cell_elmt.setAttribute("resource", logo_reference);
+	} else {
+		cell_elmt.setTagName("field");
+		
+		QDomDocument parent_document = cell_elmt.ownerDocument();
+		
+		QHash<QString, QString> names_options;
+		names_options["TagName"] = "translation";
+		names_options["ParentTagName"] = "value";
+		cell_elmt.appendChild(value.toXml(parent_document, names_options));
+		names_options["ParentTagName"] = "label";
+		cell_elmt.appendChild(label.toXml(parent_document, names_options));
+		
+		cell_elmt.setAttribute("displaylabel", display_label ? "true" : "false");
+		if (font_size != -1) {
+			cell_elmt.setAttribute("fontsize", font_size);
+		}
+		
+		if (alignment & Qt::AlignRight) {
+			cell_elmt.setAttribute("align", "right");
+		} else if (alignment & Qt::AlignHCenter) {
+			cell_elmt.setAttribute("align", "center");
+		} else {
+			cell_elmt.setAttribute("align", "left");
+		}
+		
+		if (alignment & Qt::AlignBottom) {
+			cell_elmt.setAttribute("valign", "bottom");
+		} else if (alignment & Qt::AlignTop) {
+			cell_elmt.setAttribute("valign", "top");
+		} else {
+			cell_elmt.setAttribute("valign", "center");
+		}
+		
+		if (hadjust) cell_elmt.setAttribute("hadjust", "true");
+	}
+}

Added: trunk/sources/titleblockcell.h
===================================================================
--- trunk/sources/titleblockcell.h	                        (rev 0)
+++ trunk/sources/titleblockcell.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,79 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_CELL_H
+#define TITLEBLOCK_CELL_H
+#include "nameslist.h"
+
+/**
+	This class is a container for the various parameters of a titleblock cell
+	@see TitleBlockColumnLength 
+*/
+class TitleBlockCell {
+	public:
+	enum TemplateCellType {
+		EmptyCell,
+		TextCell,
+		LogoCell
+	};
+	enum TemplateCellSpanState {
+		Disabled,     ///< the cell span parameters should not applied at all
+		Enabled,      ///< the cell span parameters should be applied without restriction
+		Restricted    ///< the cell span parameters should be applied with some restrictions
+	};
+	
+	// Constructor, destructor
+	public:
+	TitleBlockCell();
+	virtual ~TitleBlockCell();
+	
+	// methods
+	public:
+	QString toString() const;
+	TemplateCellType type() const;
+	int horizontalAlign() const;
+	int verticalAlign() const;
+	void setAttribute(const QString &, const QVariant &);
+	QVariant attribute(const QString &);
+	static QString attributeName(const QString &);
+	bool spans() const;
+	void loadContentFromCell(const TitleBlockCell &);
+	void loadContentFromXml(const QDomElement &);
+	void saveContentToXml(QDomElement &);
+	
+	
+	// attributes
+	public:
+	TemplateCellType cell_type;        ///< Cell type: empty, text, logo?
+	int num_row;                       ///< y coordinate of the cell within its parent title block template grid
+	int num_col;                       ///< x coordinate of the cell within its parent title block template grid
+	int row_span;                      ///< number of extra rows spanned by this cell
+	int col_span;                      ///< number of extra columns spanned by this cell
+	int span_state;                    ///< how should row_span and col_span be applied given other cells in the parent template
+	int applied_row_span;              ///< Actually applied row span
+	int applied_col_span;              ///< Actually applied column span
+	TitleBlockCell *spanner_cell;      ///< Cell spanning this cell, if any
+	QString value_name;                ///< name of the cell; not displayed when the title block template is rendered
+	NamesList value;                   ///< Text displayed by the cell
+	NamesList label;                   ///< Label displayed by the cell
+	bool display_label;                ///< Whether to display the label or not
+	int alignment;                     ///< Where the label+text should be displayed within the visual cell
+	int font_size;                     ///< Font size the text should be rendered with
+	bool hadjust;                      ///< Whether to reduce the font size if the text does not fit in the cell
+	QString logo_reference;            ///< Logo displayed by this cell, it it is a logo cell
+};
+#endif

Added: trunk/sources/titleblockproperties.cpp
===================================================================
--- trunk/sources/titleblockproperties.cpp	                        (rev 0)
+++ trunk/sources/titleblockproperties.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,178 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "titleblockproperties.h"
+#include "qet.h"
+
+/**
+	Constructeur. Initialise un objet TitleBlockProperties avec tous les champs
+	vides (date vide + useDate a UseDateValue).
+*/
+TitleBlockProperties::TitleBlockProperties() :
+	date(),
+	useDate(UseDateValue)
+{
+}
+
+/**
+	Destructeur
+*/
+TitleBlockProperties::~TitleBlockProperties() {
+}
+
+/**
+	@param ip autre conteneur TitleBlockProperties
+	@return true si ip et ce conteneur sont identiques, false sinon
+*/
+bool TitleBlockProperties::operator==(const TitleBlockProperties &ip) {
+	return(
+		ip.title == title &&\
+		ip.author == author &&\
+		ip.date == date &&\
+		ip.filename == filename &&\
+		ip.folio == folio &&\
+		ip.template_name == template_name &&\
+		ip.context == context
+	);
+}
+
+/**
+	@param ip autre conteneur TitleBlockProperties
+	@return false si ip et ce conteneur sont identiques, true sinon
+*/
+bool TitleBlockProperties::operator!=(const TitleBlockProperties &ip) {
+	return(!(*this == ip));
+}
+
+
+/**
+	Exporte le cartouche sous formes d'attributs XML ajoutes a l'element e.
+	@param e Element XML auquel seront ajoutes des attributs
+*/
+void TitleBlockProperties::toXml(QDomElement &e) const {
+	e.setAttribute("author",   author);
+	e.setAttribute("title",    title);
+	e.setAttribute("filename", filename);
+	e.setAttribute("folio",    folio);
+	e.setAttribute("date",     exportDate());
+	if (!template_name.isEmpty()) {
+		e.setAttribute("titleblocktemplate", template_name);
+	}
+	
+	if (context.keys().count()) {
+		QDomElement properties = e.ownerDocument().createElement("properties");
+		context.toXml(properties);
+		e.appendChild(properties);
+	}
+}
+
+/**
+	Importe le cartouche a partir des attributs XML de l'element e
+	@param e Element XML dont les attributs seront lus
+*/
+void TitleBlockProperties::fromXml(const QDomElement &e) {
+	// reads the historical fields
+	if (e.hasAttribute("author"))      author   = e.attribute("author");
+	if (e.hasAttribute("title"))       title    = e.attribute("title");
+	if (e.hasAttribute("filename"))    filename = e.attribute("filename");
+	if (e.hasAttribute("folio"))       folio    = e.attribute("folio");
+	if (e.hasAttribute("date"))        setDateFromString(e.attribute("date"));
+	
+	// reads the template used to render the title block
+	if (e.hasAttribute("titleblocktemplate")) template_name = e.attribute("titleblocktemplate");
+	
+	// reads the additional fields used to fill the title block
+	context.clear();
+	foreach (QDomElement e, QET::findInDomElement(e, "properties")) {
+		context.fromXml(e);
+	}
+}
+
+/**
+	Exporte le cartouche dans une configuration.
+	@param settings Parametres a ecrire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void TitleBlockProperties::toSettings(QSettings &settings, const QString &prefix) const {
+	settings.setValue(prefix + "title",    title);
+	settings.setValue(prefix + "author",   author);
+	settings.setValue(prefix + "filename", filename);
+	settings.setValue(prefix + "folio",    folio);
+	settings.setValue(prefix + "date",     exportDate());
+	context.toSettings(settings, prefix + "properties");
+}
+
+/**
+	Importe le cartouche depuis une configuration.
+	@param settings Parametres a lire
+	@param prefix prefixe a ajouter devant les noms des parametres
+*/
+void TitleBlockProperties::fromSettings(QSettings &settings, const QString &prefix) {
+	title    = settings.value(prefix + "title").toString();
+	author   = settings.value(prefix + "author").toString();
+	filename = settings.value(prefix + "filename").toString();
+	folio    = settings.value(prefix + "folio", "%id/%total").toString();
+	setDateFromString(settings.value(prefix + "date").toString());
+	context.fromSettings(settings, prefix + "properties");
+}
+
+/**
+	@return La date a utiliser
+*/
+QDate TitleBlockProperties::finalDate() const {
+	if (useDate == UseDateValue) {
+		return(date);
+	} else {
+		return(QDate::currentDate());
+	}
+}
+
+/**
+	@return une chaine de caracteres decrivant comment gerer la date dans le
+	cartouche : la chaine peut valoir :
+	  * null pour ne pas afficher de date
+	  * now pour afficher la date courante (a la creation du schema)
+	  * une date au format yyyyMMdd pour utiliser une date fixe
+*/
+QString TitleBlockProperties::exportDate() const {
+	QString date_setting_value;
+	if (useDate == UseDateValue) {
+		if (date.isNull()) date_setting_value = "null";
+		else date_setting_value = date.toString("yyyyMMdd");
+	} else {
+		date_setting_value = "now";
+	}
+	return(date_setting_value);
+}
+
+/**
+	Charge les attributs date et useDate a partir d'une chaine de caracteres.
+	@param date_string Chaine de caracteres a analyser
+	@see exportDate
+*/
+void TitleBlockProperties::setDateFromString(const QString &date_string) {
+	if (date_string == "now") {
+		date = QDate::currentDate();
+		useDate = CurrentDate;
+	} else if (date_string.isEmpty() || date_string == "null") {
+		date = QDate();
+		useDate = UseDateValue;
+	} else {
+		date = QDate::fromString(date_string, "yyyyMMdd");
+		useDate = UseDateValue;
+	}
+}

Added: trunk/sources/titleblockproperties.h
===================================================================
--- trunk/sources/titleblockproperties.h	                        (rev 0)
+++ trunk/sources/titleblockproperties.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,62 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_PROPERTIES_H
+#define TITLEBLOCK_PROPERTIES_H
+#include <QtCore>
+#include <QtXml>
+#include "diagramcontext.h"
+/**
+	This class provides a container for the properties of a particular title
+	block, i.e. title, author, date, filename, folio, template, custom
+	properties, ...
+*/
+class TitleBlockProperties {
+	public:
+	TitleBlockProperties();
+	virtual ~TitleBlockProperties();
+	/// Lists the various ways to handle the date
+	enum DateManagement {
+		UseDateValue, ///< use the date attribute
+		CurrentDate   ///< use the current date
+	};
+	
+	bool operator==(const TitleBlockProperties &);
+	bool operator!=(const TitleBlockProperties &);
+	
+	void toXml(QDomElement &) const;
+	void fromXml(const QDomElement &);
+	void toSettings(QSettings &, const QString & = QString()) const;
+	void fromSettings(QSettings &, const QString & = QString());
+	
+	QDate finalDate() const ;
+	
+	// attributes
+	QString title;            ///< Folio title (displayed by the default template)
+	QString author;           ///< Author of the diagram/folio (displayed by the default template)
+	QDate date;               ///< Date (displayed by the default template)
+	QString filename;         ///< Filename (displayed by the default template)
+	QString folio;            ///< Folio information (displayed by the default template)
+	DateManagement useDate;   ///< Wheter to use the date attribute
+	QString template_name;    ///< Name of the template used to render the title block - an empty string means "the default template provided by the application"
+	DiagramContext context;   ///< Container for the additional, user-defined fields
+	
+	private:
+	QString exportDate() const;
+	void setDateFromString(const QString &);
+};
+#endif

Added: trunk/sources/titleblockpropertieswidget.cpp
===================================================================
--- trunk/sources/titleblockpropertieswidget.cpp	                        (rev 0)
+++ trunk/sources/titleblockpropertieswidget.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,439 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "titleblockpropertieswidget.h"
+#include "diagramcontextwidget.h"
+#include "qeticons.h"
+#include "templatescollection.h"
+#include "titleblocktemplate.h"
+
+/**
+	Constructeur
+	@param titleblock TitleBlockProperties a afficher
+	@param current true pour afficher l'option "Date courante"
+	@param parent QWidget parent
+*/
+TitleBlockPropertiesWidget::TitleBlockPropertiesWidget(const TitleBlockProperties &titleblock, bool current, QWidget *parent) :
+	QWidget(parent),
+	display_current_date(false),
+	tbt_collection_(0)
+{
+	initWidgets(titleblock);
+	initLayouts();
+	connect(tabbar, SIGNAL(currentChanged(int)), stack_layout, SLOT(setCurrentIndex(int)));
+
+	titleblock_current_date -> setVisible(display_current_date = current);
+	setTitleBlockProperties(titleblock);
+
+	// by default, we do not display the template combo box
+	titleblock_template_label -> setVisible(false);
+	titleblock_template_name  -> setVisible(false);
+	titleblock_template_button_ -> setVisible(false);
+}
+
+/// Destructeur
+TitleBlockPropertiesWidget::~TitleBlockPropertiesWidget() {
+}
+
+/**
+	@return Les proprietes affichees par le widget
+*/
+TitleBlockProperties TitleBlockPropertiesWidget::titleBlockProperties() const {
+	TitleBlockProperties prop;
+	prop.title    = titleblock_title -> text();
+	prop.author   = titleblock_author -> text();
+	prop.filename = titleblock_filename -> text();
+	prop.folio    = titleblock_folio -> text();
+	if (titleblock_no_date -> isChecked()) {
+		prop.useDate = TitleBlockProperties::UseDateValue;
+		prop.date = QDate();
+	} else if (titleblock_fixed_date -> isChecked()) {
+		prop.useDate = TitleBlockProperties::UseDateValue;
+		prop.date = titleblock_date -> date();
+	} else if (display_current_date && titleblock_current_date -> isChecked()) {
+		prop.useDate = TitleBlockProperties::CurrentDate;
+		prop.date = QDate::currentDate();
+	}
+
+	QString current_template_name = currentTitleBlockTemplateName();
+	if (!current_template_name.isEmpty()) prop.template_name = current_template_name;
+
+	prop.context = additional_fields_ -> context();
+
+	return(prop);
+}
+
+/**
+	Specifie les proprietes que le widget doit afficher
+	@param titleblock nouvelles proprietes affichees par le widget
+*/
+void TitleBlockPropertiesWidget::setTitleBlockProperties(const TitleBlockProperties &titleblock) {
+	titleblock_title    -> setText(titleblock.title);
+	titleblock_author   -> setText(titleblock.author);
+	titleblock_filename -> setText(titleblock.filename);
+	titleblock_folio    -> setText(titleblock.folio);
+	if (display_current_date) {
+		if (titleblock.useDate == TitleBlockProperties::CurrentDate) {
+			titleblock_current_date -> setChecked(true);
+		} else {
+			if (titleblock.date.isNull()) {
+				titleblock_no_date -> setChecked(true);
+			} else {
+				titleblock_fixed_date -> setChecked(true);
+				titleblock_date -> setDate(titleblock.date);
+			}
+		}
+	} else {
+		if (titleblock.useDate == TitleBlockProperties::CurrentDate) {
+			titleblock_fixed_date -> setChecked(true);
+			titleblock_date -> setDate(QDate::currentDate());
+		} else {
+			if (titleblock.date.isNull()) {
+				titleblock_no_date -> setChecked(true);
+			} else {
+				titleblock_fixed_date -> setChecked(true);
+				titleblock_date -> setDate(titleblock.date);
+			}
+		}
+	}
+
+	if (!titleblock.template_name.isEmpty()) {
+		int matching_index = titleblock_template_name -> findData(titleblock.template_name);
+		if (matching_index != -1) {
+			titleblock_template_name -> setCurrentIndex(matching_index);
+		}
+	}
+
+	setDiagramContext(titleblock.context);
+}
+
+/**
+	Clear the custom variables list.
+*/
+void TitleBlockPropertiesWidget::clearDiagramContext() {
+	additional_fields_ -> clear();
+}
+
+/**
+	Clear the custom variables table then add the key/value pairs from \a context to it.
+*/
+void TitleBlockPropertiesWidget::setDiagramContext(const DiagramContext &context) {
+	additional_fields_ -> setContext(context);
+}
+
+/**
+	@return true si le widget affiche la proposition "Date courante", false sinon
+*/
+bool TitleBlockPropertiesWidget::displayCurrentDate() const {
+	return(display_current_date);
+}
+
+/**
+	@return true si ce widget est en lecture seule, false sinon
+*/
+bool TitleBlockPropertiesWidget::isReadOnly() const {
+	return(titleblock_title -> isReadOnly());
+}
+
+/**
+	@param ro true pour passer ce widget en lecture seule, false sinon
+*/
+void TitleBlockPropertiesWidget::setReadOnly(bool ro) {
+	titleblock_title        -> setReadOnly(ro);
+	titleblock_author       -> setReadOnly(ro);
+	titleblock_date         -> setReadOnly(ro);
+	titleblock_filename     -> setReadOnly(ro);
+	titleblock_folio        -> setReadOnly(ro);
+	titleblock_no_date      -> setDisabled(ro);
+	titleblock_current_date -> setDisabled(ro);
+	titleblock_fixed_date   -> setDisabled(ro);
+	titleblock_template_name -> setDisabled(ro);
+	additional_fields_       -> setDisabled(ro);
+}
+
+/**
+	@param templates List of template names the dedicated combo box should
+	display.
+*/
+void TitleBlockPropertiesWidget::setTitleBlockTemplatesList(const QList<QString> &templates) {
+	titleblock_template_name -> clear();
+	titleblock_template_name -> addItem(QET::Icons::TitleBlock, tr("Mod\350le par d\351faut"), QString());
+	foreach (QString template_name, templates) {
+		titleblock_template_name -> addItem(QET::Icons::TitleBlock, template_name, template_name);
+	}
+}
+
+/**
+	@param tbt_collection Collection from which title block templates should be read.
+*/
+void TitleBlockPropertiesWidget::setTitleBlockTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection) {
+	if (!tbt_collection) return;
+	if (tbt_collection_ && tbt_collection != tbt_collection_) {
+		// forget any connection with the previous collection
+		disconnect(tbt_collection_, 0, this, 0);
+	}
+
+	tbt_collection_ = tbt_collection;
+	updateTemplateList();
+	connect(tbt_collection_, SIGNAL(changed(TitleBlockTemplatesCollection*, QString)), this, SLOT(updateTemplateList()));
+}
+
+/**
+	@param visible true to display the title block templates list, false to
+	hide it.
+*/
+void TitleBlockPropertiesWidget::setTitleBlockTemplatesVisible(bool visible) {
+	titleblock_template_name  -> setVisible(visible);
+	titleblock_template_label -> setVisible(visible);
+	titleblock_template_button_ -> setVisible(visible);
+}
+
+/**
+	@return the name of the currenlty selected title block template.
+*/
+QString TitleBlockPropertiesWidget::currentTitleBlockTemplateName() const {
+	int index = titleblock_template_name -> currentIndex();
+	if (index != -1) {
+		return(titleblock_template_name -> itemData(index).toString());
+	}
+	return(QString());
+}
+
+/**
+	Set the currently selected title block template.
+	@param template_name Template to be selected
+*/
+void TitleBlockPropertiesWidget::setCurrentTitleBlockTemplateName(const QString &template_name) {
+	int matching_index = titleblock_template_name -> findData(template_name);
+	if (matching_index != -1) {
+		titleblock_template_name -> setCurrentIndex(matching_index);
+	}
+}
+
+/**
+	Update the title block templates list.
+*/
+void TitleBlockPropertiesWidget::updateTemplateList() {
+	if (!tbt_collection_) return;
+
+	QString current_template_name = currentTitleBlockTemplateName();
+	setTitleBlockTemplatesList(tbt_collection_ -> templates());
+	setCurrentTitleBlockTemplateName(current_template_name);
+}
+
+/**
+	Edit the currently selected title block template
+*/
+void TitleBlockPropertiesWidget::editCurrentTitleBlockTemplate() {
+	emit(editTitleBlockTemplate(currentTitleBlockTemplateName(), false));
+}
+
+/**
+	Duplicate the currently selected title block template (the user is asked
+	for a name), then edit it.
+*/
+void TitleBlockPropertiesWidget::duplicateCurrentTitleBlockTemplate() {
+	emit(editTitleBlockTemplate(currentTitleBlockTemplateName(), true));
+}
+
+/**
+ * @brief changeCurrentTitleBlockTemplate load the fields of the template to additional_fields_
+ * @param text
+ */
+void TitleBlockPropertiesWidget::changeCurrentTitleBlockTemplate(QString text) {
+	// delete all entry
+	additional_fields_ -> clear() ;
+	// get template
+	TitleBlockTemplate *tpl = tbt_collection_ -> getTemplate( text );
+	if(tpl != 0) {
+		// get all template fields
+		QStringList fields = tpl -> listOfVariables();
+		// set fields to additional_fields_ widget
+		DiagramContext templateContext;
+		for(int i =0; i<fields.count(); i++)
+			templateContext.addValue(fields.at(i), "");
+		setDiagramContext(templateContext);
+	}
+}
+
+/**
+	Builds the various child widgets for this widget
+*/
+void TitleBlockPropertiesWidget::initWidgets(const TitleBlockProperties &titleblock) {
+	// actions
+	titleblock_template_edit_ = new QAction(tr("\311diter ce mod\350le", "menu entry"), this);
+	titleblock_template_duplicate_ = new QAction(tr("Dupliquer et editer ce mod\350le", "menu entry"), this);
+
+	connect(titleblock_template_edit_, SIGNAL(triggered()), this, SLOT(editCurrentTitleBlockTemplate()));
+	connect(titleblock_template_duplicate_, SIGNAL(triggered()), this, SLOT(duplicateCurrentTitleBlockTemplate()));
+
+	// menu
+	titleblock_template_menu_ = new QMenu(tr("Title block templates actions"));
+	titleblock_template_menu_ -> addAction(titleblock_template_edit_);
+	titleblock_template_menu_ -> addAction(titleblock_template_duplicate_);
+
+	// widgets
+	titleblock_template_label = new QLabel(tr("Mod\350le :"), this);
+	titleblock_template_name = new QComboBox(this);
+	connect(titleblock_template_name, SIGNAL(currentIndexChanged(QString)),this, SLOT(changeCurrentTitleBlockTemplate(QString)) );
+	titleblock_template_button_ = new QPushButton(QET::Icons::TitleBlock, QString());
+	titleblock_template_button_ -> setMenu(titleblock_template_menu_);
+
+	titleblock_title    = new QLineEdit(this);
+	titleblock_author   = new QLineEdit(this);
+	titleblock_filename = new QLineEdit(this);
+
+	titleblock_folio = new QLineEdit(this);
+	folio_tip = new QLabel(
+		tr(
+			"Les variables suivantes sont utilisables dans le champ Folio :\n"
+			"  - %id : num\351ro du sch\351ma courant dans le projet\n"
+			"  - %total : nombre total de sch\351mas dans le projet"
+		)
+	);
+	folio_tip -> setWordWrap(true);
+
+	QButtonGroup *date_policy_group = new QButtonGroup(this);
+	titleblock_no_date = new QRadioButton(tr("Pas de date"), this);
+	titleblock_current_date = new QRadioButton(tr("Date courante"), this);
+	titleblock_fixed_date = new QRadioButton(tr("Date fixe : "), this);
+	date_policy_group -> addButton(titleblock_no_date);
+	date_policy_group -> addButton(titleblock_current_date);
+	date_policy_group -> addButton(titleblock_fixed_date);
+	titleblock_date = new QDateEdit(QDate::currentDate(), this);
+	titleblock_date -> setEnabled(titleblock_fixed_date -> isChecked());
+	titleblock_current_date -> setVisible(display_current_date);
+	connect(titleblock_fixed_date, SIGNAL(toggled(bool)), titleblock_date, SLOT(setEnabled(bool)));
+	titleblock_date -> setCalendarPopup(true);
+
+	titleblock_date_now = new QPushButton (QET::Icons::Start, "", this);
+	titleblock_date_now -> setEnabled(titleblock_fixed_date -> isChecked());
+	titleblock_date_now -> setIconSize(QSize(16, 16));
+	connect(titleblock_fixed_date, SIGNAL(toggled(bool)), titleblock_date_now, SLOT(setEnabled(bool)));
+	connect(titleblock_date_now, SIGNAL(clicked()), this, SLOT(setDate_now()));
+
+	// we add a bunch of tooltips for users to know how they can put these
+	// values into their title block templates
+	QString variable_tooltip = tr("Disponible en tant que %1 pour les mod\350les de cartouches.");
+	titleblock_title -> setToolTip(QString(variable_tooltip).arg("%title"));
+	titleblock_author -> setToolTip(QString(variable_tooltip).arg("%author"));
+	titleblock_filename -> setToolTip(QString(variable_tooltip).arg("%filename"));
+	titleblock_folio -> setToolTip(QString(variable_tooltip).arg("%folio"));
+	QString date_variable_tooltip = QString(variable_tooltip).arg("%date");
+	titleblock_current_date -> setToolTip(date_variable_tooltip);
+	titleblock_fixed_date -> setToolTip(date_variable_tooltip);
+	titleblock_date -> setToolTip(date_variable_tooltip);
+	titleblock_date_now -> setToolTip(tr("Appliquer la date actuelle"));
+	folio_tip -> setToolTip(tr("%id et %total sont disponibles en tant que %{folio-id} et %{folio-total} (respectivement) pour les mod\350les de cartouches."));
+
+	// widgets for users to enter their own name/value pairs
+	additional_fields_label = new QLabel(
+		tr(
+			"Vous pouvez d\351finir ici vos propres associations noms/valeurs pour"
+			" que le cartouche en tienne compte. Exemple : associer le nom "
+			"\"volta\" et la valeur \"1745\" remplacera %{volta} par 1745 dans le "
+			"cartouche."
+		)
+	);
+	additional_fields_label -> setWordWrap(true);
+	additional_fields_label -> setAlignment(Qt::AlignJustify);
+	additional_fields_ = new DiagramContextWidget();
+	additional_fields_ -> setContext(titleblock.context);
+
+	tabbar = new QTabBar(this);
+	tabbar -> addTab(tr("Principales"));
+	tabbar -> addTab(tr("Personnalis\351es"));
+	tabbar -> setShape(QTabBar::RoundedSouth);
+}
+
+/**
+	Builds the various layouts for this widget
+*/
+void TitleBlockPropertiesWidget::initLayouts() {
+	// layouts for tab #0
+	QGridLayout *layout_date = new QGridLayout();
+	layout_date -> addWidget(titleblock_no_date,      0, 0);
+	layout_date -> addWidget(titleblock_current_date, 1, 0);
+	layout_date -> addWidget(titleblock_fixed_date,   2, 0);
+	layout_date -> addWidget(titleblock_date,         2, 1);
+	layout_date -> addWidget(titleblock_date_now,      2, 2);
+	layout_date -> setColumnStretch(0, 1);
+	layout_date -> setColumnStretch(1, 500);
+
+	QWidget *widget_main_fields = new QWidget(this);
+	QGridLayout *layout_main_fields = new QGridLayout(widget_main_fields);
+	layout_main_fields -> addWidget(new QLabel(tr("Titre : ")),   0, 0);
+	layout_main_fields -> addWidget(titleblock_title,             0, 1);
+	layout_main_fields -> addWidget(new QLabel(tr("Auteur : ")),  1, 0);
+	layout_main_fields -> addWidget(titleblock_author,            1, 1);
+	layout_main_fields -> addWidget(new QLabel(tr("Date : ")),    2, 0, Qt::AlignTop);
+	layout_main_fields -> addLayout(layout_date,                  2, 1);
+	layout_main_fields -> addWidget(new QLabel(tr("Fichier : ")), 3, 0);
+	layout_main_fields -> addWidget(titleblock_filename,          3, 1);
+	layout_main_fields -> addWidget(new QLabel(tr("Folio : ")),   4, 0);
+	layout_main_fields -> addWidget(titleblock_folio,             4, 1);
+	layout_main_fields -> addWidget(folio_tip,                    5, 1, Qt::AlignTop);
+	layout_main_fields -> setContentsMargins(0, 0, 0, 0);
+	layout_main_fields -> setRowStretch(5, 500);
+
+	// layouts for tab #1
+	QWidget *widget_user_fields = new QWidget(this);
+	QVBoxLayout *layout_user_fields = new QVBoxLayout(widget_user_fields);
+	layout_user_fields -> addWidget(additional_fields_label);
+	layout_user_fields -> addWidget(additional_fields_);
+	layout_user_fields -> setContentsMargins(0, 0, 0, 0);
+
+	// stacked layout
+	stack_layout = new QStackedLayout();
+	stack_layout -> addWidget(widget_main_fields);
+	stack_layout -> addWidget(widget_user_fields);
+	stack_layout -> setContentsMargins(0, 0, 0, 0);
+	stack_layout -> setCurrentIndex(0);
+
+	// template layout
+	QHBoxLayout *template_layout = new QHBoxLayout();
+	template_layout -> addWidget(titleblock_template_label);
+	template_layout -> addWidget(titleblock_template_name);
+	template_layout -> addWidget(titleblock_template_button_);
+	template_layout -> setStretch(0, 1);
+	template_layout -> setStretch(1, 500);
+
+	// groupbox layout
+	QVBoxLayout *groupbox_layout = new QVBoxLayout();
+	groupbox_layout -> addLayout(template_layout);
+	groupbox_layout -> addLayout(stack_layout);
+	groupbox_layout -> addWidget(tabbar);
+
+	// groupbox
+	QGroupBox *titleblock_infos = new QGroupBox(tr("Informations du cartouche"), this);
+	titleblock_infos -> setLayout(groupbox_layout);
+	titleblock_infos -> setMinimumSize(300, 330);
+
+	// widget layout
+	QVBoxLayout *this_layout = new QVBoxLayout();
+	this_layout -> setContentsMargins(0, 0, 0, 0);
+	this_layout -> addWidget(titleblock_infos);
+	setLayout(this_layout);
+}
+
+/**
+	Set the current Date to the combo Date
+  */
+void TitleBlockPropertiesWidget::setDate_now(){
+	titleblock_date -> setDate( QDate::currentDate() );
+}
+

Added: trunk/sources/titleblockpropertieswidget.h
===================================================================
--- trunk/sources/titleblockpropertieswidget.h	                        (rev 0)
+++ trunk/sources/titleblockpropertieswidget.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,90 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_PROPERTIES_WIDGET_H
+#define TITLEBLOCK_PROPERTIES_WIDGET_H
+#include <QtGui>
+#include "titleblockproperties.h"
+class DiagramContextWidget;
+class TitleBlockTemplatesCollection;
+/**
+	This widget enable users to edit the properties of a title block.
+*/
+class TitleBlockPropertiesWidget : public QWidget {
+	Q_OBJECT
+	// constructors, destructor
+	public:
+	TitleBlockPropertiesWidget(const TitleBlockProperties &titleblock = TitleBlockProperties(), bool = false, QWidget * = 0);
+	virtual ~TitleBlockPropertiesWidget();
+	private:
+	TitleBlockPropertiesWidget(const TitleBlockPropertiesWidget &);
+
+	// methods
+	public:
+	TitleBlockProperties titleBlockProperties() const;
+	void setTitleBlockProperties(const TitleBlockProperties &);
+	void clearDiagramContext();
+	void setDiagramContext(const DiagramContext &);
+	bool displayCurrentDate() const;
+	bool isReadOnly() const;
+	void setReadOnly(bool);
+	void setTitleBlockTemplatesList(const QList<QString> &);
+	void setTitleBlockTemplatesCollection(TitleBlockTemplatesCollection *);
+	void setTitleBlockTemplatesVisible(bool);
+	QString currentTitleBlockTemplateName() const;
+	void setCurrentTitleBlockTemplateName(const QString &);
+
+	private slots:
+	void updateTemplateList();
+	void editCurrentTitleBlockTemplate();
+	void duplicateCurrentTitleBlockTemplate();
+	void changeCurrentTitleBlockTemplate(QString text);
+	void setDate_now();
+
+	private:
+	void initWidgets(const TitleBlockProperties &);
+	void initLayouts();
+
+	signals:
+	void editTitleBlockTemplate(const QString &, bool);
+
+	// attributes
+	private:
+	QStackedLayout *stack_layout;
+	QLabel       *titleblock_template_label;
+	QComboBox    *titleblock_template_name;
+	QPushButton  *titleblock_template_button_;
+	QMenu        *titleblock_template_menu_;
+	QAction      *titleblock_template_edit_;
+	QAction      *titleblock_template_duplicate_;
+	QLineEdit    *titleblock_title;
+	QLineEdit    *titleblock_author;
+	QDateEdit    *titleblock_date;
+	QPushButton  *titleblock_date_now;
+	QLineEdit    *titleblock_filename;
+	QLineEdit    *titleblock_folio;
+	QLabel       *folio_tip;
+	QRadioButton *titleblock_no_date;
+	QRadioButton *titleblock_current_date;
+	QRadioButton *titleblock_fixed_date;
+	bool display_current_date;
+	QLabel       *additional_fields_label;
+	DiagramContextWidget *additional_fields_;
+	QTabBar      *tabbar;
+	TitleBlockTemplatesCollection *tbt_collection_;
+};
+#endif

Added: trunk/sources/titleblocktemplate.cpp
===================================================================
--- trunk/sources/titleblocktemplate.cpp	                        (rev 0)
+++ trunk/sources/titleblocktemplate.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,1543 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "titleblocktemplate.h"
+#include "qet.h"
+#include "qetapp.h"
+#include "nameslist.h"
+// uncomment the line below to get more debug information
+//#define TITLEBLOCK_TEMPLATE_DEBUG
+
+/**
+	Constructor
+	@param parent parent QObject
+*/
+TitleBlockTemplate::TitleBlockTemplate(QObject *parent) :
+	QObject(parent)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplate::~TitleBlockTemplate() {
+	loadLogos(QDomElement(), true);
+	qDeleteAll(registered_cells_);
+}
+
+/**
+	Create a new cell and associate it with this template, which means that it
+	will be deleted when this template is destroyed.
+	@param existing_cell (optional) An existing cell that will be copied
+	@return A pointer to the newly created cell
+*/
+TitleBlockCell *TitleBlockTemplate::createCell(const TitleBlockCell *existing_cell) {
+	TitleBlockCell *new_cell = existing_cell ? new TitleBlockCell(*existing_cell) : new TitleBlockCell();
+	registered_cells_ << new_cell;
+	return(new_cell);
+}
+
+/**
+	@param count Number of cells expected in the list
+	@return a list containing count newly created (and registered) cells
+	@see createCell()
+*/
+QList<TitleBlockCell *> TitleBlockTemplate::createCellsList(int count) {
+	QList<TitleBlockCell *> new_list;
+	for (int i = 0 ; i < count ; ++ i) new_list << createCell();
+	return(new_list);
+}
+
+/**
+	@param cell An existing cell
+	@return The font that should be used to render this cell according to its properties.
+*/
+QFont TitleBlockTemplate::fontForCell(const TitleBlockCell &cell) {
+	return(QETApp::diagramTextsFont(cell.font_size));
+}
+
+/**
+	Load a titleblock template from an XML file.
+	@param filepath A file path to read the template from.
+	@return true if the reading succeeds, false otherwise.
+*/
+bool TitleBlockTemplate::loadFromXmlFile(const QString &filepath) {
+	// open the file
+	QFile template_file(filepath);
+	if (!template_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+		return(false);
+	}
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << filepath << "opened";
+#endif
+	
+	// parse its content as XML
+	QDomDocument xml_doc;
+	bool xml_parsing = xml_doc.setContent(&template_file);
+	if (!xml_parsing) {
+		return(false);
+	}
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << filepath << "opened and parsed";
+#endif
+	return(loadFromXmlElement(xml_doc.documentElement()));
+}
+
+/**
+	@param xml_element An XML document to read the template from.
+	@return true if the reading succeeds, false otherwise.
+*/
+bool TitleBlockTemplate::loadFromXmlElement(const QDomElement &xml_element) {
+	// we expect the XML element to be an <titleblocktemplate>
+	if (xml_element.tagName() != "titleblocktemplate") {
+		return(false);
+	}
+	if (!xml_element.hasAttribute("name")) {
+		return(false);
+	}
+	name_ = xml_element.attribute("name");
+	
+	loadInformation(xml_element);
+	loadLogos(xml_element, true);
+	loadGrid(xml_element);
+	
+	return(true);
+}
+
+/**
+	Save the title block template into an XML file.
+	@param filepath The file path this title block template should be saved to.
+	@return true if the operation succeeds, false otherwise
+*/
+bool TitleBlockTemplate::saveToXmlFile(const QString &filepath) {
+	if (filepath.isEmpty()) return(false);
+	
+	// generate the XML document
+	QDomDocument doc;
+	QDomElement e = doc.createElement("root");
+	bool saving = saveToXmlElement(e);
+	if (!saving) return(false);
+	doc.appendChild(e);
+	
+	return(QET::writeXmlFile(doc, filepath));
+}
+
+/**
+	Save the title block template as XML.
+	@param xml_element The XML element this title block template should be saved to.
+	@return true if the export succeeds, false otherwise
+*/
+bool TitleBlockTemplate::saveToXmlElement(QDomElement &xml_element) const {
+	// we are supposed to have at least a name
+	if (name_.isEmpty()) return(false);
+	
+	xml_element.setTagName("titleblocktemplate");
+	xml_element.setAttribute("name", name_);
+	saveInformation(xml_element);
+	saveLogos(xml_element);
+	saveGrid(xml_element);
+	return(true);
+}
+
+/**
+	@param xml_element Parent XML element to be used when exporting \a cell
+	@param cell Cell to export
+*/
+void TitleBlockTemplate::exportCellToXml(TitleBlockCell *cell, QDomElement &xml_element) const {
+	saveCell(cell, xml_element, true);
+}
+
+/**
+	@return a deep copy of the current title block template (i.e. title block
+	cells are duplicated too and associated with their parent template).
+*/
+TitleBlockTemplate *TitleBlockTemplate::clone() const {
+	TitleBlockTemplate *copy = new TitleBlockTemplate();
+	copy -> name_ = name_;
+	copy -> information_ = information_;
+	
+	// this does not really duplicates pixmaps, only the objects that hold a key to the implicitly shared pixmaps
+	foreach (QString logo_key, bitmap_logos_.keys()) {
+		copy -> bitmap_logos_[logo_key] = QPixmap(bitmap_logos_[logo_key]);
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+		qDebug() << Q_FUNC_INFO << "copying " << bitmap_logos_[logo_key] -> cacheKey() << "to" << copy -> bitmap_logos_[logo_key] -> cacheKey();
+#endif
+	}
+	
+	// we have to create new QSvgRenderer objects from the data (no copy constructor)
+	foreach (QString logo_key, vector_logos_.keys()) {
+		copy -> vector_logos_[logo_key] = new QSvgRenderer(data_logos_[logo_key]);
+	}
+	
+	copy -> data_logos_    = data_logos_;
+	copy -> storage_logos_ = storage_logos_;
+	copy -> type_logos_    = type_logos_;
+	copy -> rows_heights_  = rows_heights_;
+	copy -> columns_width_ = columns_width_;
+	
+	// copy cells basically
+	copy -> cells_ = cells_;
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			copy -> cells_[i][j] = copy -> createCell(cells_[i][j]);
+		}
+	}
+	
+	// ensure the copy has no spanner_cell attribute pointing to a cell from the original object
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			TitleBlockCell *current_cell = copy -> cells_[i][j];
+			if (TitleBlockCell *original_cell = current_cell -> spanner_cell) {
+				int original_cell_row = original_cell -> num_row;
+				int original_cell_col = original_cell -> num_col;
+				TitleBlockCell *copy_cell = copy -> cells_[original_cell_col][original_cell_row];
+				current_cell -> spanner_cell = copy_cell;
+			}
+		}
+	}
+	
+	return(copy);
+}
+
+/**
+	Import text informations from a given XML title block template.
+*/
+void TitleBlockTemplate::loadInformation(const QDomElement &xml_element) {
+	for (QDomNode n = xml_element.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
+		if (n.isElement() && n.toElement().tagName() == "information") {
+			setInformation(n.toElement().text());
+		}
+	}
+}
+
+/**
+	Import the logos from a given XML titleblock template.
+	@param xml_element An XML element representing an titleblock template.
+	@param reset true to delete all previously known logos before, false
+	otherwise.
+	@return true if the reading succeeds, false otherwise.
+*/
+bool TitleBlockTemplate::loadLogos(const QDomElement &xml_element, bool reset) {
+	if (reset) {
+		qDeleteAll(vector_logos_.begin(), vector_logos_.end());
+		vector_logos_.clear();
+		
+		// Note: QPixmap are only a key to access the implicitly shared pixmap
+		bitmap_logos_.clear();
+		
+		data_logos_.clear();
+		storage_logos_.clear();
+	}
+	
+	// we look for //logos/logo elements
+	for (QDomNode n = xml_element.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
+		if (n.isElement() && n.toElement().tagName() == "logos") {
+			for (QDomNode p = n.firstChild() ; !p.isNull() ; p = p.nextSibling()) {
+				if (p.isElement() && p.toElement().tagName() == "logo") {
+					loadLogo(p.toElement());
+				}
+			}
+		}
+	}
+	
+	return(true);
+}
+
+/**
+	Import the logo from a given XML logo description.
+	@param xml_element An XML element representing a logo within an titleblock
+	template.
+	@return true if the reading succeeds, false otherwise.
+*/
+bool TitleBlockTemplate::loadLogo(const QDomElement &xml_element) {
+	// we require a name
+	if (!xml_element.hasAttribute("name")) {
+		return(false);
+	}
+	QString logo_name    = xml_element.attribute("name");
+	QString logo_type    = xml_element.attribute("type", "png");
+	QString logo_storage = xml_element.attribute("storage", "base64");
+	
+	// Both QSvgRenderer and QPixmap read their data from a QByteArray, so
+	// we convert the available data to that format.
+	QByteArray logo_data;
+	if (logo_storage == "xml") {
+		QDomNodeList svg_nodes = xml_element.elementsByTagName("svg");
+		if (svg_nodes.isEmpty()) {
+			return(false);
+		}
+		QDomElement svg_element = svg_nodes.at(0).toElement();
+		QTextStream xml_to_byte_array(&logo_data);
+		svg_element.save(xml_to_byte_array, 0);
+	} else if (logo_storage == "base64") {
+		logo_data = QByteArray::fromBase64(xml_element.text().toAscii());
+	} else {
+		return(false);
+	}
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << logo_name << logo_type << logo_storage;
+#endif
+	addLogo(logo_name, &logo_data, logo_type, logo_storage);
+	
+	return(true);
+}
+
+/**
+	Import the grid from a given XML titleblock template.
+	@param xml_element An XML element representing an titleblock template.
+	@return true if the reading succeeds, false otherwise.
+*/
+bool TitleBlockTemplate::loadGrid(const QDomElement &xml_element) {
+	// we parse the first available "grid" XML element
+	QDomElement grid_element;
+	for (QDomNode n = xml_element.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
+		if (n.isElement() && n.toElement().tagName() == "grid") {
+			grid_element = n.toElement();
+			break;
+		}
+	}
+	
+	if (!grid_element.hasAttribute("rows") || !grid_element.hasAttribute("cols")) {
+		return(false);
+	}
+	
+	parseRows(grid_element.attribute("rows"));
+	parseColumns(grid_element.attribute("cols"));
+	initCells();
+	loadCells(grid_element);
+	applyRowColNums();
+	applyCellSpans();
+	return(true);
+}
+
+/**
+	Parse the rows heights
+	@param rows_string A string describing the rows heights of the titleblock
+*/
+void TitleBlockTemplate::parseRows(const QString &rows_string) {
+	rows_heights_.clear();
+	// parse the rows attribute: we expect a serie of absolute heights
+	QRegExp row_size_format("^([0-9]+)(?:px)?$", Qt::CaseInsensitive);
+	bool conv_ok;
+	
+	QStringList rows_descriptions = rows_string.split(QChar(';'), QString::SkipEmptyParts);
+	foreach (QString rows_description, rows_descriptions) {
+		if (row_size_format.exactMatch(rows_description)) {
+			int row_size = row_size_format.capturedTexts().at(1).toInt(&conv_ok);
+			if (conv_ok) rows_heights_ << row_size;
+		}
+	}
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << "Rows heights:" << rows_heights_;
+#endif
+}
+
+/**
+	Parse the columns widths
+	@param cols_string A string describing the columns widths of the titleblock
+*/
+void TitleBlockTemplate::parseColumns(const QString &cols_string) {
+	columns_width_.clear();
+	// parse the cols attribute: we expect a serie of absolute or relative widths
+	QRegExp abs_col_size_format("^([0-9]+)(?:px)?$", Qt::CaseInsensitive);
+	QRegExp rel_col_size_format("^([rt])([0-9]+)%$", Qt::CaseInsensitive);
+	bool conv_ok;
+	
+	QStringList cols_descriptions = cols_string.split(QChar(';'), QString::SkipEmptyParts);
+	foreach (QString cols_description, cols_descriptions) {
+		if (abs_col_size_format.exactMatch(cols_description)) {
+			int col_size = abs_col_size_format.capturedTexts().at(1).toInt(&conv_ok);
+			if (conv_ok) columns_width_ << TitleBlockDimension(col_size, QET::Absolute);
+		} else if (rel_col_size_format.exactMatch(cols_description)) {
+			int col_size = rel_col_size_format.capturedTexts().at(2).toInt(&conv_ok);
+			QET::TitleBlockColumnLength col_type = rel_col_size_format.capturedTexts().at(1) == "t" ? QET::RelativeToTotalLength : QET::RelativeToRemainingLength;
+			if (conv_ok) columns_width_ << TitleBlockDimension(col_size, col_type );
+		}
+	}
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	foreach (TitleBlockColDimension icd, columns_width_) {
+		qDebug() << Q_FUNC_INFO << QString("%1 [%2]").arg(icd.value).arg(QET::titleBlockColumnLengthToString(icd.type));
+	}
+#endif
+}
+
+/**
+	Analyze an XML element, looking for grid cells. The grid cells are checked
+	and stored in this object.
+	@param xml_element XML element to analyze
+	@return systematically true
+*/
+bool TitleBlockTemplate::loadCells(const QDomElement &xml_element) {
+	// we are interested by the "logo" and "field" elements
+	QDomElement grid_element;
+	for (QDomNode n = xml_element.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
+		if (!n.isElement()) continue;
+		QDomElement cell_element = n.toElement();
+		if (cell_element.tagName() == "field" || cell_element.tagName() == "logo") {
+			loadCell(cell_element);
+		}
+	}
+	return(true);
+}
+
+/**
+	Load a cell into this template.
+	@param cell_element XML element describing a cell within a title block template
+*/
+void TitleBlockTemplate::loadCell(const QDomElement &cell_element) {
+	TitleBlockCell *loaded_cell;
+	if (!checkCell(cell_element, &loaded_cell)) return;
+	loaded_cell -> loadContentFromXml(cell_element);
+}
+
+/**
+	Export this template's extra information.
+	@param xml_element XML element under which extra informations will be attached
+*/
+void TitleBlockTemplate::saveInformation(QDomElement &xml_element) const {
+	QDomNode information_text_node = xml_element.ownerDocument().createTextNode(information());
+	
+	QDomElement information_element = xml_element.ownerDocument().createElement("information");
+	information_element.appendChild(information_text_node);
+	xml_element.appendChild(information_element);
+}
+
+/**
+	Export this template's logos as XML
+	@param xml_element XML Element under which the \<logos\> element will be attached
+*/
+void TitleBlockTemplate::saveLogos(QDomElement &xml_element) const {
+	QDomElement logos_element = xml_element.ownerDocument().createElement("logos");
+	foreach(QString logo_name, type_logos_.keys()) {
+		QDomElement logo_element = xml_element.ownerDocument().createElement("logo");
+		saveLogo(logo_name, logo_element);
+		logos_element.appendChild(logo_element);
+	}
+	xml_element.appendChild(logos_element);
+}
+
+/**
+	Export a specific logo as XML
+	@param logo_name Name of the logo to be exported
+	@param xml_element XML element in which the logo will be exported
+*/
+void TitleBlockTemplate::saveLogo(const QString &logo_name, QDomElement &xml_element) const {
+	if (!type_logos_.contains(logo_name)) return;
+	
+	xml_element.setAttribute("name", logo_name);
+	xml_element.setAttribute("type", type_logos_[logo_name]);
+	xml_element.setAttribute("storage", storage_logos_[logo_name]);
+	
+	if (storage_logos_[logo_name] == "xml" && type_logos_[logo_name] == "svg") {
+		QDomDocument svg_logo;
+		svg_logo.setContent(data_logos_[logo_name]);
+		QDomNode svg_logo_element = xml_element.ownerDocument().importNode(svg_logo.documentElement(), true);
+		xml_element.appendChild(svg_logo_element.toElement());
+	} else if (storage_logos_[logo_name] == "base64") {
+		QDomText base64_logo = xml_element.ownerDocument().createTextNode(data_logos_[logo_name].toBase64());
+		xml_element.appendChild(base64_logo);
+	}
+}
+
+/**
+	Export this template's cells grid as XML
+	@param xml_element XML element under which the \<grid\> element will be attached
+*/
+void TitleBlockTemplate::saveGrid(QDomElement &xml_element) const {
+	QDomElement grid_element = xml_element.ownerDocument().createElement("grid");
+	
+	QString rows_attr, cols_attr;
+	foreach(int row_height, rows_heights_) rows_attr += QString("%1;").arg(row_height);
+	foreach(TitleBlockDimension col_width, columns_width_) cols_attr += col_width.toShortString();
+	grid_element.setAttribute("rows", rows_attr);
+	grid_element.setAttribute("cols", cols_attr);
+	
+	saveCells(grid_element);
+	
+	xml_element.appendChild(grid_element);
+}
+
+/**
+	Export this template's cells as XML (without the grid-related information, usch as rows and cols)
+	@param xml_element XML element under which the \<cell\> elements will be attached
+*/
+void TitleBlockTemplate::saveCells(QDomElement &xml_element) const {
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			if (cells_[i][j] -> cell_type != TitleBlockCell::EmptyCell) {
+				saveCell(cells_[i][j], xml_element);
+			}
+		}
+	}
+}
+
+/**
+	Export a specific cell as XML
+	@param cell Cell to be exported as XML
+	@param xml_element XML element under which the \<cell\> element will be attached
+	@param save_empty If true, the cell will be saved even if it is an empty one
+*/
+void TitleBlockTemplate::saveCell(TitleBlockCell *cell, QDomElement &xml_element, bool save_empty) const {
+	if (!cell) return;
+	if (cell -> spanner_cell) return;
+	if (!save_empty && cell -> cell_type == TitleBlockCell::EmptyCell) return;
+	
+	
+	QDomElement cell_elmt = xml_element.ownerDocument().createElement("cell");
+	xml_element.appendChild(cell_elmt);
+	
+	// save information dependent from this template
+	cell_elmt.setAttribute("row", cell -> num_row);
+	cell_elmt.setAttribute("col", cell -> num_col);
+	if (cell -> row_span) cell_elmt.setAttribute("rowspan", cell -> row_span);
+	if (cell -> col_span) cell_elmt.setAttribute("colspan", cell -> col_span);
+	
+	// save other information
+	cell -> saveContentToXml(cell_elmt);
+}
+
+/**
+	Load the essential attributes of a cell: row and column indices and spans.
+	@param xml_element XML element representing a cell, i.e. either an titleblock
+	logo or an titleblock field.
+	@param titleblock_cell_ptr Pointer to a TitleBlockCell object pointer - if non-zero and if
+	this method returns true, will be filled with the created TitleBlockCell
+	@return TRUE if the cell appears to be ok, FALSE otherwise
+*/
+bool TitleBlockTemplate::checkCell(const QDomElement &xml_element, TitleBlockCell **titleblock_cell_ptr) {
+	int col_count = columns_width_.count(), row_count = rows_heights_.count();
+	
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << "begin" << row_count << col_count;
+#endif
+	
+	int row_num, col_num, row_span, col_span;
+	row_num = col_num = -1;
+	row_span = col_span = 0;
+	
+	// parse the row and col attributes
+	if (!QET::attributeIsAnInteger(xml_element, "row", &row_num) || row_num < 0 || row_num >= row_count) {
+		return(false);
+	}
+	if (!QET::attributeIsAnInteger(xml_element, "col", &col_num) || col_num < 0 || col_num >= col_count) {
+		return(false);
+	}
+	
+	// check whether the target cell can be used or not
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << "cell access" << col_num << row_num;
+#endif
+	TitleBlockCell *cell_ptr = cells_[col_num][row_num];
+	if (cell_ptr -> cell_type != TitleBlockCell::EmptyCell || cell_ptr -> spanner_cell) {
+		return(false);
+	}
+	// ensure the num_row and num_col attributes are alright
+	cell_ptr -> num_row = row_num;
+	cell_ptr -> num_col = col_num;
+	
+	// parse the rowspan and colspan attributes
+	if (QET::attributeIsAnInteger(xml_element, "rowspan", &row_span) && row_span > 0) {
+		cell_ptr -> row_span = row_span;
+	}
+	
+	if (QET::attributeIsAnInteger(xml_element, "colspan", &col_span) && col_span > 0) {
+		cell_ptr -> col_span = col_span;
+	}
+	// these attributes are stored "as is" -- whether they can be applied directly or must be restricted will be checked later
+	
+	if (titleblock_cell_ptr) *titleblock_cell_ptr = cell_ptr;
+	return(true);
+}
+
+/**
+	Initialize the internal cells grid with the row and column counts.
+	Note that this method does nothing if one of the internal lists
+	columns_width_ and rows_heights_ is empty.
+*/
+void TitleBlockTemplate::initCells() {
+	if (columns_width_.count() < 1 || rows_heights_.count() < 1) return;
+	
+	cells_.clear();
+	qDeleteAll(registered_cells_);
+	registered_cells_.clear();
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		cells_ << createColumn();
+	}
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+	qDebug() << Q_FUNC_INFO << toString();
+#endif
+}
+
+/**
+	@return A string representing the titleblock template
+	@see TitleBlockCell::toString()
+*/
+QString TitleBlockTemplate::toString() const {
+	QString str = "\n";
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			str += cells_[i][j] -> toString() + "    ";
+		}
+		str += "\n";
+	}
+	return(str);
+}
+
+/**
+	@return the name of this template
+*/
+QString TitleBlockTemplate::name() const {
+	return(name_);
+}
+
+/**
+	@return the information field attached to this template
+*/
+QString TitleBlockTemplate::information() const {
+	return(information_);
+}
+
+/**
+	@param info information to be attached to this template
+*/
+void TitleBlockTemplate::setInformation(const QString &info) {
+	information_ = info;
+}
+
+/**
+	@param i row index
+	@return the height of the row at index i
+*/
+int TitleBlockTemplate::rowDimension(int i) {
+	int index = (i == -1) ? rows_heights_.count() - 1 : i;
+	if (index >= 0 && index < rows_heights_.count()) {
+		return(rows_heights_.at(index));
+	}
+	return(-1);
+}
+
+/**
+	Set the height of a row
+	@param i row index
+	@param dimension New height of the row at index i
+*/
+void TitleBlockTemplate::setRowDimension(int i, const TitleBlockDimension &dimension) {
+	int index = (i == -1) ? rows_heights_.count() - 1 : i;
+	if (index >= 0 || index < rows_heights_.count()) {
+		rows_heights_[index] = dimension.value;
+	}
+}
+
+/**
+	@param i column index
+	@return the width of the column at index i
+*/
+TitleBlockDimension TitleBlockTemplate::columnDimension(int i) {
+	int index = (i == -1) ? columns_width_.count() - 1 : i;
+	if (index >= 0 && index < columns_width_.count()) {
+		return(columns_width_.at(index));
+	}
+	return(TitleBlockDimension(-1));
+}
+
+/**
+	Set the width of a column
+	@param i column index
+	@param dimension New width of the column at index i
+*/
+void TitleBlockTemplate::setColumnDimension(int i, const TitleBlockDimension &dimension) {
+	int index = (i == -1) ? columns_width_.count() - 1 : i;
+	if (index >= 0 || index < columns_width_.count()) {
+		columns_width_[index] = dimension;
+	}
+}
+
+/**
+	@return the number of columns in this template
+*/
+int TitleBlockTemplate::columnsCount() const {
+	return(columns_width_.count());
+}
+
+/**
+	@return the number of rows in this template
+*/
+int TitleBlockTemplate::rowsCount() const {
+	return(rows_heights_.count());
+}
+
+/**
+	@param total_width The total width of the titleblock to render
+	@return the list of the columns widths for this rendering
+*/
+QList<int> TitleBlockTemplate::columnsWidth(int total_width) const {
+	if (total_width < 0) return(QList<int>());
+	
+	// we first iter to determine the absolute and total-width-related widths
+	QVector<int> final_widths(columns_width_.count());
+	int abs_widths_sum = 0, rel_widths_sum = 0;
+	QList<int> relative_columns;
+	
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		TitleBlockDimension icd = columns_width_.at(i);
+		if (icd.type == QET::Absolute) {
+			abs_widths_sum += icd.value;
+			final_widths[i] = icd.value;
+		} else if (icd.type == QET::RelativeToTotalLength) {
+			int abs_value = qRound(total_width * icd.value / 100.0);
+			relative_columns << i;
+			abs_widths_sum += abs_value;
+			final_widths[i] = abs_value;
+		}
+	}
+	
+	// we can now deduce the remaining width
+	int remaining_width = total_width - abs_widths_sum;
+	
+	// we do a second iteration to build the final widths list
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		TitleBlockDimension icd = columns_width_.at(i);
+		if (icd.type == QET::RelativeToRemainingLength) {
+			final_widths[i] = qRound(remaining_width * icd.value / 100.0);
+			relative_columns << i;
+			rel_widths_sum += final_widths[i];
+		}
+	}
+	
+	// Have we computed widths from percentage for relative columns?
+	if (relative_columns.count()) {
+		// Due to the rounding process, we may get a slight difference between the
+		// sum of the columns widths and the total width.
+		int difference = total_width - abs_widths_sum - rel_widths_sum;
+	
+		if (difference) {
+			// We consider we should not attempt to compensate this difference if it is
+			// under relative_columns_count * 0.5 (which means that each percent-based
+			// columns can "bring" up to 0.5px of difference).
+			qreal max_acceptable_difference = relative_columns.count() * 0.5;
+			
+			int share = difference > 0 ? 1 : -1;
+			if (qAbs(difference) <= max_acceptable_difference) {
+				while (difference) {
+					foreach (int index, relative_columns) {
+						final_widths[index] += share;
+						difference -= share;
+						if (!difference) break;
+					}
+				}
+			}
+		}
+	}
+	return(final_widths.toList());
+}
+
+/**
+	@return the heights of all the rows in this template
+*/
+QList<int> TitleBlockTemplate::rowsHeights() const {
+	return(rows_heights_);
+}
+
+/**
+	@param a column type
+	@return the count of \a type columns 
+*/
+int TitleBlockTemplate::columnTypeCount(QET::TitleBlockColumnLength type) {
+	int count = 0;
+	
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		if (columns_width_.at(i).type == type) ++ count;
+	}
+	
+	return(count);
+}
+
+/**
+	@param a column type
+	@return the sum of values attached to \a type columns 
+*/
+int TitleBlockTemplate::columnTypeTotal(QET::TitleBlockColumnLength type) {
+	int total = 0;
+	
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		if (columns_width_.at(i).type == type) {
+			total += columns_width_.at(i).value;
+		}
+	}
+	
+	return(total);
+}
+
+/**
+	@return the minimum width for this template
+*/
+int TitleBlockTemplate::minimumWidth() {
+	// Abbreviations: ABS: absolute, RTT: relative to total, RTR: relative to
+	// remaining, TOT: total diagram/TBT width (variable).
+	
+	// Minimum size may be enforced by ABS and RTT widths:
+	// TOT >= ((sum(REL)/100)*TOT)+sum(ABS)
+	// => (1 - (sum(REL)/100))TOT >= sum(ABS)
+	// => TOT >= sum(ABS) / (1 - (sum(REL)/100))
+	// => TOT >= sum(ABS) / ((100 - sum(REL))/100))
+	return(
+		qRound(
+			columnTypeTotal(QET::Absolute)
+			/
+			((100.0 - columnTypeTotal(QET::RelativeToTotalLength)) / 100.0)
+		)
+	);
+}
+
+/**
+	@return the maximum width for this template, or -1 if it does not have any.
+*/
+int TitleBlockTemplate::maximumWidth() {
+	if (columnTypeCount(QET::Absolute) == columns_width_.count()) {
+		// The template is composed of absolute widths only,
+		// therefore it may not extend beyond their sum.
+		return(columnTypeTotal(QET::Absolute));
+	}
+	return(-1);
+}
+
+/**
+	@return the total effective width of this template
+	@param total_width The total width initially planned for the rendering
+*/
+int TitleBlockTemplate::width(int total_width) {
+	int width = 0;
+	foreach (int col_width, columnsWidth(total_width)) {
+		width += col_width;
+	}
+	return(width);
+}
+
+/**
+	@return the total height of this template
+*/
+int TitleBlockTemplate::height() const {
+	int height = 0;
+	foreach(int row_height, rows_heights_) {
+		height += row_height;
+	}
+	return(height);
+}
+
+/**
+	Move a row within this template.
+	@param from Index of the moved row
+	@param to Arrival index of the moved row
+*/
+bool TitleBlockTemplate::moveRow(int from, int to) {
+	// checks from and to
+	if (from >= rows_heights_.count()) return(false);
+	if (to   >= rows_heights_.count()) return(false);
+	for (int j = 0 ; j < columns_width_.count() ; ++ j) {
+		cells_[j].move(from, to);
+	}
+	rows_heights_.move(from, to);
+	rowColsChanged();
+	return(true);
+}
+
+/**
+	Add a new 25px-wide row at the provided index.
+	@param i Index of the added row, -1 meaning "last position"
+*/
+void TitleBlockTemplate::addRow(int i) {
+	insertRow(25, createRow(), i);
+}
+
+/**
+	@param dimension Size of the row to be added (always absolute, in pixels)
+	@param column Row to be added
+	@param i Index of the column after insertion, -1 meaning "last position"
+*/
+bool TitleBlockTemplate::insertRow(int dimension, const QList<TitleBlockCell *> &row, int i) {
+	int index = (i == -1) ? rows_heights_.count() : i;
+	
+	for (int j = 0 ; j < columns_width_.count() ; ++ j) {
+		cells_[j].insert(index, row[j]);
+	}
+	rows_heights_.insert(index, dimension);
+	rowColsChanged();
+	return(true);
+}
+
+/**
+	Removes the row at index i
+	@param i Index of the column to be removed
+	@return the removed column
+*/
+QList<TitleBlockCell *> TitleBlockTemplate::takeRow(int i) {
+	QList<TitleBlockCell *> row;
+	int index = (i == -1) ? rows_heights_.count() - 1 : i;
+	if (index < 0 || index >= rows_heights_.count()) return(row);
+	for (int j = 0 ; j < columns_width_.count() ; ++ j) {
+		row << cells_[j].takeAt(index);
+	}
+	rows_heights_.removeAt(index);
+	rowColsChanged();
+	return(row);
+}
+
+/**
+	@return a new row that fits the current grid
+*/
+QList<TitleBlockCell *> TitleBlockTemplate::createRow() {
+	return(createCellsList(columns_width_.count()));
+	
+}
+
+/**
+	Move the column at index "from" to index "to".
+	@param from Source index of the moved column
+	@param to   Target index of the moved column
+*/
+bool TitleBlockTemplate::moveColumn(int from, int to) {
+	// checks from and to
+	if (from >= columns_width_.count()) return(false);
+	if (to   >= columns_width_.count()) return(false);
+	cells_.move(from, to);
+	columns_width_.move(from, to);
+	rowColsChanged();
+	return(true);
+}
+
+/**
+	Add a new 50px-wide column at the provided index.
+	@param i Index of the added column, -1 meaning "last position"
+*/
+void TitleBlockTemplate::addColumn(int i) {
+	insertColumn(TitleBlockDimension(50, QET::Absolute), createColumn(), i);
+}
+
+/**
+	@param dimension Size of the column to be added
+	@param column Column to be added
+	@param i Index of the column after insertion, -1 meaning "last position"
+*/
+bool TitleBlockTemplate::insertColumn(const TitleBlockDimension &dimension, const QList<TitleBlockCell *> &column, int i) {
+	int index = (i == -1) ? columns_width_.count() : i;
+	cells_.insert(index, column);
+	columns_width_.insert(index, dimension);
+	rowColsChanged();
+	return(true);
+}
+
+/**
+	Removes the column at index i
+	@param i Index of the column to be removed
+	@return the removed column
+*/
+QList<TitleBlockCell *> TitleBlockTemplate::takeColumn(int i) {
+	int index = (i == -1) ? columns_width_.count() - 1 : i;
+	if (index < 0 || index >= columns_width_.count()) {
+		return(QList<TitleBlockCell *>());
+	}
+	QList<TitleBlockCell *> column = cells_.takeAt(i);
+	columns_width_.removeAt(i);
+	rowColsChanged();
+	return(column);
+}
+
+/**
+	@return a new column that fits the current grid
+*/
+QList<TitleBlockCell *> TitleBlockTemplate::createColumn() {
+	return(createCellsList(rows_heights_.count()));
+}
+
+/**
+	@param row A row number (starting from 0)
+	@param col A column number (starting from 0)
+	@return the cell located at (row, col)
+*/
+TitleBlockCell *TitleBlockTemplate::cell(int row, int col) const {
+	if (row >= rows_heights_.count()) return(0);
+	if (col >= columns_width_.count()) return(0);
+	
+	return(cells_[col][row]);
+}
+
+/**
+	@param cell A cell belonging to this title block template
+	@param ignore_span_state (Optional, defaults to false) If true, will consider
+	cells theoretically spanned (i.e. row_span and col_span attributes).
+	Otherwise, will take span_state attribute into account.
+	@return the set of cells spanned by the provided cell
+	Note the returned set does not include the spanning, provided cell
+*/
+QSet<TitleBlockCell *> TitleBlockTemplate::spannedCells(const TitleBlockCell *given_cell, bool ignore_span_state) const {
+	QSet<TitleBlockCell *> set;
+	if (!given_cell) return(set);
+	if (!ignore_span_state && given_cell -> span_state == TitleBlockCell::Disabled) return(set);
+	
+	int final_row_span = ignore_span_state ? given_cell -> row_span : given_cell -> applied_row_span;
+	int final_col_span = ignore_span_state ? given_cell -> col_span : given_cell -> applied_col_span;
+	if (!final_row_span && !final_col_span) return(set);
+	
+	for (int i = given_cell -> num_col ; i <= given_cell -> num_col + final_col_span ; ++ i) {
+		for (int j = given_cell -> num_row ; j <= given_cell -> num_row + final_row_span ; ++ j) {
+			if (i == given_cell -> num_col && j == given_cell -> num_row) continue;
+			TitleBlockCell *current_cell = cell(j, i);
+			if (current_cell) set << current_cell;
+		}
+	}
+	return(set);
+}
+
+/**
+	Export the span parameters of all cell in the current grid.
+*/
+QHash<TitleBlockCell *, QPair<int, int> > TitleBlockTemplate::getAllSpans() const {
+	QHash<TitleBlockCell *, QPair<int, int> > spans;
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			spans.insert(
+				cells_[i][j],
+				QPair<int, int>(
+					cells_[i][j] -> row_span,
+					cells_[i][j] -> col_span
+				)
+			);
+		}
+	}
+	return(spans);
+}
+
+/**
+	Restore a set of span parameters.
+*/
+void TitleBlockTemplate::setAllSpans(const QHash<TitleBlockCell *, QPair<int, int> > &spans) {
+	foreach (TitleBlockCell *cell, spans.keys()) {
+		cell -> row_span = spans[cell].first;
+		cell -> col_span = spans[cell].second;
+	}
+}
+
+/**
+	@param logo_name Logo name to be added / replaced
+	@param logo_data Logo data
+*/
+bool TitleBlockTemplate::addLogo(const QString &logo_name, QByteArray *logo_data, const QString &logo_type, const QString &logo_storage) {
+	if (data_logos_.contains(logo_name)) {
+		// we are replacing the logo
+		removeLogo(logo_name);
+	}
+	
+	// we can now create our image object from the byte array
+	if (logo_type == "svg") {
+		// SVG format is handled by the QSvgRenderer class
+		QSvgRenderer *svg = new QSvgRenderer();
+		if (!svg -> load(*logo_data)) {
+			return(false);
+		}
+		vector_logos_.insert(logo_name, svg);
+		
+		// we also memorize the way to store them in the final XML output
+		QString final_logo_storage = logo_storage;
+		if (logo_storage != "xml" && logo_storage != "base64") {
+			final_logo_storage = "xml";
+		}
+		storage_logos_.insert(logo_name, logo_storage);
+	} else {
+		
+		// bitmap formats are handled by the QPixmap class
+		QPixmap logo_pixmap;
+		logo_pixmap.loadFromData(*logo_data);
+		if (!logo_pixmap.width() || !logo_pixmap.height()) {
+			return(false);
+		}
+		bitmap_logos_.insert(logo_name, logo_pixmap);
+		
+		// bitmap logos can only be stored using a base64 encoding
+		storage_logos_.insert(logo_name, "base64");
+	}
+	
+	// we systematically store the raw data
+	data_logos_.insert(logo_name, *logo_data);
+	type_logos_.insert(logo_name, logo_type);
+	
+	return(true);
+}
+
+/**
+	@param filepath Path of the image file to add as a logo
+	@param name Name used to store the logo; if none is provided, the
+	basename of the first argument is used.
+	@return true if the logo could be deleted, false otherwise
+*/
+bool TitleBlockTemplate::addLogoFromFile(const QString &filepath, const QString &name) {
+	QFileInfo filepath_info(filepath);
+	QString filename = name.isEmpty() ? filepath_info.fileName() : name;
+	QString filetype = filepath_info.suffix();
+	
+	// we read the provided logo
+	QFile logo_file(filepath);
+	if (!logo_file.open(QIODevice::ReadOnly)) return(false);
+	QByteArray file_content = logo_file.readAll();
+	
+	// first, we try to add it as an SVG image
+	if (addLogo(filename, &file_content, "svg", "xml")) return(true);
+	
+	// we then try to add it as a bitmap image
+	return addLogo(filename, &file_content, filepath_info.suffix(), "base64");
+}
+
+/*
+	@param logo_name Name used to store the logo
+	@param filepath Path the logo will be saved as
+	@return true if the logo could be exported, false otherwise
+*/
+bool TitleBlockTemplate::saveLogoToFile(const QString &logo_name, const QString &filepath) {
+	if (!data_logos_.contains(logo_name)) {
+		return(false);
+	}
+	
+	QFile target_file(filepath);
+	if (!target_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+		return(false);
+	}
+	
+	target_file.write(data_logos_[logo_name]);
+	target_file.close();
+	return(true);
+}
+
+/**
+	@param logo_name Name of the logo to remove
+	@return true if the logo could be deleted, false otherwise
+*/
+bool TitleBlockTemplate::removeLogo(const QString &logo_name) {
+	if (!data_logos_.contains(logo_name)) {
+		return(false);
+	}
+	
+	/// TODO check existing cells using this logo.
+	if (vector_logos_.contains(logo_name)) {
+		delete vector_logos_.take(logo_name);
+	}
+	if (bitmap_logos_.contains(logo_name)) {
+		bitmap_logos_.remove(logo_name);
+	}
+	data_logos_.remove(logo_name);
+	storage_logos_.remove(logo_name);
+	return(true);
+}
+
+/**
+	Rename the \a logo_name logo to \a new_name
+	@param logo_name Name of the logo to be renamed
+	@param new_name New name of the renamed logo
+*/
+bool TitleBlockTemplate::renameLogo(const QString &logo_name, const QString &new_name) {
+	if (!data_logos_.contains(logo_name) || data_logos_.contains(new_name)) {
+		return(false);
+	}
+	
+	/// TODO check existing cells using this logo.
+	if (vector_logos_.contains(logo_name)) {
+		vector_logos_.insert(new_name, vector_logos_.take(logo_name));
+	}
+	if (bitmap_logos_.contains(logo_name)) {
+		bitmap_logos_.insert(new_name, bitmap_logos_.take(logo_name));
+	}
+	data_logos_.insert(new_name, data_logos_.take(logo_name));
+	storage_logos_.insert(new_name, storage_logos_.take(logo_name));
+	return(true);
+}
+
+/**
+	Set the kind of storage for the \a logo_name logo.
+	@param logo_name Name of the logo which kind of storage is to be changed
+	@param storage The kind of storage to use for the logo, e.g. "xml" or "base64".
+*/
+void TitleBlockTemplate::setLogoStorage(const QString &logo_name, const QString &storage) {
+	if (storage_logos_.contains(logo_name)) {
+		storage_logos_[logo_name] = storage;
+	}
+}
+
+/**
+	@return The names of logos embedded within this title block template.
+*/
+QList<QString> TitleBlockTemplate::logos() const {
+	return(data_logos_.keys());
+}
+
+/**
+	@param logo_name Name of a logo embedded within this title block template.
+	@return the kind of storage used for the required logo, or a null QString
+	if no such logo was found in this template.
+*/
+QString TitleBlockTemplate::logoType(const QString &logo_name) const {
+	if (type_logos_.contains(logo_name)) {
+		return type_logos_[logo_name];
+	}
+	return(QString());
+}
+
+/**
+	@param logo_name Name of a vector logo embedded within this title block template.
+	@return the rendering object for the required vector logo, or 0 if no such
+	vector logo was found in this template.
+*/
+QSvgRenderer *TitleBlockTemplate::vectorLogo(const QString &logo_name) const {
+	if (vector_logos_.contains(logo_name)) {
+		return vector_logos_[logo_name];
+	}
+	return(0);
+}
+
+/**
+	@param logo_name Name of a logo embedded within this title block template.  
+	@return the pixmap for the required bitmap logo, or a null pixmap if no
+	such bitmap logo was found in this template.
+*/
+QPixmap TitleBlockTemplate::bitmapLogo(const QString &logo_name) const {
+	if (bitmap_logos_.contains(logo_name)) {
+		return bitmap_logos_[logo_name];
+	}
+	return(QPixmap());
+}
+
+/**
+	Render the titleblock.
+	@param painter Painter to use to render the titleblock
+	@param diagram_context Diagram context to use to generate the titleblock strings
+	@param titleblock_width Width of the titleblock to render
+*/
+void TitleBlockTemplate::render(QPainter &painter, const DiagramContext &diagram_context, int titleblock_width) const {
+	QList<int> widths = columnsWidth(titleblock_width);
+	int titleblock_height = height();
+	
+	// prepare the QPainter
+	painter.setPen(Qt::black);
+	painter.setBrush(Qt::white);
+	
+	// draw the titleblock border
+	painter.drawRect(QRect(0, 0, titleblock_width, titleblock_height));
+	
+	// run through each individual cell
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			if (cells_[i][j] -> spanner_cell || cells_[i][j] -> cell_type == TitleBlockCell::EmptyCell) continue;
+			
+			// calculate the border rect of the current cell
+			int x = lengthRange(0, cells_[i][j] -> num_col, widths);
+			int y = lengthRange(0, cells_[i][j] -> num_row, rows_heights_);
+			
+			int row_span = 0, col_span = 0;
+			if (cells_[i][j] -> span_state != TitleBlockCell::Disabled) {
+				row_span = cells_[i][j] -> applied_row_span;
+				col_span = cells_[i][j] -> applied_col_span;
+			}
+			int w = lengthRange(cells_[i][j] -> num_col, cells_[i][j] -> num_col + 1 + col_span, widths);
+			int h = lengthRange(cells_[i][j] -> num_row, cells_[i][j] -> num_row + 1 + row_span, rows_heights_);
+			QRect cell_rect(x, y, w, h);
+			
+			renderCell(painter, *cells_[i][j], diagram_context, cell_rect);
+		}
+	}
+}
+
+/**
+	Render a titleblock cell.
+	@param painter Painter to use to render the titleblock
+	@param diagram_context Diagram context to use to generate the titleblock strings
+	@param rect Rectangle the cell must be rendered into.
+*/
+void TitleBlockTemplate::renderCell(QPainter &painter, const TitleBlockCell &cell, const DiagramContext &diagram_context, const QRect &cell_rect) const {
+	// draw the border rect of the current cell
+	QPen pen(QBrush(), 0.0, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
+	pen.setColor(Qt::black);
+	painter.setPen(pen);
+	painter.setBrush(Qt::white);
+	painter.drawRect(cell_rect);
+	
+	painter.save();
+	// render the inner content of the current cell
+	if (cell.type() == TitleBlockCell::LogoCell) {
+		if (!cell.logo_reference.isEmpty()) {
+			// the current cell appears to be a logo - we first look for the
+			// logo reference in our vector logos list, since they offer a
+			// potentially better (or, at least, not resolution-limited) rendering
+			if (vector_logos_.contains(cell.logo_reference)) {
+				vector_logos_[cell.logo_reference] -> render(&painter, cell_rect);
+			} else if (bitmap_logos_.contains(cell.logo_reference)) {
+				painter.drawPixmap(cell_rect, bitmap_logos_[cell.logo_reference]);
+			}
+		}
+	} else if (cell.type() == TitleBlockCell::TextCell) {
+		QString final_text = finalTextForCell(cell, diagram_context);
+		renderTextCell(painter, final_text, cell, cell_rect);
+	}
+	painter.restore();
+	
+	// draw again the border rect of the current cell, without the brush this time
+	painter.setBrush(Qt::NoBrush);
+	painter.drawRect(cell_rect);
+}
+
+/**
+	@param cell A cell from this template
+	@param diagram_context Diagram context to use to generate the final text for the given cell
+	@return the final text that has to be drawn in the given cell
+*/
+QString TitleBlockTemplate::finalTextForCell(const TitleBlockCell &cell, const DiagramContext &diagram_context) const {
+	QString cell_text = cell.value.name();
+	QString cell_label = cell.label.name();
+	
+	cell_text = interpreteVariables(cell_text, diagram_context);
+	
+	if (cell.display_label && !cell.label.isEmpty()) {
+		cell_label = interpreteVariables(cell_label, diagram_context);
+		cell_text = QString(tr(" %1 : %2", "titleblock content - please let the blank space at the beginning")).arg(cell_label).arg(cell_text);
+	} else {
+		cell_text = QString(tr(" %1")).arg(cell_text);
+	}
+	return(cell_text);
+}
+
+/**
+	@param string A text containing 0 to n variables, e.g. "%var" or "%{var}"
+	@param diagram_context Diagram context to use to interprete variables
+	@return the provided string with variables replaced by the values from the diagram context
+*/
+QString TitleBlockTemplate::interpreteVariables(const QString &string, const DiagramContext &diagram_context) const {
+	QString interpreted_string = string;
+	foreach (QString key, diagram_context.keys(DiagramContext::DecreasingLength)) {
+		interpreted_string.replace("%{" + key + "}", diagram_context[key].toString());
+		interpreted_string.replace("%" + key,        diagram_context[key].toString());
+	}
+	return(interpreted_string);
+}
+
+/**
+	@brief Get list of variables
+	@return The list of string with variables
+*/
+QStringList TitleBlockTemplate::listOfVariables() {
+	QStringList list;
+	// run through each individual cell
+	for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+		for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+			if (cells_[i][j] -> spanner_cell || cells_[i][j] -> cell_type == TitleBlockCell::EmptyCell) continue;
+			// TODO: not works on all cases...
+			list << cells_[i][j] -> value.name().replace("%","");
+		}	
+	}
+	qDebug() << list;
+	return list;
+}
+
+/**
+	This method uses a \a painter to render the \a text of a \a cell
+	into the \a cell_rect rectangle.
+	The alignment, font_size and other cell parameters are taken into account
+	when rendering.
+	@param painter QPainter used to render the text
+	@param text Text to render
+	@param cell Cell the rendered text is rattached to
+	@param cell_rect Rectangle delimiting the cell area
+*/
+void TitleBlockTemplate::renderTextCell(QPainter &painter, const QString &text, const TitleBlockCell &cell, const QRectF &cell_rect) const {
+	if (text.isEmpty()) return;
+	QFont text_font = TitleBlockTemplate::fontForCell(cell);
+	painter.setFont(text_font);
+	
+	if (cell.hadjust) {
+		QFontMetricsF font_metrics(text_font);
+		QRectF font_rect = font_metrics.boundingRect(QRect(-10000, -10000, 10000, 10000), cell.alignment, text);
+		
+		if (font_rect.width() > cell_rect.width()) {
+			qreal ratio = qreal(cell_rect.width()) / qreal(font_rect.width());
+			painter.save();
+			
+			painter.translate(cell_rect.topLeft());
+			qreal vertical_adjustment = cell_rect.height() * (1 - ratio) / 2.0;
+			painter.translate(0.0, vertical_adjustment);
+			painter.scale(ratio, ratio);
+			
+			QRectF new_world_cell_rect(cell_rect);
+			new_world_cell_rect.moveTo(0, 0.0);
+			new_world_cell_rect.setWidth(new_world_cell_rect.width() / ratio);
+			painter.drawText(new_world_cell_rect, cell.alignment, text);
+			
+			painter.restore();
+			return;
+		}
+	}
+	
+	// Still here? Let's draw the text normally
+	painter.drawText(cell_rect, cell.alignment, text);
+}
+
+/**
+	Set the spanner_cell attribute of every cell to 0.
+*/
+void TitleBlockTemplate::forgetSpanning() {
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+			cells_[i][j] -> spanner_cell = 0;
+		}
+	}
+}
+
+/**
+	Set the spanner_cell attribute of every cell spanned by \a spanning_cell to 0.
+	@param modify_cell (Optional, defaults to true) Whether to set row_span and col_span of \a spanning_cell to 0.
+*/
+void TitleBlockTemplate::forgetSpanning(TitleBlockCell *spanning_cell, bool modify_cell) {
+	if (!spanning_cell) return;
+	foreach (TitleBlockCell *spanned_cell, spannedCells(spanning_cell)) {
+		spanned_cell -> spanner_cell = 0;
+	}
+	if (modify_cell) {
+		spanning_cell -> row_span = 0;
+		spanning_cell -> col_span = 0;
+		spanning_cell -> applied_row_span = 0;
+		spanning_cell -> applied_col_span = 0;
+		spanning_cell -> span_state = TitleBlockCell::Enabled;
+	}
+}
+
+/**
+	Forget any previously applied span, then apply again all spans defined
+	by existing cells.
+*/
+void TitleBlockTemplate::applyCellSpans() {
+	forgetSpanning();
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+			checkCellSpan(cells_[i][j]);
+			applyCellSpan(cells_[i][j]);
+		}
+	}
+}
+
+/**
+	Check whether a given cell can be spanned according to its row_span and
+	col_span attributes. the following attributes of \a cell are updated
+	according to what is really possible:
+	  * applied_col_span
+	  * applied_row_span
+	  * span_state
+	@param cell Cell we want to check
+	@return false if no check could be performed, true otherwise
+*/
+bool TitleBlockTemplate::checkCellSpan(TitleBlockCell *cell) {
+	if (!cell) return(false);
+	
+	cell -> span_state = TitleBlockCell::Enabled;
+	cell -> applied_row_span = cell -> row_span;
+	cell -> applied_col_span = cell -> col_span;
+	
+	// ensure the cell can span as far as required
+	if (cell -> num_col + cell -> col_span >= columnsCount()) {
+		cell -> applied_col_span = columnsCount() - 1 - cell -> num_col;
+		cell -> span_state = TitleBlockCell::Restricted;
+	}
+	if (cell -> num_row + cell -> row_span >= rowsCount()) {
+		cell -> applied_row_span = rowsCount() - 1 - cell -> num_row;
+		cell -> span_state = TitleBlockCell::Restricted;
+	}
+	
+	// ensure cells that will be spanned are either empty or free
+	for (int i = cell -> num_col ; i <= cell -> num_col + cell -> applied_col_span ; ++ i) {
+		for (int j = cell -> num_row ; j <= cell -> num_row + cell -> applied_row_span ; ++ j) {
+			if (i == cell -> num_col && j == cell -> num_row) continue;
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+			qDebug() << Q_FUNC_INFO << "span check" << i << j;
+#endif
+			TitleBlockCell *current_cell = cells_[i][j];
+			if (current_cell -> cell_type != TitleBlockCell::EmptyCell || (current_cell -> spanner_cell && current_cell -> spanner_cell != cell)) {
+				cell -> span_state = TitleBlockCell::Disabled;
+				return(true);
+			}
+		}
+	}
+	
+	return(true);
+}
+
+/**
+	Ensure the spans of the provided cell are applied within the grid structure.
+	Note: this function does not check whether the spans of the provided cell make sense.
+	@param cell Potentially spanning cell
+*/
+void TitleBlockTemplate::applyCellSpan(TitleBlockCell *cell) {
+	if (!cell || (!cell -> row_span && !cell -> col_span)) return;
+	if (cell -> span_state == TitleBlockCell::Disabled) return;
+	
+	// goes through every spanned cell
+	for (int i = cell -> num_col ; i <= cell -> num_col + cell -> applied_col_span ; ++ i) {
+		for (int j = cell -> num_row ; j <= cell -> num_row + cell -> applied_row_span ; ++ j) {
+			// avoid the spanning cell itself
+			if (i == cell -> num_col && j == cell -> num_row) continue;
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+			qDebug() << Q_FUNC_INFO << "marking cell at" << j << i <<  "as spanned by cell at" << cell -> num_row <<  cell -> num_col;
+#endif
+			// marks all spanned cells with the spanning cell
+			cells_[i][j] -> spanner_cell = cell;
+		}
+	}
+}
+
+/**
+	Ensure all cells have the right col+row numbers.
+*/
+void TitleBlockTemplate::applyRowColNums() {
+	for (int i = 0 ; i < columns_width_.count() ; ++ i) {
+		for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
+			cells_[i][j] -> num_col = i;
+			cells_[i][j] -> num_row = j;
+		}
+	}
+}
+
+/**
+	Take care of consistency and span-related problematics when
+	adding/moving/deleting rows and columns.
+*/
+void TitleBlockTemplate::rowColsChanged() {
+	applyRowColNums();
+	applyCellSpans();
+}
+
+/**
+	@return the width between two borders
+	@param start start border number
+	@param end end border number
+*/
+int TitleBlockTemplate::lengthRange(int start, int end, const QList<int> &lengths_list) const {
+	if (start > end || start >= lengths_list.count() || end > lengths_list.count()) {
+#ifdef TITLEBLOCK_TEMPLATE_DEBUG
+		qDebug() << Q_FUNC_INFO << "wont use" << start << "and" << end;
+#endif
+		return(0);
+	}
+	
+	int length = 0;
+	for (int i = start ; i < end ; ++i) {
+		length += lengths_list[i];
+	}
+	return(length);
+}
+

Added: trunk/sources/titleblocktemplate.h
===================================================================
--- trunk/sources/titleblocktemplate.h	                        (rev 0)
+++ trunk/sources/titleblocktemplate.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,153 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_TEMPLATE_H
+#define TITLEBLOCK_TEMPLATE_H
+#include <QtXml>
+#include <QtSvg>
+#include "diagramcontext.h"
+#include "titleblockcell.h"
+#include "dimension.h"
+#include "qet.h"
+
+/**
+	This class represents an title block template for an electric diagram.
+	It can read from an XML document the layout of the table that graphically
+	represents the title block, and can produce a graphical rendering of it from a
+	diagram context (object embedding the informations of the diagram we want to
+	represent the title block.
+*/
+class TitleBlockTemplate : public QObject {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	TitleBlockTemplate(QObject * = 0);
+	virtual ~TitleBlockTemplate();
+	private:
+	TitleBlockTemplate(const TitleBlockTemplate &);
+	
+	// methods
+	public:
+	TitleBlockCell *createCell(const TitleBlockCell * = 0);
+	static QFont fontForCell(const TitleBlockCell &);
+	bool loadFromXmlFile(const QString &);
+	bool loadFromXmlElement(const QDomElement &);
+	bool saveToXmlFile(const QString &);
+	bool saveToXmlElement(QDomElement &) const;
+	void exportCellToXml(TitleBlockCell *,QDomElement &) const;
+	TitleBlockTemplate *clone() const;
+	QString name() const;
+	QString information() const;
+	void setInformation(const QString &);
+	int rowDimension(int);
+	void setRowDimension(int, const TitleBlockDimension &);
+	TitleBlockDimension columnDimension(int);
+	void setColumnDimension(int, const TitleBlockDimension &);
+	int columnsCount() const;
+	int rowsCount() const;
+	QList<int> columnsWidth(int) const;
+	QList<int> rowsHeights() const;
+	int columnTypeCount(QET::TitleBlockColumnLength);
+	int columnTypeTotal(QET::TitleBlockColumnLength);
+	int minimumWidth();
+	int maximumWidth();
+	int width(int);
+	int height() const;
+	
+	bool moveRow(int, int);
+	void addRow(int = -1);
+	bool insertRow(int, const QList<TitleBlockCell *> &, int = -1);
+	QList<TitleBlockCell *> takeRow(int);
+	QList<TitleBlockCell *> createRow();
+	
+	bool moveColumn(int, int);
+	void addColumn(int = -1);
+	bool insertColumn(const TitleBlockDimension &, const QList<TitleBlockCell *> &, int = -1);
+	QList<TitleBlockCell *> takeColumn(int);
+	QList<TitleBlockCell *> createColumn();
+	
+	TitleBlockCell *cell(int, int) const;
+	QSet<TitleBlockCell *> spannedCells(const TitleBlockCell *, bool = false) const;
+	QHash<TitleBlockCell *, QPair<int, int> > getAllSpans() const;
+	void setAllSpans(const QHash<TitleBlockCell *, QPair<int, int> > &);
+	bool addLogo(const QString &, QByteArray *, const QString & = "svg", const QString & = "xml");
+	bool addLogoFromFile(const QString &, const QString & = QString());
+	bool saveLogoToFile(const QString &, const QString &);
+	bool removeLogo(const QString &);
+	bool renameLogo(const QString &, const QString &);
+	void setLogoStorage(const QString &, const QString &);
+	QList<QString> logos() const;
+	QString logoType(const QString &) const;
+	QSvgRenderer *vectorLogo(const QString &) const;
+	QPixmap bitmapLogo(const QString &) const;
+	
+	void render(QPainter &, const DiagramContext &, int) const;
+	void renderCell(QPainter &, const TitleBlockCell &, const DiagramContext &, const QRect &) const;
+	QString toString() const;
+	void applyCellSpans();
+	void forgetSpanning();
+	void forgetSpanning(TitleBlockCell *, bool = true);
+	bool checkCellSpan(TitleBlockCell *);
+	void applyCellSpan(TitleBlockCell *);
+	void applyRowColNums();
+	void rowColsChanged();
+	QStringList listOfVariables();
+	
+	protected:
+	void loadInformation(const QDomElement &);
+	bool loadLogos(const QDomElement &, bool = false);
+	bool loadLogo(const QDomElement &);
+	bool loadGrid(const QDomElement &);
+	bool loadCells(const QDomElement &);
+	void loadCell(const QDomElement &);
+	void saveInformation(QDomElement &) const;
+	void saveLogos(QDomElement &) const;
+	void saveLogo(const QString &, QDomElement &) const;
+	void saveGrid(QDomElement &) const;
+	void saveCells(QDomElement &) const;
+	void saveCell(TitleBlockCell *, QDomElement &, bool = false) const;
+	QList<TitleBlockCell *> createCellsList(int);
+	
+	private:
+	void parseRows(const QString &);
+	void parseColumns(const QString &);
+	bool checkCell(const QDomElement &, TitleBlockCell ** = 0);
+	void flushCells();
+	void initCells();
+	int lengthRange(int, int, const QList<int> &) const;
+	QString finalTextForCell(const TitleBlockCell &, const DiagramContext &) const;
+	QString interpreteVariables(const QString &, const DiagramContext &) const;
+	void renderTextCell(QPainter &, const QString &, const TitleBlockCell &, const QRectF &) const;
+	
+	// attributes
+	private:
+	QString name_;                                   ///< name identifying the Title Block Template within its parent collection
+	QString information_;
+	
+	QHash<QString, QByteArray >    data_logos_;      ///< Logos raw data
+	QHash<QString, QString>        storage_logos_;   ///< Logos applied storage type (e.g. "xml" or "base64")
+	QHash<QString, QString>        type_logos_;      ///< Logos types (e.g. "png", "jpeg", "svg")
+	QHash<QString, QSvgRenderer *> vector_logos_;    ///< Rendered objects for vector logos
+	QHash<QString, QPixmap>        bitmap_logos_;    ///< Pixmaps for bitmap logos
+	
+	QList<int> rows_heights_;                        ///< rows heights -- simple integers
+	QList<TitleBlockDimension> columns_width_;       ///< columns widths -- @see TitleBlockColDimension
+	QList<TitleBlockCell *> registered_cells_;       ///< Cells objects created rattached to this template, but not mandatorily used
+	QList< QList<TitleBlockCell *> > cells_;         ///< Cells grid
+};
+#endif

Added: trunk/sources/titleblocktemplaterenderer.cpp
===================================================================
--- trunk/sources/titleblocktemplaterenderer.cpp	                        (rev 0)
+++ trunk/sources/titleblocktemplaterenderer.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,118 @@
+#include "titleblocktemplaterenderer.h"
+#include "titleblocktemplate.h"
+
+/**
+	Constructor
+	@param parnet Parent QObject of this renderer
+*/
+TitleBlockTemplateRenderer::TitleBlockTemplateRenderer(QObject *parent) :
+	QObject(parent),
+	titleblock_template_(0),
+	use_cache_(true),
+	last_known_titleblock_width_(-1)
+{
+}
+
+/**
+	Destructor
+*/
+TitleBlockTemplateRenderer::~TitleBlockTemplateRenderer() {
+}
+
+/**
+	@return the titleblock template used for the rendering
+*/
+const TitleBlockTemplate *TitleBlockTemplateRenderer::titleBlockTemplate() const {
+	return(titleblock_template_);
+}
+
+/**
+	@param titleblock_template TitleBlock template to render.
+*/
+void TitleBlockTemplateRenderer::setTitleBlockTemplate(const TitleBlockTemplate *titleblock_template) {
+	if (titleblock_template != titleblock_template_) {
+		titleblock_template_ = titleblock_template;
+		invalidateRenderedTemplate();
+	}
+}
+
+/**
+	@param context Diagram Context to use when rendering the titleblock
+*/
+void TitleBlockTemplateRenderer::setContext(const DiagramContext &context) {
+	context_ = context;
+	invalidateRenderedTemplate();
+}
+
+/**
+	@return the height of the rendered template, or -1 if no template has been
+	set for this renderer.
+	@see TitleBlockTemplate::height()
+*/
+int TitleBlockTemplateRenderer::height() const {
+	if (!titleblock_template_) return(-1);
+	return(titleblock_template_ -> height());
+}
+
+/**
+	Render the titleblock.
+	@param provided_painter QPainter to use to render the titleblock.
+	@param titleblock_width The total width of the titleblock to render
+*/
+void TitleBlockTemplateRenderer::render(QPainter *provided_painter, int titleblock_width) {
+	if (!titleblock_template_) return;
+	
+	if (use_cache_) {
+		// Do we really need to calculate all this again?
+		if (titleblock_width != last_known_titleblock_width_ || rendered_template_.isNull()) {
+			renderToQPicture(titleblock_width);
+		}
+		
+		provided_painter -> save();
+		rendered_template_.play(provided_painter);
+		provided_painter -> restore();
+	} else {
+		titleblock_template_ -> render(*provided_painter, context_, titleblock_width);
+	}
+}
+
+/**
+	Renders the titleblock to the internal QPicture
+	@param titleblock_width Width of the titleblock to render
+*/
+void TitleBlockTemplateRenderer::renderToQPicture(int titleblock_width) {
+	if (!titleblock_template_) return;
+	
+	// we render the template on our internal QPicture
+	QPainter painter(&rendered_template_);
+	
+	titleblock_template_ -> render(painter, context_, titleblock_width);
+	
+	// memorize the last known width
+	last_known_titleblock_width_ = titleblock_width;
+}
+
+/**
+	Invalidates the previous rendering of the template by resetting the internal
+	QPicture.
+*/
+void TitleBlockTemplateRenderer::invalidateRenderedTemplate() {
+	rendered_template_ = QPicture();
+}
+
+/**
+	@param use_cache true for this renderer to use its QPicture-based cache,
+	false otherwise.
+*/
+void TitleBlockTemplateRenderer::setUseCache(bool use_cache) {
+	use_cache_ = use_cache;
+}
+
+/**
+	@return true if this renderer uses its QPicture-based cache, false
+	otherwise.
+*/
+bool TitleBlockTemplateRenderer::useCache() const {
+	return(use_cache_);
+}
+

Added: trunk/sources/titleblocktemplaterenderer.h
===================================================================
--- trunk/sources/titleblocktemplaterenderer.h	                        (rev 0)
+++ trunk/sources/titleblocktemplaterenderer.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,48 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TITLEBLOCK_TEMPLATE_RENDERER_H
+#define TITLEBLOCK_TEMPLATE_RENDERER_H
+#include <QPicture>
+#include "diagramcontext.h"
+class TitleBlockTemplate;
+class TitleBlockTemplateRenderer : public QObject {
+	Q_OBJECT
+	
+	public:
+	TitleBlockTemplateRenderer(QObject * = 0);
+	virtual ~TitleBlockTemplateRenderer();
+	const TitleBlockTemplate *titleBlockTemplate() const;
+	void setTitleBlockTemplate(const TitleBlockTemplate *);
+	void setContext(const DiagramContext &context);
+	int height() const;
+	void render(QPainter *, int);
+	void invalidateRenderedTemplate();
+	void setUseCache(bool);
+	bool useCache() const;
+	
+	private:
+	void renderToQPicture(int);
+	
+	private:
+	const TitleBlockTemplate *titleblock_template_;
+	bool use_cache_;
+	QPicture rendered_template_;
+	DiagramContext context_;
+	int last_known_titleblock_width_;
+};
+#endif

Added: trunk/sources/treecoloranimation.cpp
===================================================================
--- trunk/sources/treecoloranimation.cpp	                        (rev 0)
+++ trunk/sources/treecoloranimation.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,35 @@
+#include "treecoloranimation.h"
+
+/**
+	Constructor
+	@param items List of items whose background color will be animated.
+	@param parent Parent QObject
+*/
+TreeColorAnimation::TreeColorAnimation(const QList<QTreeWidgetItem *> &items, QObject *parent) : 
+	QVariantAnimation(parent),
+	items_(items)
+{
+}
+
+/**
+	Destructor
+*/
+TreeColorAnimation::~TreeColorAnimation() {
+}
+
+/**
+	@return the list of items whose background color will be animated.
+*/
+QList<QTreeWidgetItem *> TreeColorAnimation::items() const {
+	return(items_);
+}
+
+/**
+	Apply the provided color to animated items.
+	@param color Color to be applied on animated items.
+*/
+void TreeColorAnimation::updateCurrentValue(const QVariant &color) {
+	foreach (QTreeWidgetItem *item, items_) {
+		item -> setBackgroundColor(0, color.value<QColor>());
+	}
+}

Added: trunk/sources/treecoloranimation.h
===================================================================
--- trunk/sources/treecoloranimation.h	                        (rev 0)
+++ trunk/sources/treecoloranimation.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,43 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef TREE_COLOR_ANIMATION_H
+#define TREE_COLOR_ANIMATION_H
+#include <QtGui>
+
+/**
+	This class allows animating a background color change for a
+	set of QTreeWidgetItem.
+*/
+class TreeColorAnimation : public QVariantAnimation {
+	// Constructors, destructor
+	public:
+	TreeColorAnimation(const QList<QTreeWidgetItem *> &items, QObject * = 0);
+	virtual ~TreeColorAnimation();
+	
+	// methods
+	public:
+	QList<QTreeWidgetItem *> items() const;
+	
+	protected:
+	void updateCurrentValue(const QVariant &);
+	
+	// attributes
+	private:
+	QList<QTreeWidgetItem *> items_; ///< Items this object will animate
+};
+#endif

Added: trunk/sources/ui/diagramselection.cpp
===================================================================
--- trunk/sources/ui/diagramselection.cpp	                        (rev 0)
+++ trunk/sources/ui/diagramselection.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,116 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "diagramselection.h"
+#include "ui_diagramselection.h"
+
+diagramselection::diagramselection(QETProject *prj, QWidget *parent) :
+	QWidget(parent),
+	ui(new Ui::diagramselection),
+	prj_ (prj),
+	list_diagram_ (prj -> diagrams())
+{
+	ui -> setupUi(this);
+
+	QString project_title = prj_ -> title();
+	if (project_title.isEmpty()) project_title = tr("Projet sans titre");
+
+	ui -> label_prj -> setText( tr("Projet : ") + project_title );
+	load_TableDiagram();
+}
+
+diagramselection::~diagramselection() {
+	delete ui;
+}
+
+/**
+ * @brief load all Diagrams of project in table
+ */
+void diagramselection::load_TableDiagram() {
+	// Clear all items
+	ui -> tableDiagram -> clear();
+	for (int i=ui -> tableDiagram -> rowCount()-1; i >= 0; --i) {
+		ui -> tableDiagram->removeRow(i);
+	}
+	for (int i=ui -> tableDiagram -> columnCount()-1; i>=0; --i) {
+		ui -> tableDiagram->removeColumn(i);
+	}
+
+	// Set the setting of table
+	ui -> tableDiagram -> setColumnCount(2);
+	ui -> tableDiagram -> setSelectionBehavior	(QAbstractItemView::SelectRows);
+	ui -> tableDiagram -> setSelectionMode		(QAbstractItemView::SingleSelection);
+	ui -> tableDiagram -> setEditTriggers		(QAbstractItemView::NoEditTriggers);
+	QStringList titles;
+	titles.clear();
+	titles << tr("S\351lection") << tr("Nom");
+	ui-> tableDiagram -> setHorizontalHeaderLabels( titles );
+	
+	// List Diagrams
+	for(int i=0,j=0; i<list_diagram_.count(); i++,j++){
+		QTableWidgetItem *item_Name  = new QTableWidgetItem();
+		QTableWidgetItem *item_State = new QTableWidgetItem();
+
+		QString diagram_title = list_diagram_.at(i) -> title();
+		if (diagram_title.isEmpty()) diagram_title = tr("Sch\351ma sans titre");
+
+		item_Name  -> setData(Qt::DisplayRole, diagram_title);
+		item_State -> setData(Qt::CheckStateRole, Qt::Checked);
+
+		ui -> tableDiagram -> setRowCount(j+1);
+		ui -> tableDiagram -> setItem(j, 0, item_State);
+		ui -> tableDiagram -> setItem(j, 1, item_Name);
+
+	}
+	ui -> tableDiagram -> horizontalHeader() -> setStretchLastSection(true);
+}
+
+/**
+ * @brief get list of Diagrams is selected
+ * @return this list of Diagrams
+ */
+QList<Diagram *> diagramselection::list_of_DiagramSelected() {
+	QList<Diagram *> listDiag;
+	for(int i=0; i<ui -> tableDiagram -> rowCount();i++){
+		if(ui -> tableDiagram -> item(i, 0)->checkState()){
+			listDiag.push_back( list_diagram_[i] );
+		}
+	}
+	return listDiag;
+}
+
+/**
+ * @brief contextMenuRequested
+ * @param pos
+ */
+void diagramselection::on_tableDiagram_customContextMenuRequested(const QPoint &pos){
+	QMenu menu(this);
+	QAction *desl = menu.addAction( tr("D\351s\351lectionner tout") );
+	QAction *sel  = menu.addAction(QIcon(":/ico/16x16/dialog-ok.png"), tr("S\351lectionner tout") );
+	
+	// Exec Menu
+	QAction *ret = menu.exec(ui -> tableDiagram -> viewport() -> mapToGlobal(pos));	
+	if (ret == desl)	{
+		for(int i=0; i<ui -> tableDiagram -> rowCount();i++)
+			ui -> tableDiagram -> item(i, 0)->setCheckState(Qt::Unchecked);
+	}
+	else{
+		for(int i=0; i<ui -> tableDiagram -> rowCount();i++)
+			ui -> tableDiagram -> item(i, 0)->setCheckState(Qt::Checked);
+	}
+}
+

Added: trunk/sources/ui/diagramselection.h
===================================================================
--- trunk/sources/ui/diagramselection.h	                        (rev 0)
+++ trunk/sources/ui/diagramselection.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,51 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIAGRAMSELECTION_H
+#define DIAGRAMSELECTION_H
+
+#include <QWidget>
+
+#include "diagram.h"
+#include "qetproject.h"
+
+namespace Ui {
+	class diagramselection;
+}
+
+class diagramselection : public QWidget
+{
+	Q_OBJECT
+	
+	public:
+	explicit diagramselection(QETProject *prj, QWidget *parent = 0);
+	~diagramselection();
+	
+	QList<Diagram *> list_of_DiagramSelected();
+	
+	private slots:
+	void on_tableDiagram_customContextMenuRequested(const QPoint &pos);
+	
+	private:
+	Ui::diagramselection *ui;
+	QETProject *prj_;
+	QList<Diagram *> list_diagram_;
+	
+	void load_TableDiagram();
+};
+
+#endif // DIAGRAMSELECTION_H

Added: trunk/sources/ui/diagramselection.ui
===================================================================
--- trunk/sources/ui/diagramselection.ui	                        (rev 0)
+++ trunk/sources/ui/diagramselection.ui	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>diagramselection</class>
+ <widget class="QWidget" name="diagramselection">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="label_prj">
+       <property name="text">
+        <string>TextLabel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QTableWidget" name="tableDiagram">
+       <property name="contextMenuPolicy">
+        <enum>Qt::CustomContextMenu</enum>
+       </property>
+       <property name="frameShape">
+        <enum>QFrame::StyledPanel</enum>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

Added: trunk/sources/ui/dialogautonum.cpp
===================================================================
--- trunk/sources/ui/dialogautonum.cpp	                        (rev 0)
+++ trunk/sources/ui/dialogautonum.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,121 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "dialogautonum.h"
+#include "ui_dialogautonum.h"
+
+#include "conductorautonumerotation.h"
+#include "qetmessagebox.h"
+#include "ui/selectautonumw.h"
+
+/**
+ * @brief DialogAutoNum::DialogAutoNum
+ * @param dg
+ * @param parent
+ */
+DialogAutoNum::DialogAutoNum(Diagram *dg, QWidget *parent) :
+	QDialog(parent),
+	ui(new Ui::DialogAutoNum),
+	dg_ (dg)
+{
+	ui -> setupUi(this);
+	
+	ui -> configuration_layout -> addWidget (new SelectAutonumW(dg_ -> project() -> diagrams(), dg, ui -> configuration_tab));
+
+	dgselect_ = new diagramselection( dg_ -> project(), ui -> annotation_tab);
+	ui -> verticalLayout_Selection -> addWidget(dgselect_);
+}
+
+/**
+ * @brief Destructor
+ */
+DialogAutoNum::~DialogAutoNum(){
+	delete ui;
+}
+
+/**
+ * @brief DialogAutoNum::on_pushButton_delete_clicked
+ */
+void DialogAutoNum::on_pushButton_delete_clicked() {
+	// get list of diagrams selected
+	QList<Diagram *>listDiag = dgselect_ -> list_of_DiagramSelected();
+	if(listDiag.count()<=0) return;
+	
+	QString diagramsTitle;
+	for(int i=0; i<listDiag.count(); i++){
+		diagramsTitle += listDiag.at(i) -> title();
+		if(i+1 < listDiag.count()) diagramsTitle += ", ";
+	}
+	// Ask if user is sure to delete the conductor numerotation
+	QMessageBox::StandardButton answer = QET::MessageBox::critical(
+		this,
+		tr("Suppression des annotations conducteurs", "Attention"),
+		QString(
+			tr("Voulez-vous vraiment supprimer les annotations conducteurs de :\n\n%1 ?")
+		).arg(diagramsTitle),
+		QMessageBox::Yes | QMessageBox::No,
+		QMessageBox::No
+	);
+	
+	// if yes remove all
+	if( answer ==  QMessageBox::Yes) {
+		for(int i=0; i<listDiag.count(); i++){
+			ConductorAutoNumerotation can(listDiag.at(i));
+			can.removeNumOfDiagram();
+		}
+	}
+}
+
+/**
+ * @brief set the autonum to all diagram selected
+ */
+void DialogAutoNum::on_pushButton_annotation_clicked(){
+	// Get list of diagrams selected
+	QList<Diagram *>listDiag = dgselect_ -> list_of_DiagramSelected();
+	if(listDiag.count()<=0) return;
+	
+	QString diagramsTitle;
+	for(int i=0; i<listDiag.count(); i++){
+		diagramsTitle += listDiag.at(i) -> title();
+		if(i+1 < listDiag.count()) diagramsTitle += ", ";
+	}
+	// Ask if user is sure to numerate the conductor
+	QMessageBox::StandardButton answer = QET::MessageBox::warning(
+		this,
+		tr("Annotation des conducteurs", "Attention"),
+		QString(
+			tr("Voulez-vous vraiment annoter les conducteurs de :\n\n%1 ?")
+		).arg(diagramsTitle),
+		QMessageBox::Yes | QMessageBox::No,
+		QMessageBox::No
+	);
+	
+	// if yes numerate all
+	if( answer ==  QMessageBox::Yes) {
+		foreach (Diagram *d, listDiag) {
+			ConductorAutoNumerotation can(d);
+			can.numerateDiagram();
+		}
+	}
+}
+
+/**
+ * @brief Close the dialog
+ */
+void DialogAutoNum::on_pushButton_close_clicked() {
+	close();
+}

Added: trunk/sources/ui/dialogautonum.h
===================================================================
--- trunk/sources/ui/dialogautonum.h	                        (rev 0)
+++ trunk/sources/ui/dialogautonum.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,49 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef DIALOGAUTONUM_H
+#define DIALOGAUTONUM_H
+
+#include <QDialog>
+
+#include "diagram.h"
+#include "ui/diagramselection.h"
+
+namespace Ui {
+	class DialogAutoNum;
+}
+
+class DialogAutoNum : public QDialog
+{
+	Q_OBJECT
+	
+	public:
+	explicit DialogAutoNum(Diagram *dg, QWidget *parent = 0);
+	~DialogAutoNum();
+	
+	private slots:
+	void on_pushButton_annotation_clicked();
+	void on_pushButton_delete_clicked();
+	void on_pushButton_close_clicked();
+	
+	private:
+	Ui::DialogAutoNum *ui;
+	Diagram *dg_;
+	diagramselection *dgselect_;
+};
+
+#endif // DialogAutoNum_H

Added: trunk/sources/ui/dialogautonum.ui
===================================================================
--- trunk/sources/ui/dialogautonum.ui	                        (rev 0)
+++ trunk/sources/ui/dialogautonum.ui	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DialogAutoNum</class>
+ <widget class="QDialog" name="DialogAutoNum">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>482</width>
+    <height>416</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Annotation des schémas</string>
+  </property>
+  <property name="windowIcon">
+   <iconset resource="../../qelectrotech.qrc">
+    <normaloff>:/ico/oxygen-icons/32x32/apps/qelectrotech.png</normaloff>:/ico/oxygen-icons/32x32/apps/qelectrotech.png</iconset>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="configuration_tab">
+      <attribute name="title">
+       <string>Configuration</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <layout class="QVBoxLayout" name="configuration_layout">
+         <property name="spacing">
+          <number>0</number>
+         </property>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="annotation_tab">
+      <attribute name="title">
+       <string>Annotation</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout">
+         <item>
+          <widget class="QGroupBox" name="groupBox_Selection">
+           <property name="title">
+            <string>Sélection</string>
+           </property>
+           <layout class="QVBoxLayout" name="verticalLayout_4">
+            <item>
+             <layout class="QHBoxLayout" name="horizontalLayout_">
+              <item>
+               <widget class="QRadioButton" name="radioButton_conductor">
+                <property name="text">
+                 <string>Conducteurs</string>
+                </property>
+                <property name="icon">
+                 <iconset resource="../../qelectrotech.qrc">
+                  <normaloff>:/ico/22x22/conductor2.png</normaloff>:/ico/22x22/conductor2.png</iconset>
+                </property>
+                <property name="checked">
+                 <bool>true</bool>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QRadioButton" name="radioButton_component">
+                <property name="enabled">
+                 <bool>false</bool>
+                </property>
+                <property name="text">
+                 <string>Composants</string>
+                </property>
+                <property name="icon">
+                 <iconset resource="../../qelectrotech.qrc">
+                  <normaloff>:/ico/22x22/single_page.png</normaloff>:/ico/22x22/single_page.png</iconset>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+            <item>
+             <layout class="QVBoxLayout" name="verticalLayout_Selection"/>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_2">
+           <item>
+            <widget class="QPushButton" name="pushButton_annotation">
+             <property name="toolTip">
+              <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Numérotée les folio sélectionné&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+             </property>
+             <property name="text">
+              <string>Annotation (alpha)</string>
+             </property>
+             <property name="icon">
+              <iconset resource="../../qelectrotech.qrc">
+               <normaloff>:/ico/16x16/edit-select-all.png</normaloff>:/ico/16x16/edit-select-all.png</iconset>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="pushButton_delete">
+             <property name="toolTip">
+              <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Supprimé la numérotation des folio sélectionné&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+             </property>
+             <property name="text">
+              <string>Supprimer l'annotation</string>
+             </property>
+             <property name="icon">
+              <iconset resource="../../qelectrotech.qrc">
+               <normaloff>:/ico/16x16/edit-delete.png</normaloff>:/ico/16x16/edit-delete.png</iconset>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="pushButton_close">
+       <property name="text">
+        <string>Fermer</string>
+       </property>
+       <property name="default">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../../qelectrotech.qrc"/>
+ </resources>
+ <connections/>
+</ui>

Added: trunk/sources/ui/dialogwaiting.cpp
===================================================================
--- trunk/sources/ui/dialogwaiting.cpp	                        (rev 0)
+++ trunk/sources/ui/dialogwaiting.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,83 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dialogwaiting.h"
+#include "ui_dialogwaiting.h"
+#include <QPushButton>
+
+/**
+ * @brief DialogWaiting::DialogWaiting
+ * @param parent
+ */
+DialogWaiting::DialogWaiting(QWidget *parent) :
+	QDialog(parent),
+	ui(new Ui::DialogWaiting)
+{
+	ui->setupUi(this);
+	setTitle(  "..." );
+	setDetail( "..." );
+}
+
+/**
+ * @brief DialogWaiting::~DialogWaiting
+ */
+DialogWaiting::~DialogWaiting() {
+	delete ui;
+}
+
+/**
+ * @brief DialogWaiting::setProgressBar
+ * @param val is the progressBar value
+ */
+void DialogWaiting::setProgressBar(int val){
+	ui->progressBar->setValue(val);
+}
+
+/**
+ * @brief DialogWaiting::setProgressReset, clear progressBar and reset
+ */
+void DialogWaiting::setProgressReset(){
+	ui->progressBar->reset();
+}
+
+/**
+ * @brief DialogWaiting::setProgressBarRange
+ * @param min is the minimum of progressBar
+ * @param max is the maximun of progressBar
+ */
+void DialogWaiting::setProgressBarRange(int min, int max){
+	ui->progressBar->setRange(min,max);
+}
+
+/**
+ * @brief DialogWaiting::setTitle of action
+ * @param val is the string of action
+ */
+void DialogWaiting::setTitle(const QString& val){
+	QString title="<b> "+val+" </b>";
+	ui->labelTitle->setText(title);
+}
+
+/**
+ * @brief DialogWaiting::setDetail of action
+ * @param val is the string of detail action
+ */
+void DialogWaiting::setDetail(const QString& val){
+	ui->label_detail->setText(val);
+}
+

Added: trunk/sources/ui/dialogwaiting.h
===================================================================
--- trunk/sources/ui/dialogwaiting.h	                        (rev 0)
+++ trunk/sources/ui/dialogwaiting.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,46 @@
+/*
+		Copyright 2006-2013 The QElectroTech Team
+		This file is part of QElectroTech.
+		
+		QElectroTech is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 2 of the License, or
+		(at your option) any later version.
+		
+		QElectroTech is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+		
+		You should have received a copy of the GNU General Public License
+		along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DIALOGWAITING_H
+#define DIALOGWAITING_H
+
+#include <QDialog>
+
+namespace Ui {
+	class DialogWaiting;
+}
+
+class DialogWaiting : public QDialog
+{
+	Q_OBJECT
+
+public:
+	explicit DialogWaiting(QWidget *parent = 0);
+	~DialogWaiting();
+
+	void setProgressBar(int val);
+	void setProgressBarRange(int min, int max);
+	void setProgressReset();
+	void setTitle(const QString& val);
+	void setDetail(const QString& val);
+
+	private:
+	Ui::DialogWaiting *ui;
+};
+
+#endif // DIALOGWAITING_H

Added: trunk/sources/ui/dialogwaiting.ui
===================================================================
--- trunk/sources/ui/dialogwaiting.ui	                        (rev 0)
+++ trunk/sources/ui/dialogwaiting.ui	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DialogWaiting</class>
+ <widget class="QDialog" name="DialogWaiting">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>110</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Merci de patienter</string>
+  </property>
+  <property name="windowIcon">
+   <iconset>
+    <normaloff>:/app/process</normaloff>:/app/process</iconset>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="labelTitle">
+       <property name="text">
+        <string>Titre</string>
+       </property>
+       <property name="textFormat">
+        <enum>Qt::AutoText</enum>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QLabel" name="label">
+         <property name="maximumSize">
+          <size>
+           <width>24</width>
+           <height>24</height>
+          </size>
+         </property>
+         <property name="autoFillBackground">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="pixmap">
+          <pixmap resource="../../qelectrotech.qrc">:/ico/48x48/user-away-extended.png</pixmap>
+         </property>
+         <property name="scaledContents">
+          <bool>true</bool>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QProgressBar" name="progressBar">
+         <property name="value">
+          <number>24</number>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_detail">
+       <property name="text">
+        <string>TextLabel</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../../qelectrotech.qrc"/>
+ </resources>
+ <connections/>
+</ui>

Added: trunk/sources/ui/numparteditorw.cpp
===================================================================
--- trunk/sources/ui/numparteditorw.cpp	                        (rev 0)
+++ trunk/sources/ui/numparteditorw.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,162 @@
+#include <QRegExp>
+#include "numparteditorw.h"
+#include "ui_numparteditorw.h"
+
+/**
+ * Constructor
+ */
+NumPartEditorW::NumPartEditorW(QWidget *parent) :
+	QWidget(parent),
+	ui(new Ui::NumPartEditorW),
+	intValidator (new QIntValidator(0,99999,this))
+{
+	ui -> setupUi(this);
+	setType(NumPartEditorW::unit, true);
+}
+
+/**
+ * Constructor
+ * Build with value of @context at position i
+ */
+NumPartEditorW::NumPartEditorW (NumerotationContext &context, int i, QWidget *parent):
+	QWidget(parent),
+	ui(new Ui::NumPartEditorW),
+	intValidator (new QIntValidator(0,99999,this))
+{
+	ui -> setupUi(this);
+
+	//if @context contains nothing build with default value
+	if(context.size()==0) setType(NumPartEditorW::unit, true);
+
+	else {
+		QStringList strl = context.itemAt(i);
+		if (strl.at(0)=="unit") setType(NumPartEditorW::unit, true);
+		else if (strl.at(0)=="ten") setType(NumPartEditorW::ten, true);
+		else if (strl.at(0)=="hundred") setType(NumPartEditorW::hundred, true);
+		else if (strl.at(0)=="string") setType(NumPartEditorW::string);
+		else if (strl.at(0)== "folio") setType(NumPartEditorW::folio);
+		ui -> value_field -> setText(strl.at(1));
+		ui -> increase_spinBox -> setValue(strl.at(2).toInt());
+	}
+}
+
+/**
+ * Destructor
+ */
+NumPartEditorW::~NumPartEditorW()
+{
+	delete intValidator;
+	delete ui;
+}
+
+/**
+ * @brief NumPartEditorW::toNumContext
+ * @return the display to NumerotationContext
+ */
+NumerotationContext NumPartEditorW::toNumContext() {
+	NumerotationContext nc;
+	QString type_str;
+	switch (type_) {
+		case unit:
+			type_str = "unit";
+			break;
+		case ten:
+			type_str = "ten";
+			break;
+		case hundred:
+			type_str = "hundred";
+			break;
+		case string:
+			type_str = "string";
+			break;
+		case folio:
+			type_str = "folio";
+			break;
+	}
+	nc.addValue(type_str, ui -> value_field -> displayText(), ui -> increase_spinBox -> value());
+	return nc;
+}
+
+/**
+ * @brief NumPartEditorW::isValid
+ * @return true if value field isn't empty or if type is folio
+ */
+bool NumPartEditorW::isValid() {
+	if (type_ != folio && ui -> value_field -> text().isEmpty()) return false;
+	return true;
+}
+
+/**
+ * @brief NumPartEditorW::on_type_combo_activated
+ * Action when user change the type comboBox
+ */
+void NumPartEditorW::on_type_combo_activated(int index) {
+	switch (index) {
+		case unit:
+			setType(unit);
+			break;
+		case ten:
+			setType(ten);
+			break;
+		case hundred:
+			setType(hundred);
+			break;
+		case string:
+			setType(string);
+			break;
+		case folio:
+			setType(folio);
+			break;
+	};
+	emit changed();
+}
+
+/**
+ * @brief NumPartEditorW::on_value_field_textChanged
+ * emit changed when @value_field text changed
+ */
+void NumPartEditorW::on_value_field_textEdited() {
+	emit changed();
+}
+
+/**
+ * @brief NumPartEditorW::on_increase_spinBox_valueChanged
+ * emit changed when @increase_spinBox value changed
+ */
+void NumPartEditorW::on_increase_spinBox_valueChanged() {
+	if (!ui -> value_field -> text().isEmpty()) emit changed();
+}
+
+/**
+ * @brief NumPartEditorW::setType
+ * Set good behavior by type @t
+ * @param t, type used
+ * @param fnum, force the behavior of numeric type
+ */
+void NumPartEditorW::setType(NumPartEditorW::type t, bool fnum) {
+	ui -> type_combo -> setCurrentIndex(t);
+
+	//if @t is a numeric type and preview type @type_ isn't a numeric type
+	//or @fnum is true, we set numeric behavior
+	if ( ((t==unit || t==ten || t==hundred) && (type_==string || type_==folio)) || fnum) {
+		ui -> value_field -> clear();
+		ui -> value_field -> setEnabled(true);
+		ui -> value_field -> setValidator(intValidator);
+		ui -> increase_spinBox -> setEnabled(true);
+		ui -> increase_spinBox -> setValue(1);
+	}
+	//@t isn't a numeric type
+	else if (t==string || t==folio) {
+		ui -> value_field -> clear();
+		ui -> increase_spinBox -> setDisabled(true);
+		if (t==string) {
+			ui -> value_field -> setValidator(0);
+			ui -> value_field -> setEnabled(true);
+		}
+		else if (t==folio) {
+			ui -> value_field -> setDisabled(true);
+			ui -> increase_spinBox -> setDisabled(true);
+		}
+	}
+	type_= t;
+}

Added: trunk/sources/ui/numparteditorw.h
===================================================================
--- trunk/sources/ui/numparteditorw.h	                        (rev 0)
+++ trunk/sources/ui/numparteditorw.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,48 @@
+#ifndef NUMPARTEDITORW_H
+#define NUMPARTEDITORW_H
+
+#include <QWidget>
+#include <QValidator>
+#include "numerotationcontext.h"
+
+/**
+ *This class represent a single part num widget. By this widget, we can define and edit
+ *how the num auto must work .
+ *This widget is called by selectautonumw.
+ */
+namespace Ui {
+	class NumPartEditorW;
+}
+
+class NumPartEditorW : public QWidget
+{
+	Q_OBJECT
+	
+	//METHODS
+	public:
+	explicit NumPartEditorW(QWidget *parent = 0);
+	NumPartEditorW (NumerotationContext &, int, QWidget *parent=0);
+	~NumPartEditorW();
+
+	enum type {unit,ten,hundred,string,folio};
+	NumerotationContext toNumContext();
+	bool isValid ();
+
+
+	private slots:
+	void on_type_combo_activated(int);
+	void on_value_field_textEdited();
+	void on_increase_spinBox_valueChanged();
+	void setType (NumPartEditorW::type t, bool=false);
+
+	signals:
+	void changed ();
+	
+	private:
+	Ui::NumPartEditorW *ui;
+	QValidator *intValidator;
+	type type_;
+
+};
+
+#endif // NUMPARTEDITORW_H

Added: trunk/sources/ui/numparteditorw.ui
===================================================================
--- trunk/sources/ui/numparteditorw.ui	                        (rev 0)
+++ trunk/sources/ui/numparteditorw.ui	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NumPartEditorW</class>
+ <widget class="QWidget" name="NumPartEditorW">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QComboBox" name="type_combo">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <item>
+        <property name="text">
+         <string>Chiffre 1</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Chiffre 01</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Chiffre 001</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Texte</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>N° folio</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="value_field">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="increase_spinBox">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="wrapping">
+        <bool>false</bool>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="specialValueText">
+        <string/>
+       </property>
+       <property name="accelerated">
+        <bool>true</bool>
+       </property>
+       <property name="prefix">
+        <string/>
+       </property>
+       <property name="minimum">
+        <number>1</number>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

Added: trunk/sources/ui/selectautonumw.cpp
===================================================================
--- trunk/sources/ui/selectautonumw.cpp	                        (rev 0)
+++ trunk/sources/ui/selectautonumw.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,162 @@
+#include "selectautonumw.h"
+#include "ui_selectautonumw.h"
+#include "diagram.h"
+
+/**
+ * Constructor
+ */
+SelectAutonumW::SelectAutonumW(const QList <Diagram *> &diagrams, Diagram *current_diagram ,QWidget *parent) :
+	QWidget(parent),
+	ui(new Ui::SelectAutonumW),
+	diagram_list(diagrams)
+{
+	ui->setupUi(this);
+
+	initDiagramChooser();
+	if (current_diagram) ui -> diagram_chooser -> setCurrentIndex (diagram_list.indexOf(current_diagram));
+	setCurrentContext();
+}
+
+/**
+ * Destructor
+ */
+SelectAutonumW::~SelectAutonumW()
+{
+	delete ui;
+}
+
+/**
+ * @brief SelectAutonumW::setDiagramChooser
+ * build the content of QComboBox @diagram_chooser.
+ */
+void SelectAutonumW::initDiagramChooser() {
+	for (int i=0; i<diagram_list.size(); ++i) {
+		QString diagram_title = diagram_list.at(i) -> title();
+		if (diagram_title.isEmpty()) diagram_title = (tr("Sch\351ma sans titre"));
+		ui -> diagram_chooser -> addItem(diagram_title);
+	}
+}
+
+/**
+ * @brief SelectAutonumW::setCurrentContext
+ * build the context of current diagram selected in the @diagram_chooser QcomboBox
+ */
+void SelectAutonumW::setCurrentContext() {
+	NumerotationContext nc = diagram_list.at(ui->diagram_chooser->currentIndex()) -> getNumerotation(Diagram::Conductors);
+
+	if (nc.size() == 0) { //@nc contain nothing, build a default numPartEditor
+		on_add_button_clicked();
+		applyEnable(false);
+		return;
+	}
+	for (int i=0; i<nc.size(); ++i) { //build with the content of @nc
+		NumPartEditorW *part= new NumPartEditorW(nc, i, this);
+		connect (part, SIGNAL(changed()), this, SLOT(applyEnable()));
+		num_part_list_ << part;
+		ui -> editor_layout -> addWidget(part);
+	}
+	applyEnable(false);
+}
+
+/**
+ * @brief SelectAutonumW::toNumContext
+ * @return the content to @num_part_list to NumerotationContext
+ */
+NumerotationContext SelectAutonumW::toNumContext() const {
+	NumerotationContext nc;
+	foreach (NumPartEditorW *npew, num_part_list_) nc << npew -> toNumContext();
+	return nc;
+}
+
+/**
+ * @brief SelectAutonumW::on_add_button_clicked
+ *	Action on add_button, add a @NumPartEditor
+ */
+void SelectAutonumW::on_add_button_clicked() {
+	applyEnable(false);
+	NumPartEditorW *part = new NumPartEditorW(this);
+	connect (part, SIGNAL(changed()), this, SLOT(applyEnable()));
+	num_part_list_ << part;
+	ui -> editor_layout -> addWidget(part);
+}
+
+/**
+ * @brief SelectAutonumW::on_remove_button_clicked
+ *	Action on remove button, remove the last @NumPartEditor
+ */
+void SelectAutonumW::on_remove_button_clicked() {
+	//remove if @num_part_list contains more than one item
+	if (num_part_list_.size() > 1) {
+		NumPartEditorW *part = num_part_list_.takeLast();
+		disconnect(part, SIGNAL(changed()), this, SLOT(applyEnable()));
+		delete part;
+	}
+	applyEnable();
+}
+
+/**
+ * @brief SelectAutonumW::on_diagram_chooser_activated
+ * Action on diagram_chooser
+ */
+void SelectAutonumW::on_diagram_chooser_activated() {
+	foreach(NumPartEditorW *npew, num_part_list_) delete npew;
+	num_part_list_.clear();
+	setCurrentContext();
+}
+
+/**
+ * @brief SelectAutonumW::on_buttonBox_clicked
+ * Action on @buttonBox clicked
+ */
+void SelectAutonumW::on_buttonBox_clicked(QAbstractButton *button) {
+	//transform button to int
+	int answer = ui -> buttonBox -> buttonRole(button);
+
+	switch (answer) {
+			//reset the displayed context to default context of @diagram_chooser.
+		case QDialogButtonBox::ResetRole:
+			on_diagram_chooser_activated();
+			applyEnable(false);
+			break;
+			//help dialog
+		case QDialogButtonBox::HelpRole:
+			QMessageBox::information (this, tr("Autonum\351rotation", "title window"),
+																	tr("C'est ici que vous pouvez d\351finir la mani\350re dont sera num\351rot\351 les nouveaux conducteurs.\n"
+																	   "-Chaque Folio poss\350de sa propre m\351thode de num\351rotation.\n"
+																	   "-Une num\351rotation est compos\351e d'une variable minimum.\n"
+																	   "-Vous pouvez ajouter ou supprimer une variable de num\351rotation par le biais des boutons - et +.\n"
+																	   "-Une variable de num\351rotation comprant: un type, une valeur et une incr\351mentation.\n"
+
+																	   "\n-les types \"Chiffre 1\", \"Chiffre 01\" et \"Chiffre 001\", repr\351sente un type num\351rique d\351finie dans le champs \"Valeur\", "
+																	   "qui s'incr\351mente \340 chaque nouveau conducteur de la valeur du champ \"Incr\351mentation\".\n"
+																	   "-\"Chiffre 01\" et \"Chiffre 001\", sont respectivement repr\351sent\351 sur le sch\351ma par deux et trois digits minimum.\n"
+																	   "Si le chiffre d\351finie dans le champs Valeur poss\351de moins de digits que le type choisit,"
+																	   "celui-ci sera pr\351c\351d\351 par un ou deux 0 afin de respecter son type.\n"
+
+																	   "\n-Le type \"Texte\", repr\351sente un texte fixe.\nLe champs \"Incr\351mentation\" n'est pas utilis\351.\n"
+
+																	   "\n-Le type \"N\260 folio\" repr\351sente le n\260 du folio en cours.\nLes autres champs ne sont pas utilis\351s.",
+																	   "help dialog about the autonumerotation"));
+
+			//apply the context in the diagram displayed by @diagram_chooser.
+		case QDialogButtonBox::ApplyRole:
+			NumerotationContext nc = toNumContext();
+			diagram_list.at(ui -> diagram_chooser -> currentIndex()) -> setNumerotation(Diagram::Conductors, nc);
+			applyEnable(false);
+			break;
+	};
+}
+
+/**
+ * @brief SelectAutonumW::applyEnable
+ * enable/disable the apply button
+ */
+void SelectAutonumW::applyEnable(bool b) {
+	if (b){
+		bool valid= true;
+		foreach (NumPartEditorW *npe, num_part_list_) if (!npe -> isValid()) valid= false;
+		ui -> buttonBox -> button(QDialogButtonBox::Apply) -> setEnabled(valid);
+	}
+	else
+		ui -> buttonBox -> button(QDialogButtonBox::Apply) -> setEnabled(b);
+}

Added: trunk/sources/ui/selectautonumw.h
===================================================================
--- trunk/sources/ui/selectautonumw.h	                        (rev 0)
+++ trunk/sources/ui/selectautonumw.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,41 @@
+#ifndef SELECTAUTONUMW_H
+#define SELECTAUTONUMW_H
+
+#include <QWidget>
+#include "diagram.h"
+#include "ui/numparteditorw.h"
+
+namespace Ui {
+	class SelectAutonumW;
+}
+
+class SelectAutonumW : public QWidget
+{
+	Q_OBJECT
+	
+	//METHODS
+	public:
+	explicit SelectAutonumW(const QList <Diagram *> &, Diagram * = 0, QWidget *parent = 0);
+	~SelectAutonumW();
+	
+	private:
+	void initDiagramChooser();
+	void setCurrentContext ();
+	NumerotationContext toNumContext() const;
+
+	//SLOT
+	private slots:
+	void on_add_button_clicked();
+	void on_remove_button_clicked();
+	void on_diagram_chooser_activated();
+	void on_buttonBox_clicked(QAbstractButton *);
+	void applyEnable (bool = true);
+
+	//ATTRIBUTS
+	private:
+	Ui::SelectAutonumW *ui;
+	const QList <Diagram *> diagram_list;
+	QList <NumPartEditorW *> num_part_list_;
+};
+
+#endif // SELECTAUTONUMW_H

Added: trunk/sources/ui/selectautonumw.ui
===================================================================
--- trunk/sources/ui/selectautonumw.ui	                        (rev 0)
+++ trunk/sources/ui/selectautonumw.ui	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SelectAutonumW</class>
+ <widget class="QWidget" name="SelectAutonumW">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Folio:  </string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="diagram_chooser">
+       <property name="toolTip">
+        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choisir le folio&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+       </property>
+       <property name="maxVisibleItems">
+        <number>5</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="remove_button">
+       <property name="toolTip">
+        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Supprimer une variable de numérotation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="icon">
+        <iconset resource="../../qelectrotech.qrc">
+         <normaloff>:/ico/22x22/list-remove.png</normaloff>:/ico/22x22/list-remove.png</iconset>
+       </property>
+       <property name="flat">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="add_button">
+       <property name="toolTip">
+        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Ajouter une variable de numérotation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="icon">
+        <iconset resource="../../qelectrotech.qrc">
+         <normaloff>:/ico/22x22/list-add.png</normaloff>:/ico/22x22/list-add.png</iconset>
+       </property>
+       <property name="flat">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="definition_groupe">
+     <property name="title">
+      <string>Définition</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <layout class="QVBoxLayout" name="editor_layout">
+        <property name="spacing">
+         <number>0</number>
+        </property>
+        <item>
+         <layout class="QHBoxLayout" name="label_layout">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="type_label">
+            <property name="text">
+             <string>Type</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="value_label">
+            <property name="text">
+             <string>Valeur</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="increase_label">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="text">
+             <string>Incrémentation</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Apply|QDialogButtonBox::Help|QDialogButtonBox::Reset</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../../qelectrotech.qrc"/>
+ </resources>
+ <connections/>
+</ui>

Added: trunk/sources/xmlelementdefinition.cpp
===================================================================
--- trunk/sources/xmlelementdefinition.cpp	                        (rev 0)
+++ trunk/sources/xmlelementdefinition.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,231 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "xmlelementdefinition.h"
+#include "xmlelementscategory.h"
+#include "xmlelementscollection.h"
+#include "qet.h"
+#include "qetproject.h"
+
+/**
+	Construit une definition d'element vide
+*/
+XmlElementDefinition::XmlElementDefinition(const QString &name, XmlElementsCategory *category, XmlElementsCollection *collection) :
+	ElementDefinition(category, collection),
+	parent_category_(category)
+{
+	name_ = name;
+	QDomElement new_elmt = xml_element_.createElement("element");
+	new_elmt.setAttribute("name", name_);
+	xml_element_.appendChild(new_elmt);
+	element_definition_ = xml_element_.createElement("definition");
+	new_elmt.appendChild(element_definition_);
+}
+
+/**
+	Construit une definition d'element a partir de sa representation XML
+*/
+XmlElementDefinition::XmlElementDefinition(const QDomElement &xml_element, XmlElementsCategory *category, XmlElementsCollection *collection) :
+	ElementDefinition(category, collection),
+	parent_category_(category)
+{
+	xml_element_.appendChild(xml_element_.importNode(xml_element, true));
+	reload();
+}
+
+/**
+	Destructeur
+*/
+XmlElementDefinition::~XmlElementDefinition() {
+}
+
+/**
+	@return la definition XML de l'element
+*/
+QDomElement XmlElementDefinition::xml() {
+	return(element_definition_);
+}
+
+/**
+	Change la definition XML de l'element
+	@param xml_element Nouvelle definition XML de l'element
+	@return true si l'operation s'est bien passee, false sinon
+*/
+bool XmlElementDefinition::setXml(const QDomElement &xml_element) {
+	// oublie toute la structure XML
+	xml_element_.clear();
+	
+	// cree la structure XML contenant le nom de l'element
+	QDomElement new_elmt = xml_element_.createElement("element");
+	new_elmt.setAttribute("name", name_);
+	xml_element_.appendChild(new_elmt);
+	
+	// importe la nouvelle definition XML de l'element
+	element_definition_ = xml_element_.importNode(xml_element, true).toElement();
+	new_elmt.appendChild(element_definition_);
+	is_null_ = false;
+	return(true);
+}
+
+bool XmlElementDefinition::write() {
+	/*
+		Contrairement a un schema, cet objet ne contient pas deux versions de
+		l'element (element avant edition et element edite) Cette methode ne
+		fait donc rien d'autre qu'emettre le signal written(), le travail ayant
+		deja ete fait par la methode setXml().
+	*/
+	emit(written());
+	return(true);
+}
+
+/**
+	@return true si l'element n'est pas exploitable (definition ou nom non
+	trouve)
+*/
+bool XmlElementDefinition::isNull() const {
+	return(name_.isEmpty() || is_null_);
+}
+
+/**
+	@return Le nom de cet element dans l'arborescence
+*/
+QString XmlElementDefinition::pathName() const {
+	return(name_);
+}
+
+/**
+	@return le chemin virtuel de cet element
+*/
+QString XmlElementDefinition::virtualPath() {
+	// il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection
+	if (!hasParentCollection() || name_.isEmpty()) return(QString());
+	
+	if (parent_category_) {
+		QString tmp(parent_category_ -> virtualPath());
+		if (!tmp.isEmpty()) tmp += "/";
+		return(tmp + name_);
+	} else {
+		return(name_);
+	}
+}
+
+/**
+	Recharge le contenu de l'element
+*/
+void XmlElementDefinition::reload() {
+	is_null_ = true;
+	
+	// on recupere le nom de l'element
+	QDomElement doc_elmt = xml_element_.documentElement();
+	name_ = doc_elmt.attribute("name");
+	if (name_.isEmpty()) return;
+	
+	// on recupere la definition de l'element
+	for (QDomNode node = doc_elmt.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		if (!node.isElement()) continue;
+		QDomElement current_element = node.toElement();
+		if (current_element.tagName() == "definition" && current_element.attribute("type") == "element") {
+			element_definition_ = current_element;
+			break;
+		}
+	}
+	
+	// l'element est nul si aucune definition n'a ete trouvee
+	is_null_ = (element_definition_.isNull());
+}
+
+/**
+	@return true si l'element existe, false sinon
+*/
+bool XmlElementDefinition::exists() {
+	// la seule raison qu'un element aurait de ne pas exister est l'absence
+	// de nom
+	return(!name_.isEmpty());
+}
+
+/**
+	@return true si la categorie est accessible en lecture, false sinon
+*/
+bool XmlElementDefinition::isReadable() {
+	// une categorie XML n'a aucune raison de ne pas etre accessible en lecture
+	return(true);
+}
+
+/**
+	@return true si la categorie est accessible en ecriture, false sinon
+*/
+bool XmlElementDefinition::isWritable() {
+	// une categorie XML peut etre en lecture seule si le projet auquel elle
+	// appartient l'est
+	if (QETProject *parent_project = project()) {
+		return(!parent_project -> isReadOnly());
+	} else {
+		return(true);
+	}
+}
+
+/**
+	Supprime l'element
+	@return true si l'operation s'est bien passee, false sinon
+*/
+bool XmlElementDefinition::remove() {
+	removeContent();
+	emit(removed(name_));
+	return(true);
+}
+
+/**
+	@return toujours false, car un element XML n'a pas de chemin de type
+	fichier
+*/
+bool XmlElementDefinition::hasFilePath() {
+	// une categorie XML n'a pas de chemin de type fichier
+	return(false);
+}
+
+/**
+	@return une chaine vide, car un element XML n'a pas de chemin de type
+	fichier
+*/
+QString XmlElementDefinition::filePath() {
+	// une categorie XML n'a pas de chemin de type fichier
+	return(QString());
+}
+
+/**
+	Ne fait rien, car un element XML n'a pas de chemin de type fichier
+*/
+void XmlElementDefinition::setFilePath(const QString &) {
+	// une categorie XML n'a pas de chemin de type fichier
+}
+
+/**
+	@return a null QDateTime object since an XML element does not have a
+	modification time.
+*/
+/**
+	@return the time of the last modification (mtime) for this element file
+*/
+QDateTime XmlElementDefinition::modificationTime() const {
+	return QDateTime();
+}
+
+QDomElement XmlElementDefinition::writeXml(QDomDocument &xml_doc) const {
+	QDomElement element_elmt = xml_element_.documentElement();
+	QDomNode new_node = xml_doc.importNode(element_elmt, true);
+	return(new_node.toElement());
+}

Added: trunk/sources/xmlelementdefinition.h
===================================================================
--- trunk/sources/xmlelementdefinition.h	                        (rev 0)
+++ trunk/sources/xmlelementdefinition.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,70 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef XML_ELEMENT_DEFINITION
+#define XML_ELEMENT_DEFINITION
+#include <QtXml>
+#include "elementdefinition.h"
+class XmlElementsCategory;
+class XmlElementsCollection;
+/**
+	This class represents an element definition stored within an XML document
+	(e.g. the embedded collection of a QET project).
+*/
+class XmlElementDefinition : public ElementDefinition {
+	Q_OBJECT
+	
+	public:
+	XmlElementDefinition(const QString &, XmlElementsCategory * = 0, XmlElementsCollection * = 0);
+	XmlElementDefinition(const QDomElement &, XmlElementsCategory * = 0, XmlElementsCollection * = 0);
+	virtual ~XmlElementDefinition();
+	
+	private:
+	XmlElementDefinition(const XmlElementDefinition &);
+	
+	// methods
+	public:
+	virtual QDomElement xml();
+	virtual bool setXml(const QDomElement &);
+	virtual bool write();
+	virtual bool isNull() const;
+	virtual QString pathName() const;
+	virtual QString virtualPath();
+	virtual void reload();
+	virtual bool exists();
+	virtual bool isReadable();
+	virtual bool isWritable();
+	virtual bool remove();
+	virtual bool hasFilePath();
+	virtual QString filePath();
+	virtual void setFilePath(const QString &);
+	virtual QDateTime modificationTime() const;
+	virtual QDomElement writeXml(QDomDocument &) const;
+	
+	signals:
+	void written();
+	void removed(const QString &);
+	
+	// attributes
+	private:
+	bool is_null_;
+	QString name_;
+	XmlElementsCategory *parent_category_;
+	QDomDocument xml_element_;
+	QDomElement element_definition_;
+};
+#endif

Added: trunk/sources/xmlelementscategory.cpp
===================================================================
--- trunk/sources/xmlelementscategory.cpp	                        (rev 0)
+++ trunk/sources/xmlelementscategory.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,487 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "xmlelementscategory.h"
+#include "xmlelementscollection.h"
+#include "xmlelementdefinition.h"
+#include "qetproject.h"
+
+/**
+	Cree une categorie XML vide
+	@param parent Categorie parente
+	@param collection Collection a laquelle cette categorie appartient
+*/
+XmlElementsCategory::XmlElementsCategory(XmlElementsCategory *parent, XmlElementsCollection *collection) :
+	ElementsCategory(parent, collection),
+	xml_parent_collection_(collection),
+	xml_parent_category_(parent)
+{
+}
+
+/**
+	Cree une categorie XML a partir d'un element XML
+	@param xml_element Element XML a analyser
+	@param parent Categorie parente
+	@param collection Collection a laquelle cette categorie appartient
+*/
+XmlElementsCategory::XmlElementsCategory(const QDomElement &xml_element, XmlElementsCategory *parent, XmlElementsCollection *collection) :
+	ElementsCategory(parent, collection),
+	xml_parent_collection_(collection),
+	xml_parent_category_(parent)
+{
+	loadContent(xml_element);
+}
+
+/**
+	Destructeur
+*/
+XmlElementsCategory::~XmlElementsCategory() {
+	deleteContent();
+}
+
+/**
+	@return le nom de la categorie utilisable dans un chemin (virtuel ou reel)
+*/
+QString XmlElementsCategory::pathName() const {
+	return(name_);
+}
+
+/**
+	@return le chemin virtuel de la categorie, sans le protocole
+*/
+QString XmlElementsCategory::virtualPath() {
+	// il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection
+	if (!hasParentCollection() || name_.isEmpty()) return(QString());
+	
+	if (parent_category_) {
+		QString tmp(parent_category_ -> virtualPath());
+		if (!tmp.isEmpty()) tmp += "/";
+		return(tmp + name_);
+	} else {
+		return(name_);
+	}
+}
+
+/**
+	@return toujours false, car une categorie XML n'a pas de chemin de type
+	fichier
+*/
+bool XmlElementsCategory::hasFilePath() {
+	// une categorie XML n'a pas de chemin de type fichier
+	return(false);
+}
+
+/**
+	@return une chaine vide, car une categorie XML n'a pas de chemin de type
+	fichier
+*/
+QString XmlElementsCategory::filePath() {
+	// une categorie XML n'a pas de chemin de type fichier
+	return(QString());
+}
+
+/**
+	Ne fait rien, car une categorie XML n'a pas de chemin de type fichier
+*/
+void XmlElementsCategory::setFilePath(const QString &) {
+	// une categorie XML n'a pas de chemin de type fichier
+}
+
+/**
+	@return la liste des sous-categories de la categorie
+*/
+QList<ElementsCategory *> XmlElementsCategory::categories() {
+	QList<ElementsCategory *> cat_list;
+	
+	QList<QString> keys(categories_.keys());
+	qSort(keys.begin(), keys.end());
+	foreach(QString key, keys) cat_list << categories_[key];
+	return(cat_list);
+}
+
+/**
+	@return la categorie correspondant au chemin virtuel cat_path, ou 0 en cas d'echec
+	@param cat_path Chemin virtuel de la categorie voulue
+*/
+ElementsCategory *XmlElementsCategory::category(const QString &cat_path) {
+	// recupere les differentes parties du chemin
+	QString cat_path_(cat_path);
+	QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		return(this);
+	} else {
+		// a-t-on la premiere sous-categorie ?
+		if (!categories_.contains(path_parts.first())) {
+			return(0);
+		}
+		
+		// on a la premiere sous-categorie
+		ElementsCategory *first_sub_cat = categories_.value(path_parts.first());
+		
+		if (path_parts.count() == 1) return(first_sub_cat);
+		
+		// on demande a la premiere sous-categorie de fournir la categorie finale
+		path_parts.removeFirst();
+		return(first_sub_cat -> category(path_parts.join("/")));
+	}
+}
+
+/**
+	Cree une categorie. La categorie parente doit exister.
+	@param path chemin d'une categorie a creer sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3
+	@return la nouvelle categorie demandee, ou 0 en cas d'echec
+*/
+ElementsCategory *XmlElementsCategory::createCategory(const QString &path) {
+	// on ne doit pas etre en lecture seule
+	if (!isWritable()) return(0);
+	
+	// recupere les differentes parties du chemin
+	QString cat_path_(path);
+	QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		// le chemin est vide, on renvoie 0
+		return(0);
+	} else if (path_parts.count() == 1) {
+		// il n'y a plus qu'une categorie dans le chemin : il faut la creer ici
+		
+		// on verifie si la categorie n'existe pas deja
+		if (categories_.contains(path_parts[0])) {
+			return(categories_.value(path_parts[0]));
+		}
+		
+		// on cree un objet
+		XmlElementsCategory *new_category = new XmlElementsCategory(
+			this,
+			xml_parent_collection_
+		);
+		new_category -> name_ = path_parts[0];
+		
+		// on l'integre dans la liste des sous-categories connues
+		categories_.insert(path_parts[0], new_category);
+		connect(new_category, SIGNAL(written()), this, SLOT(componentWritten()));
+		connect(new_category, SIGNAL(removed(const QString &)), this, SLOT(componentRemoved(const QString &)));
+		
+		// on le renvoie
+		return(new_category);
+	} else {
+		// il y a plusieurs categories dans le chemin :
+		// on delegue le travail a la premiere sous-categorie
+		
+		// a-t-on la premiere sous-categorie ?
+		if (!categories_.contains(path_parts.first())) {
+			return(0);
+		}
+		
+		// on a la premiere sous-categorie
+		ElementsCategory *first_sub_cat = categories_.value(path_parts.first());
+		
+		// on demande a la premiere sous-categorie de fournir la categorie finale
+		path_parts.removeFirst();
+		return(first_sub_cat -> category(path_parts.join("/")));
+	}
+}
+
+/**
+	@return la liste des elements de la categorie
+*/
+QList<ElementDefinition *> XmlElementsCategory::elements() {
+	QList<ElementDefinition *> elmt_list;
+	
+	QList<QString> keys(elements_.keys());
+	qSort(keys.begin(), keys.end());
+	foreach(QString key, keys) elmt_list << elements_[key];
+	return(elmt_list);
+}
+
+/**
+	@return l'element correspondant au chemin virtuel elmt_path, ou 0 en cas d'echec
+	@param elmt_path Chemin virtuel de l'element voulu
+*/
+ElementDefinition *XmlElementsCategory::element(const QString &elmt_path) {
+	// recupere les differentes parties du chemin
+	QString elmt_path_(elmt_path);
+	QStringList path_parts = elmt_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		// chemin vide
+		return(0);
+	} else if (path_parts.count() == 1) {
+		// seulement le nom de fichier
+		QString element_filename = path_parts.takeLast();
+		if (!elements_.contains(element_filename)) {
+			return(0);
+		} else {
+			return(elements_.value(element_filename));
+		}
+	} else {
+		// separe le nom de fichier du chemin de la categorie et recupere celle-ci
+		QString element_filename = path_parts.takeLast();
+		ElementsCategory *elmt_cat = category(path_parts.join("/"));
+		if (!elmt_cat) {
+			return(0);
+		} else {
+			return(elmt_cat -> element(element_filename));
+		}
+	}
+}
+
+/**
+	Cree un element. La categorie parente doit exister.
+	@param path chemin d'un element a creer sous la forme d'une adresse
+	virtuelle comme common://cat1/cat2/cat3/dog.elmt
+	@return le nouvel element demande, ou 0 en cas d'echec
+*/
+ElementDefinition *XmlElementsCategory::createElement(const QString &path) {
+	// on ne doit pas etre en lecture seule
+	if (!isWritable()) return(0);
+	
+	// recupere les differentes parties du chemin
+	QString cat_path_(path);
+	QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive);
+	
+	if (!path_parts.count()) {
+		// le chemin est vide, on renvoie 0
+		return(0);
+	} else if (path_parts.count() == 1) {
+		// il n'y a plus que l'element dans le chemin : il faut le creer ici
+		
+		// on verifie si l'element n'existe pas deja
+		if (elements_.contains(path_parts[0])) {
+			return(elements_.value(path_parts[0]));
+		}
+		
+		// on cree un objet
+		XmlElementDefinition *new_element = new XmlElementDefinition(
+			path_parts[0],
+			this,
+			xml_parent_collection_
+		);
+		
+		// on l'integre dans la liste des elements connus
+		elements_.insert(path_parts[0], new_element);
+		connect(new_element, SIGNAL(written()), this, SLOT(componentWritten()));
+		connect(new_element, SIGNAL(removed(const QString &)), this, SLOT(componentRemoved(const QString &)));
+		
+		// on le renvoie
+		return(new_element);
+	} else {
+		// il y a plusieurs categories dans le chemin :
+		// on delegue le travail a la premiere sous-categorie
+		
+		// a-t-on la premiere sous-categorie ?
+		if (!categories_.contains(path_parts.first())) {
+			return(0);
+		}
+		
+		// on a la premiere sous-categorie
+		ElementsCategory *first_sub_cat = categories_.value(path_parts.first());
+		
+		// on demande a la premiere sous-categorie de fournir la categorie finale
+		path_parts.removeFirst();
+		return(first_sub_cat -> createElement(path_parts.join("/")));
+	}
+}
+
+/**
+	@return true si la categorie existe, false sinon
+*/
+bool XmlElementsCategory::exists() {
+	// la seule raison qu'une categorie aurait de ne pas exister est l'absence
+	// de nom
+	return(!name_.isEmpty());
+}
+
+/**
+	@return true si la categorie est accessible en lecture, false sinon
+*/
+bool XmlElementsCategory::isReadable() {
+	// une categorie XML n'a aucune raison de ne pas etre accessible en lecture
+	return(true);
+}
+
+/**
+	@return true si la categorie est accessible en ecriture, false sinon
+*/
+bool XmlElementsCategory::isWritable() {
+	// une categorie XML peut etre en lecture seule si le projet auquel elle
+	// appartient l'est
+	if (QETProject *parent_project = project()) {
+		return(!parent_project -> isReadOnly());
+	} else {
+		return(true);
+	}
+}
+
+/**
+	Cette methode ne fait rien. Recharger une categorie XML n'a pas vraiment de
+	sens.
+*/
+void XmlElementsCategory::reload() {
+}
+
+/**
+	Supprime la categorie et son contenu
+*/
+bool XmlElementsCategory::remove() {
+	removeContent();
+	emit(removed(name_));
+	write();
+	return(true);
+}
+
+/**
+	Supprime le contenu de la categorie sans supprimer la categorie elle-meme.
+*/
+bool XmlElementsCategory::removeContent() {
+	// suppression des sous-categories
+	foreach(QString cat_name, categories_.keys()) {
+		ElementsCategory *cat = categories_.value(cat_name);
+		if (cat -> remove()) {
+			categories_.take(cat_name);
+			delete cat;
+		} else {
+			return(false);
+		}
+	}
+	
+	// suppression des elements
+	foreach(QString elmt_name, elements_.keys()) {
+		ElementDefinition *elmt = elements_.value(elmt_name);
+		if (elmt -> remove()) {
+			elements_.take(elmt_name);
+			delete elmt;
+		} else {
+			return(false);
+		}
+	}
+	write();
+	return(true);
+}
+
+/**
+	Ecrit la categorie.
+	Comme il s'agit d'une categorie embarquee, cette methode emet simplement le
+	signal written pour indiquer qu'il faut enregistrer la collection / le
+	projet.
+*/
+bool XmlElementsCategory::write() {
+	// indique que la categorie a ete changee
+	emit(written());
+	return(true);
+}
+
+/**
+	@return un Element XML decrivant la categorie et son contenu
+	@param xml_doc Document XML a utiliser pour creer l'element XML
+*/
+QDomElement XmlElementsCategory::writeXml(QDomDocument &xml_doc) const {
+	QDomElement category_elmt = xml_doc.createElement("category");
+	if (!isRootCategory()) {
+		category_elmt.setAttribute("name", name_);
+		category_elmt.appendChild(category_names.toXml(xml_doc));
+	}
+	
+	foreach(XmlElementsCategory *subcat, categories_) {
+		category_elmt.appendChild(subcat -> writeXml(xml_doc));
+	}
+	
+	foreach(XmlElementDefinition *elmt, elements_) {
+		category_elmt.appendChild(elmt -> writeXml(xml_doc));
+	}
+	
+	return(category_elmt);
+}
+
+/**
+	Gere le fait qu'une sous-categorie ou un element ait ete enregistre
+*/
+void XmlElementsCategory::componentWritten() {
+	write();
+}
+
+/**
+	Gere le fait qu'une sous-categorie ou un element ait ete supprime
+	@param path Chemin de l'element ou de la categorie supprime(e)
+*/
+void XmlElementsCategory::componentRemoved(const QString &path) {
+	if (elements_.contains(path)) {
+		elements_.remove(path);
+		write();
+	} else if (categories_.contains(path)) {
+		categories_.remove(path);
+		write();
+	}
+}
+
+/**
+	Supprime le contenu de la categorie en memoire
+*/
+void XmlElementsCategory::deleteContent() {
+	// suppression des elements
+	foreach(QString elmt_name, elements_.keys()) {
+		XmlElementDefinition *elmt = elements_.take(elmt_name);
+		delete elmt;
+	}
+	
+	// suppression des categories
+	foreach(QString cat_name, categories_.keys()) {
+		XmlElementsCategory *cat = categories_.take(cat_name);
+		delete cat;
+	}
+}
+
+/**
+	Charge dans cet objet le contenu de la categorie a partir d'un element XML.
+	@param xml_element element XML a analyser
+*/
+void XmlElementsCategory::loadContent(const QDomElement &xml_element) {
+	deleteContent();
+	name_.clear();
+	category_names.clearNames();
+	
+	// charge le nom de la categorie pour son chemin virtuel
+	name_ = xml_element.attribute("name");
+	
+	// charge les noms affiches de la categorie
+	category_names.fromXml(xml_element);
+	
+	// charge les categories et elements
+	QDomElement current_element;
+	for (QDomNode node = xml_element.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
+		if (!node.isElement()) continue;
+		current_element = node.toElement();
+		
+		// les sous-categories et elements sans nom sont ignores
+		if (!current_element.hasAttribute("name")) continue;
+		
+		if (current_element.tagName() == "category") {
+			XmlElementsCategory *new_category = new XmlElementsCategory(current_element, this, xml_parent_collection_);
+			categories_.insert(current_element.attribute("name"), new_category);
+			connect(new_category, SIGNAL(written()), this, SLOT(componentWritten()));
+			connect(new_category, SIGNAL(removed(const QString &)), this, SLOT(componentRemoved(const QString &)));
+		} else if (current_element.tagName() == "element") {
+			
+			XmlElementDefinition *new_element = new XmlElementDefinition(current_element, this, xml_parent_collection_);
+			elements_.insert(current_element.attribute("name"), new_element);
+			connect(new_element, SIGNAL(written()), this, SLOT(componentWritten()));
+			connect(new_element, SIGNAL(removed(const QString &)), this, SLOT(componentRemoved(const QString &)));
+		}
+	}
+}

Added: trunk/sources/xmlelementscategory.h
===================================================================
--- trunk/sources/xmlelementscategory.h	                        (rev 0)
+++ trunk/sources/xmlelementscategory.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,95 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef XML_ELEMENTS_CATEGORY
+#define XML_ELEMENTS_CATEGORY
+#include <QtXml>
+#include "elementscategory.h"
+class XmlElementsCollection;
+class XmlElementDefinition;
+/**
+	This class represents an elements category stored within an XML document
+	(e.g. the embedded collection of a QET project).
+*/
+class XmlElementsCategory : public ElementsCategory {
+	Q_OBJECT
+	
+	// constructors, destructor
+	public:
+	XmlElementsCategory(XmlElementsCategory * = 0, XmlElementsCollection * = 0);
+	XmlElementsCategory(const QDomElement &, XmlElementsCategory * = 0, XmlElementsCollection * = 0);
+	virtual ~XmlElementsCategory();
+	
+	private:
+	XmlElementsCategory(const XmlElementsCategory &);
+	
+	// methods
+	public:
+	virtual QString pathName() const;
+	virtual QString virtualPath();
+	
+	virtual QString filePath();
+	virtual bool hasFilePath();
+	virtual void setFilePath(const QString &);
+	
+	virtual QList<ElementsCategory *> categories();
+	virtual ElementsCategory *category(const QString &);
+	virtual ElementsCategory *createCategory(const QString &);
+	
+	virtual QList<ElementDefinition *> elements();
+	virtual ElementDefinition *element(const QString &);
+	virtual ElementDefinition *createElement(const QString &);
+	
+	virtual bool exists();
+	virtual bool isReadable();
+	virtual bool isWritable();
+	
+	virtual void reload();
+	virtual bool remove();
+	virtual bool removeContent();
+	virtual bool write();
+	
+	virtual QDomElement writeXml(QDomDocument &) const;
+	
+	public slots:
+	void componentWritten();
+	void componentRemoved(const QString &path);
+	
+	signals:
+	void written();
+	void removed(const QString &);
+	
+	private:
+	void deleteContent();
+	void loadContent(const QDomElement &);
+	
+	// attributes
+	protected:
+	/// Parent collection
+	XmlElementsCollection *xml_parent_collection_;
+	/// Parent category
+	XmlElementsCategory   *xml_parent_category_;
+	/// Child categories
+	QHash<QString, XmlElementsCategory  *> categories_;
+	/// Child elements
+	QHash<QString, XmlElementDefinition *> elements_;
+	/// Nae of this category within the tree
+	QString name_;
+	/// XML description of this category
+	QDomDocument xml_element_;
+};
+#endif

Added: trunk/sources/xmlelementscollection.cpp
===================================================================
--- trunk/sources/xmlelementscollection.cpp	                        (rev 0)
+++ trunk/sources/xmlelementscollection.cpp	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,173 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "xmlelementscollection.h"
+#include "xmlelementscategory.h"
+#include "qetproject.h"
+#include "qetapp.h"
+
+/**
+	Construit une collection vide
+	@param parent Item parent
+*/
+XmlElementsCollection::XmlElementsCollection(ElementsCollectionItem *parent) :
+	ElementsCollection(parent)
+{
+	protocol_ = "unknown";
+	project_ = 0;
+	// cree une categorie racine vide
+	root = new XmlElementsCategory(0, this);
+	connect(root, SIGNAL(written()), this, SLOT(componentWritten()));
+}
+
+/**
+	Construit une collection a partir d'un element XML suppose la decrire
+	@param xml_element Element XML decrivant la collection
+	@param parent Item parent
+*/
+XmlElementsCollection::XmlElementsCollection(const QDomElement &xml_element, ElementsCollectionItem *parent) :
+	ElementsCollection(parent)
+{
+	protocol_ = "unknown";
+	project_ = 0;
+	// cree sa categorie racine a partir de l'element XML
+	root = new XmlElementsCategory(xml_element, 0, this);
+	connect(root, SIGNAL(written()), this, SLOT(componentWritten()));
+}
+
+/**
+	Destructeur
+*/
+XmlElementsCollection::~XmlElementsCollection() {
+	deleteContent();
+}
+
+QString XmlElementsCollection::title() const {
+	if (!title_.isEmpty()) return(title_);
+	
+	// if the title attribute is empty, we generate a suitable one using the
+	// parent project
+	QString final_title;
+	if (project_) {
+		QString project_title = project_ -> title();
+		if (project_title.isEmpty()) {
+			final_title = QString(
+				tr(
+					"Collection du projet sans titre (id %1)",
+					"Elements collection title when the parent project has an empty title -- %1 is the project internal id"
+				)
+			);
+			final_title = final_title.arg(QETApp::projectId(project_));
+		} else {
+			final_title = QString(
+				tr(
+					"Collection du projet \"%1\"",
+					"Elements collection title when the project has a suitable title -- %1 is the project title"
+				)
+			);
+			final_title = final_title.arg(project_title);
+		}
+	}
+	return(final_title);
+}
+
+ElementsCategory *XmlElementsCollection::rootCategory() {
+	return(root);
+}
+
+/**
+	@return toujours false ; une collection XML n'est representee nul part sur
+	le systeme de fichiers.
+*/
+bool XmlElementsCollection::hasFilePath() {
+	return(false);
+}
+
+/**
+	@return le chemin du repertoire representant cette collection
+*/
+QString XmlElementsCollection::filePath() {
+	return(QString());
+}
+
+/**
+	Ne fait rien - une collection XML n'est representee nul part sur le systeme
+	de fichiers.
+*/
+void XmlElementsCollection::setFilePath(const QString &) {
+}
+
+void XmlElementsCollection::reload() {
+	if (root) root -> reload();
+}
+
+/**
+	@return toujours true
+*/
+bool XmlElementsCollection::exists() {
+	return(true);
+}
+
+/**
+	@return true si la collection est accessible en lecture
+*/
+bool XmlElementsCollection::isReadable() {
+	// une collection XML n'a aucune raison de ne pas etre accessible en lecture
+	return(true);
+}
+
+bool XmlElementsCollection::isWritable() {
+	// une collection XML peut etre en lecture seule si le projet auquel elle
+	// appartient l'est
+	if (QETProject *parent_project = project()) {
+		return(!parent_project -> isReadOnly());
+	} else {
+		return(true);
+	}
+}
+
+bool XmlElementsCollection::write() {
+	emit(written());
+	return(true);
+}
+
+/**
+	@return always false, since an XMl-based elements collection should never
+	be cached.
+*/
+bool XmlElementsCollection::isCacheable() const {
+	return(false);
+}
+
+QDomElement XmlElementsCollection::writeXml(QDomDocument &xml_doc) const {
+	QDomElement collection_elmt = root -> writeXml(xml_doc);
+	collection_elmt.setTagName("collection");
+	xml_doc.appendChild(collection_elmt);
+	return(collection_elmt);
+}
+
+void XmlElementsCollection::componentWritten() {
+	write();
+}
+
+/**
+	Supprime le contenu en memoire de cette collection
+*/
+void XmlElementsCollection::deleteContent() {
+	delete root;
+	root = 0;
+}

Added: trunk/sources/xmlelementscollection.h
===================================================================
--- trunk/sources/xmlelementscollection.h	                        (rev 0)
+++ trunk/sources/xmlelementscollection.h	2013-11-14 10:11:22 UTC (rev 2613)
@@ -0,0 +1,67 @@
+/*
+	Copyright 2006-2012 Xavier Guerrin
+	This file is part of QElectroTech.
+	
+	QElectroTech is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+	
+	QElectroTech is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+	
+	You should have received a copy of the GNU General Public License
+	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef XML_ELEMENTS_COLLECTION
+#define XML_ELEMENTS_COLLECTION
+#include <QtXml>
+#include "elementscollection.h"
+class XmlElementsCategory;
+/**
+	This class represents an elements collection stored within an XML document
+	(e.g. the embedded collection of a QET project).
+*/
+class XmlElementsCollection : public ElementsCollection {
+	Q_OBJECT
+	public:
+	// constructors, destructor
+	XmlElementsCollection(ElementsCollectionItem * = 0);
+	XmlElementsCollection(const QDomElement &, ElementsCollectionItem * = 0);
+	virtual ~XmlElementsCollection();
+	
+	private:
+	XmlElementsCollection(const XmlElementsCollection &);
+	
+	// methods
+	public:
+	virtual QString title() const;
+	virtual ElementsCategory *rootCategory();
+	virtual bool hasFilePath();
+	virtual QString filePath();
+	virtual void setFilePath(const QString &);
+	virtual void reload();
+	virtual bool exists();
+	virtual bool isReadable();
+	virtual bool isWritable();
+	virtual bool write();
+	virtual bool isCacheable() const;
+	
+	virtual QDomElement writeXml(QDomDocument &) const;
+	
+	public slots:
+	void componentWritten();
+	
+	signals:
+	void written();
+	
+	private:
+	void deleteContent();
+	
+	// attributes
+	private:
+	XmlElementsCategory *root;
+};
+#endif


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