1 /*
2 	Copyright 2006-2019 The QElectroTech Team
3 	This file is part of QElectroTech.
4 
5 	QElectroTech is free software: you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation, either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	QElectroTech is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "qetproject.h"
19 #include "diagram.h"
20 #include "diagramfoliolist.h"
21 #include "qetapp.h"
22 #include "qetresult.h"
23 #include "movetemplateshandler.h"
24 #include "qetmessagebox.h"
25 #include "titleblocktemplate.h"
26 #include "ui/dialogwaiting.h"
27 #include "numerotationcontext.h"
28 #include "reportproperties.h"
29 #include "integrationmovetemplateshandler.h"
30 #include "xmlelementcollection.h"
31 #include "importelementdialog.h"
32 #include "numerotationcontextcommands.h"
33 #include "assignvariables.h"
34 
35 #include <QTimer>
36 #include <QStandardPaths>
37 #include <utility>
38 #include <KAutoSaveFile>
39 
40 static int BACKUP_INTERVAL = 120000; //interval in ms of backup = 2min
41 
42 /**
43  * @brief QETProject::QETProject
44  * Create a empty project
45  * @param parent
46  */
QETProject(QObject * parent)47 QETProject::QETProject(QObject *parent) :
48 	QObject              (parent),
49 	m_titleblocks_collection(this)
50 {
51 	m_elements_collection = new XmlElementCollection(this);
52 	init();
53 }
54 
55 /**
56  * @brief QETProject::QETProject
57  * Construct a project from a .qet file
58  * @param path : path of the file
59  * @param parent : parent QObject
60  */
QETProject(const QString & path,QObject * parent)61 QETProject::QETProject(const QString &path, QObject *parent) :
62 	QObject              (parent),
63 	m_titleblocks_collection(this)
64 {
65 	QFile file(path);
66 	m_state = openFile(&file);
67 	if (m_state != ProjectState::Ok) {
68 		return;
69 	}
70 
71 	init();
72 }
73 
74 /**
75  * @brief QETProject::QETProject
76  * @param backup : backup file to open, QETProject take ownership of backup.
77  * @param parent : parent QObject
78  */
QETProject(KAutoSaveFile * backup,QObject * parent)79 QETProject::QETProject(KAutoSaveFile *backup, QObject *parent) :
80 	QObject              (parent),
81 	m_titleblocks_collection(this)
82 {
83 	m_state = openFile(backup);
84 		//Failed to open from the backup, try to open the crashed
85 	if (m_state != ProjectState::Ok)
86 	{
87 		QFile file(backup->managedFile().path());
88 		m_state = openFile(&file);
89 		if(m_state != ProjectState::Ok)
90 		{
91 			backup->open(QIODevice::ReadWrite);
92 			delete backup;
93 			return;
94 		}
95 	}
96 		//Set the real path, instead of the path of the backup.
97 	setFilePath(backup->managedFile().path());
98 	delete  backup;
99 
100 		//Set the project to read only mode if the file it.
101 	QFileInfo fi(m_file_path);
102 	setReadOnly(!fi.isWritable());
103 
104 	init();
105 }
106 
107 /**
108  * @brief QETProject::~QETProject
109  * Destructor
110  */
~QETProject()111 QETProject::~QETProject() {
112 	qDeleteAll(m_diagrams_list);
113 }
114 
115 /**
116  * @brief QETProject::init
117  */
init()118 void QETProject::init()
119 {
120 	connect(&m_titleblocks_collection, &TitleBlockTemplatesCollection::changed, this, &QETProject::updateDiagramsTitleBlockTemplate);
121 	connect(&m_titleblocks_collection, &TitleBlockTemplatesCollection::aboutToRemove, this, &QETProject::removeDiagramsTitleBlockTemplate);
122 
123 	m_undo_stack = new QUndoStack(this);
124 	connect(m_undo_stack, SIGNAL(cleanChanged(bool)), this, SLOT(undoStackChanged(bool)));
125 
126 	m_save_backup_timer.setInterval(BACKUP_INTERVAL);
127 	connect(&m_save_backup_timer, &QTimer::timeout, this, &QETProject::writeBackup);
128 	m_save_backup_timer.start();
129 	writeBackup();
130 
131 	QSettings settings;
132 	int autosave_interval = settings.value("diagrameditor/autosave-interval", 0).toInt();
133 	if(autosave_interval > 0)
134 	{
135 		int ms = autosave_interval*60*1000;
136 		m_autosave_timer.setInterval(ms);
137 		connect(&m_autosave_timer, &QTimer::timeout, [this]()
138 		{
139 			if(!this->m_file_path.isEmpty())
140 				this->write();
141 		});
142 		m_autosave_timer.start();
143 	}
144 }
145 
146 /**
147  * @brief QETProject::openFile
148  * @param file
149  * @return
150  */
openFile(QFile * file)151 QETProject::ProjectState QETProject::openFile(QFile *file)
152 {
153 	bool opened_here = file->isOpen() ? false : true;
154 	if (!file->isOpen() && !file->open(QIODevice::ReadOnly | QIODevice::Text)) {
155 		return FileOpenFailed;
156 	}
157 	QFileInfo fi(*file);
158 	setFilePath(fi.absoluteFilePath());
159 
160 		//Extract the content of the xml
161 	QDomDocument xml_project;
162 	if (!xml_project.setContent(file))
163 	{
164 		if(opened_here) {
165 			file->close();
166 		}
167 		return XmlParsingFailed;
168 	}
169 
170 		//Build the project from the xml
171 	readProjectXml(xml_project);
172 
173 	if (!fi.isWritable()) {
174 		setReadOnly(true);
175 	}
176 	if(opened_here) {
177 		file->close();
178 	}
179 	return ProjectState::Ok;
180 }
181 
182 /**
183 	Cette methode peut etre utilisee pour tester la bonne ouverture d'un projet
184 	@return l'etat du projet
185 	@see ProjectState
186 */
state() const187 QETProject::ProjectState QETProject::state() const {
188 	return(m_state);
189 }
190 
191 /**
192 	Get the folioSheetQuantity
193 	@return folio Sheets Quantity.
194 */
getFolioSheetsQuantity() const195 int QETProject::getFolioSheetsQuantity() const {
196 	return(m_folio_sheets_quantity);
197 }
198 
199 /**
200 	Set the folioSheetQuantity to quantity
201 	@param New value of quantity to be set.
202 */
setFolioSheetsQuantity(int quantity)203 void QETProject::setFolioSheetsQuantity(int quantity) {
204 	m_folio_sheets_quantity = quantity;
205 }
206 
207 /**
208 	@return la liste des schemas de ce projet
209 */
diagrams() const210 QList<Diagram *> QETProject::diagrams() const {
211 	return(m_diagrams_list);
212 }
213 
214 /**
215 	@param diagram Pointer to a Diagram object
216 	@return the folio number of the given diagram object within the project,
217 	or -1 if it is not part of this project.
218 	Note: this returns 0 for the first diagram, not 1
219 */
folioIndex(const Diagram * diagram) const220 int QETProject::folioIndex(const Diagram *diagram) const {
221 	// QList::indexOf returns -1 if no item matched.
222 	return(m_diagrams_list.indexOf(const_cast<Diagram *>(diagram)));
223 }
224 
225 /**
226  * @brief QETProject::embeddedCollection
227  * @return The embedded collection
228  */
embeddedElementCollection() const229 XmlElementCollection *QETProject::embeddedElementCollection() const {
230 	return m_elements_collection;
231 }
232 
233 /**
234 	@return the title block templates collection enbeedded within this project
235 */
embeddedTitleBlockTemplatesCollection()236 TitleBlockTemplatesProjectCollection *QETProject::embeddedTitleBlockTemplatesCollection() {
237 	return(&m_titleblocks_collection);
238 }
239 
240 /**
241 	@return le chemin du fichier dans lequel ce projet est enregistre
242 */
filePath()243 QString QETProject::filePath() {
244 	return(m_file_path);
245 }
246 
247 /**
248  * @brief QETProject::setFilePath
249  * Set the filepath of this project file
250  * Set a file path also create a backup file according to the path.
251  * If a previous path was set, the previous backup file is deleted and a new one
252  * is created according to the path.
253  * @param filepath
254  */
setFilePath(const QString & filepath)255 void QETProject::setFilePath(const QString &filepath)
256 {
257 	if (filepath == m_file_path) {
258 		return;
259 	}
260 
261 	if (m_backup_file)
262 	{
263 		delete m_backup_file;
264 		m_backup_file = nullptr;
265 	}
266 
267     m_backup_file = new KAutoSaveFile(QUrl::fromLocalFile(filepath), this);
268 	if (!m_backup_file->open(QIODevice::WriteOnly)) {
269 		delete m_backup_file;
270 		m_backup_file = nullptr;
271 	}
272 	m_file_path = filepath;
273 
274 	QFileInfo fi(m_file_path);
275 	if (fi.isWritable()) {
276 		setReadOnly(false);
277 	}
278 
279 		//title block variables should be updated after file save as dialog is confirmed, before file is saved.
280 	m_project_properties.addValue("saveddate", QDate::currentDate().toString("yyyy-MM-dd"));
281 	m_project_properties.addValue("savedtime", QDateTime::currentDateTime().toString("HH:mm"));
282 	m_project_properties.addValue("savedfilename", QFileInfo(filePath()).baseName());
283 	m_project_properties.addValue("savedfilepath", filePath());
284 
285 
286 
287 	emit(projectFilePathChanged(this, m_file_path));
288 	emit(projectInformationsChanged(this));
289 	updateDiagramsFolioData();
290 }
291 
292 /**
293 	@return le dossier contenant le fichier projet si celui-ci a ete
294 	enregistre ; dans le cas contraire, cette methode retourne l'emplacement
295 	du bureau de l'utilisateur.
296 */
currentDir() const297 QString QETProject::currentDir() const {
298 	QString current_directory;
299 	if (m_file_path.isEmpty()) {
300 		current_directory = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
301 	} else {
302 		current_directory = QFileInfo(m_file_path).absoluteDir().absolutePath();
303 	}
304 	return(current_directory);
305 }
306 
307 /**
308 
309 	@return une chaine de caractere du type "Projet titre du projet".
310 	Si le projet n'a pas de titre, le nom du fichier est utilise.
311 	Si le projet n'est pas associe a un fichier, cette methode retourne "Projet
312 	sans titre".
313 	De plus, si le projet est en lecture seule, le tag "[lecture seule]" est
314 	ajoute.
315 */
pathNameTitle() const316 QString QETProject::pathNameTitle() const {
317 	QString final_title;
318 
319 	if (!project_title_.isEmpty()) {
320 		final_title = QString(
321 			tr(
322 				"Projet « %1 : %2»",
323 				"displayed title for a ProjectView - %1 is the project title, -%2 is the project path"
324 			)
325 		).arg(project_title_).arg (m_file_path);
326 	} else if (!m_file_path.isEmpty()) {
327 		final_title = QString(
328 			tr(
329 				"Projet %1",
330 				"displayed title for a title-less project - %1 is the file name"
331 			)
332 		).arg(QFileInfo(m_file_path).completeBaseName());
333 	} else {
334 		final_title = QString(
335 			tr(
336 				"Projet sans titre",
337 				"displayed title for a project-less, file-less project"
338 			)
339 		);
340 	}
341 
342 	if (isReadOnly()) {
343 		final_title = QString(
344 			tr(
345 				"%1 [lecture seule]",
346 				"displayed title for a read-only project - %1 is a displayable title"
347 			)
348 		).arg(final_title);
349 	}
350 	if (m_modified) {
351 		final_title = QString(
352 			tr(
353 				"%1 [modifié]",
354 				"displayed title for a modified project - %1 is a displayable title"
355 			)
356 		).arg(final_title);
357 	}
358 
359 	return(final_title);
360 }
361 
362 /**
363 	@return le titre du projet
364 */
title() const365 QString QETProject::title() const {
366 	return(project_title_);
367 }
368 
369 /**
370 	@return la version de QElectroTech declaree dans le fichier projet lorsque
371 	celui-ci a ete ouvert ; si ce projet n'a jamais ete enregistre / ouvert
372 	depuis un fichier, cette methode retourne -1.
373 */
declaredQElectroTechVersion()374 qreal QETProject::declaredQElectroTechVersion() {
375 	return(m_project_qet_version);
376 }
377 
378 /**
379 	@param title le nouveau titre du projet
380 */
setTitle(const QString & title)381 void QETProject::setTitle(const QString &title) {
382 	// ne fait rien si le projet est en lecture seule
383 	if (isReadOnly()) return;
384 
385 	// ne fait rien si le titre du projet n'est pas change par l'appel de cette methode
386 	if (project_title_ == title) return;
387 
388 	project_title_ = title;
389 	emit(projectTitleChanged(this, project_title_));
390 	emit(projectInformationsChanged(this));
391 	updateDiagramsFolioData();
392 }
393 
394 /**
395 	@return les dimensions par defaut utilisees lors de la creation d'un
396 	nouveau schema dans ce projet.
397 */
defaultBorderProperties() const398 BorderProperties QETProject::defaultBorderProperties() const {
399 	return(default_border_properties_);
400 }
401 
402 /**
403 	Permet de specifier les dimensions par defaut utilisees lors de la creation
404 	d'un nouveau schema dans ce projet.
405 	@param border dimensions d'un schema
406 */
setDefaultBorderProperties(const BorderProperties & border)407 void QETProject::setDefaultBorderProperties(const BorderProperties &border) {
408 	default_border_properties_ = border;
409 }
410 
411 /**
412 	@return le cartouche par defaut utilise lors de la creation d'un
413 	nouveau schema dans ce projet.
414 */
defaultTitleBlockProperties() const415 TitleBlockProperties QETProject::defaultTitleBlockProperties() const {
416 	return(default_titleblock_properties_);
417 }
418 
419 /**
420  * @brief QETProject::setDefaultTitleBlockProperties
421  * Specify the title block to be used at the creation of a new diagram for this project
422  * @param titleblock
423  */
setDefaultTitleBlockProperties(const TitleBlockProperties & titleblock)424 void QETProject::setDefaultTitleBlockProperties(const TitleBlockProperties &titleblock) {
425 	default_titleblock_properties_ = titleblock;
426 		//Integrate the title block in this project
427 	if (!titleblock.template_name.isEmpty())
428 	{
429 		TitleBlockTemplatesFilesCollection *collection = nullptr;
430 		switch (titleblock.collection)
431 		{
432 			case QET::Common :
433 				collection = QETApp::commonTitleBlockTemplatesCollection();
434 				break;
435 			case QET::Custom :
436 				collection = QETApp::customTitleBlockTemplatesCollection();
437 				break;
438 			case QET::Embedded :
439 				//Titleblock is already embedded to project
440 				return;
441 			default:
442 				return;
443 		}
444 
445 		QScopedPointer<IntegrationMoveTitleBlockTemplatesHandler> m(new IntegrationMoveTitleBlockTemplatesHandler);
446 		integrateTitleBlockTemplate(collection -> location(titleblock.template_name), m.data());
447 	}
448 	emit defaultTitleBlockPropertiesChanged();
449 }
450 
451 /**
452 	@return le type de conducteur par defaut utilise lors de la creation d'un
453 	nouveau schema dans ce projet.
454 */
defaultConductorProperties() const455 ConductorProperties QETProject::defaultConductorProperties() const {
456 	return(default_conductor_properties_);
457 }
458 
459 /**
460 	Permet de specifier e type de conducteur par defaut utilise lors de la
461 	creation d'un nouveau schema dans ce projet.
462 */
setDefaultConductorProperties(const ConductorProperties & conductor)463 void QETProject::setDefaultConductorProperties(const ConductorProperties &conductor) {
464 	default_conductor_properties_ = conductor;
465 }
466 
defaultReportProperties() const467 QString QETProject::defaultReportProperties() const {
468 	return m_default_report_properties;
469 }
470 
setDefaultReportProperties(const QString & properties)471 void QETProject::setDefaultReportProperties(const QString &properties)
472 {
473 	QString old = m_default_report_properties;
474 	m_default_report_properties = properties;
475 
476 	emit reportPropertiesChanged(old, properties);
477 }
478 
setDefaultXRefProperties(const QString & type,const XRefProperties & properties)479 void QETProject::setDefaultXRefProperties(const QString& type, const XRefProperties &properties) {
480 	m_default_xref_properties.insert(type, properties);
481 	emit XRefPropertiesChanged();
482 }
483 
setDefaultXRefProperties(QHash<QString,XRefProperties> hash)484 void QETProject::setDefaultXRefProperties(QHash<QString, XRefProperties> hash)
485 {
486 	m_default_xref_properties.swap(hash);
487 	emit XRefPropertiesChanged();
488 }
489 
490 /**
491  * @brief QETProject::conductorAutoNum
492  * @return All value of conductor autonum stored in project
493  */
conductorAutoNum() const494 QHash <QString, NumerotationContext> QETProject::conductorAutoNum() const {
495 	return m_conductor_autonum;
496 }
497 
498 /**
499  * @brief QETProject::elementAutoNum
500  * @return All value of element autonum stored in project
501  */
elementAutoNum() const502 QHash <QString, NumerotationContext> QETProject::elementAutoNum() const {
503 	return m_element_autonum;
504 }
505 
506 /**
507  * @brief QETProject::elementAutoNumFormula
508  * @param element autonum title
509  * @return Formula of element autonum stored in element autonum
510  */
elementAutoNumFormula(const QString & key) const511 QString QETProject::elementAutoNumFormula (const QString& key) const
512 {
513 	if (m_element_autonum.contains(key)) {
514 		return autonum::numerotationContextToFormula(m_element_autonum[key]);
515 	}
516 
517 	return QString();
518 }
519 
520 /**
521  * @brief QETProject::elementAutoNumCurrentFormula
522  * @return current formula being used by project
523  */
elementAutoNumCurrentFormula() const524 QString QETProject::elementAutoNumCurrentFormula() const {
525 	return elementAutoNumFormula(m_current_element_autonum);
526 }
527 
528 /**
529  * @brief QETProject::elementCurrentAutoNum
530  * @return current element autonum title
531  */
elementCurrentAutoNum() const532 QString QETProject::elementCurrentAutoNum () const {
533 	return m_current_element_autonum;
534 }
535 
536 /**
537  * @brief QETProject::setCurrrentElementAutonum
538  * @param autoNum : set the current element autonum to @autonum
539  */
setCurrrentElementAutonum(QString autoNum)540 void QETProject::setCurrrentElementAutonum(QString autoNum) {
541 	m_current_element_autonum = std::move(autoNum);
542 }
543 
544 /**
545  * @brief QETProject::conductorAutoNumFormula
546  * @param conductor autonum title
547  * @return Formula of element autonum stored in conductor autonum
548  */
conductorAutoNumFormula(const QString & key) const549 QString QETProject::conductorAutoNumFormula (const QString& key) const
550 {
551 	if (m_conductor_autonum.contains(key))
552 		return autonum::numerotationContextToFormula(m_conductor_autonum.value(key));
553 	else
554 		return QString();
555 }
556 
557 /**
558  * @brief QETProject::conductorCurrentAutoNum
559  * @return current conductor autonum title
560  */
conductorCurrentAutoNum() const561 QString QETProject::conductorCurrentAutoNum () const {
562 	return m_current_conductor_autonum;
563 }
564 
565 /**
566  * @brief QETProject::setCurrentConductorAutoNum
567  * @param autoNum set the current conductor autonum to @autonum
568  */
setCurrentConductorAutoNum(QString autoNum)569 void QETProject::setCurrentConductorAutoNum(QString autoNum) {
570 	m_current_conductor_autonum = std::move(autoNum);
571 }
572 
573 /**
574  * @brief QETProject::folioAutoNum
575  * @return All value of folio autonum stored in project
576  */
folioAutoNum() const577 QHash <QString, NumerotationContext> QETProject::folioAutoNum() const {
578 	return  m_folio_autonum;
579 }
580 
581 /**
582  * @brief QETProject::addConductorAutoNum
583  * Add a new conductor numerotation context. If key already exist,
584  * replace old context by the new context
585  * @param key
586  * @param context
587  */
addConductorAutoNum(const QString & key,const NumerotationContext & context)588 void QETProject::addConductorAutoNum(const QString& key, const NumerotationContext& context) {
589 	m_conductor_autonum.insert(key, context);
590 }
591 
592 /**
593  * @brief QETProject::addElementAutoNum
594  * Add a new element numerotation context. If key already exist,
595  * replace old context by the new context
596  * @param key
597  * @param context
598  */
addElementAutoNum(const QString & key,const NumerotationContext & context)599 void QETProject::addElementAutoNum(const QString& key, const NumerotationContext& context)
600 {
601 	m_element_autonum.insert(key, context);
602 	emit elementAutoNumAdded(key);
603 }
604 
605 /**
606  * @brief QETProject::addFolioAutoNum
607  * Add a new folio numerotation context. If key already exist,
608  * replace old context by the new context
609  * @param key
610  * @param context
611  */
addFolioAutoNum(const QString & key,const NumerotationContext & context)612 void QETProject::addFolioAutoNum(const QString& key, const NumerotationContext& context) {
613 	m_folio_autonum.insert(key, context);
614 }
615 
616 /**
617  * @brief QETProject::removeConductorAutoNum
618  * Remove Conductor Numerotation Context stored with key
619  * @param key
620  */
removeConductorAutoNum(const QString & key)621 void QETProject::removeConductorAutoNum(const QString& key) {
622 	m_conductor_autonum.remove(key);
623 }
624 
625 /**
626  * @brief QETProject::removeElementAutonum
627  * Remove Element Numerotation Context stored with key
628  * @param key
629  */
removeElementAutoNum(const QString & key)630 void QETProject::removeElementAutoNum(const QString& key)
631 {
632 	m_element_autonum.remove(key);
633 	emit elementAutoNumRemoved(key);
634 }
635 
636 /**
637  * @brief QETProject::removeFolioAutonum
638  * Remove Folio Numerotation Context stored with key
639  * @param key
640  */
removeFolioAutoNum(const QString & key)641 void QETProject::removeFolioAutoNum(const QString& key) {
642 	m_folio_autonum.remove(key);
643 }
644 
645 /**
646  * @brief QETProject::conductorAutoNum
647  * Return conductor numerotation context stored with @key.
648  * If key is not found, return an empty numerotation context
649  * @param key
650  */
conductorAutoNum(const QString & key) const651 NumerotationContext QETProject::conductorAutoNum (const QString &key) const {
652 	if (m_conductor_autonum.contains(key)) return m_conductor_autonum[key];
653 	else return NumerotationContext();
654 }
655 
656 /**
657  * @brief QETProject::elementAutoNum
658  * Return element numerotation context stored with @key.
659  * If key is not found, return an empty numerotation context
660  * @param key
661  */
elementAutoNum(const QString & key)662 NumerotationContext QETProject::elementAutoNum (const QString &key) {
663 	if (m_element_autonum.contains(key)) return m_element_autonum[key];
664 	else return NumerotationContext();
665 }
666 
667 /**
668  * @brief QETProject::folioAutoNum
669  * Return folio numerotation context stored with @key.
670  * If key is not found, return an empty numerotation context
671  * @param key
672  */
folioAutoNum(const QString & key) const673 NumerotationContext QETProject::folioAutoNum (const QString &key) const {
674 	if (m_folio_autonum.contains(key)) return m_folio_autonum[key];
675 	else return NumerotationContext();
676 }
677 
678 /**
679  * @brief QETProject::freezeExistentConductorLabel
680  * Freeze Existent Conductors in the selected folios
681  * @param from - first folio index to apply freeze
682  * @param to - last folio index to apply freeze
683  */
freezeExistentConductorLabel(bool freeze,int from,int to)684 void QETProject::freezeExistentConductorLabel(bool freeze, int from, int to) {
685 	for (int i = from; i <= to; i++) {
686 		m_diagrams_list.at(i)->freezeConductors(freeze);
687 	}
688 }
689 
690 /**
691  * @brief QETProject::freezeNewConductorLabel
692  * Freeze New Conductors in the selected folios
693  * @param from - first folio index to apply freeze
694  * @param to - last folio index to apply freeze
695  */
freezeNewConductorLabel(bool freeze,int from,int to)696 void QETProject::freezeNewConductorLabel(bool freeze, int from, int to) {
697 	for (int i = from; i <= to; i++) {
698 		m_diagrams_list.at(i)->setFreezeNewConductors(freeze);
699 	}
700 }
701 
702 /**
703  * @brief QETProject::isFreezeNewConductors
704  * @return freeze new conductors Project Wide status
705  */
isFreezeNewConductors()706 bool QETProject::isFreezeNewConductors() {
707 	return m_freeze_new_conductors;
708 }
709 
710 /**
711  * @brief QETProject::setfreezeNewConductors
712  * Set Project Wide freeze new conductors
713  */
setFreezeNewConductors(bool set)714 void QETProject::setFreezeNewConductors(bool set) {
715 	m_freeze_new_conductors = set;
716 }
717 
718 /**
719  * @brief QETProject::freezeExistentElementLabel
720  * Freeze Existent Elements in the selected folios
721  * @param from - first folio index to apply freeze
722  * @param to - last folio index to apply freeze
723  */
freezeExistentElementLabel(bool freeze,int from,int to)724 void QETProject::freezeExistentElementLabel(bool freeze, int from, int to) {
725 	for (int i = from; i <= to; i++) {
726 		m_diagrams_list.at(i)->freezeElements(freeze);
727 	}
728 }
729 
730 /**
731  * @brief QETProject::freezeNewElementLabel
732  * Freeze New Elements in the selected folios
733  * @param from - first folio index to apply freeze
734  * @param to - last folio index to apply freeze
735  */
freezeNewElementLabel(bool freeze,int from,int to)736 void QETProject::freezeNewElementLabel(bool freeze, int from, int to) {
737 	for (int i = from; i <= to; i++) {
738 		m_diagrams_list.at(i)->setFreezeNewElements(freeze);
739 	}
740 }
741 
742 /**
743  * @brief QETProject::freezeNewElements
744  * @return freeze new elements Project Wide status
745  */
isFreezeNewElements()746 bool QETProject::isFreezeNewElements() {
747 	return m_freeze_new_elements;
748 }
749 
750 /**
751  * @brief QETProject::setfreezeNewElements
752  * Set Project Wide freeze new elements
753  */
setFreezeNewElements(bool set)754 void QETProject::setFreezeNewElements(bool set) {
755 	m_freeze_new_elements = set;
756 }
757 
758 /**
759  * @brief QETProject::autoConductor
760  * @return true if use of auto conductor is authorized.
761  * See also Q_PROPERTY autoConductor
762  */
autoConductor() const763 bool QETProject::autoConductor() const
764 {
765 	return m_auto_conductor;
766 }
767 
768 /**
769  * @brief QETProject::setAutoConductor
770  * @param ac
771  * Enable the use of auto conductor if true
772  * See also Q_PROPERTY autoConductor
773  */
setAutoConductor(bool ac)774 void QETProject::setAutoConductor(bool ac)
775 {
776 	if (ac != m_auto_conductor)
777 		m_auto_conductor = ac;
778 }
779 
780 /**
781  * @brief QETProject::autoFolioNumberingNewFolios
782  * emit Signal to add new Diagram with autonum
783  * properties
784  */
autoFolioNumberingNewFolios()785 void QETProject::autoFolioNumberingNewFolios(){
786 	emit addAutoNumDiagram();
787 }
788 
789 /**
790  * @brief QETProject::autoFolioNumberingNewFolios
791  * @param autonum used, index from selected tabs "from" and "to"
792  * rename folios with selected autonum
793  */
autoFolioNumberingSelectedFolios(int from,int to,const QString & autonum)794 void QETProject::autoFolioNumberingSelectedFolios(int from, int to, const QString& autonum){
795 	int total_folio = m_diagrams_list.count();
796 	DiagramContext project_wide_properties = m_project_properties;
797 	for (int i=from; i<=to; i++) {
798 		QString title = m_diagrams_list[i] -> title();
799 		NumerotationContext nC = folioAutoNum(autonum);
800 		NumerotationContextCommands nCC = NumerotationContextCommands(nC);
801 		m_diagrams_list[i] -> border_and_titleblock.setFolio("%autonum");
802 		m_diagrams_list[i] -> border_and_titleblock.setFolioData(i + 1, total_folio, nCC.toRepresentedString(), project_wide_properties);
803 		m_diagrams_list[i] -> project() -> addFolioAutoNum(autonum,nCC.next());
804 		m_diagrams_list[i] -> update();
805 	}
806 }
807 
808 /**
809 	@return un document XML representant le projet
810 */
toXml()811 QDomDocument QETProject::toXml() {
812 	// racine du projet
813 	QDomDocument xml_doc;
814 	QDomElement project_root = xml_doc.createElement("project");
815 	project_root.setAttribute("version", QET::version);
816 	project_root.setAttribute("title", project_title_);
817 
818 	// write the present value of folioSheetsQuantity to XML.
819 	project_root.setAttribute("folioSheetQuantity", QString::number(m_folio_sheets_quantity));
820 	xml_doc.appendChild(project_root);
821 
822 	// titleblock templates, if any
823 	if (m_titleblocks_collection.templates().count()) {
824 		QDomElement titleblocktemplates_elmt = xml_doc.createElement("titleblocktemplates");
825 		foreach (QString template_name, m_titleblocks_collection.templates()) {
826 			QDomElement e = m_titleblocks_collection.getTemplateXmlDescription(template_name);
827 			titleblocktemplates_elmt.appendChild(xml_doc.importNode(e, true));
828 		}
829 		project_root.appendChild(titleblocktemplates_elmt);
830 	}
831 
832 	// project-wide properties
833 	QDomElement project_properties = xml_doc.createElement("properties");
834 	writeProjectPropertiesXml(project_properties);
835 	project_root.appendChild(project_properties);
836 
837 	// Properties for news diagrams
838 	QDomElement new_diagrams_properties = xml_doc.createElement("newdiagrams");
839 	writeDefaultPropertiesXml(new_diagrams_properties);
840 	project_root.appendChild(new_diagrams_properties);
841 
842 	// schemas
843 
844 	// qDebug() << "Export XML de" << diagrams_.count() << "schemas";
845 	int order_num = 1;
846 	const QList<Diagram *> diagrams_list = m_diagrams_list;
847 	for(Diagram *diagram : diagrams_list)
848 	{
849 		// Write the diagram to XML only if it is not of type DiagramFolioList.
850 		DiagramFolioList *ptr = dynamic_cast<DiagramFolioList *>(diagram);
851 		if ( !ptr )
852 		{
853 			qDebug() << qPrintable(QString("QETProject::toXml() : exporting diagram \"%1\"").arg(diagram -> title())) << "[" << diagram << "]";
854 			QDomElement xml_diagram = diagram->toXml().documentElement();
855 			QDomNode xml_node = xml_doc.importNode(xml_diagram, true);
856 
857 			QDomNode appended_diagram = project_root.appendChild(xml_node);
858 			appended_diagram.toElement().setAttribute("order", order_num ++);
859 		}
860 	}
861 
862 		//Write the elements collection.
863 	project_root.appendChild(m_elements_collection->root().cloneNode(true));
864 
865 	return(xml_doc);
866 }
867 
868 /**
869 	Ferme le projet
870 */
close()871 bool QETProject::close() {
872 	return(true);
873 }
874 
875 /**
876  * @brief QETProject::write
877  * Save the project in a file
878  * @see filePath()
879  * @see setFilePath()
880  * @return true if the project was successfully saved, else false
881  */
write()882 QETResult QETProject::write()
883 {
884 		// this operation requires a filepath
885 	if (m_file_path.isEmpty())
886 		return(QString("unable to save project to file: no filepath was specified"));
887 
888 		// if the project was opened read-only and the file is still non-writable, do not save the project
889 	if (isReadOnly() && !QFileInfo(m_file_path).isWritable())
890 		return(QString("the file %1 was opened read-only and thus will not be written").arg(m_file_path));
891 
892 		//Get the project in xml
893 	QDomDocument xml_project;
894 	xml_project.appendChild(xml_project.importNode(toXml().documentElement(), true));
895 
896 	QString error_message;
897 	if (!QET::writeXmlFile(xml_project, m_file_path, &error_message)) return(error_message);
898 
899 	//title block variables should be updated after file save dialog is confirmed, before file is saved.
900 	m_project_properties.addValue("saveddate", QDate::currentDate().toString(Qt::SystemLocaleShortDate));
901 	m_project_properties.addValue("savedtime", QDateTime::currentDateTime().toString("HH:mm"));
902 	m_project_properties.addValue("savedfilename", QFileInfo(filePath()).baseName());
903 	m_project_properties.addValue("savedfilepath", filePath());
904 
905  	emit(projectInformationsChanged(this));
906  	updateDiagramsFolioData();
907 
908 	setModified(false);
909 	return(QETResult());
910 }
911 
912 /**
913 	@return true si le projet est en mode readonly, false sinon
914 */
isReadOnly() const915 bool QETProject::isReadOnly() const {
916 	return(m_read_only && read_only_file_path_ == m_file_path);
917 }
918 
919 /**
920  * @brief QETProject::setReadOnly
921  * Set this project to read only if @read_only = true
922  * @param read_only
923  */
setReadOnly(bool read_only)924 void QETProject::setReadOnly(bool read_only)
925 {
926 	if (m_read_only != read_only)
927 	{
928 			//keep the file to which this project is read-only
929 		read_only_file_path_ = m_file_path;
930 		m_read_only = read_only;
931 		emit(readOnlyChanged(this, read_only));
932 	}
933 }
934 
935 /**
936 	@return true si le projet peut etre considere comme vide, c'est-a-dire :
937 	  - soit avec une collection embarquee vide
938 	  - soit avec uniquement des schemas consideres comme vides
939 	  - soit avec un titre de projet
940 */
isEmpty() const941 bool QETProject::isEmpty() const {
942 	// si le projet a un titre, on considere qu'il n'est pas vide
943 	if (!project_title_.isEmpty()) return(false);
944 
945 	//@TODO check if the embedded element collection is empty
946 
947 	// compte le nombre de schemas non vides
948 	int pertinent_diagrams = 0;
949 	foreach(Diagram *diagram, m_diagrams_list) {
950 		if (!diagram -> isEmpty()) ++ pertinent_diagrams;
951 	}
952 
953 	return(pertinent_diagrams > 0);
954 }
955 
956 /**
957  * @brief QETProject::importElement
958  * Import the element represented by @location to the embbeded collection of this project
959  * @param location
960  * @return the location of the imported element, location can be null.
961  */
importElement(ElementsLocation & location)962 ElementsLocation QETProject::importElement(ElementsLocation &location)
963 {
964 		//Location isn't an element or doesn't exist
965 	if (! (location.isElement() && location.exist()) ) {
966 		return ElementsLocation();
967 	}
968 
969 		//Get the path where the element must be imported
970 	QString import_path;
971 	if (location.isFileSystem()) {
972 		import_path = "import/" + location.collectionPath(false);
973 	}
974 	else if (location.isProject()) {
975 		if (location.project() == this) {
976 			return location;
977 		}
978 
979 		import_path = location.collectionPath(false);
980 	}
981 
982 		//Element already exist in the embedded collection, we ask what to do to user
983 	if (m_elements_collection->exist(import_path)) {
984 		ElementsLocation existing_location(import_path, this);
985 
986 			//@existing_location and @location have the same uuid, so it is the same element
987 		if (existing_location.uuid() == location.uuid()) {
988 			return existing_location;
989 		}
990 
991 		ImportElementDialog ied;
992 		if (ied.exec() == QDialog::Accepted) {
993 			QET::Action action = ied.action();
994 
995 				//Use the exisitng element
996 			if (action == QET::Ignore) {
997 				return existing_location;
998 			}
999 				//Erase the existing element, and use the newer instead
1000 			else if (action == QET::Erase) {
1001 				ElementsLocation parent_loc = existing_location.parent();
1002 				return m_elements_collection->copy(location, parent_loc);
1003 			}
1004 				//Add the new element with an other name.
1005 			else if (action == QET::Rename) {
1006 				int a = 0;
1007 				QString parent_path = existing_location.parent().projectCollectionPath();
1008 				QString name_ = existing_location.fileName();
1009 				name_.remove(".elmt");
1010 
1011 				ElementsLocation loc;
1012 				do
1013 				{
1014 					a++;
1015 					QString new_path = parent_path + "/" + name_ + QString::number(a) + ".elmt";
1016 					loc = ElementsLocation (new_path);
1017 				} while (loc.exist());
1018 
1019 				ElementsLocation parent_loc = existing_location.parent();
1020 				return m_elements_collection->copy(location, parent_loc, loc.fileName());
1021 			}
1022 			else {
1023 				return ElementsLocation();
1024 			}
1025 		}
1026 		else {
1027 			return ElementsLocation();
1028 		}
1029 	}
1030 		//Element doesn't exist in the collection, we just import it
1031 	else {
1032 		ElementsLocation loc(m_elements_collection->addElement(location), this);
1033 
1034 		if (!loc.exist()) {
1035 			qDebug() << "QETProject::importElement : failed to import location. " << location;
1036 			return ElementsLocation();
1037 		}
1038 		else {
1039 			return loc;
1040 		}
1041 	}
1042 
1043 	return ElementsLocation();
1044 }
1045 
1046 /**
1047 	Integrate a title block template into this project.
1048 	@param src_tbt The location of the title block template to be integrated into this project
1049 	@param handler
1050 	@return the name of the template after integration, or an empty QString if a problem occurred.
1051 */
integrateTitleBlockTemplate(const TitleBlockTemplateLocation & src_tbt,MoveTitleBlockTemplatesHandler * handler)1052 QString QETProject::integrateTitleBlockTemplate(const TitleBlockTemplateLocation &src_tbt, MoveTitleBlockTemplatesHandler *handler) {
1053 	TitleBlockTemplateLocation dst_tbt(src_tbt.name(), &m_titleblocks_collection);
1054 
1055 	// check whether a TBT having the same name already exists within this project
1056 	QString target_name = dst_tbt.name();
1057 	while (m_titleblocks_collection.templates().contains(target_name))
1058 	{
1059 		QET::Action action = handler -> templateAlreadyExists(src_tbt, dst_tbt);
1060 		if (action == QET::Retry) {
1061 			continue;
1062 		} else if (action == QET::Erase) {
1063 			break;
1064 		} else if (action == QET::Abort || action == QET::Ignore) {
1065 			return(QString());
1066 		} else if (action == QET::Rename) {
1067 			target_name = handler -> nameForRenamingOperation();
1068 		} else if (action == QET::Managed) {
1069 			return(target_name);
1070 		}
1071 	}
1072 
1073 	if (!m_titleblocks_collection.setTemplateXmlDescription(target_name, src_tbt.getTemplateXmlDescription()))
1074 	{
1075 		handler -> errorWithATemplate(src_tbt, tr("Une erreur s'est produite durant l'intégration du modèle.", "error message"));
1076 		target_name = QString();
1077 	}
1078 	return(target_name);
1079 }
1080 
1081 /**
1082 	Permet de savoir si un element est utilise dans un projet
1083 	@param location Emplacement d'un element
1084 	@return true si l'element location est utilise sur au moins un des schemas
1085 	de ce projet, false sinon
1086 */
usesElement(const ElementsLocation & location) const1087 bool QETProject::usesElement(const ElementsLocation &location) const
1088 {
1089 	foreach(Diagram *diagram, diagrams()) {
1090 		if (diagram -> usesElement(location)) {
1091 			return(true);
1092 		}
1093 	}
1094 	return(false);
1095 }
1096 
1097 /**
1098  * @brief QETProject::unusedElements
1099  * @return the list of unused element (exactly her location)
1100  * An unused element, is an element present in the embedded collection but not present in a diagram of this project.
1101  * Be aware that an element can be not present in a diagram,
1102  * but managed by an undo command (delete an element), so an unused element can be used after an undo.
1103  */
unusedElements() const1104 QList<ElementsLocation> QETProject::unusedElements() const
1105 {
1106 	QList <ElementsLocation> unused_list;
1107 
1108 	foreach(ElementsLocation location, m_elements_collection->elementsLocation())
1109 		if (location.isElement() && !usesElement(location))
1110 			unused_list << location;
1111 
1112 	return unused_list;
1113 }
1114 
1115 /**
1116 	@param location Location of a title block template
1117 	@return true if the provided template is used by at least one diagram
1118 	within this project, false otherwise
1119 */
usesTitleBlockTemplate(const TitleBlockTemplateLocation & location)1120 bool QETProject::usesTitleBlockTemplate(const TitleBlockTemplateLocation &location) {
1121 	// a diagram can only use a title block template embedded wihtin its parent project
1122 	if (location.parentProject() != this) return(false);
1123 
1124 	foreach (Diagram *diagram, diagrams()) {
1125 		if (diagram -> usesTitleBlockTemplate(location.name())) {
1126 			return(true);
1127 		}
1128 	}
1129 	return(false);
1130 }
1131 
1132 /**
1133 	Ajoute un nouveau schema au projet et emet le signal diagramAdded
1134 */
addNewDiagram()1135 Diagram *QETProject::addNewDiagram() {
1136 	// ne fait rien si le projet est en lecture seule
1137 	if (isReadOnly()) return(nullptr);
1138 
1139 	// cree un nouveau schema
1140 	Diagram *diagram = new Diagram(this);
1141 
1142 	// lui transmet les parametres par defaut
1143 	diagram -> border_and_titleblock.importBorder(defaultBorderProperties());
1144 	diagram -> border_and_titleblock.importTitleBlock(defaultTitleBlockProperties());
1145 	diagram -> defaultConductorProperties = defaultConductorProperties();
1146 
1147 	addDiagram(diagram);
1148 	emit(diagramAdded(this, diagram));
1149 	return(diagram);
1150 }
1151 
1152 /**
1153  * @brief QETProject::addNewDiagramFolioList
1154  * Add new diagram folio list
1155  * @return the created diagram
1156  */
addNewDiagramFolioList()1157 QList <Diagram *> QETProject::addNewDiagramFolioList() {
1158 	// do nothing if project is read only or folio sheet is alredy created
1159 	QList <Diagram *> diagram_list;
1160 
1161 	if (!isReadOnly() && getFolioSheetsQuantity() == 0) {
1162 
1163 		//reset the number of folio sheet
1164 		setFolioSheetsQuantity(0);
1165 
1166 		int diagCount = diagrams().size();
1167 		for (int i = 0; i <= diagCount/29; i++) {
1168 
1169 			//create new diagram
1170 			Diagram *diagram_folio_list = new DiagramFolioList(this);
1171 
1172 			// setup default properties
1173 			diagram_folio_list -> border_and_titleblock.importBorder(defaultBorderProperties());
1174 			diagram_folio_list -> border_and_titleblock.importTitleBlock(defaultTitleBlockProperties());
1175 			diagram_folio_list -> defaultConductorProperties = defaultConductorProperties();
1176 
1177 			diagram_folio_list -> border_and_titleblock.setTitle(tr("Liste des Folios"));
1178 			// no need to display rows and columns
1179 			diagram_folio_list -> border_and_titleblock.displayRows(false);
1180 			diagram_folio_list -> border_and_titleblock.displayColumns(false);
1181 
1182 			addDiagram(diagram_folio_list);
1183 			setFolioSheetsQuantity( getFolioSheetsQuantity()+1 );
1184 			emit(diagramAdded(this, diagram_folio_list));
1185 			diagram_list << diagram_folio_list;
1186 			diagCount++;
1187 		}
1188 	}
1189 
1190 	return(diagram_list);
1191 }
1192 
1193 /**
1194 	Enleve un schema du projet et emet le signal diagramRemoved
1195 	@param diagram le schema a enlever
1196 */
removeDiagram(Diagram * diagram)1197 void QETProject::removeDiagram(Diagram *diagram) {
1198 	// ne fait rien si le projet est en lecture seule
1199 	if (isReadOnly()) return;
1200 	if (!diagram || !m_diagrams_list.contains(diagram)) return;
1201 
1202 	if (m_diagrams_list.removeAll(diagram)) {
1203 		emit(diagramRemoved(this, diagram));
1204 		delete diagram;
1205 	}
1206 
1207 	updateDiagramsFolioData();
1208 }
1209 
1210 /**
1211 	Gere le fait que l'ordre des schemas ait change
1212 	@param old_index ancien indice du schema deplace
1213 	@param new_index nouvel indice du schema deplace
1214 	Si l'ancien ou le nouvel index est negatif ou superieur au nombre de schemas
1215 	dans le projet, cette methode ne fait rien.
1216 	Les index vont de 0 a "nombre de schemas - 1"
1217 */
diagramOrderChanged(int old_index,int new_index)1218 void QETProject::diagramOrderChanged(int old_index, int new_index) {
1219 	if (old_index < 0 || new_index < 0) return;
1220 
1221 	int diagram_max_index = m_diagrams_list.size() - 1;
1222 	if (old_index > diagram_max_index || new_index > diagram_max_index) return;
1223 
1224 	m_diagrams_list.move(old_index, new_index);
1225 	updateDiagramsFolioData();
1226 	setModified(true);
1227 	emit(projectDiagramsOrderChanged(this, old_index, new_index));
1228 }
1229 
1230 /**
1231 	Mark this project as modified and emit the projectModified() signal.
1232 */
setModified(bool modified)1233 void QETProject::setModified(bool modified) {
1234 	if (m_modified != modified) {
1235 		m_modified = modified;
1236 		emit(projectModified(this, m_modified));
1237 		emit(projectInformationsChanged(this));
1238 	}
1239 }
1240 
1241 /**
1242  * @brief QETProject::readProjectXml
1243  * Read and make the project from an xml description
1244  * @param xml_project : the description of the project from an xml
1245  */
readProjectXml(QDomDocument & xml_project)1246 void QETProject::readProjectXml(QDomDocument &xml_project)
1247 {
1248 	QDomElement root_elmt = xml_project.documentElement();
1249 	m_state = ProjectParsingRunning;
1250 
1251 		//The roots of the xml document must be a "project" element
1252 	if (root_elmt.tagName() == "project")
1253 	{
1254 			//Normal opening mode
1255 		if (root_elmt.hasAttribute("version"))
1256 		{
1257 			bool conv_ok;
1258 			m_project_qet_version = root_elmt.attribute("version").toDouble(&conv_ok);
1259 
1260 			if (conv_ok && QET::version.toDouble() < m_project_qet_version)
1261 			{
1262 				int ret = QET::QetMessageBox::warning(
1263 							  nullptr,
1264 							  tr("Avertissement", "message box title"),
1265 							  tr(
1266 								  "Ce document semble avoir été enregistré avec "
1267 								  "une version ultérieure de QElectroTech. Il est "
1268 								  "possible que l'ouverture de tout ou partie de ce "
1269 								  "document échoue.\n"
1270 								  "Que désirez vous faire ?",
1271 								  "message box content"
1272 								  ),
1273 							  QMessageBox::Open | QMessageBox::Cancel
1274 							  );
1275 
1276 				if (ret == QMessageBox::Cancel)
1277 				{
1278 					m_state = FileOpenDiscard;
1279 					return;
1280 				}
1281 			}
1282 		}
1283 		setTitle(root_elmt.attribute("title"));
1284 	}
1285 	else
1286 	{
1287 		m_state = ProjectParsingFailed;
1288 	}
1289 
1290 		//Load the project-wide properties
1291 	readProjectPropertiesXml(xml_project);
1292 		//Load the default properties for the new diagrams
1293 	readDefaultPropertiesXml(xml_project);
1294 		//load the embedded titleblock templates
1295 	m_titleblocks_collection.fromXml(xml_project.documentElement());
1296 		//Load the embedded elements collection
1297 	readElementsCollectionXml(xml_project);
1298 		//Load the diagrams
1299 	readDiagramsXml(xml_project);
1300 
1301 		// if there is an attribute for folioSheetQuantity, then set it accordingly.
1302 		// If not, then the value remains at the initial value of zero.
1303 	if (root_elmt.attribute("folioSheetQuantity","0").toInt())
1304 		addNewDiagramFolioList();
1305 
1306 	m_state = Ok;
1307 }
1308 
1309 /**
1310  * @brief QETProject::readDiagramsXml
1311  * Load the diagrams from the xml description of the project.
1312  * Note a project can have 0 diagram
1313  * @param xml_project
1314  */
readDiagramsXml(QDomDocument & xml_project)1315 void QETProject::readDiagramsXml(QDomDocument &xml_project)
1316 {
1317 	QMultiMap<int, Diagram *> loaded_diagrams;
1318 
1319 	//@TODO try to solve a weird bug (dialog is black) since port to Qt5 with the DialogWaiting
1320 	//show DialogWaiting
1321 	DialogWaiting *dlgWaiting = nullptr;
1322 	if(DialogWaiting::hasInstance())
1323 	{
1324 		dlgWaiting = DialogWaiting::instance();
1325 		dlgWaiting -> setModal(true);
1326 		dlgWaiting -> show();
1327 		dlgWaiting -> setTitle( tr("<p align=\"center\">"
1328 								   "<b>Ouverture du projet en cours...</b><br/>"
1329 								   "Création des folios"
1330 								   "</p>"));
1331 	}
1332 
1333 		//Search the diagrams in the project
1334 	QDomNodeList diagram_nodes = xml_project.elementsByTagName("diagram");
1335 
1336 	if(dlgWaiting)
1337 		dlgWaiting->setProgressBarRange(0, diagram_nodes.length()*3);
1338 
1339 	for (int i = 0 ; i < diagram_nodes.length() ; ++ i)
1340 	{
1341 		if(dlgWaiting)
1342 			dlgWaiting->setProgressBar(i+1);
1343 
1344 		if (diagram_nodes.at(i).isElement())
1345 		{
1346 			QDomElement diagram_xml_element = diagram_nodes.at(i).toElement();
1347 			Diagram *diagram = new Diagram(this);
1348 			bool diagram_loading = diagram -> initFromXml(diagram_xml_element);
1349 			if (diagram_loading)
1350 			{
1351 				if(dlgWaiting)
1352 					dlgWaiting->setDetail( diagram->title() );
1353 
1354 					//Get the attribute "order" of the diagram
1355 				int diagram_order = -1;
1356 				if (!QET::attributeIsAnInteger(diagram_xml_element, "order", &diagram_order)) diagram_order = 500000;
1357 				loaded_diagrams.insert(diagram_order, diagram);
1358 			}
1359 			else
1360 			{
1361 				delete diagram;
1362 			}
1363 		}
1364 	}
1365 
1366 		//Add the diagrams according to there "order" attribute
1367 	foreach(Diagram *diagram, loaded_diagrams.values())
1368 		addDiagram(diagram);
1369 
1370 		//Initialise links between elements in this project
1371 		//and refresh the text of conductor
1372 	if(dlgWaiting)
1373 	{
1374 		dlgWaiting->setTitle( tr("<p align=\"center\">"
1375 								 "<b>Ouverture du projet en cours...</b><br/>"
1376 								 "Mise en place des références croisées"
1377 								 "</p>"));
1378 	}
1379 	for(Diagram *d : diagrams())
1380 	{
1381 		if(dlgWaiting)
1382 		{
1383 			dlgWaiting->setProgressBar(dlgWaiting->progressBarValue()+1);
1384 			dlgWaiting->setDetail(d->title());
1385 		}
1386 		d->refreshContents();
1387 	}
1388 }
1389 
1390 /**
1391  * @brief QETProject::readElementsCollectionXml
1392  * Load the diagrams from the xml description of the project
1393  * @param xml_project : the xml description of the project
1394  */
readElementsCollectionXml(QDomDocument & xml_project)1395 void QETProject::readElementsCollectionXml(QDomDocument &xml_project)
1396 {
1397 		//Get the embedded elements collection of the project
1398 	QDomNodeList collection_roots = xml_project.elementsByTagName("collection");
1399 	QDomElement collection_root;
1400 
1401 	if (!collection_roots.isEmpty())
1402 	{
1403 			//Only the first found collection is take
1404 		collection_root = collection_roots.at(0).toElement();
1405 	}
1406 		//Make an empty collection
1407 	if (collection_root.isNull())  {
1408 		m_elements_collection = new XmlElementCollection(this);
1409 	}
1410 		//Read the collection
1411 	else {
1412 		m_elements_collection = new XmlElementCollection(collection_root, this);
1413 	}
1414 }
1415 
1416 /**
1417  * @brief QETProject::readProjectPropertiesXml
1418  * Load project properties from the XML description of the project
1419  * @param xml_project : the xml description of the project
1420  */
readProjectPropertiesXml(QDomDocument & xml_project)1421 void QETProject::readProjectPropertiesXml(QDomDocument &xml_project)
1422 {
1423 	foreach (QDomElement e, QET::findInDomElement(xml_project.documentElement(), "properties"))
1424 		m_project_properties.fromXml(e);
1425 }
1426 
1427 /**
1428  * @brief QETProject::readDefaultPropertiesXml
1429  * load default properties for new diagram, found in the xml of this project
1430  * or by default find in the QElectroTech global conf
1431  * @param xml_project : the xml description of the project
1432  */
readDefaultPropertiesXml(QDomDocument & xml_project)1433 void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project)
1434 {
1435 		// Find xml element where is stored properties for new diagram
1436 	QDomNodeList newdiagrams_nodes = xml_project.elementsByTagName("newdiagrams");
1437 	if (newdiagrams_nodes.isEmpty()) return;
1438 
1439 	QDomElement newdiagrams_elmt = newdiagrams_nodes.at(0).toElement();
1440 
1441 		// By default, use value find in the global conf of QElectroTech
1442 	default_border_properties_	   = BorderProperties::    defaultProperties();
1443 	default_titleblock_properties_ = TitleBlockProperties::defaultProperties();
1444 	default_conductor_properties_  = ConductorProperties:: defaultProperties();
1445 	m_default_report_properties	   = ReportProperties::    defaultProperties();
1446 	m_default_xref_properties	   = XRefProperties::      defaultProperties();
1447 
1448 		//Read values indicate in project
1449 	QDomElement border_elmt, titleblock_elmt, conductors_elmt, report_elmt, xref_elmt, conds_autonums, folio_autonums, element_autonums;
1450 
1451 	for (QDomNode child = newdiagrams_elmt.firstChild() ; !child.isNull() ; child = child.nextSibling())
1452 	{
1453 		QDomElement child_elmt = child.toElement();
1454 		if (child_elmt.isNull()) continue;
1455 
1456 		if (child_elmt.tagName() == "border")
1457 			border_elmt = child_elmt;
1458 		else if (child_elmt.tagName() == "inset")
1459 			titleblock_elmt = child_elmt;
1460 		else if (child_elmt.tagName() == "conductors")
1461 			conductors_elmt = child_elmt;
1462 		else if (child_elmt.tagName() == "report")
1463 			report_elmt = child_elmt;
1464 		else if (child_elmt.tagName() == "xrefs")
1465 			xref_elmt = child_elmt;
1466 		else if (child_elmt.tagName() == "conductors_autonums")
1467 			conds_autonums = child_elmt;
1468 		else if (child_elmt.tagName()== "folio_autonums")
1469 			folio_autonums = child_elmt;
1470 		else if (child_elmt.tagName()== "element_autonums")
1471 			element_autonums = child_elmt;
1472 	}
1473 
1474 		// size, titleblock, conductor, report, conductor autonum, folio autonum, element autonum
1475 	if (!border_elmt.isNull())	   default_border_properties_.fromXml(border_elmt);
1476 	if (!titleblock_elmt.isNull()) default_titleblock_properties_.fromXml(titleblock_elmt);
1477 	if (!conductors_elmt.isNull()) default_conductor_properties_.fromXml(conductors_elmt);
1478 	if (!report_elmt.isNull())	   setDefaultReportProperties(report_elmt.attribute("label"));
1479 	if (!xref_elmt.isNull())
1480 	{
1481 		foreach(QDomElement elmt, QET::findInDomElement(xref_elmt, "xref"))
1482 		{
1483 			XRefProperties xrp;
1484 			xrp.fromXml(elmt);
1485 			m_default_xref_properties.insert(elmt.attribute("type"), xrp);
1486 		}
1487 	}
1488 	if (!conds_autonums.isNull())
1489 	{
1490 		m_current_conductor_autonum = conds_autonums.attribute("current_autonum");
1491 		m_freeze_new_conductors = conds_autonums.attribute("freeze_new_conductors") == "true";
1492 		foreach (QDomElement elmt, QET::findInDomElement(conds_autonums, "conductor_autonum"))
1493 		{
1494 			NumerotationContext nc;
1495 			nc.fromXml(elmt);
1496 			m_conductor_autonum.insert(elmt.attribute("title"), nc);
1497 		}
1498 	}
1499 	if (!folio_autonums.isNull())
1500 	{
1501 		foreach (QDomElement elmt, QET::findInDomElement(folio_autonums, "folio_autonum"))
1502 		{
1503 			NumerotationContext nc;
1504 			nc.fromXml(elmt);
1505 			m_folio_autonum.insert(elmt.attribute("title"), nc);
1506 		}
1507 	}
1508 	if (!element_autonums.isNull())
1509 	{
1510 		m_current_element_autonum = element_autonums.attribute("current_autonum");
1511 		m_freeze_new_elements = element_autonums.attribute("freeze_new_elements") == "true";
1512 		foreach (QDomElement elmt, QET::findInDomElement(element_autonums, "element_autonum"))
1513 		{
1514 			NumerotationContext nc;
1515 			nc.fromXml(elmt);
1516 			m_element_autonum.insert(elmt.attribute("title"), nc);
1517 		}
1518 	}
1519 }
1520 
1521 /**
1522 	Export project properties under the \a xml_element XML element.
1523 */
writeProjectPropertiesXml(QDomElement & xml_element)1524 void QETProject::writeProjectPropertiesXml(QDomElement &xml_element) {
1525 	m_project_properties.toXml(xml_element);
1526 }
1527 
1528 /**
1529  * @brief QETProject::writeDefaultPropertiesXml
1530  * Export all defaults properties used by a new diagram and his content
1531  * #size of border
1532  * #content of titleblock
1533  * #default conductor
1534  * #defaut folio report
1535  * #default Xref
1536  * @param xml_element xml element to use for store default propertie.
1537  */
writeDefaultPropertiesXml(QDomElement & xml_element)1538 void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) {
1539 	QDomDocument xml_document = xml_element.ownerDocument();
1540 
1541 	// export size of border
1542 	QDomElement border_elmt = xml_document.createElement("border");
1543 	default_border_properties_.toXml(border_elmt);
1544 	xml_element.appendChild(border_elmt);
1545 
1546 	// export content of titleblock
1547 	QDomElement titleblock_elmt = xml_document.createElement("inset");
1548 	default_titleblock_properties_.toXml(titleblock_elmt);
1549 	xml_element.appendChild(titleblock_elmt);
1550 
1551 	// exporte default conductor
1552 	QDomElement conductor_elmt = xml_document.createElement("conductors");
1553 	default_conductor_properties_.toXml(conductor_elmt);
1554 	xml_element.appendChild(conductor_elmt);
1555 
1556 	// export default report properties
1557 	QDomElement report_elmt = xml_document.createElement("report");
1558 	report_elmt.setAttribute("label", defaultReportProperties());
1559 	xml_element.appendChild(report_elmt);
1560 
1561 	// export default XRef properties
1562 	QDomElement xrefs_elmt = xml_document.createElement("xrefs");
1563 	foreach (QString key, defaultXRefProperties().keys()) {
1564 		QDomElement xref_elmt = xml_document.createElement("xref");
1565 		xref_elmt.setAttribute("type", key);
1566 		defaultXRefProperties()[key].toXml(xref_elmt);
1567 		xrefs_elmt.appendChild(xref_elmt);
1568 	}
1569 	xml_element.appendChild(xrefs_elmt);
1570 
1571 	//Export Conductor Autonums
1572 	QDomElement conductor_autonums = xml_document.createElement("conductors_autonums");
1573 	conductor_autonums.setAttribute("current_autonum", m_current_conductor_autonum);
1574 	conductor_autonums.setAttribute("freeze_new_conductors", m_freeze_new_conductors ? "true" : "false");
1575 	foreach (QString key, conductorAutoNum().keys()) {
1576 	QDomElement conductor_autonum = conductorAutoNum(key).toXml(xml_document, "conductor_autonum");
1577 		if (key != "" && conductorAutoNumFormula(key) != "") {
1578 			conductor_autonum.setAttribute("title", key);
1579 			conductor_autonum.setAttribute("formula", conductorAutoNumFormula(key));
1580 			conductor_autonums.appendChild(conductor_autonum);
1581 		}
1582 	}
1583 	xml_element.appendChild(conductor_autonums);
1584 
1585 	//Export Folio Autonums
1586 	QDomElement folio_autonums = xml_document.createElement("folio_autonums");
1587 	foreach (QString key, folioAutoNum().keys()) {
1588 	QDomElement folio_autonum = folioAutoNum(key).toXml(xml_document, "folio_autonum");
1589 		folio_autonum.setAttribute("title", key);
1590 		folio_autonums.appendChild(folio_autonum);
1591 	}
1592 	xml_element.appendChild(folio_autonums);
1593 
1594 	//Export Element Autonums
1595 	QDomElement element_autonums = xml_document.createElement("element_autonums");
1596 	element_autonums.setAttribute("current_autonum", m_current_element_autonum);
1597 	element_autonums.setAttribute("freeze_new_elements", m_freeze_new_elements ? "true" : "false");
1598 	foreach (QString key, elementAutoNum().keys()) {
1599 	QDomElement element_autonum = elementAutoNum(key).toXml(xml_document, "element_autonum");
1600 		if (key != "" && elementAutoNumFormula(key) != "") {
1601 			element_autonum.setAttribute("title", key);
1602 			element_autonum.setAttribute("formula", elementAutoNumFormula(key));
1603 			element_autonums.appendChild(element_autonum);
1604 		}
1605 	}
1606 	xml_element.appendChild(element_autonums);
1607 }
1608 
1609 /**
1610 	Cette methode ajoute un schema donne au projet
1611 	@param diagram Schema a ajouter
1612 */
1613 /**
1614  * @brief QETProject::addDiagram
1615  * Add a diagram in this project
1616  * @param diagram added diagram
1617  * @param position postion of the new diagram, by default at the end
1618  */
addDiagram(Diagram * diagram)1619 void QETProject::addDiagram(Diagram *diagram) {
1620 	if (!diagram) return;
1621 
1622 	// Ensure diagram know is parent project
1623 	diagram -> setProject(this);
1624 
1625 	connect(
1626 		&(diagram -> border_and_titleblock),
1627 		SIGNAL(needFolioData()),
1628 		this,
1629 		SLOT(updateDiagramsFolioData())
1630 	);
1631 	connect(
1632 		diagram, SIGNAL(usedTitleBlockTemplateChanged(const QString &)),
1633 		this, SLOT(usedTitleBlockTemplateChanged(const QString &))
1634 	);
1635 
1636 	// add diagram to project
1637 		m_diagrams_list << diagram;
1638 
1639 	updateDiagramsFolioData();
1640 }
1641 
1642 /**
1643 	@return La liste des noms a utiliser pour la categorie dediee aux elements
1644 	integres automatiquement dans le projet.
1645 
1646 */
namesListForIntegrationCategory()1647 NamesList QETProject::namesListForIntegrationCategory() {
1648 	NamesList names;
1649 
1650 	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 };
1651 	const QChar greek_data[18] = { 0x0395, 0x03b9, 0x03c3, 0x03b7, 0x03b3, 0x03bc, 0x03ad, 0x03bd, 0x03b1, 0x0020, 0x03c3, 0x03c4, 0x03bf, 0x03b9, 0x03c7, 0x03b5, 0x03af, 0x03b1 };
1652 
1653 	names.addName("fr", "Éléments importés");
1654 	names.addName("en", "Imported elements");
1655 	names.addName("de", "Importierte elemente");
1656 	names.addName("es", "Elementos importados");
1657 	names.addName("ru", QString(russian_data, 24));
1658 	names.addName("cs", "Zavedené prvky");
1659 	names.addName("pl", "Elementy importowane");
1660 	names.addName("pt", "elementos importados");
1661 	names.addName("it", "Elementi importati");
1662 	names.addName("el", QString(greek_data, 18));
1663 	names.addName("nl", "Elementen geïmporteerd");
1664 	names.addName("hr", "Uvezeni elementi");
1665 	names.addName("ca", "Elements importats");
1666 	names.addName("ro", "Elemente importate");
1667 
1668 	return(names);
1669 }
1670 
1671 /**
1672  * @brief QETProject::writeBackup
1673  * Write a backup file of this project, in the case that QET crash
1674  */
writeBackup()1675 void QETProject::writeBackup()
1676 {
1677 	if (!m_backup_file ||
1678 		(!m_backup_file->isOpen() && !m_backup_file->open(QIODevice::ReadWrite))) {
1679 		return;
1680 	}
1681 
1682 	QDomDocument xml_project;
1683 	xml_project.appendChild(xml_project.importNode(toXml().documentElement(), true));
1684 	QET::writeToFile(xml_project, m_backup_file);
1685 }
1686 
1687 /**
1688 	@return true if project options (title, project-wide properties, settings
1689 	for new diagrams, diagrams order...) were modified, false otherwise.
1690 */
projectOptionsWereModified()1691 bool QETProject::projectOptionsWereModified() {
1692 	// unlike similar methods, this method does not compare the content against
1693 	// expected values; instead, we just check whether we have been set as modified.
1694 	return(m_modified);
1695 }
1696 
1697 /**
1698 	@return the project-wide properties made available to child diagrams.
1699 */
projectProperties()1700 DiagramContext QETProject::projectProperties() {
1701 	return(m_project_properties);
1702 }
1703 
1704 /**
1705 	Use \a context as project-wide properties made available to child diagrams.
1706 */
setProjectProperties(const DiagramContext & context)1707 void QETProject::setProjectProperties(const DiagramContext &context) {
1708 	m_project_properties = context;
1709 	updateDiagramsFolioData();
1710 }
1711 
1712 /**
1713 	Cette methode sert a reperer un projet vide, c-a-d un projet identique a ce
1714 	que l'on obtient en faisant Fichier > Nouveau.
1715 	@return true si les schemas, la collection embarquee ou les proprietes de ce
1716 	projet ont ete modifies.
1717 	Concretement, le projet doit avoir un titre vide et ni ses schemas ni sa
1718 	collection embarquee ne doivent avoir ete modifies.
1719 	@see diagramsWereModified(), embeddedCollectionWasModified()
1720 */
projectWasModified()1721 bool QETProject::projectWasModified() {
1722 
1723 	if ( projectOptionsWereModified()    ||
1724 		 !m_undo_stack -> isClean()       ||
1725 		 m_titleblocks_collection.templates().count() )
1726 		return(true);
1727 
1728 	else
1729 		return(false);
1730 }
1731 
1732 /**
1733 	Indique a chaque schema du projet quel est son numero de folio et combien de
1734 	folio le projet contient.
1735 */
updateDiagramsFolioData()1736 void QETProject::updateDiagramsFolioData()
1737 {
1738 	int total_folio = m_diagrams_list.count();
1739 
1740 	DiagramContext project_wide_properties = m_project_properties;
1741 	project_wide_properties.addValue("projecttitle", title());
1742 	project_wide_properties.addValue("projectpath", filePath());
1743 	project_wide_properties.addValue("projectfilename", QFileInfo(filePath()).baseName());
1744 
1745 	for (int i = 0 ; i < total_folio ; ++ i)
1746 	{
1747 		QString autopagenum = m_diagrams_list[i]->border_and_titleblock.autoPageNum();
1748 		NumerotationContext nC = folioAutoNum(autopagenum);
1749 		NumerotationContextCommands nCC = NumerotationContextCommands(nC);
1750 
1751 		if ((m_diagrams_list[i]->border_and_titleblock.folio().contains("%autonum")) &&
1752 			(!autopagenum.isNull()))
1753 		{
1754 			m_diagrams_list[i] -> border_and_titleblock.setFolioData(i + 1, total_folio, nCC.toRepresentedString(), project_wide_properties);
1755 			m_diagrams_list[i]->project()->addFolioAutoNum(autopagenum,nCC.next());
1756 		}
1757 		else {
1758 			m_diagrams_list[i] -> border_and_titleblock.setFolioData(i + 1, total_folio, nullptr, project_wide_properties);
1759 		}
1760 
1761 		if (i > 0)
1762 		{
1763 			m_diagrams_list.at(i)->border_and_titleblock.setPreviousFolioNum(m_diagrams_list.at(i-1)->border_and_titleblock.finalfolio());
1764 			m_diagrams_list.at(i-1)->border_and_titleblock.setNextFolioNum(m_diagrams_list.at(i)->border_and_titleblock.finalfolio());
1765 
1766 			if (i == total_folio-1) {
1767 				m_diagrams_list.at(i)->border_and_titleblock.setNextFolioNum(QString());
1768 			}
1769 		}
1770 		else {
1771 			m_diagrams_list.at(i)->border_and_titleblock.setPreviousFolioNum(QString());
1772 		}
1773 	}
1774 
1775 	for (Diagram *d : m_diagrams_list) {
1776 		d->update();
1777 	}
1778 }
1779 
1780 /**
1781 	Inform each diagram that the \a template_name title block changed.
1782 	@param collection Title block templates collection
1783 	@param template_name Name of the changed template
1784 */
updateDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection * collection,const QString & template_name)1785 void QETProject::updateDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *collection, const QString &template_name) {
1786 	Q_UNUSED(collection)
1787 
1788 	foreach (Diagram *diagram, m_diagrams_list) {
1789 		diagram -> titleBlockTemplateChanged(template_name);
1790 	}
1791 }
1792 
1793 /**
1794 	Inform each diagram that the \a template_name title block is about to be removed.
1795 	@param collection Title block templates collection
1796 	@param template_name Name of the removed template
1797 */
removeDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection * collection,const QString & template_name)1798 void QETProject::removeDiagramsTitleBlockTemplate(TitleBlockTemplatesCollection *collection, const QString &template_name) {
1799 	Q_UNUSED(collection)
1800 
1801 	// warn diagrams that the given template is about to be removed
1802 	foreach (Diagram *diagram, m_diagrams_list) {
1803 		diagram -> titleBlockTemplateRemoved(template_name);
1804 	}
1805 }
1806 
1807 /**
1808 	Handles the fact a digram changed the title block template it used
1809 	@param template_name Name of the template
1810 */
usedTitleBlockTemplateChanged(const QString & template_name)1811 void QETProject::usedTitleBlockTemplateChanged(const QString &template_name) {
1812 	emit(diagramUsedTemplate(embeddedTitleBlockTemplatesCollection(), template_name));
1813 }
1814