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 "projectview.h"
19 #include "qetproject.h"
20 #include "diagramview.h"
21 #include "diagram.h"
22 #include "diagramprintdialog.h"
23 #include "exportdialog.h"
24 #include "qetapp.h"
25 #include "qetelementeditor.h"
26 #include "borderpropertieswidget.h"
27 #include "titleblockpropertieswidget.h"
28 #include "conductorpropertieswidget.h"
29 #include "qeticons.h"
30 #include "qetmessagebox.h"
31 #include "qettemplateeditor.h"
32 #include "diagramfoliolist.h"
33 #include "projectpropertiesdialog.h"
34 #include "xmlelementcollection.h"
35 #include "autoNum/assignvariables.h"
36 #include "dialogwaiting.h"
37
38 /**
39 Constructeur
40 @param project projet a visualiser
41 @param parent Widget parent
42 */
ProjectView(QETProject * project,QWidget * parent)43 ProjectView::ProjectView(QETProject *project, QWidget *parent) :
44 QWidget(parent),
45 m_project(nullptr)
46 {
47 initActions();
48 initWidgets();
49 initLayout();
50
51 setProject(project);
52 }
53
54 /**
55 Destructeur
56 Supprime les DiagramView embarquees
57 */
~ProjectView()58 ProjectView::~ProjectView() {
59 // qDebug() << "Suppression du ProjectView" << ((void *)this);
60 foreach(int id, m_diagram_ids.keys()) {
61 DiagramView *diagram_view = m_diagram_ids.take(id);
62 delete diagram_view;
63 }
64 }
65
66 /**
67 @return le projet actuellement visualise par le ProjectView
68 */
project()69 QETProject *ProjectView::project() {
70 return(m_project);
71 }
72
73 /**
74 Definit le projet visualise par le ProjectView. Ne fait rien si le projet a
75 deja ete defini.
76 @param project projet a visualiser
77 */
setProject(QETProject * project)78 void ProjectView::setProject(QETProject *project) {
79 if (!m_project) {
80 m_project = project;
81 connect(m_project, SIGNAL(projectTitleChanged(QETProject *, const QString &)), this, SLOT(updateWindowTitle()));
82 connect(m_project, SIGNAL(projectModified (QETProject *, bool)), this, SLOT(updateWindowTitle()));
83 connect(m_project, SIGNAL(readOnlyChanged (QETProject *, bool)), this, SLOT(adjustReadOnlyState()));
84 connect(m_project, SIGNAL(addAutoNumDiagram()), this, SLOT(addNewDiagram()));
85 adjustReadOnlyState();
86 loadDiagrams();
87 }
88 }
89
90 /**
91 @return la liste des schemas ouverts dans le projet
92 */
diagram_views() const93 QList<DiagramView *> ProjectView::diagram_views() const {
94 return(m_diagram_view_list);
95 }
96
97 /**
98 * @brief ProjectView::currentDiagram
99 * @return The current active diagram view or nullptr if there isn't diagramView in this project view.
100 */
currentDiagram() const101 DiagramView *ProjectView::currentDiagram() const {
102 int current_tab_index = m_tab -> currentIndex();
103 if (current_tab_index == -1)
104 return nullptr;
105 return(m_diagram_ids[current_tab_index]);
106 }
107
108 /**
109 Gere la fermeture du schema.
110 @param qce Le QCloseEvent decrivant l'evenement
111 */
closeEvent(QCloseEvent * qce)112 void ProjectView::closeEvent(QCloseEvent *qce) {
113 bool can_close_project = tryClosing();
114 if (can_close_project) {
115 qce -> accept();
116 emit(projectClosed(this));
117 } else {
118 qce -> ignore();
119 }
120 }
121
122 /**
123 @brief change current diagramview to next folio
124 */
changeTabDown()125 void ProjectView::changeTabDown(){
126 DiagramView *nextDiagramView = this->nextDiagram();
127 if (nextDiagramView!=nullptr){
128 rebuildDiagramsMap();
129 m_tab -> setCurrentWidget(nextDiagramView);
130 }
131 }
132
133 /**
134 @return next folio of current diagramview
135 */
nextDiagram()136 DiagramView *ProjectView::nextDiagram() {
137 int current_tab_index = m_tab -> currentIndex();
138 int next_tab_index = current_tab_index + 1; //get next tab index
139 if (next_tab_index<m_diagram_ids.count()) //if next tab index >= greatest tab the last tab is activated so no need to change tab.
140 return(m_diagram_ids[next_tab_index]);
141 else
142 return nullptr;
143 }
144
145 /**
146 @brief change current diagramview to previous tab
147 */
changeTabUp()148 void ProjectView::changeTabUp(){
149 DiagramView *previousDiagramView = this->previousDiagram();
150 if (previousDiagramView!=nullptr){
151 rebuildDiagramsMap();
152 m_tab -> setCurrentWidget(previousDiagramView);
153 }
154 }
155
156 /**
157 @return previous folio of current diagramview
158 */
previousDiagram()159 DiagramView *ProjectView::previousDiagram() {
160 int current_tab_index = m_tab -> currentIndex();
161 int previous_tab_index = current_tab_index - 1; //get previous tab index
162 if (previous_tab_index>=0) //if previous tab index = 0 then the first tab is activated so no need to change tab.
163 return(m_diagram_ids[previous_tab_index]);
164 else
165 return nullptr;
166 }
167
168 /**
169 @brief change current diagramview to last tab
170 */
changeLastTab()171 void ProjectView::changeLastTab(){
172 DiagramView *lastDiagramView = this->lastDiagram();
173 m_tab->setCurrentWidget(lastDiagramView);
174 }
175
176 /**
177 @return last folio of current project
178 */
lastDiagram()179 DiagramView *ProjectView::lastDiagram(){
180 return(m_diagram_ids.last());
181 }
182
183 /**
184 @brief change current diagramview to first tab
185 */
changeFirstTab()186 void ProjectView::changeFirstTab(){
187 DiagramView *firstDiagramView = this->firstDiagram();
188 m_tab->setCurrentWidget(firstDiagramView);
189 }
190
191 /**
192 @return first folio of current project
193 */
firstDiagram()194 DiagramView *ProjectView::firstDiagram(){
195 return(m_diagram_ids.first());
196 }
197
198
199 /**
200 Cette methode essaye de fermer successivement les editeurs d'element puis
201 les schemas du projet. L'utilisateur peut refuser de fermer un schema ou un
202 editeur.
203 @return true si tout a pu etre ferme, false sinon
204 @see tryClosingElementEditors()
205 @see tryClosingDiagrams()
206 */
tryClosing()207 bool ProjectView::tryClosing() {
208 if (!m_project) return(true);
209
210 // First step: require external editors closing -- users may either cancel
211 // the whole closing process or save (and therefore add) content into this
212 // project. Of course, they may also discard them.
213 if (!tryClosingElementEditors()) {
214 return(false);
215 }
216
217 // Check how different the current situation is from a brand new, untouched project
218 if (m_project -> filePath().isEmpty() && !m_project -> projectWasModified()) {
219 return(true);
220 }
221
222 // Second step: users are presented with a dialog that enables them to
223 // choose whether they want to:
224 // - cancel the closing process,
225 // - discard all modifications,
226 // - or specify what is to be saved, i.e. they choose whether they wants to
227 // save/give up/remove diagrams considered as modified.
228 int user_input = tryClosingDiagrams();
229 if (user_input == QMessageBox::Cancel) {
230 return(false); // the closing process was cancelled
231 } else if (user_input == QMessageBox::Discard) {
232 return(true); // all modifications were discarded
233 }
234
235 // Check how different the current situation is from a brand new, untouched project (yes , again)
236 if (m_project -> filePath().isEmpty() && !m_project -> projectWasModified()) {
237 return(true);
238 }
239
240 if (m_project -> filePath().isEmpty()) {
241 QString filepath = askUserForFilePath();
242 if (filepath.isEmpty()) return(false); // users may cancel the closing
243 }
244 QETResult result = m_project -> write();
245 updateWindowTitle();
246 if (!result.isOk()) emit(errorEncountered(result.errorMessage()));
247 return(result.isOk());
248 }
249
250 /**
251 Un projet comporte des elements integres. Cette methode ferme les editeurs
252 d'elements associes a ce projet. L'utilisateur peut refuser la fermeture
253 d'un editeur d'element.
254 @return true si tous les editeurs d'element ont pu etre fermes, false sinon
255 */
tryClosingElementEditors()256 bool ProjectView::tryClosingElementEditors() {
257 if (!m_project) return(true);
258 /*
259 La QETApp permet d'acceder rapidement aux editeurs d'element
260 editant un element du projet.
261 */
262 QList<QETElementEditor *> editors = QETApp::elementEditors(m_project);
263 foreach(QETElementEditor *editor, editors) {
264 if (!editor -> close()) return(false);
265 }
266
267 QList<QETTitleBlockTemplateEditor *> template_editors = QETApp::titleBlockTemplateEditors(m_project);
268 foreach(QETTitleBlockTemplateEditor *template_editor, template_editors) {
269 if (!template_editor -> close()) return(false);
270 }
271 return(true);
272 }
273
274 /**
275 * @brief ProjectView::tryClosingDiagrams
276 * try to close this project, if diagram or project option are changed
277 * a dialog ask if user want to save the modification.
278 * @return the answer of dialog or discard if no change.
279 */
tryClosingDiagrams()280 int ProjectView::tryClosingDiagrams() {
281 if (!m_project) return(QMessageBox::Discard);
282
283 if (!project()->projectOptionsWereModified() &&
284 project()->undoStack()->isClean() &&
285 !project()->filePath().isEmpty()) {
286 // nothing was modified, and we have a filepath, i.e. everything was already
287 // saved, i.e we can close the project right now
288 return(QMessageBox::Discard);
289 }
290
291 QString title = project()->title();
292 if (title.isEmpty()) title = "QElectroTech ";
293
294 int close_dialog = QMessageBox::question(this, title,
295 tr("Le projet à été modifié.\n"
296 "Voulez-vous enregistrer les modifications ?"),
297 QMessageBox::Save | QMessageBox::Discard
298 | QMessageBox::Cancel,
299 QMessageBox::Save);
300
301 return(close_dialog);
302 }
303
304 /**
305 Ask the user to provide a file path in which the currently edited project will
306 be saved.
307 @param assign When true, assign the provided filepath to the project through
308 setFilePath(). Defaults to true.
309 @return the file path, or an empty string if none were provided
310 */
askUserForFilePath(bool assign)311 QString ProjectView::askUserForFilePath(bool assign) {
312 // ask the user for a filepath in order to save the project
313 QString filepath = QFileDialog::getSaveFileName(
314 this,
315 tr("Enregistrer sous", "dialog title"),
316 m_project -> currentDir(),
317 tr("Projet QElectroTech (*.qet)", "filetypes allowed when saving a project file")
318 );
319
320 // if no filepath is provided, return an empty string
321 if (filepath.isEmpty()) return(filepath);
322
323 // if the name does not end with the .qet extension, append it
324 if (!filepath.endsWith(".qet", Qt::CaseInsensitive)) filepath += ".qet";
325
326 if (assign) {
327 // assign the provided filepath to the currently edited project
328 m_project -> setFilePath(filepath);
329 }
330
331 return(filepath);
332 }
333
334 /**
335 @return the QETResult object to be returned when it appears this project
336 view is not associated to any project.
337 */
noProjectResult() const338 QETResult ProjectView::noProjectResult() const {
339 QETResult no_project(tr("aucun projet affiché", "error message"), false);
340 return(no_project);
341 }
342
343 /**
344 * @brief ProjectView::addNewDiagram
345 * Add new diagram to project view
346 */
addNewDiagram()347 void ProjectView::addNewDiagram() {
348 if (m_project -> isReadOnly()) return;
349
350 Diagram *new_diagram = m_project -> addNewDiagram();
351 DiagramView *new_diagram_view = new DiagramView(new_diagram);
352 addDiagram(new_diagram_view);
353
354 if (m_project -> diagrams().size() % 58 == 1 && m_project -> getFolioSheetsQuantity() != 0)
355 addNewDiagramFolioList();
356 showDiagram(new_diagram_view);
357 }
358
359 /**
360 * @brief ProjectView::addNewDiagramFolioList
361 * Add new diagram folio list to project
362 */
addNewDiagramFolioList()363 void ProjectView::addNewDiagramFolioList() {
364 if (m_project -> isReadOnly()) return;
365 int i = 1; //< Each new diagram is added to the end of the project.
366 //< We use @i to move the folio list at second position in the project
367 foreach (Diagram *d, m_project -> addNewDiagramFolioList()) {
368 DiagramView *new_diagram_view = new DiagramView(d);
369 addDiagram(new_diagram_view);
370 showDiagram(new_diagram_view);
371 m_tab->tabBar()->moveTab(diagram_views().size()-1, i);
372 i++;
373 }
374 }
375
376 /**
377 * @brief ProjectView::addDiagram
378 * Add diagram view to this project view
379 * @param diagram_view
380 */
addDiagram(DiagramView * diagram_view)381 void ProjectView::addDiagram(DiagramView *diagram_view)
382 {
383 if (!diagram_view)
384 return;
385
386 //Check if diagram isn't present in the project
387 if (m_diagram_ids.values().contains(diagram_view))
388 return;
389
390 // Add new tab for the diagram
391 m_tab->addTab(diagram_view, QET::Icons::Diagram, diagram_view -> title());
392 diagram_view->setFrameStyle(QFrame::Plain | QFrame::NoFrame);
393
394 m_diagram_view_list << diagram_view;
395
396 rebuildDiagramsMap();
397 updateAllTabsTitle();
398
399 connect(diagram_view, SIGNAL(showDiagram(Diagram*)), this, SLOT(showDiagram(Diagram*)));
400 connect(diagram_view, SIGNAL(titleChanged(DiagramView *, const QString &)), this, SLOT(updateTabTitle(DiagramView *)));
401 connect(diagram_view, SIGNAL(findElementRequired(const ElementsLocation &)), this, SIGNAL(findElementRequired(const ElementsLocation &)));
402 connect(diagram_view, SIGNAL(editElementRequired(const ElementsLocation &)), this, SIGNAL(editElementRequired(const ElementsLocation &)));
403 connect(&diagram_view->diagram()->border_and_titleblock , &BorderTitleBlock::titleBlockFolioChanged, [this, diagram_view]() {this->updateTabTitle(diagram_view);});
404
405 // signal diagram view was added
406 emit(diagramAdded(diagram_view));
407 m_project -> setModified(true);
408 }
409
410 /**
411 * @brief ProjectView::removeDiagram
412 * Remove a diagram (folio) of the project
413 * @param diagram_view : diagram view to remove
414 */
removeDiagram(DiagramView * diagram_view)415 void ProjectView::removeDiagram(DiagramView *diagram_view)
416 {
417 if (!diagram_view)
418 return;
419 if (m_project -> isReadOnly())
420 return;
421 if (!m_diagram_ids.values().contains(diagram_view))
422 return;
423
424
425 //Ask confirmation to user.
426 int answer = QET::QetMessageBox::question(
427 this,
428 tr("Supprimer le folio ?", "message box title"),
429 tr("Êtes-vous sûr de vouloir supprimer ce folio du projet ? Ce changement est irréversible.", "message box content"),
430 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
431 QMessageBox::No
432 );
433 if (answer != QMessageBox::Yes) {
434 return;
435 }
436
437 //Remove the diagram view of the tabs widget
438 int index_to_remove = m_diagram_ids.key(diagram_view);
439 m_tab->removeTab(index_to_remove);
440 m_diagram_view_list.removeAll(diagram_view);
441 rebuildDiagramsMap();
442
443 m_project -> removeDiagram(diagram_view -> diagram());
444 delete diagram_view;
445
446 emit(diagramRemoved(diagram_view));
447
448 //Make definitve the withdrawal
449 m_project -> write();
450 updateAllTabsTitle();
451 m_project -> setModified(true);
452
453 }
454
455 /**
456 Enleve un schema du ProjectView
457 @param diagram Schema a enlever
458 */
removeDiagram(Diagram * diagram)459 void ProjectView::removeDiagram(Diagram *diagram) {
460 if (!diagram) return;
461
462 if (DiagramView *diagram_view = findDiagram(diagram)) {
463 removeDiagram(diagram_view);
464 }
465 }
466
467 /**
468 Active l'onglet adequat pour afficher le schema passe en parametre
469 @param diagram Schema a afficher
470 */
showDiagram(DiagramView * diagram)471 void ProjectView::showDiagram(DiagramView *diagram) {
472 if (!diagram) return;
473 m_tab -> setCurrentWidget(diagram);
474 }
475
476 /**
477 Active l'onglet adequat pour afficher le schema passe en parametre
478 @param diagram Schema a afficher
479 */
showDiagram(Diagram * diagram)480 void ProjectView::showDiagram(Diagram *diagram) {
481 if (!diagram) return;
482 if (DiagramView *diagram_view = findDiagram(diagram)) {
483 m_tab -> setCurrentWidget(diagram_view);
484 }
485 }
486
487 /**
488 Enable the user to edit properties of the current project through a
489 configuration dialog.
490 */
editProjectProperties()491 void ProjectView::editProjectProperties() {
492 if (!m_project) return;
493 ProjectPropertiesDialog dialog(m_project, parentWidget());
494 dialog.exec();
495 }
496
497 /**
498 Edite les proprietes du schema courant
499 */
editCurrentDiagramProperties()500 void ProjectView::editCurrentDiagramProperties() {
501 editDiagramProperties(currentDiagram());
502 }
503
504 /**
505 Edite les proprietes du schema diagram_view
506 */
editDiagramProperties(DiagramView * diagram_view)507 void ProjectView::editDiagramProperties(DiagramView *diagram_view) {
508 if (!diagram_view) return;
509 showDiagram(diagram_view);
510 diagram_view -> editDiagramProperties();
511 }
512
513 /**
514 Edite les proprietes du schema diagram
515 */
editDiagramProperties(Diagram * diagram)516 void ProjectView::editDiagramProperties(Diagram *diagram) {
517 editDiagramProperties(findDiagram(diagram));
518 }
519
520 /**
521 Deplace le schema diagram_view vers le haut / la gauche
522 */
moveDiagramUp(DiagramView * diagram_view)523 void ProjectView::moveDiagramUp(DiagramView *diagram_view) {
524 if (!diagram_view) return;
525
526 int diagram_view_position = m_diagram_ids.key(diagram_view);
527 if (!diagram_view_position) {
528 // le schema est le premier du projet
529 return;
530 }
531 m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position - 1);
532 }
533
534 /**
535 Deplace le schema diagram vers le haut / la gauche
536 */
moveDiagramUp(Diagram * diagram)537 void ProjectView::moveDiagramUp(Diagram *diagram) {
538 moveDiagramUp(findDiagram(diagram));
539 }
540
541 /**
542 Deplace le schema diagram_view vers le bas / la droite
543 */
moveDiagramDown(DiagramView * diagram_view)544 void ProjectView::moveDiagramDown(DiagramView *diagram_view) {
545 if (!diagram_view) return;
546
547 int diagram_view_position = m_diagram_ids.key(diagram_view);
548 if (diagram_view_position + 1 == m_diagram_ids.count()) {
549 // le schema est le dernier du projet
550 return;
551 }
552 m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position + 1);
553 }
554
555 /**
556 Deplace le schema diagram vers le bas / la droite
557 */
moveDiagramDown(Diagram * diagram)558 void ProjectView::moveDiagramDown(Diagram *diagram) {
559 moveDiagramDown(findDiagram(diagram));
560 }
561
562 /*
563 * Deplace le schema diagram_view vers le haut / la gauche en position 0
564 */
moveDiagramUpTop(DiagramView * diagram_view)565 void ProjectView::moveDiagramUpTop(DiagramView *diagram_view)
566 {
567 if (!diagram_view) return;
568
569 int diagram_view_position = m_diagram_ids.key(diagram_view);
570 if (!diagram_view_position) {
571 // le schema est le premier du projet
572 return;
573 }
574 m_tab -> tabBar() -> moveTab(diagram_view_position, (diagram_views().size(), 0));
575 }
576
577 /*
578 * Deplace le schema diagram vers le haut / la gauche en position 0
579 */
moveDiagramUpTop(Diagram * diagram)580 void ProjectView::moveDiagramUpTop(Diagram *diagram)
581 {
582 moveDiagramUpTop(findDiagram(diagram));
583 }
584
585 /**
586 Deplace le schema diagram_view vers le haut / la gauche x10
587 */
moveDiagramUpx10(DiagramView * diagram_view)588 void ProjectView::moveDiagramUpx10(DiagramView *diagram_view) {
589 if (!diagram_view) return;
590
591 int diagram_view_position = m_diagram_ids.key(diagram_view);
592 if (!diagram_view_position) {
593 // le schema est le premier du projet
594 return;
595 }
596 m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position - 10);
597 }
598
599 /**
600 Deplace le schema diagram vers le haut / la gauche x10
601 */
moveDiagramUpx10(Diagram * diagram)602 void ProjectView::moveDiagramUpx10(Diagram *diagram) {
603 moveDiagramUpx10(findDiagram(diagram));
604 }
605
606 /**
607 Deplace le schema diagram_view vers le bas / la droite x10
608 */
moveDiagramDownx10(DiagramView * diagram_view)609 void ProjectView::moveDiagramDownx10(DiagramView *diagram_view) {
610 if (!diagram_view) return;
611
612 int diagram_view_position = m_diagram_ids.key(diagram_view);
613 if (diagram_view_position + 1 == m_diagram_ids.count()) {
614 // le schema est le dernier du projet
615 return;
616 }
617 m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position + 10);
618 }
619
620 /**
621 Deplace le schema diagram vers le bas / la droite x10
622 */
moveDiagramDownx10(Diagram * diagram)623 void ProjectView::moveDiagramDownx10(Diagram *diagram) {
624 moveDiagramDownx10(findDiagram(diagram));
625 }
626
627 /**
628 Ce slot demarre un dialogue permettant a l'utilisateur de parametrer et de
629 lancer l'impression de toute ou partie du projet.
630 */
printProject()631 void ProjectView::printProject() {
632 if (!m_project) return;
633
634 // transforme le titre du projet en nom utilisable pour le document
635 QString doc_name;
636 if (!(m_project -> title().isEmpty())) {
637 doc_name = m_project -> title();
638 } else if (!m_project -> filePath().isEmpty()) {
639 doc_name = QFileInfo(m_project -> filePath()).baseName();
640 }
641 doc_name = QET::stringToFileName(doc_name);
642 if (doc_name.isEmpty()) {
643 doc_name = tr("projet", "string used to generate a filename");
644 }
645
646 // recupere le dossier contenant le fichier courant
647 QString dir_path = m_project -> currentDir();
648
649 // determine un chemin pour le pdf / ps
650 QString file_name = QDir::toNativeSeparators(QDir::cleanPath(dir_path + "/" + doc_name));
651
652 DiagramPrintDialog print_dialog(m_project, this);
653 print_dialog.setDocName(doc_name);
654 print_dialog.setFileName(file_name);
655 print_dialog.exec();
656 }
657
658 /**
659 Exporte le schema.
660 */
exportProject()661 void ProjectView::exportProject() {
662 if (!m_project) return;
663
664 ExportDialog ed(m_project, parentWidget());
665 #ifdef Q_OS_MAC
666 ed.setWindowFlags(Qt::Sheet);
667 #endif
668 ed.exec();
669 }
670
671 /**
672 Save project properties along with all modified diagrams.
673 @see filePath()
674 @see setFilePath()
675 @return a QETResult object reflecting the situation
676 */
save()677 QETResult ProjectView::save() {
678 return(doSave());
679 }
680
681 /**
682 Ask users for a filepath in order to save the project.
683 @param options May be used to specify what should be saved; defaults to
684 all modified diagrams.
685 @return a QETResult object reflecting the situation; note that a valid
686 QETResult object is returned if the operation was cancelled.
687 */
saveAs()688 QETResult ProjectView::saveAs()
689 {
690 if (!m_project) return(noProjectResult());
691
692 QString filepath = askUserForFilePath();
693 if (filepath.isEmpty()) return(QETResult());
694 return(doSave());
695 }
696
697 /**
698 Save project content, then write the project file. May
699 call saveAs if no filepath was provided before.
700 @return a QETResult object reflecting the situation; note that a valid
701 QETResult object is returned if the operation was cancelled.
702 */
doSave()703 QETResult ProjectView::doSave()
704 {
705 if (!m_project) return(noProjectResult());
706
707 if (m_project -> filePath().isEmpty()) {
708 // The project has not been saved to a file yet,
709 // so save() actually means saveAs().
710 return(saveAs());
711 }
712
713 // write to file
714 QETResult result = m_project -> write();
715 updateWindowTitle();
716 project()->undoStack()->clear();
717 return(result);
718 }
719
720 /**
721 Allow the user to clean the project, which includes:
722 * deleting unused title block templates
723 * deleting unused elements
724 * deleting empty categories
725 @return an integer value above zero if elements and/or categories were
726 cleaned.
727 */
cleanProject()728 int ProjectView::cleanProject() {
729 if (!m_project) return(0);
730
731 // s'assure que le schema n'est pas en lecture seule
732 if (m_project -> isReadOnly()) {
733 QET::QetMessageBox::critical(
734 this,
735 tr("Projet en lecture seule", "message box title"),
736 tr("Ce projet est en lecture seule. Il n'est donc pas possible de le nettoyer.", "message box content")
737 );
738 return(0);
739 }
740
741 // construit un petit dialogue pour parametrer le nettoyage
742 QCheckBox *clean_tbt = new QCheckBox(tr("Supprimer les modèles de cartouche inutilisés dans le projet"));
743 QCheckBox *clean_elements = new QCheckBox(tr("Supprimer les éléments inutilisés dans le projet"));
744 QCheckBox *clean_categories = new QCheckBox(tr("Supprimer les catégories vides"));
745 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
746
747 clean_tbt -> setChecked(true);
748 clean_elements -> setChecked(true);
749 clean_categories -> setChecked(true);
750
751 QDialog clean_dialog(parentWidget());
752 #ifdef Q_OS_MAC
753 clean_dialog.setWindowFlags(Qt::Sheet);
754 #endif
755
756 clean_dialog.setWindowTitle(tr("Nettoyer le projet", "window title"));
757 QVBoxLayout *clean_dialog_layout = new QVBoxLayout();
758 clean_dialog_layout -> addWidget(clean_tbt);
759 clean_dialog_layout -> addWidget(clean_elements);
760 clean_dialog_layout -> addWidget(clean_categories);
761 clean_dialog_layout -> addWidget(buttons);
762 clean_dialog.setLayout(clean_dialog_layout);
763
764 connect(buttons, SIGNAL(accepted()), &clean_dialog, SLOT(accept()));
765 connect(buttons, SIGNAL(rejected()), &clean_dialog, SLOT(reject()));
766
767 int clean_count = 0;
768 if (clean_dialog.exec() == QDialog::Accepted)
769 {
770 if (clean_tbt -> isChecked()) {
771 m_project->embeddedTitleBlockTemplatesCollection()->deleteUnusedTitleBlocKTemplates();
772 }
773 if (clean_elements->isChecked()) {
774 m_project->embeddedElementCollection()->cleanUnusedElement();
775 }
776 if (clean_categories->isChecked()) {
777 m_project->embeddedElementCollection()->cleanUnusedDirectory();
778 }
779 }
780
781 m_project -> setModified(true);
782 return(clean_count);
783 }
784
785 /**
786 Initialize actions for this widget.
787 */
initActions()788 void ProjectView::initActions() {
789 add_new_diagram_ = new QAction(QET::Icons::AddFolio, tr("Ajouter un folio"), this);
790 connect(add_new_diagram_, SIGNAL(triggered()), this, SLOT(addNewDiagram()));
791 }
792
793 /**
794 Initialize child widgets for this widget.
795 */
initWidgets()796 void ProjectView::initWidgets() {
797 setObjectName("ProjectView");
798 setWindowIcon(QET::Icons::ProjectFileGP);
799
800 // initialize the "fallback" widget
801 fallback_widget_ = new QWidget();
802 fallback_label_ = new QLabel(
803 tr(
804 "Ce projet ne contient aucun folio",
805 "label displayed when a project contains no diagram"
806 )
807 );
808 fallback_label_ -> setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
809
810 // initialize tabs
811 m_tab = new QTabWidget(this);
812 m_tab -> setMovable(true);
813
814 QToolButton *add_new_diagram_button = new QToolButton;
815 add_new_diagram_button -> setDefaultAction(add_new_diagram_);
816 add_new_diagram_button -> setAutoRaise(true);
817 m_tab -> setCornerWidget(add_new_diagram_button, Qt::TopRightCorner);
818
819 connect(m_tab, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
820 connect(m_tab, SIGNAL(tabBarDoubleClicked(int)), this, SLOT(tabDoubleClicked(int)));
821 connect(m_tab->tabBar(), SIGNAL(tabMoved(int, int)), this, SLOT(tabMoved(int, int)));
822
823 fallback_widget_ -> setVisible(false);
824 m_tab -> setVisible(false);
825 }
826
827 /**
828 Initialize layout for this widget.
829 */
initLayout()830 void ProjectView::initLayout() {
831 QVBoxLayout *fallback_widget_layout_ = new QVBoxLayout(fallback_widget_);
832 fallback_widget_layout_ -> addWidget(fallback_label_);
833
834 layout_ = new QVBoxLayout(this);
835 #ifdef Q_OS_MAC
836 layout_ -> setContentsMargins(0, 8, 0, 0);
837 #else
838 layout_ -> setContentsMargins(0, 0, 0, 0);
839 #endif
840 layout_ -> setSpacing(0);
841 layout_ -> addWidget(fallback_widget_);
842 layout_ -> addWidget(m_tab);
843 }
844
845
846 /**
847 * @brief ProjectView::loadDiagrams
848 * Load diagrams of project.
849 * We create a diagram view for each diagram,
850 * and add it to the project view.
851 */
loadDiagrams()852 void ProjectView::loadDiagrams()
853 {
854 if (!m_project) return;
855
856 setDisplayFallbackWidget(m_project -> diagrams().isEmpty());
857
858 DialogWaiting *dialog = nullptr;
859 if(DialogWaiting::hasInstance())
860 {
861 dialog = DialogWaiting::instance();
862 dialog->setTitle( tr("<p align=\"center\">"
863 "<b>Ouverture du projet en cours...</b><br/>"
864 "Création des onglets de folio :"
865 "</p>"));
866 }
867 for(Diagram *diagram : m_project->diagrams())
868 {
869 if(dialog)
870 {
871 dialog->setDetail(diagram->title());
872 dialog->setProgressBar(dialog->progressBarValue()+1);
873 }
874
875 DiagramView *sv = new DiagramView(diagram);
876 addDiagram(sv);
877 }
878
879 this->currentDiagram()->diagram()->loadElmtFolioSeq();
880 this->currentDiagram()->diagram()->loadCndFolioSeq();
881
882 // If project have the folios list, move it at the beginning of the project
883 if (m_project -> getFolioSheetsQuantity()) {
884 for (int i = 0; i < m_project->getFolioSheetsQuantity(); i++)
885 m_tab -> tabBar() -> moveTab(diagram_views().size()-1, + 1);
886 m_project->setModified(false);
887 }
888 }
889
890 /**
891 * @brief ProjectView::updateWindowTitle
892 * Update the project view title
893 */
updateWindowTitle()894 void ProjectView::updateWindowTitle() {
895 QString title;
896 if (m_project) {
897 title = m_project -> pathNameTitle();
898 } else {
899 title = tr("Projet", "window title for a project-less ProjectView");
900 }
901 setWindowTitle(title);
902 }
903
904 /**
905 Effectue les actions necessaires lorsque le projet visualise entre ou sort
906 du mode lecture seule.
907 */
adjustReadOnlyState()908 void ProjectView::adjustReadOnlyState() {
909 bool editable = !(m_project -> isReadOnly());
910
911 // prevent users from moving existing diagrams
912 m_tab -> setMovable(editable);
913 // prevent users from adding new diagrams
914 add_new_diagram_ -> setEnabled(editable);
915
916 // on met a jour le titre du widget, qui reflete l'etat de lecture seule
917 updateWindowTitle();
918 }
919
920 /**
921 * @brief ProjectView::updateTabTitle
922 * Update the title of the tab which display the diagram view @diagram_view.
923 * @param diagram : The diagram view.
924 */
updateTabTitle(DiagramView * diagram_view)925 void ProjectView::updateTabTitle(DiagramView *diagram_view)
926 {
927 int diagram_tab_id = m_diagram_ids.key(diagram_view, -1);
928
929 if (diagram_tab_id != -1)
930 {
931 QSettings settings;
932 QString title;
933 Diagram *diagram = diagram_view->diagram();
934
935 if (settings.value("genericpanel/folio", false).toBool())
936 {
937 QString formula = diagram->border_and_titleblock.folio();
938 autonum::sequentialNumbers seq;
939 title = autonum::AssignVariables::formulaToLabel(formula, seq, diagram);
940 }
941 else
942 title = QString::number(diagram->folioIndex() + 1);
943
944 title += " - ";
945 title += diagram->title();
946 m_tab->setTabText(diagram_tab_id ,title);
947 }
948 }
949
950 /**
951 * @brief ProjectView::updateAllTabsTitle
952 * Update all tabs title
953 */
updateAllTabsTitle()954 void ProjectView::updateAllTabsTitle()
955 {
956 for (DiagramView *dv : m_diagram_ids.values())
957 updateTabTitle(dv);
958 }
959
960 /**
961 @param from Index de l'onglet avant le deplacement
962 @param to Index de l'onglet apres le deplacement
963 */
tabMoved(int from,int to)964 void ProjectView::tabMoved(int from, int to)
965 {
966 if (!m_project)
967 return;
968
969 m_project->diagramOrderChanged(from, to);
970 rebuildDiagramsMap();
971
972 //Rebuild the title of each diagram in range from - to
973 for (int i= qMin(from,to) ; i< qMax(from,to)+1 ; ++i)
974 {
975 DiagramView *dv = m_diagram_ids.value(i);
976 updateTabTitle(dv);
977 }
978 }
979
980 /**
981 @param diagram Schema a trouver
982 @return le DiagramView correspondant au schema passe en parametre, ou 0 si
983 le schema n'est pas trouve
984 */
findDiagram(Diagram * diagram)985 DiagramView *ProjectView::findDiagram(Diagram *diagram) {
986 foreach(DiagramView *diagram_view, diagram_views()) {
987 if (diagram_view -> diagram() == diagram) {
988 return(diagram_view);
989 }
990 }
991 return(nullptr);
992 }
993
994 /**
995 Reconstruit la map associant les index des onglets avec les DiagramView
996 */
rebuildDiagramsMap()997 void ProjectView::rebuildDiagramsMap() {
998 // vide la map
999 m_diagram_ids.clear();
1000
1001 foreach(DiagramView *diagram_view, m_diagram_view_list) {
1002 int dv_idx = m_tab -> indexOf(diagram_view);
1003 if (dv_idx == -1) continue;
1004 m_diagram_ids.insert(dv_idx, diagram_view);
1005 }
1006 }
1007
1008 /**
1009 * @brief ProjectView::tabChanged
1010 * Manage the tab change.
1011 * If tab_id == -1 (there is no diagram opened),
1012 * we display the fallback widget.
1013 * @param tab_id
1014 */
tabChanged(int tab_id)1015 void ProjectView::tabChanged(int tab_id)
1016 {
1017 if (tab_id == -1)
1018 setDisplayFallbackWidget(true);
1019 else if(m_tab->count() == 1)
1020 setDisplayFallbackWidget(false);
1021
1022 emit(diagramActivated(m_diagram_ids[tab_id]));
1023
1024 if (m_diagram_ids[tab_id] != nullptr)
1025 m_diagram_ids[tab_id]->diagram()->diagramActivated();
1026
1027 //Clear the event interface of the previous diagram
1028 if (DiagramView *dv = m_diagram_ids[m_previous_tab_index])
1029 dv->diagram()->clearEventInterface();
1030 m_previous_tab_index = tab_id;
1031 }
1032
1033 /**
1034 Gere le double-clic sur un onglet : edite les proprietes du schema
1035 @param tab_id Index de l'onglet concerne
1036 */
tabDoubleClicked(int tab_id)1037 void ProjectView::tabDoubleClicked(int tab_id) {
1038 // repere le schema concerne
1039 DiagramView *diagram_view = m_diagram_ids[tab_id];
1040 if (!diagram_view) return;
1041
1042 diagram_view -> editDiagramProperties();
1043 }
1044
1045 /**
1046 @param fallback true pour afficher le widget de fallback, false pour
1047 afficher les onglets.
1048 Le widget de Fallback est le widget affiche lorsque le projet ne comporte
1049 aucun schema.
1050 */
setDisplayFallbackWidget(bool fallback)1051 void ProjectView::setDisplayFallbackWidget(bool fallback) {
1052 fallback_widget_ -> setVisible(fallback);
1053 m_tab -> setVisible(!fallback);
1054 }
1055