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 "qetapp.h"
19 #include "aboutqet.h"
20 #include "configdialog.h"
21 #include "configpages.h"
22 #include "qetdiagrameditor.h"
23 #include "qetelementeditor.h"
24 #include "elementscollectioncache.h"
25 #include "titleblocktemplate.h"
26 #include "qettemplateeditor.h"
27 #include "qetproject.h"
28 #include "qtextorientationspinboxwidget.h"
29 #include "recentfiles.h"
30 #include "qeticons.h"
31 #include "templatescollection.h"
32 #include "generalconfigurationpage.h"
33 #include "qetmessagebox.h"
34 #include "projectview.h"
35 #include "elementpicturefactory.h"
36 
37 #include <cstdlib>
38 #include <iostream>
39 #define QUOTE(x) STRINGIFY(x)
40 #define STRINGIFY(x) #x
41 #include <QProcessEnvironment>
42 #include "factory/elementfactory.h"
43 
44 #include <KAutoSaveFile>
45 
46 #ifdef QET_ALLOW_OVERRIDE_CED_OPTION
47 QString QETApp::common_elements_dir = QString();
48 #endif
49 #ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
50 QString QETApp::common_tbt_dir_ = QString();
51 #endif
52 #ifdef QET_ALLOW_OVERRIDE_CD_OPTION
53 QString QETApp::config_dir = QString();
54 #endif
55 QString QETApp::lang_dir = QString();
56 TitleBlockTemplatesFilesCollection *QETApp::m_common_tbt_collection;
57 TitleBlockTemplatesFilesCollection *QETApp::m_custom_tbt_collection;
58 ElementsCollectionCache *QETApp::collections_cache_ = nullptr;
59 QMap<uint, QETProject *> QETApp::registered_projects_ = QMap<uint, QETProject *>();
60 uint QETApp::next_project_id = 0;
61 RecentFiles *QETApp::m_projects_recent_files = nullptr;
62 RecentFiles *QETApp::m_elements_recent_files = nullptr;
63 TitleBlockTemplate *QETApp::default_titleblock_template_ = nullptr;
64 QString QETApp::m_user_common_elements_dir = QString();
65 QString QETApp::m_user_custom_elements_dir = QString();
66 QString QETApp::m_user_custom_tbt_dir = QString();
67 QETApp *QETApp::m_qetapp = nullptr;
68 
69 
70 /**
71  * @brief QETApp::QETApp
72  */
QETApp()73 QETApp::QETApp() :
74 	m_splash_screen(nullptr),
75 	non_interactive_execution_(false)
76 {
77 	m_qetapp = this;
78 	parseArguments();
79     if (non_interactive_execution_) {
80         std::exit(EXIT_SUCCESS);
81     }
82 	initConfiguration();
83 	initLanguage();
84 	QET::Icons::initIcons();
85 	initStyle();
86 	initSplashScreen();
87 	initSystemTray();
88 
89 	connect(&signal_map, SIGNAL(mapped(QWidget *)), this, SLOT(invertMainWindowVisibility(QWidget *)));
90 	qApp->setQuitOnLastWindowClosed(false);
91 	connect(qApp, &QApplication::lastWindowClosed, this, &QETApp::checkRemainingWindows);
92 
93 	setSplashScreenStep(tr("Chargement... Initialisation du cache des collections d'éléments", "splash screen caption"));
94 	if (!collections_cache_) {
95 		QString cache_path = QETApp::configDir() + "/elements_cache.sqlite";
96 		collections_cache_ = new ElementsCollectionCache(cache_path, this);
97 		collections_cache_->setLocale(langFromSetting());
98 	}
99 
100 	if (qet_arguments_.files().isEmpty())
101 	{
102 		setSplashScreenStep(tr("Chargement... Éditeur de schéma", "splash screen caption"));
103 		new QETDiagramEditor();
104 	} else
105 	{
106 		setSplashScreenStep(tr("Chargement... Ouverture des fichiers", "splash screen caption"));
107 		openFiles(qet_arguments_);
108 	}
109 
110 	buildSystemTrayMenu();
111     if (m_splash_screen) {
112         m_splash_screen -> hide();
113     }
114 
115 	checkBackupFiles();
116 }
117 
118 /**
119  * @brief QETApp::~QETApp
120  */
~QETApp()121 QETApp::~QETApp()
122 {
123 	m_elements_recent_files->save();
124 	m_projects_recent_files->save();
125 
126 	delete m_splash_screen;
127 	delete m_elements_recent_files;
128 	delete m_projects_recent_files;
129 	delete m_qsti;
130 
131 	if (m_custom_tbt_collection)
132 		delete m_custom_tbt_collection;
133 	if (m_common_tbt_collection)
134 		delete m_common_tbt_collection;
135 
136 	ElementFactory::dropInstance();
137 	ElementPictureFactory::dropInstance();
138 }
139 
140 /**
141 	@return l'instance de la QETApp
142 */
instance()143 QETApp *QETApp::instance()
144 {
145 	return m_qetapp;
146 }
147 
148 /**
149 	Change le langage utilise par l'application.
150 	@param desired_language langage voulu
151 */
setLanguage(const QString & desired_language)152 void QETApp::setLanguage(const QString &desired_language) {
153 	QString languages_path = languagesPath();
154 
155 	// load Qt library translations
156 	QString qt_l10n_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
157 	if (!qtTranslator.load("qt_" + desired_language, qt_l10n_path)) {
158 		qtTranslator.load("qt_" + desired_language, languages_path);
159 	}
160 	qApp->installTranslator(&qtTranslator);
161 
162 	// charge les traductions pour l'application QET
163 	if (!qetTranslator.load("qet_" + desired_language, languages_path)) {
164 		// en cas d'echec, on retombe sur les chaines natives pour le francais
165 		if (desired_language != "fr") {
166 			// utilisation de la version anglaise par defaut
167 			qetTranslator.load("qet_en", languages_path);
168 		}
169 	}
170 	qApp->installTranslator(&qetTranslator);
171 
172 	QString ltr_special_string = tr(
173 		"LTR",
174 		"Translate this string to RTL if you are translating to a Right-to-Left language, else translate to LTR"
175 	);
176 	if (ltr_special_string == "RTL") switchLayout(Qt::RightToLeft);
177 }
178 
179 /**
180  * @brief QETApp::langFromSetting
181  * @return the langage found in setting file
182  * if nothing was found return the system local.
183  */
langFromSetting()184 QString QETApp::langFromSetting()
185 {
186 	QSettings settings;
187 	QString system_language = settings.value("lang", "system").toString();
188 	if(system_language == "system") {system_language = QLocale::system().name().left(2);}
189 	return system_language;
190 }
191 /**
192 	Switches the application to the provided layout.
193 */
switchLayout(Qt::LayoutDirection direction)194 void QETApp::switchLayout(Qt::LayoutDirection direction) {
195 	qApp->setLayoutDirection(direction);
196 }
197 
198 /**
199 	Gere les evenements relatifs au QSystemTrayIcon
200 	@param reason un entier representant l'evenement survenu sur le systray
201 */
systray(QSystemTrayIcon::ActivationReason reason)202 void QETApp::systray(QSystemTrayIcon::ActivationReason reason) {
203 	if (!QSystemTrayIcon::isSystemTrayAvailable()) return;
204 	switch(reason) {
205 		case QSystemTrayIcon::Context:
206 			// affichage du menu
207 			buildSystemTrayMenu();
208 			m_qsti -> contextMenu() -> show();
209 			break;
210 		case QSystemTrayIcon::DoubleClick:
211 		case QSystemTrayIcon::Trigger:
212 			// reduction ou restauration de l'application
213 			fetchWindowStats(diagramEditors(), elementEditors(), titleBlockTemplateEditors());
214 			if (every_editor_reduced) restoreEveryEditor(); else reduceEveryEditor();
215 			break;
216 		case QSystemTrayIcon::Unknown:
217 		default: // ne rien faire
218 		break;
219 	}
220 }
221 
222 /// Reduit toutes les fenetres de l'application dans le systray
reduceEveryEditor()223 void QETApp::reduceEveryEditor() {
224 	reduceDiagramEditors();
225 	reduceElementEditors();
226 	reduceTitleBlockTemplateEditors();
227 	every_editor_reduced = true;
228 }
229 
230 /// Restaure toutes les fenetres de l'application dans le systray
restoreEveryEditor()231 void QETApp::restoreEveryEditor() {
232 	restoreDiagramEditors();
233 	restoreElementEditors();
234 	restoreTitleBlockTemplateEditors();
235 	every_editor_reduced = false;
236 }
237 
238 /// Reduit tous les editeurs de schemas dans le systray
reduceDiagramEditors()239 void QETApp::reduceDiagramEditors() {
240 	setMainWindowsVisible<QETDiagramEditor>(false);
241 }
242 
243 /// Restaure tous les editeurs de schemas dans le systray
restoreDiagramEditors()244 void QETApp::restoreDiagramEditors() {
245 	setMainWindowsVisible<QETDiagramEditor>(true);
246 }
247 
248 /// Reduit tous les editeurs d'element dans le systray
reduceElementEditors()249 void QETApp::reduceElementEditors() {
250 	setMainWindowsVisible<QETElementEditor>(false);
251 }
252 
253 /// Restaure tous les editeurs d'element dans le systray
restoreElementEditors()254 void QETApp::restoreElementEditors() {
255 	setMainWindowsVisible<QETElementEditor>(true);
256 }
257 
258 /// Reduce all known template editors
reduceTitleBlockTemplateEditors()259 void QETApp::reduceTitleBlockTemplateEditors() {
260 	setMainWindowsVisible<QETTitleBlockTemplateEditor>(false);
261 }
262 
263 /// Restore all known template editors
restoreTitleBlockTemplateEditors()264 void QETApp::restoreTitleBlockTemplateEditors() {
265 	setMainWindowsVisible<QETTitleBlockTemplateEditor>(true);
266 }
267 
268 /// lance un nouvel editeur de schemas
newDiagramEditor()269 void QETApp::newDiagramEditor() {
270 	new QETDiagramEditor();
271 }
272 
273 /// lance un nouvel editeur d'element
newElementEditor()274 void QETApp::newElementEditor() {
275 	new QETElementEditor();
276 }
277 
278 /**
279 	@return the collection cache provided by the application itself.
280 */
collectionCache()281 ElementsCollectionCache *QETApp::collectionCache() {
282 	return(collections_cache_);
283 }
284 
285 /**
286  * @brief QETApp::elementInfoKeys
287  * @return all available key for describe an element
288  */
elementInfoKeys()289 QStringList QETApp::elementInfoKeys()
290 {
291 	QStringList info_list;
292 	info_list << "formula"
293 			  << "label"
294 			  << "plant"
295 			  << "location"
296 
297 			  << "comment"
298 			  << "function"
299 			  << "tension-protocol"
300 			  << "auxiliary1"
301 			  << "auxiliary2"
302 
303 			  << "description"
304 			  << "designation"
305 			  << "manufacturer"
306 			  << "manufacturer-reference"
307 			  << "machine-manufacturer-reference"
308 			  << "supplier"
309 			  << "quantity"
310 			  << "unity";
311 	return info_list;
312 }
313 
314 /**
315  * @brief ElementsProperties::translatedInfo
316  * Return the translated information key given by @info
317  * If @info don't match, return an empty string
318  * @param info the key to be translated
319  * @return
320  */
elementTranslatedInfoKey(const QString & info)321 QString QETApp::elementTranslatedInfoKey(const QString &info)
322 {
323 	if (info == "formula") return tr("Formule du label");
324 	else if (info == "label") return tr("Label");
325 	else if (info == "plant") return tr("Installation");
326 	else if (info == "location") return tr("Localisation");
327 
328 	else if (info == "comment") return tr("Commentaire");
329 	else if (info == "function") return tr("Fonction");
330 	else if (info == "tension-protocol") return tr("Tension / Protocole");
331 	else if (info == "auxiliary1") return tr("Bloc auxiliaire 1");
332 	else if (info == "auxiliary2") return tr("Bloc auxiliaire 2");
333 
334 	else if (info == "description") return tr("Description textuelle");
335 	else if (info == "designation") return tr("Numéro d'article");
336 	else if (info == "manufacturer") return tr("Fabricant");
337 	else if (info == "manufacturer-reference") return tr("Numéro de commande");
338 	else if (info == "machine-manufacturer-reference") return tr("Numéro interne");
339 	else if (info == "supplier") return tr("Fournisseur");
340 	else if (info == "quantity") return tr("Quantité");
341 	else if (info == "unity") return tr("Unité");
342 
343 
344 
345 
346 	return (info);
347 }
348 
349 /**
350  * @brief QETApp::elementInfoToVar
351  * @param info
352  * @return var in form %{my-var} corresponding to the info, if there is not available var for the given info
353  * the returned var is %{void}
354  */
elementInfoToVar(const QString & info)355 QString QETApp::elementInfoToVar(const QString &info)
356 {
357 	if (info == "formula")                             return QString("%{formula}");
358 	else if (info == "label")                          return QString("%{label}");
359 	else if (info == "plant")                          return QString("%{plant}");
360 	else if (info == "comment")                        return QString("%{comment}");
361 	else if (info == "description")                    return QString("%{description}");
362 	else if (info == "designation")                    return QString("%{designation}");
363 	else if (info == "manufacturer")                   return QString("%{manufacturer}");
364 	else if (info == "manufacturer-reference")         return QString("%{manufacturer-reference}");
365 	else if (info == "supplier")                       return QString("%{supplier}");
366 	else if (info == "quantity")                       return QString("%{quantity}");
367 	else if (info == "unity")                          return QString("%{unity}");
368 	else if (info == "auxiliary1")                     return QString("%{auxiliary1}");
369 	else if (info == "auxiliary2")                     return QString("%{auxiliary2}");
370 	else if (info == "machine-manufacturer-reference") return QString("%{machine-manufacturer-reference}");
371 	else if (info == "location")                       return QString("%{location}");
372 	else if (info == "function")                       return QString("%{function}");
373 	else if (info == "tension-protocol")               return QString("%{tension-protocol}");
374 
375 	return (QString ("%{void}"));
376 }
377 
378 /**
379  * @brief QETApp::conductorInfoKeys
380  * @return the conductor information keys
381  */
conductorInfoKeys()382 QStringList QETApp::conductorInfoKeys()
383 {
384 	QStringList keys;
385 	keys.append("formula");
386 	keys.append("text");
387 	keys.append("function");
388 	keys.append("tension/protocol");
389 
390 	return keys;
391 }
392 
393 /**
394  * @brief QETApp::conductorTranslatedInfoKey
395  * @param key
396  * @return the translated information key given by @key
397  * If @key don't match, return an empty string
398  */
conductorTranslatedInfoKey(const QString & key)399 QString QETApp::conductorTranslatedInfoKey(const QString &key)
400 {
401 	if      (key == "formula")           return tr("Formule du texte");
402 	else if (key == "text")              return tr("Texte");
403 	else if (key == "function")          return tr("Fonction");
404 	else if (key ==  "tension/protocol") return tr("Tension / Protocole");
405 	return QString();
406 }
407 
408 /**
409  * @brief QETApp::diagramInfoKeys
410  * @return the diagram default information keys
411  */
diagramInfoKeys()412 QStringList QETApp::diagramInfoKeys()
413 {
414 	QStringList list;
415 	list.append("title");
416 	list.append("author");
417 	list.append("filename");
418 	list.append("folio");
419 	list.append("plant");
420 	list.append("locmach");
421 	list.append("indexrev");
422 
423 	return list;
424 }
425 
426 /**
427  * @brief QETApp::diagramTranslatedInfoKey
428  * @param key
429  * @return the translated information key given by @key
430  * If @key don't match, return an empty string
431  */
diagramTranslatedInfoKey(const QString & key)432 QString QETApp::diagramTranslatedInfoKey(const QString &key)
433 {
434 	if      (key == "title")    return tr("Titre");
435 	else if (key == "author")   return tr("Auteur");
436 	else if (key == "filename") return tr("Fichier");
437 	else if (key == "folio")    return tr("Folio");
438 	else if (key == "plant")    return tr("Installation");
439 	else if (key == "locmach")  return tr("Localisation");
440 	else if (key == "indexrev") return tr("Indice Rev");
441 	else return QString();
442 }
443 
444 /**
445 	@return the common title block templates collection, i.e. the one provided
446 	by QElecrotTech
447 */
commonTitleBlockTemplatesCollection()448 TitleBlockTemplatesFilesCollection *QETApp::commonTitleBlockTemplatesCollection() {
449 	if (!m_common_tbt_collection) {
450 		m_common_tbt_collection = new TitleBlockTemplatesFilesCollection(QETApp::commonTitleBlockTemplatesDir());
451 		m_common_tbt_collection -> setTitle(tr("Cartouches QET", "title of the title block templates collection provided by QElectroTech"));
452 		m_common_tbt_collection -> setProtocol(QETAPP_COMMON_TBT_PROTOCOL);
453 		m_common_tbt_collection -> setCollection(QET::QetCollection::Common);
454 	}
455 	return(m_common_tbt_collection);
456 }
457 
458 /**
459 	@return the custom title block templates collection, i.e. the one managed
460 	by the end user
461 */
customTitleBlockTemplatesCollection()462 TitleBlockTemplatesFilesCollection *QETApp::customTitleBlockTemplatesCollection() {
463 	if (!m_custom_tbt_collection) {
464 		m_custom_tbt_collection = new TitleBlockTemplatesFilesCollection(QETApp::customTitleBlockTemplatesDir());
465 		m_custom_tbt_collection -> setTitle(tr("Cartouches utilisateur", "title of the user's title block templates collection"));
466 		m_custom_tbt_collection -> setProtocol(QETAPP_CUSTOM_TBT_PROTOCOL);
467 		m_custom_tbt_collection -> setCollection(QET::QetCollection::Custom);
468 	}
469 	return(m_custom_tbt_collection);
470 }
471 
472 /**
473 	@return the list of all available title block tempaltes collections,
474 	beginning with the common and custom ones, plus the projects-embedded ones.
475 */
availableTitleBlockTemplatesCollections()476 QList<TitleBlockTemplatesCollection *> QETApp::availableTitleBlockTemplatesCollections() {
477 	QList<TitleBlockTemplatesCollection *> collections_list;
478 
479 	collections_list << commonTitleBlockTemplatesCollection();
480 	collections_list << customTitleBlockTemplatesCollection();
481 
482 	foreach(QETProject *opened_project, registered_projects_) {
483 		collections_list << opened_project -> embeddedTitleBlockTemplatesCollection();
484 	}
485 
486 	return(collections_list);
487 }
488 
489 /**
490 	@param protocol Protocol string
491 	@return the templates collection matching the provided protocol, or 0 if none could be found
492 */
titleBlockTemplatesCollection(const QString & protocol)493 TitleBlockTemplatesCollection *QETApp::titleBlockTemplatesCollection(const QString &protocol) {
494 	if (protocol == QETAPP_COMMON_TBT_PROTOCOL) {
495 		return(m_common_tbt_collection);
496 	} else if (protocol == QETAPP_CUSTOM_TBT_PROTOCOL) {
497 		return(m_custom_tbt_collection);
498 	} else {
499 		QETProject *project = QETApp::projectFromString(protocol);
500 		if (project) {
501 			return(project -> embeddedTitleBlockTemplatesCollection());
502 		}
503 	}
504 	return(nullptr);
505 }
506 
507 /**
508  * @brief QETApp::commonElementsDir
509  * @return the dir path of the common elements collection.
510  */
commonElementsDir()511 QString QETApp::commonElementsDir()
512 {
513 	if (m_user_common_elements_dir.isEmpty())
514 	{
515 		QSettings settings;
516 		QString path = settings.value("elements-collections/common-collection-path", "default").toString();
517 		if (path != "default" && !path.isEmpty())
518 		{
519 			QDir dir(path);
520 			if (dir.exists())
521 			{
522 				m_user_common_elements_dir = path;
523 				return m_user_common_elements_dir;
524 			}
525 		}
526 		else {
527 			m_user_common_elements_dir = "default";
528 		}
529 	}
530 	else if (m_user_common_elements_dir != "default") {
531 		return m_user_common_elements_dir;
532 	}
533 
534 #ifdef QET_ALLOW_OVERRIDE_CED_OPTION
535 	if (common_elements_dir != QString()) return(common_elements_dir);
536 #endif
537 #ifndef QET_COMMON_COLLECTION_PATH
538 	// en l'absence d'option de compilation, on utilise le dossier elements, situe a cote du binaire executable
539 	return(QCoreApplication::applicationDirPath() + "/elements/");
540 #else
541 	#ifndef QET_COMMON_COLLECTION_PATH_RELATIVE_TO_BINARY_PATH
542 		// l'option de compilation represente un chemin absolu ou relatif classique
543 		return(QUOTE(QET_COMMON_COLLECTION_PATH));
544 	#else
545 		// l'option de compilation represente un chemin relatif au dossier contenant le binaire executable
546 		return(QCoreApplication::applicationDirPath() + "/" + QUOTE(QET_COMMON_COLLECTION_PATH));
547 	#endif
548 #endif
549 }
550 
551 /**
552  * @brief QETApp::customElementsDir
553  * @return the dir path of user elements collection
554  */
customElementsDir()555 QString QETApp::customElementsDir()
556 {
557 	if (m_user_custom_elements_dir.isEmpty())
558 	{
559 		QSettings settings;
560 		QString path = settings.value("elements-collections/custom-collection-path", "default").toString();
561 		if (path != "default" && !path.isEmpty())
562 		{
563 			QDir dir(path);
564 			if (dir.exists())
565 				{
566 					m_user_custom_elements_dir = path;
567 					return m_user_custom_elements_dir;
568 			}
569 		}
570 		else {
571 			m_user_custom_elements_dir = "default";
572 		}
573 	}
574 	else if (m_user_custom_elements_dir != "default") {
575 		return m_user_custom_elements_dir;
576 	}
577 
578 	return(configDir() + "elements/");
579 }
580 
581 /**
582  * @brief QETApp::commonElementsDirN
583  * like QString QETApp::commonElementsDir but without "/" at the end
584  * @return
585  */
commonElementsDirN()586 QString QETApp::commonElementsDirN()
587 {
588 	QString path = commonElementsDir();
589 	if (path.endsWith("/")) path.remove(path.length()-1, 1);
590 	return path;
591 }
592 
593 /**
594  * @brief QETApp::customElementsDirN
595  * like QString QETApp::customElementsDir but without "/" at the end
596  * @return
597  */
customElementsDirN()598 QString QETApp::customElementsDirN()
599 {
600 	QString path = customElementsDir();
601 	if (path.endsWith("/")) path.remove(path.length()-1, 1);
602 	return path;
603 }
604 
605 /**
606  * @brief QETApp::resetUserElementsDir
607  * Reset the path of the user common and custom elements dir.
608  * Use this function when the user path (common and/or custom) change.
609  */
resetUserElementsDir()610 void QETApp::resetUserElementsDir()
611 {
612 	m_user_common_elements_dir.clear();
613 	m_user_custom_elements_dir.clear();
614 	m_user_custom_tbt_dir.clear();
615 }
616 
617 /**
618 	@return the path of the directory containing the common title block
619 	templates collection.
620 */
commonTitleBlockTemplatesDir()621 QString QETApp::commonTitleBlockTemplatesDir() {
622 #ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
623 	if (common_tbt_dir_ != QString()) return(common_tbt_dir_);
624 #endif
625 #ifndef QET_COMMON_TBT_PATH
626 	// without any compile-time option, use the "titleblocks" directory next to the executable binary
627 	return(QCoreApplication::applicationDirPath() + "/titleblocks/");
628 #else
629 	#ifndef QET_COMMON_COLLECTION_PATH_RELATIVE_TO_BINARY_PATH
630 		// the compile-time option represents a usual path (be it absolute or relative)
631 		return(QUOTE(QET_COMMON_TBT_PATH));
632 	#else
633 		// the compile-time option represents a path relative to the directory that contains the executable binary
634 		return(QCoreApplication::applicationDirPath() + "/" + QUOTE(QET_COMMON_TBT_PATH));
635 	#endif
636 #endif
637 }
638 
639 /**
640 	@return the path of the directory containing the custom title block
641 	templates collection.
642 */
customTitleBlockTemplatesDir()643 QString QETApp::customTitleBlockTemplatesDir() {
644 		if (m_user_custom_tbt_dir.isEmpty())
645 	{
646 			QSettings settings;
647 			QString path = settings.value("elements-collections/custom-tbt-path", "default").toString();
648 			if (path != "default" && !path.isEmpty())
649 			{
650 				QDir dir(path);
651 				if (dir.exists())
652 				{
653 					m_user_custom_tbt_dir = path;
654 					return m_user_custom_tbt_dir;
655 				}
656 			}
657 		else {
658 			m_user_custom_tbt_dir = "default";
659 		}
660 	}
661 	else if (m_user_custom_tbt_dir != "default") {
662 		return m_user_custom_tbt_dir;
663 	}
664 
665 	return(configDir() + "titleblocks/");
666 }
667 
668 /**
669 	Renvoie le dossier de configuration de QET, c-a-d le chemin du dossier dans
670 	lequel QET lira les informations de configuration et de personnalisation
671 	propres a l'utilisateur courant. Ce dossier est generalement
672 	C:\\Documents And Settings\\utilisateur\\Application Data\\qet sous Windows et
673 	~/.qet sous les systemes type UNIX.
674 	@return Le chemin du dossier de configuration de QElectroTech
675 */
configDir()676 QString QETApp::configDir() {
677 #ifdef QET_ALLOW_OVERRIDE_CD_OPTION
678 	if (config_dir != QString()) return(config_dir);
679 #endif
680 #ifdef Q_OS_WIN32
681 	// recupere l'emplacement du dossier Application Data
682 	// char *app_data_env = getenv("APPDATA");
683 	// QString app_data_str(app_data_env);
684 	QProcess * process = new QProcess();
685 	QString app_data_str = (process->processEnvironment()).value("APPDATA");
686 	// delete app_data_env;
687 	delete process;
688 	if (app_data_str.isEmpty()) {
689 		app_data_str = QDir::homePath() + "/Application Data";
690 	}
691 	return(app_data_str + "/qet/");
692 #else
693 	return(QDir::homePath() + "/.qet/");
694 #endif
695 }
696 
697 /**
698 	Permet de connaitre le chemin absolu du fichier *.elmt correspondant a un
699 	chemin symbolique (du type custom://outils_pervers/sado_maso/contact_bizarre)
700 	@param sym_path Chaine de caracteres representant le chemin absolu du fichier
701 	@return Une chaine de caracteres vide en cas d'erreur ou le chemin absolu du
702 	fichier *.elmt.
703 */
realPath(const QString & sym_path)704 QString QETApp::realPath(const QString &sym_path) {
705 	QString directory;
706 	if (sym_path.startsWith("common://")) {
707 		directory = commonElementsDir();
708 	} else if (sym_path.startsWith("custom://")) {
709 		directory = customElementsDir();
710 	} else if (sym_path.startsWith(QETAPP_COMMON_TBT_PROTOCOL "://")) {
711 		directory = commonTitleBlockTemplatesDir();
712 	} else if (sym_path.startsWith(QETAPP_CUSTOM_TBT_PROTOCOL "://")) {
713 		directory = customTitleBlockTemplatesDir();
714 	} else return(QString());
715 	return(directory + QDir::toNativeSeparators(sym_path.right(sym_path.length() - 9)));
716 }
717 
718 /**
719 	Construit le chemin symbolique (du type custom://outils_pervers/sado_maso/
720 	contact_bizarre) correspondant a un fichier.
721 	@param real_path Chaine de caracteres representant le chemin symbolique du fichier
722 	@return Une chaine de caracteres vide en cas d'erreur ou le chemin
723 	symbolique designant l'element.
724 */
symbolicPath(const QString & real_path)725 QString QETApp::symbolicPath(const QString &real_path) {
726 	// recupere les dossier common et custom
727 	QString commond = commonElementsDir();
728 	QString customd = customElementsDir();
729 	QString chemin;
730 	// analyse le chemin de fichier passe en parametre
731 	if (real_path.startsWith(commond)) {
732 		chemin = "common://" + real_path.right(real_path.length() - commond.length());
733 	} else if (real_path.startsWith(customd)) {
734 		chemin = "custom://" + real_path.right(real_path.length() - customd.length());
735 	} else chemin = QString();
736 	return(chemin);
737 }
738 
739 /**
740 	@return the list of file extensions QElectroTech is able to open and
741 	supposed to handle. Note they are provided with no leading point.
742 */
handledFileExtensions()743 QStringList QETApp::handledFileExtensions() {
744 	static QStringList ext;
745 	if (!ext.count()) {
746 		ext << "qet";
747 		ext << "elmt";
748 		ext << QString(TITLEBLOCKS_FILE_EXTENSION).remove(QRegExp("^\\."));
749 	}
750 	return(ext);
751 }
752 
753 /**
754 	@param an URLs list
755 	@return the list of filepaths QElectroTech is able to open.
756 */
handledFiles(const QList<QUrl> & urls)757 QStringList QETApp::handledFiles(const QList<QUrl> &urls) {
758 	QList<QString> filepaths;
759 	foreach (QUrl url, urls) {
760 		if (url.scheme() != "file") continue;
761 		QString local_path = url.toLocalFile();
762 		QFileInfo local_path_info(local_path);
763 		QString local_path_ext = local_path_info.completeSuffix();
764 		if (handledFileExtensions().contains(local_path_ext)) {
765 			filepaths << local_path;
766 		}
767 	}
768 	return(filepaths);
769 }
770 
771 /**
772 	@param filepath Un chemin de fichier
773 	Note : si filepath est une chaine vide, cette methode retourne 0.
774 	@return le QETDiagramEditor editant le fichier filepath, ou 0 si ce fichier
775 	n'est pas edite par l'application.
776 */
diagramEditorForFile(const QString & filepath)777 QETDiagramEditor *QETApp::diagramEditorForFile(const QString &filepath) {
778 	if (filepath.isEmpty()) return(nullptr);
779 
780 	QETApp *qet_app(QETApp::instance());
781 	foreach (QETDiagramEditor *diagram_editor, qet_app -> diagramEditors()) {
782 		if (diagram_editor -> viewForFile(filepath)) {
783 			return(diagram_editor);
784 		}
785 	}
786 
787 	return(nullptr);
788 }
789 
790 /**
791  * @brief QETApp::diagramEditorAncestorOf
792  * @param child
793  * @return the parent QETDiagramEditor (or grandparent and so on to any level) of the given child.
794  * If not return nullptr;
795  */
diagramEditorAncestorOf(const QWidget * child)796 QETDiagramEditor *QETApp::diagramEditorAncestorOf (const QWidget *child)
797 {
798 	foreach (QETDiagramEditor *qde, QETApp::diagramEditors()) {
799 		if (qde->isAncestorOf(child)) {
800 			return qde;
801 		}
802 	}
803 
804 	return nullptr;
805 }
806 
807 #ifdef QET_ALLOW_OVERRIDE_CED_OPTION
808 /**
809 	Redefinit le chemin du dossier des elements communs
810 	@param new_ced Nouveau chemin du dossier des elements communs
811 */
overrideCommonElementsDir(const QString & new_ced)812 void QETApp::overrideCommonElementsDir(const QString &new_ced) {
813 	QFileInfo new_ced_info(new_ced);
814 	if (new_ced_info.isDir()) {
815 		common_elements_dir = new_ced_info.absoluteFilePath();
816 		if (!common_elements_dir.endsWith("/")) common_elements_dir += "/";
817 	}
818 }
819 #endif
820 
821 #ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
822 /**
823 	Define the path of the directory containing the common title block
824 	tempaltes collection.
825 */
overrideCommonTitleBlockTemplatesDir(const QString & new_ctbtd)826 void QETApp::overrideCommonTitleBlockTemplatesDir(const QString &new_ctbtd) {
827 	QFileInfo new_ctbtd_info(new_ctbtd);
828 	if (new_ctbtd_info.isDir()) {
829 		common_tbt_dir_ = new_ctbtd_info.absoluteFilePath();
830 		if (!common_tbt_dir_.endsWith("/")) common_tbt_dir_ += "/";
831 	}
832 }
833 #endif
834 
835 #ifdef QET_ALLOW_OVERRIDE_CD_OPTION
836 /**
837 	Redefinit le chemin du dossier de configuration
838 	@param new_cd Nouveau chemin du dossier de configuration
839 */
overrideConfigDir(const QString & new_cd)840 void QETApp::overrideConfigDir(const QString &new_cd) {
841 	QFileInfo new_cd_info(new_cd);
842 	if (new_cd_info.isDir()) {
843 		config_dir = new_cd_info.absoluteFilePath();
844 		if (!config_dir.endsWith("/")) config_dir += "/";
845 	}
846 }
847 #endif
848 
849 /**
850 	Redefinit le chemin du dossier contenant les fichiers de langue
851 	@param new_ld Nouveau chemin du dossier contenant les fichiers de langue
852 */
overrideLangDir(const QString & new_ld)853 void QETApp::overrideLangDir(const QString &new_ld) {
854 	QFileInfo new_ld_info(new_ld);
855 	if (new_ld_info.isDir()) {
856 		lang_dir = new_ld_info.absoluteFilePath();
857 		if (!lang_dir.endsWith("/")) lang_dir += "/";
858 	}
859 }
860 
861 /**
862 	@return Le chemin du dossier contenant les fichiers de langue
863 */
languagesPath()864 QString QETApp::languagesPath() {
865 	if (!lang_dir.isEmpty()) {
866 		return(lang_dir);
867 	} else {
868 #ifndef QET_LANG_PATH
869 	// en l'absence d'option de compilation, on utilise le dossier lang, situe a cote du binaire executable
870 	return(QCoreApplication::applicationDirPath() + "/lang/");
871 #else
872 	#ifndef QET_LANG_PATH_RELATIVE_TO_BINARY_PATH
873 		// l'option de compilation represente un chemin absolu ou relatif classique
874 		return(QUOTE(QET_LANG_PATH));
875 	#else
876 		// l'option de compilation represente un chemin relatif au dossier contenant le binaire executable
877 		return(QCoreApplication::applicationDirPath() + "/" + QUOTE(QET_LANG_PATH));
878 	#endif
879 #endif
880 	}
881 }
882 
883 /**
884 	Ferme tous les editeurs
885 	@return true si l'utilisateur a accepte toutes les fermetures, false sinon
886 */
closeEveryEditor()887 bool QETApp::closeEveryEditor() {
888 	// s'assure que toutes les fenetres soient visibles avant de quitter
889 	restoreEveryEditor();
890 	foreach(QETProject *project, registered_projects_) {
891 		project -> close();
892 	}
893 	bool every_window_closed = true;
894 	foreach(QETDiagramEditor *e, diagramEditors()) {
895 		every_window_closed = every_window_closed && e -> close();
896 	}
897 	foreach(QETElementEditor *e, elementEditors()) {
898 		every_window_closed = every_window_closed && e -> close();
899 	}
900 	return(every_window_closed);
901 }
902 
903 /**
904  * @brief QETApp::diagramTextsFont
905  * The font to use
906  * By default the font is "sans Serif" and size 9.
907  * @param size : the size of font
908  * @return the font to use
909  */
diagramTextsFont(qreal size)910 QFont QETApp::diagramTextsFont(qreal size)
911 {
912 	QSettings settings;
913 
914 		//Font to use
915 	QString diagram_texts_family = settings.value("diagramfont", "Sans Serif").toString();
916 	qreal diagram_texts_size     = settings.value("diagramsize", 9.0).toDouble();
917 
918 	if (size != -1.0) {
919 		diagram_texts_size = size;
920 	}
921 	QFont diagram_texts_font = QFont(diagram_texts_family);
922 	diagram_texts_font.setPointSizeF(diagram_texts_size);
923 	if (diagram_texts_size <= 4.0) {
924 		diagram_texts_font.setWeight(QFont::Light);
925 	}
926 	return(diagram_texts_font);
927 }
928 /**
929  * @brief QETApp::diagramTextsItemFont
930  * the font for to use in independent text item
931  * @param size of font
932  * @return
933  */
diagramTextsItemFont(qreal size)934 QFont QETApp::diagramTextsItemFont(qreal size)
935 {
936 	QSettings settings;
937 
938 		//Font to use
939 	QString diagram_texts_item_family = settings.value("diagramitemfont", "Sans Serif").toString();
940 	qreal diagram_texts_item_size     = settings.value("diagramitemsize", 9.0).toDouble();
941 	qreal diagram_texts_item_weight   = settings.value("diagramitemweight").toDouble();
942 	QString diagram_texts_item_style  = settings.value("diagramitemstyle", "normal").toString();
943 
944 	if (size != -1.0) {
945 		diagram_texts_item_size = size;
946 	}
947 	QFont diagram_texts_item_font = QFont(diagram_texts_item_family);
948 	diagram_texts_item_font.setPointSizeF(diagram_texts_item_size);
949 	diagram_texts_item_font.setWeight(diagram_texts_item_weight);
950 	diagram_texts_item_font.setStyleName(diagram_texts_item_style);
951 	if (diagram_texts_item_size <= 4.0) {
952 		diagram_texts_item_font.setWeight(QFont::Light);
953 	}
954 	return(diagram_texts_item_font);
955 }
956 /**
957  * @brief QETApp::dynamicTextsFont
958  * the defaukt font of dynamic element text item
959  * @param size
960  * @return
961  */
dynamicTextsItemFont(qreal size)962  QFont QETApp::dynamicTextsItemFont(qreal size)
963 {
964 	QSettings settings;
965 		//Font to use
966 	QFont font_ = diagramTextsItemFont();
967 	if (settings.contains("diagrameditor/dynamic_text_font")) {
968 		font_.fromString(settings.value("diagrameditor/dynamic_text_font").toString());
969 	}
970 	if (size > 0) {
971 		font_.setPointSizeF(size);
972 	}
973 	return(font_);
974 }
975 
976 /**
977  * @brief QETApp::foliolistTextsFont
978  * the font for to use in summary pages
979  * @param size
980  * @return
981  */
foliolistTextsFont(qreal size)982 QFont QETApp::foliolistTextsFont(qreal size)
983 {
984 	QSettings settings;
985 
986 		//Font to use
987 	QString foliolist_texts_family = settings.value("foliolistfont", "Sans Serif").toString();
988 	qreal foliolist_texts_size     = settings.value("foliolistsize", 9.0).toDouble();
989 
990 	if (size != -1.0) {
991 		foliolist_texts_size = size;
992 	}
993 	QFont foliolist_texts_font = QFont(foliolist_texts_family);
994 	foliolist_texts_font.setPointSizeF(foliolist_texts_size);
995 	if (foliolist_texts_size <= 4.0) {
996 		foliolist_texts_font.setWeight(QFont::Light);
997 	}
998 	return(foliolist_texts_font);
999 }
1000 
1001 /**
1002  * @brief QETApp::indiTextsItemFont
1003  * The default font to use for independent text item
1004  * @param size
1005  * @return
1006  */
indiTextsItemFont(qreal size)1007 QFont QETApp::indiTextsItemFont(qreal size)
1008 {
1009 	QSettings settings;
1010 		//Font to use
1011 	QFont font_ = diagramTextsItemFont();
1012 	if (settings.contains("diagrameditor/independent_text_font")) {
1013 		font_.fromString(settings.value("diagrameditor/independent_text_font").toString());
1014 	}
1015 	if (size > 0) {
1016 		font_.setPointSizeF(size);
1017 	}
1018 	return(font_);
1019 }
1020 
1021 
1022 /**
1023 	@return les editeurs de schemas
1024 */
diagramEditors()1025 QList<QETDiagramEditor *> QETApp::diagramEditors() {
1026 	return(QETApp::instance() -> detectWindows<QETDiagramEditor>());
1027 }
1028 
1029 /**
1030 	@return les editeurs d'elements
1031 */
elementEditors()1032 QList<QETElementEditor *> QETApp::elementEditors() {
1033 	return(QETApp::instance() -> detectWindows<QETElementEditor>());
1034 }
1035 
1036 /**
1037 	@return the title block template editors
1038 */
titleBlockTemplateEditors()1039 QList<QETTitleBlockTemplateEditor *> QETApp::titleBlockTemplateEditors() {
1040 	return(QETApp::instance() -> detectWindows<QETTitleBlockTemplateEditor>());
1041 }
1042 
1043 /**
1044 	@param project Opened project object.
1045 	@return the list of title block template editors which are currently
1046 	editing a template embedded within \a project.
1047 */
titleBlockTemplateEditors(QETProject * project)1048 QList<QETTitleBlockTemplateEditor *> QETApp::titleBlockTemplateEditors(QETProject *project) {
1049 	QList<QETTitleBlockTemplateEditor *> editors;
1050 	if (!project) return(editors);
1051 
1052 	// foreach known template editor
1053 	foreach (QETTitleBlockTemplateEditor *tbt_editor, titleBlockTemplateEditors()) {
1054 		if (tbt_editor -> location().parentProject() == project) {
1055 			editors << tbt_editor;
1056 		}
1057 	}
1058 
1059 	return(editors);
1060 }
1061 
1062 /**
1063 	Instancie un QTextOrientationSpinBoxWidget et configure :
1064 	  * sa police de caracteres
1065 	  * ses chaines de caracteres
1066 	A noter que la suppression du widget ainsi alloue est a la charge de
1067 	l'appelant.
1068 	@return un QTextOrientationSpinBoxWidget adapte pour une utilisation
1069 	"directe" dans QET.
1070 	@see QTextOrientationSpinBoxWidget
1071 */
createTextOrientationSpinBoxWidget()1072 QTextOrientationSpinBoxWidget *QETApp::createTextOrientationSpinBoxWidget() {
1073 	QTextOrientationSpinBoxWidget *widget = new QTextOrientationSpinBoxWidget();
1074 	widget -> orientationWidget() -> setFont(QETApp::diagramTextsFont());
1075 	widget -> orientationWidget() -> setUsableTexts(QList<QString>()
1076 		<< QETApp::tr("Q",            "Single-letter example text - translate length, not meaning")
1077 		<< QETApp::tr("QET",          "Small example text - translate length, not meaning")
1078 		<< QETApp::tr("Schema",       "Normal example text - translate length, not meaning")
1079 		<< QETApp::tr("Electrique",   "Normal example text - translate length, not meaning")
1080 		<< QETApp::tr("QElectroTech", "Long example text - translate length, not meaning")
1081 	);
1082 	return(widget);
1083 }
1084 
1085 /**
1086 	@return the default titleblock template for diagrams
1087 */
defaultTitleBlockTemplate()1088 TitleBlockTemplate *QETApp::defaultTitleBlockTemplate() {
1089 	if (!QETApp::default_titleblock_template_) {
1090 		TitleBlockTemplate *titleblock_template = new TitleBlockTemplate(QETApp::instance());
1091 		if (titleblock_template -> loadFromXmlFile(":/titleblocks/default.titleblock")) {
1092 			QETApp::default_titleblock_template_ = titleblock_template;
1093 		}
1094 	}
1095 	return(default_titleblock_template_);
1096 }
1097 
1098 
1099 /**
1100 	@param project un projet
1101 	@return les editeurs d'elements editant un element appartenant au projet
1102 	project
1103 */
elementEditors(QETProject * project)1104 QList<QETElementEditor *> QETApp::elementEditors(QETProject *project) {
1105 	QList<QETElementEditor *> editors;
1106 	if (!project) return(editors);
1107 
1108 	// pour chaque editeur d'element...
1109 	foreach(QETElementEditor *elmt_editor, elementEditors()) {
1110 		// on recupere l'emplacement de l'element qu'il edite
1111 		ElementsLocation elmt_editor_loc(elmt_editor -> location());
1112 
1113 		// il se peut que l'editeur edite un element non enregistre ou un fichier
1114 		if (elmt_editor_loc.isNull()) continue;
1115 
1116 		if (elmt_editor_loc.project() == project) {
1117 			editors << elmt_editor;
1118 		}
1119 	}
1120 
1121 	return(editors);
1122 }
1123 
receiveMessage(int instanceId,QByteArray message)1124 void QETApp::receiveMessage(int instanceId, QByteArray message)
1125 {
1126 	Q_UNUSED(instanceId);
1127 
1128 	QString str(message);
1129 
1130 	if (str.startsWith("launched-with-args: "))
1131 	{
1132 		QString my_message(str.mid(20));
1133 		QStringList args_list = QET::splitWithSpaces(my_message);
1134 		openFiles(QETArguments(args_list));
1135 	}
1136 }
1137 
1138 /**
1139 	@param T a class inheriting QMainWindow
1140 	@return the list of windows of type T
1141 */
detectWindows() const1142 template <class T> QList<T *> QETApp::detectWindows() const {
1143 	QList<T *> windows;
1144 	foreach(QWidget *widget, qApp->topLevelWidgets()) {
1145 		if (!widget -> isWindow()) continue;
1146 		if (T *window = qobject_cast<T *>(widget)) {
1147 			windows << window;
1148 		}
1149 	}
1150 	return(windows);
1151 }
1152 
1153 /**
1154 	@param T a class inheriting QMainWindow
1155 	@param visible whether detected main windows should be visible
1156 */
setMainWindowsVisible(bool visible)1157 template <class T> void QETApp::setMainWindowsVisible(bool visible) {
1158 	foreach(T *e, detectWindows<T>()) {
1159 		setMainWindowVisible(e, visible);
1160 	}
1161 }
1162 
1163 /**
1164 	@return La liste des fichiers recents pour les projets
1165 */
projectsRecentFiles()1166 RecentFiles *QETApp::projectsRecentFiles() {
1167 	return(m_projects_recent_files);
1168 }
1169 
1170 /**
1171 	@return La liste des fichiers recents pour les elements
1172 */
elementsRecentFiles()1173 RecentFiles *QETApp::elementsRecentFiles() {
1174 	return(m_elements_recent_files);
1175 }
1176 
1177 /**
1178 	Affiche ou cache une fenetre (editeurs de schemas / editeurs d'elements)
1179 	@param window fenetre a afficher / cacher
1180 	@param visible true pour affiche la fenetre, false sinon
1181 */
setMainWindowVisible(QMainWindow * window,bool visible)1182 void QETApp::setMainWindowVisible(QMainWindow *window, bool visible) {
1183 	if (window -> isVisible() == visible) return;
1184 	if (!visible) {
1185 		window_geometries.insert(window, window -> saveGeometry());
1186 		window_states.insert(window, window -> saveState());
1187 		window -> hide();
1188 		// cache aussi les toolbars et les docks
1189 		foreach (QWidget *qw, floatingToolbarsAndDocksForMainWindow(window)) {
1190 			qw -> hide();
1191 		}
1192 	} else {
1193 		window -> show();
1194 #ifndef Q_OS_WIN32
1195 		window -> restoreGeometry(window_geometries[window]);
1196 #endif
1197 		window -> restoreState(window_states[window]);
1198 	}
1199 }
1200 
1201 /**
1202 	Affiche une fenetre (editeurs de schemas / editeurs d'elements) si
1203 	celle-ci est cachee ou la cache si elle est affichee.
1204 	@param window fenetre a afficher / cacher
1205 */
invertMainWindowVisibility(QWidget * window)1206 void QETApp::invertMainWindowVisibility(QWidget *window) {
1207 	if (QMainWindow *w = qobject_cast<QMainWindow *>(window)) setMainWindowVisible(w, !w -> isVisible());
1208 }
1209 
1210 /**
1211 	Autodetec Windows style
1212 	@param Windows style
1213 */
1214 #if defined(Q_OS_WIN) && defined(Q_OS_WINCE)
1215 
1216 if defined(Q_OS_WIN)
1217 			if ((QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
1218 				&& QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
1219 				style = QLatin1String("WindowsVista");
1220 			else if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP
1221 				&& QSysInfo::WindowsVersion < QSysInfo::WV_NT_based))
1222 				style = QLatin1String("WindowsXP");
1223 			else
1224 				style = QLatin1String("Windows");                // default styles for Windows
1225 #endif
1226 
1227 
1228 /**
1229 	Change la palette de l'application
1230 	@param use true pour utiliser les couleurs du systeme, false pour utiliser celles du theme en cours
1231 */
useSystemPalette(bool use)1232 void QETApp::useSystemPalette(bool use) {
1233 	if (use) {
1234 		qApp->setPalette(initial_palette_);
1235 		qApp->setStyleSheet(
1236 					"QTabBar::tab:!selected { background-color: transparent; }"
1237 					"QAbstractScrollArea#mdiarea {"
1238 					"background-color -> setPalette(initial_palette_);"
1239 					"}"
1240 		);
1241 	} else {
1242 		QFile file(configDir() + "style.css");
1243 		file.open(QFile::ReadOnly);
1244 		QString styleSheet = QLatin1String(file.readAll());
1245 		qApp->setStyleSheet(styleSheet);
1246 		file.close();
1247 	}
1248 }
1249 
1250 /**
1251 	Demande la fermeture de toutes les fenetres ; si l'utilisateur les accepte,
1252 	l'application quitte
1253 */
quitQET()1254 void QETApp::quitQET() {
1255 	if (closeEveryEditor()) {
1256 		qApp->quit();
1257 	}
1258 }
1259 
1260 /**
1261 	Verifie s'il reste des fenetres (cachees ou non) et quitte s'il n'en reste
1262 	plus.
1263 */
checkRemainingWindows()1264 void QETApp::checkRemainingWindows() {
1265 	/* petite bidouille : le slot se rappelle apres 500 ms d'attente
1266 	afin de compenser le fait que certaines fenetres peuvent encore
1267 	paraitre vivantes alors qu'elles viennent d'etre fermees
1268 	*/
1269 	static bool sleep = true;
1270 	if (sleep) {
1271 		QTimer::singleShot(500, this, SLOT(checkRemainingWindows()));
1272 	} else {
1273 		if (!diagramEditors().count() && !elementEditors().count()) {
1274 			qApp->quit();
1275 		}
1276 	}
1277 	sleep = !sleep;
1278 }
1279 
1280 /**
1281 	Ouvre les fichiers passes en arguments
1282 	@param args Objet contenant des arguments ; les fichiers
1283 	@see openProjectFiles openElementFiles
1284 */
openFiles(const QETArguments & args)1285 void QETApp::openFiles(const QETArguments &args) {
1286 	openProjectFiles(args.projectFiles());
1287 	openElementFiles(args.elementFiles());
1288 	openTitleBlockTemplateFiles(args.titleBlockTemplateFiles());
1289 }
1290 
1291 /**
1292 	Ouvre une liste de fichiers.
1293 	Les fichiers sont ouverts dans le premier editeur de schemas visible venu.
1294 	Sinon, le premier editeur de schemas existant venu devient visible et est
1295 	utilise. S'il n'y a aucun editeur de schemas ouvert, un nouveau est cree et
1296 	utilise.
1297 	@param files_list Fichiers a ouvrir
1298 */
openProjectFiles(const QStringList & files_list)1299 void QETApp::openProjectFiles(const QStringList &files_list) {
1300 	if (files_list.isEmpty()) return;
1301 
1302 	// liste des editeurs de schema ouverts
1303 	QList<QETDiagramEditor *> diagrams_editors = diagramEditors();
1304 
1305 	// s'il y a des editeur de schemas ouvert, on cherche ceux qui sont visibles
1306 	if (diagrams_editors.count()) {
1307 		QList<QETDiagramEditor *> visible_diagrams_editors;
1308 		foreach(QETDiagramEditor *de, diagrams_editors) {
1309 			if (de -> isVisible()) visible_diagrams_editors << de;
1310 		}
1311 
1312 		// on choisit soit le premier visible soit le premier tout court
1313 		QETDiagramEditor *de_open;
1314 		if (visible_diagrams_editors.count()) {
1315 			de_open = visible_diagrams_editors.first();
1316 		} else {
1317 			de_open = diagrams_editors.first();
1318 			de_open -> setVisible(true);
1319 		}
1320 
1321 		// ouvre les fichiers dans l'editeur ainsi choisi
1322 		foreach(QString file, files_list) {
1323 			de_open -> openAndAddProject(file);
1324 		}
1325 	} else {
1326 		// cree un nouvel editeur qui ouvrira les fichiers
1327 		new QETDiagramEditor(files_list);
1328 	}
1329 }
1330 
1331 /**
1332 	Ouvre les fichiers elements passes en parametre. Si un element est deja
1333 	ouvert, la fenetre qui l'edite est activee.
1334 	@param files_list Fichiers a ouvrir
1335 */
openElementFiles(const QStringList & files_list)1336 void QETApp::openElementFiles(const QStringList &files_list) {
1337 	if (files_list.isEmpty()) return;
1338 
1339 	// evite autant que possible les doublons dans la liste fournie
1340 	QSet<QString> files_set;
1341 	foreach(QString file, files_list) {
1342 		QString canonical_filepath = QFileInfo(file).canonicalFilePath();
1343 		if (!canonical_filepath.isEmpty()) files_set << canonical_filepath;
1344 	}
1345 	// a ce stade, tous les fichiers dans le Set existent et sont a priori differents
1346 	if (files_set.isEmpty()) return;
1347 
1348 	// liste des editeurs d'element ouverts
1349 	QList<QETElementEditor *> element_editors = elementEditors();
1350 
1351 	// on traite les fichiers a la queue leu leu...
1352 	foreach(QString element_file, files_set) {
1353 		bool already_opened_in_existing_element_editor = false;
1354 		foreach(QETElementEditor *element_editor, element_editors) {
1355 			if (element_editor -> isEditing(element_file)) {
1356 				// ce fichier est deja ouvert dans un editeur
1357 				already_opened_in_existing_element_editor = true;
1358 				element_editor -> setVisible(true);
1359 				element_editor -> raise();
1360 				element_editor -> activateWindow();
1361 				break;
1362 			}
1363 		}
1364 		if (!already_opened_in_existing_element_editor) {
1365 			// ce fichier n'est ouvert dans aucun editeur
1366 			QETElementEditor *element_editor = new QETElementEditor();
1367 			element_editor -> fromFile(element_file);
1368 		}
1369 	}
1370 }
1371 
1372 /**
1373 	Ouvre les elements dont l'emplacement est passe en parametre. Si un element
1374 	est deja ouvert, la fentre qui l'edite est activee.
1375 	@param locations_list Emplacements a ouvrir
1376 */
openElementLocations(const QList<ElementsLocation> & locations_list)1377 void QETApp::openElementLocations(const QList<ElementsLocation> &locations_list) {
1378 	if (locations_list.isEmpty()) return;
1379 
1380 	// liste des editeurs d'element ouverts
1381 	QList<QETElementEditor *> element_editors = elementEditors();
1382 
1383 	// on traite les emplacements  a la queue leu leu...
1384 	foreach(ElementsLocation element_location, locations_list) {
1385 		bool already_opened_in_existing_element_editor = false;
1386 		foreach(QETElementEditor *element_editor, element_editors) {
1387 			if (element_editor -> isEditing(element_location)) {
1388 				// cet emplacement est deja ouvert dans un editeur
1389 				already_opened_in_existing_element_editor = true;
1390 				element_editor -> setVisible(true);
1391 				element_editor -> raise();
1392 				element_editor -> activateWindow();
1393 				break;
1394 			}
1395 		}
1396 		if (!already_opened_in_existing_element_editor) {
1397 			// cet emplacement n'est ouvert dans aucun editeur
1398 			QETElementEditor *element_editor = new QETElementEditor();
1399 			element_editor -> fromLocation(element_location);
1400 		}
1401 	}
1402 }
1403 
1404 /**
1405 	Launch a new title block template editor to edit the given template
1406 	@param location location of the title block template to be edited
1407 
1408 	@param duplicate if true, the template is opened for duplication, which means
1409 	the user will be prompter for a new template name.
1410 	@see QETTitleBlockTemplateEditor::setOpenForDuplication()
1411 */
openTitleBlockTemplate(const TitleBlockTemplateLocation & location,bool duplicate)1412 void QETApp::openTitleBlockTemplate(const TitleBlockTemplateLocation &location, bool duplicate) {
1413 	QETTitleBlockTemplateEditor *qet_template_editor = new QETTitleBlockTemplateEditor();
1414 	qet_template_editor -> setOpenForDuplication(duplicate);
1415 	qet_template_editor -> edit(location);
1416 	qet_template_editor -> show();
1417 }
1418 
1419 /**
1420 	Launch a new title block template editor to edit the given template
1421 	@param filepath Path of the .titleblock file to be opened
1422 */
openTitleBlockTemplate(const QString & filepath)1423 void QETApp::openTitleBlockTemplate(const QString &filepath) {
1424 	QETTitleBlockTemplateEditor *qet_template_editor = new QETTitleBlockTemplateEditor();
1425 	qet_template_editor -> edit(filepath);
1426 	qet_template_editor -> show();
1427 }
1428 
1429 /**
1430 	Open provided title block template files. If a title block template is already
1431 	opened, the adequate window is activated.
1432 	@param files_list Files to be opened
1433 */
openTitleBlockTemplateFiles(const QStringList & files_list)1434 void QETApp::openTitleBlockTemplateFiles(const QStringList &files_list) {
1435 	if (files_list.isEmpty()) return;
1436 
1437 	// avoid duplicates in the provided files list
1438 	QSet<QString> files_set;
1439 	foreach (QString file, files_list) {
1440 		QString canonical_filepath = QFileInfo(file).canonicalFilePath();
1441 		if (!canonical_filepath.isEmpty()) files_set << canonical_filepath;
1442 	}
1443 	// here, we can assume all files in the set exist and are different
1444 	if (files_set.isEmpty()) return;
1445 
1446 	// opened title block template editors
1447 	QList<QETTitleBlockTemplateEditor *> tbt_editors = titleBlockTemplateEditors();
1448 
1449 	foreach(QString tbt_file, files_set) {
1450 		bool already_opened_in_existing_tbt_editor = false;
1451 		foreach(QETTitleBlockTemplateEditor *tbt_editor, tbt_editors) {
1452 			if (tbt_editor -> isEditing(tbt_file)) {
1453 				// this file is already opened
1454 				already_opened_in_existing_tbt_editor = true;
1455 				tbt_editor -> setVisible(true);
1456 				tbt_editor -> raise();
1457 				tbt_editor -> activateWindow();
1458 				break;
1459 			}
1460 		}
1461 		if (!already_opened_in_existing_tbt_editor) {
1462 			// this file is not opened yet
1463 			openTitleBlockTemplate(tbt_file);
1464 		}
1465 	}
1466 }
1467 
1468 /**
1469 	Permet a l'utilisateur de configurer QET en lancant un dialogue approprie.
1470 	@see ConfigDialog
1471 */
configureQET()1472 void QETApp::configureQET() {
1473 	// determine le widget parent a utiliser pour le dialogue
1474 	QWidget *parent_widget = qApp->activeWindow();
1475 
1476 	// cree le dialogue
1477 	ConfigDialog cd;
1478 	cd.setWindowTitle(tr("Configurer QElectroTech", "window title"));
1479 	cd.setWindowModality(Qt::WindowModal);
1480 	cd.addPage(new GeneralConfigurationPage());
1481 	cd.addPage(new NewDiagramPage());
1482 	cd.addPage(new ExportConfigPage());
1483 	cd.addPage(new PrintConfigPage());
1484 
1485 
1486 	// associe le dialogue a un eventuel widget parent
1487 	if (parent_widget) {
1488 		cd.setParent(parent_widget, cd.windowFlags());
1489 	}
1490 
1491 	// affiche le dialogue puis evite de le lier a un quelconque widget parent
1492 	cd.exec();
1493 	cd.setParent(nullptr, cd.windowFlags());
1494 }
1495 
1496 /**
1497  * @brief QETApp::aboutQET
1498  * Open the dialog about qet.
1499  */
aboutQET()1500 void QETApp::aboutQET()
1501 {
1502 	AboutQET aq(qApp->activeWindow());
1503 
1504 #ifdef Q_OS_MACOS
1505 	aq.setWindowFlags(Qt::Sheet);
1506 #endif
1507 	aq.exec();
1508 }
1509 
1510 /**
1511 	@param window fenetre dont il faut trouver les barres d'outils et dock flottants
1512 	@return les barres d'outils et dock flottants de la fenetre
1513 */
floatingToolbarsAndDocksForMainWindow(QMainWindow * window) const1514 QList<QWidget *> QETApp::floatingToolbarsAndDocksForMainWindow(QMainWindow *window) const {
1515 	QList<QWidget *> widgets;
1516 	foreach(QWidget *qw, qApp->topLevelWidgets()) {
1517 		if (!qw -> isWindow()) continue;
1518 		if (qobject_cast<QToolBar *>(qw) || qobject_cast<QDockWidget *>(qw)) {
1519 			if (qw -> parent() == window) widgets << qw;
1520 		}
1521 	}
1522 	return(widgets);
1523 }
1524 
1525 /**
1526 	Parse les arguments suivants :
1527 	  * --common-elements-dir=
1528 	  * --config-dir
1529 	  * --help
1530 	  * --version
1531 	  * -v
1532 	  * --license
1533 	Les autres arguments sont normalement des chemins de fichiers.
1534 	S'ils existent, ils sont juste memorises dans l'attribut arguments_files_.
1535 	Sinon, ils sont memorises dans l'attribut arguments_options_.
1536 */
parseArguments()1537 void QETApp::parseArguments() {
1538 	// recupere les arguments
1539 	QList<QString> arguments_list(qApp->arguments());
1540 
1541 	// enleve le premier argument : il s'agit du fichier binaire
1542 	arguments_list.takeFirst();
1543 
1544 	// analyse les arguments
1545 	qet_arguments_ = QETArguments(arguments_list);
1546 
1547 #ifdef QET_ALLOW_OVERRIDE_CED_OPTION
1548 	if (qet_arguments_.commonElementsDirSpecified()) {
1549 		overrideCommonElementsDir(qet_arguments_.commonElementsDir());
1550 	}
1551 #endif
1552 #ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
1553 	if (qet_arguments_.commonTitleBlockTemplatesDirSpecified()) {
1554 		overrideCommonTitleBlockTemplatesDir(qet_arguments_.commonTitleBlockTemplatesDir());
1555 	}
1556 #endif
1557 #ifdef QET_ALLOW_OVERRIDE_CD_OPTION
1558 	if (qet_arguments_.configDirSpecified()) {
1559 		overrideConfigDir(qet_arguments_.configDir());
1560 	}
1561 #endif
1562 
1563 	if (qet_arguments_.langDirSpecified()) {
1564 		overrideLangDir(qet_arguments_.langDir());
1565 	}
1566 
1567 	if (qet_arguments_.printLicenseRequested()) {
1568 		printLicense();
1569 		non_interactive_execution_ = true;
1570 	}
1571 	if (qet_arguments_.printHelpRequested()) {
1572 		printHelp();
1573 		non_interactive_execution_ = true;
1574 	}
1575 	if (qet_arguments_.printVersionRequested()) {
1576 		printVersion();
1577 		non_interactive_execution_ = true;
1578 	}
1579 }
1580 
1581 /**
1582 	Initialise le splash screen si et seulement si l'execution est interactive.
1583 	Autrement, l'attribut splash_screen_ vaut 0.
1584 */
initSplashScreen()1585 void QETApp::initSplashScreen() {
1586 	if (non_interactive_execution_) return;
1587 	m_splash_screen = new QSplashScreen(QPixmap(":/ico/splash.png"));
1588 	m_splash_screen -> show();
1589 	setSplashScreenStep(tr("Chargement...", "splash screen caption"));
1590 }
1591 
1592 /**
1593 	Change le texte du splash screen et prend en compte les evenements.
1594 	Si l'application s'execute de facon non interactive, cette methode ne fait
1595 	rien.
1596 */
setSplashScreenStep(const QString & message)1597 void QETApp::setSplashScreenStep(const QString &message) {
1598 	if (!m_splash_screen) return;
1599 	if (!message.isEmpty()) {
1600 		m_splash_screen -> showMessage(message, Qt::AlignBottom | Qt::AlignLeft);
1601 	}
1602 	qApp->processEvents();
1603 }
1604 
1605 /**
1606 	Determine et applique le langage a utiliser pour l'application
1607 */
initLanguage()1608 void QETApp::initLanguage() {
1609 	setLanguage(langFromSetting());
1610 }
1611 
1612 /**
1613  * @brief QETApp::initStyle
1614  * Setup the gui style
1615  */
initStyle()1616 void QETApp::initStyle() {
1617 	initial_palette_ = qApp->palette();
1618 
1619 	//Apply or not the system style
1620 	QSettings settings;
1621 	useSystemPalette(settings.value("usesystemcolors", true).toBool());
1622 }
1623 
1624 /**
1625 	Lit et prend en compte la configuration de l'application.
1626 	Cette methode creera, si necessaire :
1627 	  * le dossier de configuration
1628 	  * le dossier de la collection perso
1629 	  * the directory for custom title blocks
1630 */
initConfiguration()1631 void QETApp::initConfiguration() {
1632 	// cree les dossiers de configuration si necessaire
1633 	QDir config_dir(QETApp::configDir());
1634 	if (!config_dir.exists()) config_dir.mkpath(QETApp::configDir());
1635 
1636 	QDir custom_elements_dir(QETApp::customElementsDir());
1637 	if (!custom_elements_dir.exists()) custom_elements_dir.mkpath(QETApp::customElementsDir());
1638 
1639 	QDir custom_tbt_dir(QETApp::customTitleBlockTemplatesDir());
1640 	if (!custom_tbt_dir.exists()) custom_tbt_dir.mkpath(QETApp::customTitleBlockTemplatesDir());
1641 
1642 	// fichiers recents
1643 	// note : les icones doivent etre initialisees avant ces instructions (qui creent des menus en interne)
1644 	m_projects_recent_files = new RecentFiles("projects");
1645 	m_projects_recent_files -> setIconForFiles(QET::Icons::ProjectFile);
1646 	m_elements_recent_files = new RecentFiles("elements");
1647 	m_elements_recent_files -> setIconForFiles(QET::Icons::Element);
1648 }
1649 
1650 /**
1651 	Construit l'icone dans le systray et son menu
1652 */
initSystemTray()1653 void QETApp::initSystemTray() {
1654 	setSplashScreenStep(tr("Chargement... icône du systray", "splash screen caption"));
1655 	// initialisation des menus de l'icone dans le systray
1656 	menu_systray = new QMenu(tr("QElectroTech", "systray menu title"));
1657 
1658 	quitter_qet       = new QAction(QET::Icons::ApplicationExit,       tr("&Quitter"),                                        this);
1659 	reduce_appli      = new QAction(QET::Icons::Hide,    tr("&Masquer"),                                        this);
1660 	restore_appli     = new QAction(QET::Icons::Restore,  tr("&Restaurer"),                                      this);
1661 	reduce_diagrams   = new QAction(QET::Icons::Hide,    tr("&Masquer tous les éditeurs de schéma"),      this);
1662 	restore_diagrams  = new QAction(QET::Icons::Restore,  tr("&Restaurer tous les éditeurs de schéma"),    this);
1663 	reduce_elements   = new QAction(QET::Icons::Hide,    tr("&Masquer tous les éditeurs d'élément"),   this);
1664 	restore_elements  = new QAction(QET::Icons::Restore,  tr("&Restaurer tous les éditeurs d'élément"), this);
1665 	reduce_templates  = new QAction(QET::Icons::Hide,      tr("&Masquer tous les éditeurs de cartouche",   "systray submenu entry"), this);
1666 	restore_templates = new QAction(QET::Icons::Restore,   tr("&Restaurer tous les éditeurs de cartouche", "systray submenu entry"), this);
1667 	new_diagram       = new QAction(QET::Icons::WindowNew, tr("&Nouvel éditeur de schéma"),                 this);
1668 	new_element       = new QAction(QET::Icons::WindowNew, tr("&Nouvel éditeur d'élément"),              this);
1669 
1670 	quitter_qet   -> setStatusTip(tr("Ferme l'application QElectroTech"));
1671 	reduce_appli  -> setToolTip(tr("Réduire QElectroTech dans le systray"));
1672 	restore_appli -> setToolTip(tr("Restaurer QElectroTech"));
1673 
1674 	connect(quitter_qet,      SIGNAL(triggered()), this, SLOT(quitQET()));
1675 	connect(reduce_appli,     SIGNAL(triggered()), this, SLOT(reduceEveryEditor()));
1676 	connect(restore_appli,    SIGNAL(triggered()), this, SLOT(restoreEveryEditor()));
1677 	connect(reduce_diagrams,  SIGNAL(triggered()), this, SLOT(reduceDiagramEditors()));
1678 	connect(restore_diagrams, SIGNAL(triggered()), this, SLOT(restoreDiagramEditors()));
1679 	connect(reduce_elements,  SIGNAL(triggered()), this, SLOT(reduceElementEditors()));
1680 	connect(restore_elements, SIGNAL(triggered()), this, SLOT(restoreElementEditors()));
1681 	connect(reduce_templates, SIGNAL(triggered()), this, SLOT(reduceTitleBlockTemplateEditors()));
1682 	connect(restore_templates,SIGNAL(triggered()), this, SLOT(restoreTitleBlockTemplateEditors()));
1683 	connect(new_diagram,      SIGNAL(triggered()), this, SLOT(newDiagramEditor()));
1684 	connect(new_element,      SIGNAL(triggered()), this, SLOT(newElementEditor()));
1685 
1686 	// initialisation de l'icone du systray
1687 	m_qsti = new QSystemTrayIcon(QET::Icons::QETLogo, this);
1688 	m_qsti -> setToolTip(tr("QElectroTech", "systray icon tooltip"));
1689 	connect(m_qsti, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(systray(QSystemTrayIcon::ActivationReason)));
1690 	m_qsti -> setContextMenu(menu_systray);
1691 	m_qsti -> show();
1692 }
1693 
1694 /**
1695 	Add a list of \a windows to \a menu.
1696 	This template function assumes it will be given a QList of pointers to
1697 	objects inheriting the QMainWindow class.
1698 	@param T the class inheriting QMainWindow
1699 	@param menu the menu windows will be added to
1700 	@param windows A list of top-level windows.
1701 */
addWindowsListToMenu(QMenu * menu,const QList<T * > & windows)1702 template <class T> void QETApp::addWindowsListToMenu(QMenu *menu, const QList<T *> &windows) {
1703 	menu -> addSeparator();
1704 	foreach (QMainWindow *window, windows) {
1705 		QAction *current_menu = menu -> addAction(window -> windowTitle());
1706 		current_menu -> setCheckable(true);
1707 		current_menu -> setChecked(window -> isVisible());
1708 		connect(current_menu, SIGNAL(triggered()), &signal_map, SLOT(map()));
1709 		signal_map.setMapping(current_menu, window);
1710 	}
1711 }
1712 
1713 /**
1714 	@param url The location of a collection item (title block template,
1715 	element, category, ...).
1716 	@return the id of the project mentionned in the URL, or -1 if none could be
1717 	found.
1718 */
projectIdFromString(const QString & url)1719 int QETApp::projectIdFromString(const QString &url) {
1720 	QRegExp embedded("^project([0-9]+)\\+embed.*$", Qt::CaseInsensitive);
1721 	if (embedded.exactMatch(url)) {
1722 		bool conv_ok = false;
1723 		int project_id = embedded.capturedTexts().at(1).toInt(&conv_ok);
1724 		if (conv_ok) {
1725 			return(project_id);
1726 		}
1727 	}
1728 	return(-1);
1729 }
1730 
1731 /**
1732 	@param url The location of a collection item (title block template,
1733 	element, category, ...).
1734 	@return the project mentionned in the URL, or 0 if none could be
1735 	found.
1736 */
projectFromString(const QString & url)1737 QETProject *QETApp::projectFromString(const QString &url) {
1738 	int project_id = projectIdFromString(url);
1739 	if (project_id == -1) return(nullptr);
1740 	return(project(project_id));
1741 }
1742 
1743 /// construit le menu de l'icone dans le systray
buildSystemTrayMenu()1744 void QETApp::buildSystemTrayMenu() {
1745 	menu_systray -> clear();
1746 
1747 	// recupere les editeurs
1748 	QList<QETDiagramEditor *> diagrams = diagramEditors();
1749 	QList<QETElementEditor *> elements = elementEditors();
1750 	QList<QETTitleBlockTemplateEditor *> tbtemplates = titleBlockTemplateEditors();
1751 	fetchWindowStats(diagrams, elements, tbtemplates);
1752 
1753 	// ajoute le bouton reduire / restaurer au menu
1754 	menu_systray -> addAction(every_editor_reduced ? restore_appli : reduce_appli);
1755 
1756 	// ajoute les editeurs de schemas dans un sous-menu
1757 	QMenu *diagrams_submenu = menu_systray -> addMenu(tr("Éditeurs de schémas"));
1758 	diagrams_submenu -> addAction(reduce_diagrams);
1759 	diagrams_submenu -> addAction(restore_diagrams);
1760 	diagrams_submenu -> addAction(new_diagram);
1761 	reduce_diagrams -> setEnabled(!diagrams.isEmpty() && !every_diagram_reduced);
1762 	restore_diagrams -> setEnabled(!diagrams.isEmpty() && !every_diagram_visible);
1763 	addWindowsListToMenu<QETDiagramEditor>(diagrams_submenu, diagrams);
1764 
1765 	// ajoute les editeurs d'elements au menu
1766 	QMenu *elements_submenu = menu_systray -> addMenu(tr("Éditeurs d'élément"));
1767 	elements_submenu -> addAction(reduce_elements);
1768 	elements_submenu -> addAction(restore_elements);
1769 	elements_submenu -> addAction(new_element);
1770 	reduce_elements -> setEnabled(!elements.isEmpty() && !every_element_reduced);
1771 	restore_elements -> setEnabled(!elements.isEmpty() && !every_element_visible);
1772 	elements_submenu -> addSeparator();
1773 	addWindowsListToMenu<QETElementEditor>(elements_submenu, elements);
1774 
1775 	// add title block template editors in a submenu
1776 	QMenu *tbtemplates_submenu = menu_systray -> addMenu(tr("Éditeurs de cartouche", "systray menu entry"));
1777 	tbtemplates_submenu -> addAction(reduce_templates);
1778 	tbtemplates_submenu -> addAction(restore_templates);
1779 	reduce_templates  -> setEnabled(!tbtemplates.isEmpty() && !every_template_reduced);
1780 	restore_templates -> setEnabled(!tbtemplates.isEmpty() && !every_template_visible);
1781 	addWindowsListToMenu<QETTitleBlockTemplateEditor>(tbtemplates_submenu, tbtemplates);
1782 
1783 	// ajoute le bouton quitter au menu
1784 	menu_systray -> addSeparator();
1785 	menu_systray -> addAction(quitter_qet);
1786 }
1787 
1788 /**
1789  * @brief QETApp::checkBackupFiles
1790  * Check for backup files.
1791  * If backup was found, open a dialog and ask user what to do.
1792  */
checkBackupFiles()1793 void QETApp::checkBackupFiles()
1794 {
1795     QList<KAutoSaveFile *> stale_files = KAutoSaveFile::allStaleFiles();
1796 
1797 		//Remove from the list @stale_files, the stales file of opened project
1798 	const QList<KAutoSaveFile *> sf = stale_files;
1799     for (KAutoSaveFile *kasf : sf)
1800     {
1801         for (QETProject *project : registeredProjects().values())
1802         {
1803                 //We must to adjust with the flag QUrl::StripTrailingSlash to compar a path formated like the path returned by KAutoSaveFile
1804             const QString path = QUrl::fromLocalFile(project->filePath()).adjusted(QUrl::RemoveScheme | QUrl::StripTrailingSlash).path();
1805             if (kasf->managedFile() == path) {
1806 				stale_files.removeOne(kasf);
1807 			}
1808 		}
1809 	}
1810 
1811 	if (stale_files.isEmpty()) {
1812 		return;
1813 	}
1814 
1815 	QString text;
1816 	if(stale_files.size() == 1) {
1817 		text.append(tr("<b>Le fichier de restauration suivant a été trouvé,<br>"
1818 					   "Voulez-vous l'ouvrir ?</b><br>"));
1819 	} else {
1820 		text.append(tr("<b>Les fichiers de restauration suivant on été trouvé,<br>"
1821 					   "Voulez-vous les ouvrir ?</b><br>"));
1822 	}
1823     for(const KAutoSaveFile *kasf : stale_files)
1824     {
1825 #ifdef Q_OS_WIN
1826         //Remove the first character '/' before the name of the drive
1827         text.append("<br>" + kasf->managedFile().path().remove(0,1));
1828 #else
1829         text.append("<br>" + kasf->managedFile().path());
1830 #endif
1831 	}
1832 
1833 		//Open backup file
1834 	if (QET::QetMessageBox::question(nullptr, tr("Fichier de restauration"), text, QMessageBox::Ok|QMessageBox::Cancel) == QMessageBox::Ok)
1835 	{
1836 			//If there is opened editors, we find those who are visible
1837 		if (diagramEditors().count())
1838 		{
1839 			diagramEditors().first()->setVisible(true);
1840 			diagramEditors().first()->openBackupFiles(stale_files);
1841 		}
1842 		else
1843 		{
1844 			QETDiagramEditor *editor = new QETDiagramEditor();
1845 			editor->openBackupFiles(stale_files);
1846 		}
1847 	}
1848 	else //Clear backup file
1849 	{
1850 			//Remove the stale files
1851 		for (KAutoSaveFile *stale : stale_files)
1852 		{
1853 			stale->open(QIODevice::ReadWrite);
1854 			delete stale;
1855 		}
1856 	}
1857 }
1858 
1859 /// Met a jour les booleens concernant l'etat des fenetres
fetchWindowStats(const QList<QETDiagramEditor * > & diagrams,const QList<QETElementEditor * > & elements,const QList<QETTitleBlockTemplateEditor * > & tbtemplates)1860 void QETApp::fetchWindowStats(
1861 	const QList<QETDiagramEditor *> &diagrams,
1862 	const QList<QETElementEditor *> &elements,
1863 	const QList<QETTitleBlockTemplateEditor *> &tbtemplates
1864 ) {
1865 	// compte le nombre de schemas visibles
1866 	int visible_diagrams = 0;
1867 	foreach(QMainWindow *w, diagrams) if (w -> isVisible()) ++ visible_diagrams;
1868 	every_diagram_reduced = !visible_diagrams;
1869 	every_diagram_visible = visible_diagrams == diagrams.count();
1870 
1871 	// compte le nombre de schemas visibles
1872 	int visible_elements = 0;
1873 	foreach(QMainWindow *w, elements) if (w -> isVisible()) ++ visible_elements;
1874 	every_element_reduced = !visible_elements;
1875 	every_element_visible = visible_elements == elements.count();
1876 
1877 	// count visible template editors
1878 	int visible_templates = 0;
1879 	foreach(QMainWindow *window, tbtemplates) {
1880 		if (window -> isVisible()) ++ visible_templates;
1881 	}
1882 	every_template_reduced = !visible_templates;
1883 	every_template_visible = visible_templates == tbtemplates.count();
1884 
1885 	// determine si tous les elements sont reduits
1886 	every_editor_reduced = every_element_reduced && every_diagram_reduced;
1887 }
1888 
1889 #ifdef Q_OS_DARWIN
1890 /**
1891     Gere les evenements, en particulier l'evenement FileOpen sous MacOs.
1892     @param e Evenement a gerer
1893 */
eventFiltrer(QObject * object,QEvent * e)1894 bool QETApp::eventFiltrer(QObject *object, QEvent *e) {
1895     // gere l'ouverture de fichiers (sous MacOs)
1896     if (e -> type() == QEvent::FileOpen) {
1897         // nom du fichier a ouvrir
1898         QString filename = static_cast<QFileOpenEvent *>(e) -> file();
1899         openFiles(QStringList() << filename);
1900         return(true);
1901     } else {
1902         return QObject::eventFilter(object, e);
1903     }
1904 }
1905 #endif
1906 
1907 /**
1908 	Affiche l'aide et l'usage sur la sortie standard
1909 */
printHelp()1910 void QETApp::printHelp() {
1911 	QString help(
1912 		tr("Usage : ") + QFileInfo(qApp->applicationFilePath()).fileName() + tr(" [options] [fichier]...\n\n") +
1913 		tr("QElectroTech, une application de réalisation de schémas électriques.\n\n"
1914 		"Options disponibles : \n"
1915 		"  --help                        Afficher l'aide sur les options\n"
1916 		"  -v, --version                 Afficher la version\n"
1917 		"  --license                     Afficher la licence\n")
1918 #ifdef QET_ALLOW_OVERRIDE_CED_OPTION
1919 		+ tr("  --common-elements-dir=DIR     Definir le dossier de la collection d'elements\n")
1920 #endif
1921 #ifdef QET_ALLOW_OVERRIDE_CTBTD_OPTION
1922 		+ tr("  --common-tbt-dir=DIR          Definir le dossier de la collection de modeles de cartouches\n")
1923 #endif
1924 #ifdef QET_ALLOW_OVERRIDE_CD_OPTION
1925 		+ tr("  --config-dir=DIR              Definir le dossier de configuration\n")
1926 #endif
1927 		+ tr("  --lang-dir=DIR                Definir le dossier contenant les fichiers de langue\n")
1928 	);
1929 	std::cout << qPrintable(help) << std::endl;
1930 }
1931 
1932 /**
1933 	Affiche la version sur la sortie standard
1934 */
printVersion()1935 void QETApp::printVersion() {
1936 	std::cout << qPrintable(QET::displayedVersion) << std::endl;
1937 }
1938 
1939 /**
1940 	Affiche la licence sur la sortie standard
1941 */
printLicense()1942 void QETApp::printLicense() {
1943 	std::cout << qPrintable(QET::license()) << std::endl;
1944 }
1945 
1946 /**
1947 	@return la liste des projets avec leurs ids associes
1948 */
registeredProjects()1949 QMap<uint, QETProject *> QETApp::registeredProjects() {
1950 	return(registered_projects_);
1951 }
1952 
1953 /**
1954 	@param project Projet a enregistrer aupres de l'application
1955 	@return true si le projet a pu etre enregistre, false sinon
1956 	L'echec de l'enregistrement d'un projet signifie generalement qu'il est deja enregistre.
1957 */
registerProject(QETProject * project)1958 bool QETApp::registerProject(QETProject *project) {
1959 	// le projet doit sembler valide
1960 	if (!project) return(false);
1961 
1962 	// si le projet est deja enregistre, renvoie false
1963 	if (projectId(project) != -1) return(false);
1964 
1965 	// enregistre le projet
1966 	registered_projects_.insert(next_project_id ++, project);
1967 	return(true);
1968 }
1969 
1970 /**
1971 	Annule l'enregistrement du projet project
1972 	@param project Projet dont il faut annuler l'enregistrement
1973 	@return true si l'annulation a reussi, false sinon
1974 	L'echec de cette methode signifie generalement que le projet n'etait pas enregistre.
1975 */
unregisterProject(QETProject * project)1976 bool QETApp::unregisterProject(QETProject *project) {
1977 	int project_id = projectId(project);
1978 
1979 	// si le projet n'est pas enregistre, renvoie false
1980 	if (project_id == -1) return(false);
1981 
1982 	// annule l'enregistrement du projet
1983 	return(registered_projects_.remove(project_id) == 1);
1984 }
1985 
1986 /**
1987 	@param id Id du projet voulu
1988 	@return le projet correspond a l'id passe en parametre
1989 */
project(const uint & id)1990 QETProject *QETApp::project(const uint &id) {
1991 	if (registered_projects_.contains(id)) {
1992 		return(registered_projects_[id]);
1993 	} else {
1994 		return(nullptr);
1995 	}
1996 }
1997 
1998 /**
1999 	@param project Projet dont on souhaite recuperer l'id
2000 	@return l'id du projet en parametre si celui-ci est enregistre, -1 sinon
2001 */
projectId(const QETProject * project)2002 int QETApp::projectId(const QETProject *project) {
2003 	foreach(int id, registered_projects_.keys()) {
2004 		if (registered_projects_[id] == project) {
2005 			return(id);
2006 		}
2007 	}
2008 	return(-1);
2009 }
2010