[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ł Ś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ć", "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 <<a href=\"mailto:%3\">%3</a>>‎<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 ¤t, 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("<", "<").replace(">", ">"));
+
+
+ // 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 += "<";
+ else if (c == '>') result += ">";
+ 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 ®exp, 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[] = {
+ { "&& (&&)", "&" },
+ { "& ", " " },
+ { "&< (<)", "<" },
+ { "&> (>)", ">" },
+ { "&© (Copyright)", "©" },
+ { "&® (Trade Mark)", "®" },
+ };
+
+ 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 ©) :
+ 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><html><head/><body><p>Numérotée les folio sélectionné</p></body></html></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><html><head/><body><p>Supprimé la numérotation des folio sélectionné</p></body></html></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><html><head/><body><p>Choisir le folio</p></body></html></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><html><head/><body><p>Supprimer une variable de numérotation</p></body></html></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><html><head/><body><p>Ajouter une variable de numérotation</p></body></html></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