1 /*
2  outline_view.h     MindForger thinking notebook
3 
4  Copyright (C) 2016-2020 Martin Dvorak <martin.dvorak@mindforger.com>
5 
6  This program is free software; you can redistribute it and/or
7  modify it under the terms of the GNU General Public License
8  as published by the Free Software Foundation; either version 2
9  of the License, or (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "orloj_presenter.h"
20 
21 #include "../../lib/src/gear/lang_utils.h"
22 
23 using namespace std;
24 
25 namespace m8r {
26 
OrlojPresenter(MainWindowPresenter * mainPresenter,OrlojView * view,Mind * mind)27 OrlojPresenter::OrlojPresenter(MainWindowPresenter* mainPresenter,
28                                OrlojView* view,
29                                Mind* mind)
30     : activeFacet{OrlojPresenterFacets::FACET_NONE},
31       config{Configuration::getInstance()},
32       skipEditNoteCheck{false}
33 {
34     this->mainPresenter = mainPresenter;
35     this->view = view;
36     this->mind = mind;
37 
38     this->dashboardPresenter = new DashboardPresenter(view->getDashboard(), this);
39     this->organizerPresenter = new OrganizerPresenter(view->getOrganizer(), this);
40     this->tagCloudPresenter = new TagsTablePresenter(view->getTagCloud(), mainPresenter->getHtmlRepresentation());
41     this->outlinesTablePresenter = new OutlinesTablePresenter(view->getOutlinesTable(), mainPresenter->getHtmlRepresentation());
42     this->recentNotesTablePresenter = new RecentNotesTablePresenter(view->getRecentNotesTable(), mainPresenter->getHtmlRepresentation());
43     this->outlineViewPresenter = new OutlineViewPresenter(view->getOutlineView(), this);
44     this->outlineHeaderViewPresenter = new OutlineHeaderViewPresenter(view->getOutlineHeaderView(), this);
45     this->outlineHeaderEditPresenter = new OutlineHeaderEditPresenter(view->getOutlineHeaderEdit(), mainPresenter, this);
46     this->noteViewPresenter = new NoteViewPresenter(view->getNoteView(), this);
47     this->noteEditPresenter = new NoteEditPresenter(view->getNoteEdit(), mainPresenter, this);
48     this->navigatorPresenter = new NavigatorPresenter(view->getNavigator(), this, mind->getKnowledgeGraph());
49 
50     /* Orloj presenter WIRES signals and slots between VIEWS and PRESENTERS.
51      *
52      * It's done by Orloj presenter as it has access to all its child windows
53      * and widgets - it can show/hide what's needed and then pass control to children.
54      */
55 
56     // hit enter in Os to view O detail
57     QObject::connect(
58         view->getOutlinesTable(),
59         SIGNAL(signalShowSelectedOutline()),
60         this,
61         SLOT(slotShowSelectedOutline()));
62     QObject::connect(
63         view->getDashboard()->getOutlinesDashboardlet(),
64         SIGNAL(signalShowSelectedOutline()),
65         this,
66         SLOT(slotShowSelectedOutline()));
67     QObject::connect(
68         view->getOutlinesTable(),
69         SIGNAL(signalFindOutlineByName()),
70         mainPresenter,
71         SLOT(doActionFindOutlineByName()));
72     // click O tree to view Note
73     QObject::connect(
74         view->getOutlineView()->getOutlineTree()->selectionModel(),
75         SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
76         this,
77         SLOT(slotShowNote(const QItemSelection&, const QItemSelection&)));
78     // hit ENTER in recent Os/Ns to view O/N detail
79     QObject::connect(
80         view->getRecentNotesTable(),
81         SIGNAL(signalShowSelectedRecentNote()),
82         this,
83         SLOT(slotShowSelectedRecentNote()));
84     QObject::connect(
85         view->getDashboard()->getRecentDashboardlet(),
86         SIGNAL(signalShowSelectedRecentNote()),
87         this,
88         SLOT(slotShowSelectedRecentNote()));
89     // hit ENTER in Tags to view Recall by Tag detail
90     QObject::connect(
91         view->getTagCloud(),
92         SIGNAL(signalShowDialogForTag()),
93         this,
94         SLOT(slotShowSelectedTagRecallDialog()));
95     QObject::connect(
96         view->getDashboard()->getTagsDashboardlet(),
97         SIGNAL(signalShowDialogForTag()),
98         this,
99         SLOT(slotShowSelectedTagRecallDialog()));
100     // navigator
101     QObject::connect(
102         navigatorPresenter, SIGNAL(signalOutlineSelected(Outline*)),
103         this, SLOT(slotShowOutlineNavigator(Outline*)));
104     QObject::connect(
105         navigatorPresenter, SIGNAL(signalNoteSelected(Note*)),
106         this, SLOT(slotShowNoteNavigator(Note*)));
107     QObject::connect(
108         navigatorPresenter, SIGNAL(signalThingSelected()),
109         this, SLOT(slotShowNavigator()));
110     // N editor
111 #ifdef __APPLE__
112     QObject::connect(
113         mainPresenter->getMainMenu()->getView()->actionEditComplete, SIGNAL(triggered()),
114         this, SLOT(slotEditStartLinkCompletion()));
115 #endif
116     // editor getting data from the backend
117     QObject::connect(
118         view->getNoteEdit()->getNoteEditor(), SIGNAL(signalGetLinksForPattern(const QString&)),
119         this, SLOT(slotGetLinksForPattern(const QString&)));
120     QObject::connect(
121         this, SIGNAL(signalLinksForPattern(const QString&, std::vector<std::string>*)),
122         view->getNoteEdit()->getNoteEditor(), SLOT(slotPerformLinkCompletion(const QString&, std::vector<std::string>*)));
123     QObject::connect(
124         view->getOutlineHeaderEdit()->getHeaderEditor(), SIGNAL(signalGetLinksForPattern(const QString&)),
125         this, SLOT(slotGetLinksForPattern(const QString&)));
126     QObject::connect(
127         this, SIGNAL(signalLinksForHeaderPattern(const QString&, std::vector<std::string>*)),
128         view->getOutlineHeaderEdit()->getHeaderEditor(), SLOT(slotPerformLinkCompletion(const QString&, std::vector<std::string>*)));
129     QObject::connect(
130         outlineHeaderEditPresenter->getView()->getButtonsPanel(), SIGNAL(signalShowLivePreview()),
131         mainPresenter, SLOT(doActionToggleLiveNotePreview()));
132     QObject::connect(
133         noteEditPresenter->getView()->getButtonsPanel(), SIGNAL(signalShowLivePreview()),
134         mainPresenter, SLOT(doActionToggleLiveNotePreview()));
135     // intercept Os table column sorting
136     QObject::connect(
137         view->getOutlinesTable()->horizontalHeader(), SIGNAL(sectionClicked(int)),
138         this, SLOT(slotOutlinesTableSorted(int)));
139     // toggle full O HTML preview
140     QObject::connect(
141         view->getOutlineHeaderView()->getEditPanel()->getFullOPreviewButton(), SIGNAL(clicked()),
142         this, SLOT(slotToggleFullOutlinePreview()));
143 }
144 
dialogSaveOrCancel()145 int dialogSaveOrCancel()
146 {
147     // l10n by moving this dialog either to Qt class OR view class
148     QMessageBox msgBox{
149         QMessageBox::Question,
150         "Save Note",
151         "Do you want to save Note changes?"};
152     QPushButton* discard = msgBox.addButton("&Discard changes", QMessageBox::DestructiveRole);
153     QPushButton* autosave = msgBox.addButton("Do not ask && &autosave", QMessageBox::AcceptRole);
154     QPushButton* edit = msgBox.addButton("Continue &editing", QMessageBox::YesRole);
155     QPushButton* save = msgBox.addButton("&Save", QMessageBox::ActionRole);
156     msgBox.exec();
157 
158     QAbstractButton* choosen = msgBox.clickedButton();
159     if(discard == choosen) {
160         return OrlojButtonRoles::DISCARD_ROLE;
161     } else if(autosave == choosen) {
162         return OrlojButtonRoles::AUTOSAVE_ROLE;
163     } else if(edit == choosen) {
164         return OrlojButtonRoles::EDIT_ROLE;
165     } else if(save == choosen) {
166         return OrlojButtonRoles::SAVE_ROLE;
167     }
168 
169     return OrlojButtonRoles::INVALID_ROLE;
170 }
171 
onFacetChange(const OrlojPresenterFacets targetFacet) const172 void OrlojPresenter::onFacetChange(const OrlojPresenterFacets targetFacet) const
173 {
174     MF_DEBUG("Facet CHANGE: " << activeFacet << " > " << targetFacet << endl);
175 
176     if(activeFacet == OrlojPresenterFacets::FACET_NAVIGATOR) {
177         navigatorPresenter->cleanupBeforeHide();
178     } else if(targetFacet == OrlojPresenterFacets::FACET_VIEW_OUTLINE) {
179         outlineViewPresenter->getOutlineTree()->focus();
180     }
181 }
182 
showFacetRecentNotes(const vector<Note * > & notes)183 void OrlojPresenter::showFacetRecentNotes(const vector<Note*>& notes)
184 {
185     setFacet(OrlojPresenterFacets::FACET_RECENT_NOTES);
186     recentNotesTablePresenter->refresh(notes);
187     view->showFacetRecentNotes();
188     mainPresenter->getStatusBar()->showMindStatistics();
189 }
190 
showFacetDashboard()191 void OrlojPresenter::showFacetDashboard() {
192     setFacet(OrlojPresenterFacets::FACET_DASHBOARD);
193 
194     vector<Note*> allNotes{};
195     mind->getAllNotes(allNotes, true, true);
196     map<const Tag*,int> allTags{};
197     mind->getTagsCardinality(allTags);
198 
199     dashboardPresenter->refresh(
200         mind->getOutlines(),
201         allNotes,
202         allTags,
203         mind->remind().getOutlineMarkdownsSize(),
204         mind->getStatistics()
205     );
206     view->showFacetDashboard();
207     mainPresenter->getMainMenu()->showFacetDashboard();
208     mainPresenter->getStatusBar()->showMindStatistics();
209 }
210 
showFacetOrganizer(const vector<Outline * > & outlines)211 void OrlojPresenter::showFacetOrganizer(const vector<Outline*>& outlines)
212 {
213     setFacet(OrlojPresenterFacets::FACET_ORGANIZER);
214     organizerPresenter->refresh(outlines);
215     view->showFacetOrganizer();
216     mainPresenter->getMainMenu()->showFacetOrganizer();
217     mainPresenter->getStatusBar()->showMindStatistics();
218 }
219 
showFacetKnowledgeGraphNavigator()220 void OrlojPresenter::showFacetKnowledgeGraphNavigator()
221 {
222     switch(activeFacet) {
223     case OrlojPresenterFacets::FACET_VIEW_OUTLINE_HEADER:
224     case OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER:
225     case OrlojPresenterFacets::FACET_VIEW_OUTLINE:
226         setFacet(OrlojPresenterFacets::FACET_NAVIGATOR);
227         navigatorPresenter->showInitialView(outlineViewPresenter->getCurrentOutline());
228         slotShowOutlineNavigator(outlineViewPresenter->getCurrentOutline());
229         break;
230     case OrlojPresenterFacets::FACET_VIEW_NOTE:
231         setFacet(OrlojPresenterFacets::FACET_NAVIGATOR);
232         navigatorPresenter->showInitialView(noteViewPresenter->getCurrentNote());
233         slotShowNoteNavigator(noteViewPresenter->getCurrentNote());
234         break;
235     case OrlojPresenterFacets::FACET_EDIT_NOTE:
236         setFacet(OrlojPresenterFacets::FACET_NAVIGATOR);
237         navigatorPresenter->showInitialView(noteEditPresenter->getCurrentNote());
238         slotShowNoteNavigator(noteEditPresenter->getCurrentNote());
239         break;
240     case OrlojPresenterFacets::FACET_TAG_CLOUD:
241         setFacet(OrlojPresenterFacets::FACET_NAVIGATOR);
242         navigatorPresenter->showInitialViewTags();
243         view->showFacetNavigator();
244         break;
245     default:
246         setFacet(OrlojPresenterFacets::FACET_NAVIGATOR);
247         navigatorPresenter->showInitialView();
248         view->showFacetNavigator();
249         break;
250     }
251 
252     mainPresenter->getMainMenu()->showFacetNavigator();
253     mainPresenter->getStatusBar()->showMindStatistics();
254 }
255 
showFacetOutlineList(const vector<Outline * > & outlines)256 void OrlojPresenter::showFacetOutlineList(const vector<Outline*>& outlines)
257 {
258     setFacet(OrlojPresenterFacets::FACET_LIST_OUTLINES);
259     // IMPROVE reload ONLY if dirty, otherwise just show
260     outlinesTablePresenter->refresh(outlines);
261     view->showFacetOutlines();
262     mainPresenter->getMainMenu()->showFacetOutlineList();
263     mainPresenter->getStatusBar()->showMindStatistics();
264 }
265 
slotShowOutlines()266 void OrlojPresenter::slotShowOutlines()
267 {
268     showFacetOutlineList(mind->getOutlines());
269 }
270 
showFacetOutline(Outline * outline)271 void OrlojPresenter::showFacetOutline(Outline* outline)
272 {
273     if(activeFacet == OrlojPresenterFacets::FACET_NAVIGATOR) {
274         outlineHeaderViewPresenter->refresh(outline);
275         view->showFacetNavigatorOutline();
276     } else {
277         setFacet(OrlojPresenterFacets::FACET_VIEW_OUTLINE);
278 
279         outlineViewPresenter->refresh(outline);
280         outlineHeaderViewPresenter->refresh(outline);
281         view->showFacetOutlineHeaderView();
282     }
283 
284     outline->incReads();
285     outline->makeDirty();
286 
287     mainPresenter->getMainMenu()->showFacetOutlineView();
288 
289     mainPresenter->getStatusBar()->showInfo(QString("Notebook '%1'   %2").arg(outline->getName().c_str()).arg(outline->getKey().c_str()));
290 }
291 
slotShowSelectedOutline()292 void OrlojPresenter::slotShowSelectedOutline()
293 {
294     if(activeFacet!=OrlojPresenterFacets::FACET_ORGANIZER
295          &&
296        activeFacet!=OrlojPresenterFacets::FACET_TAG_CLOUD
297          &&
298        activeFacet!=OrlojPresenterFacets::FACET_NAVIGATOR
299          &&
300        activeFacet!=OrlojPresenterFacets::FACET_RECENT_NOTES
301       )
302     {
303         int row;
304         if(activeFacet==OrlojPresenterFacets::FACET_DASHBOARD) {
305             row = dashboardPresenter->getOutlinesPresenter()->getCurrentRow();
306         } else {
307             row = outlinesTablePresenter->getCurrentRow();
308         }
309         if(row != OutlinesTablePresenter::NO_ROW) {
310             QStandardItem* item;
311             if(activeFacet==OrlojPresenterFacets::FACET_DASHBOARD) {
312                 item = dashboardPresenter->getOutlinesPresenter()->getModel()->item(row);
313             } else {
314                 item = outlinesTablePresenter->getModel()->item(row);
315             }
316             // TODO introduce name my user role - replace constant with my enum name > do it for whole file e.g. MfDataRole
317             if(item) {
318                 Outline* outline = item->data(Qt::UserRole + 1).value<Outline*>();
319                 showFacetOutline(outline);
320                 return;
321             } else {
322                 mainPresenter->getStatusBar()->showInfo(QString(tr("Selected Notebook not found!")));
323             }
324         }
325         mainPresenter->getStatusBar()->showInfo(QString(tr("No Notebook selected!")));
326     }
327 }
328 
slotShowOutline(const QItemSelection & selected,const QItemSelection & deselected)329 void OrlojPresenter::slotShowOutline(const QItemSelection& selected, const QItemSelection& deselected)
330 {
331     Q_UNUSED(deselected);
332 
333     if(activeFacet!=OrlojPresenterFacets::FACET_ORGANIZER
334          &&
335        activeFacet!=OrlojPresenterFacets::FACET_TAG_CLOUD
336          &&
337        activeFacet!=OrlojPresenterFacets::FACET_NAVIGATOR
338          &&
339        activeFacet!=OrlojPresenterFacets::FACET_RECENT_NOTES
340       )
341     {
342         QModelIndexList indices = selected.indexes();
343         if(indices.size()) {
344             const QModelIndex& index = indices.at(0);
345             QStandardItem* item = outlinesTablePresenter->getModel()->itemFromIndex(index);
346             // TODO introduce name my user role - replace constant with my enum name > do it for whole file e.g. MfDataRole
347             Outline* outline = item->data(Qt::UserRole + 1).value<Outline*>();
348             showFacetOutline(outline);
349         } else {
350             mainPresenter->getStatusBar()->showInfo(QString(tr("No Notebook selected!")));
351         }
352     }
353 }
354 
showFacetTagCloud()355 void OrlojPresenter::showFacetTagCloud()
356 {
357     setFacet(OrlojPresenterFacets::FACET_TAG_CLOUD);
358 
359     map<const Tag*,int> tags{};
360     mind->getTagsCardinality(tags);
361     tagCloudPresenter->refresh(tags);
362 
363     view->showFacetTagCloud();
364 
365     mainPresenter->getStatusBar()->showInfo(QString("%2 Tags").arg(tags.size()));
366 }
367 
slotShowSelectedTagRecallDialog()368 void OrlojPresenter::slotShowSelectedTagRecallDialog()
369 {
370     if(activeFacet == OrlojPresenterFacets::FACET_TAG_CLOUD
371          ||
372        activeFacet == OrlojPresenterFacets::FACET_DASHBOARD
373       )
374     {
375         int row;
376         if(activeFacet==OrlojPresenterFacets::FACET_DASHBOARD) {
377             row = dashboardPresenter->getTagsPresenter()->getCurrentRow();
378         } else {
379             row = tagCloudPresenter->getCurrentRow();
380         }
381         if(row != OutlinesTablePresenter::NO_ROW) {
382             QStandardItem* item;
383             switch(activeFacet) {
384             case OrlojPresenterFacets::FACET_TAG_CLOUD:
385                 item = tagCloudPresenter->getModel()->item(row);
386                 break;
387             case OrlojPresenterFacets::FACET_DASHBOARD:
388                 item = dashboardPresenter->getTagsPresenter()->getModel()->item(row);
389                 break;
390             default:
391                 item = nullptr;
392             }
393             // TODO introduce name my user role - replace constant with my enum name > do it for whole file e.g. MfDataRole
394             if(item) {
395                 const Tag* tag = item->data(Qt::UserRole + 1).value<const Tag*>();
396                 mainPresenter->doTriggerFindNoteByTag(tag);
397             } else {
398                 mainPresenter->getStatusBar()->showInfo(QString(tr("Selected Tag not found!")));
399             }
400         } else {
401             mainPresenter->getStatusBar()->showInfo(QString(tr("No Tag selected!")));
402         }
403     }
404 }
405 
slotShowTagRecallDialog(const QItemSelection & selected,const QItemSelection & deselected)406 void OrlojPresenter::slotShowTagRecallDialog(const QItemSelection& selected, const QItemSelection& deselected)
407 {
408     Q_UNUSED(deselected);
409 
410     if(activeFacet == OrlojPresenterFacets::FACET_TAG_CLOUD
411          ||
412        activeFacet == OrlojPresenterFacets::FACET_DASHBOARD
413       )
414     {
415         QModelIndexList indices = selected.indexes();
416         if(indices.size()) {
417             const QModelIndex& index = indices.at(0);
418             QStandardItem* item;
419             // TODO if 2 switch
420             if(activeFacet == OrlojPresenterFacets::FACET_TAG_CLOUD) {
421                 item = tagCloudPresenter->getModel()->itemFromIndex(index);
422             } else {
423                 item = dashboardPresenter->getTagsPresenter()->getModel()->itemFromIndex(index);
424             }
425             // TODO introduce name my user role - replace constant with my enum name > do it for whole file e.g. MfDataRole
426             const Tag* tag = item->data(Qt::UserRole + 1).value<const Tag*>();
427             mainPresenter->doTriggerFindNoteByTag(tag);
428         } else {
429             mainPresenter->getStatusBar()->showInfo(QString(tr("No Tag selected!")));
430         }
431     }
432 }
433 
showFacetNoteView()434 void OrlojPresenter::showFacetNoteView()
435 {
436     view->showFacetNoteView();
437     mainPresenter->getMainMenu()->showFacetOutlineView();
438     setFacet(OrlojPresenterFacets::FACET_VIEW_NOTE);
439 }
440 
showFacetNoteView(Note * note)441 void OrlojPresenter::showFacetNoteView(Note* note)
442 {
443     if(activeFacet == OrlojPresenterFacets::FACET_NAVIGATOR) {
444         noteViewPresenter->refresh(note);
445         view->showFacetNavigatorNote();
446         mainPresenter->getMainMenu()->showFacetOutlineView();
447     } else {
448         if(outlineViewPresenter->getCurrentOutline()!=note->getOutline()) {
449             showFacetOutline(note->getOutline());
450         }
451         noteViewPresenter->refresh(note);
452         view->showFacetNoteView();
453         outlineViewPresenter->selectRowByNote(note);
454         mainPresenter->getMainMenu()->showFacetOutlineView();
455 
456         setFacet(OrlojPresenterFacets::FACET_VIEW_NOTE);
457     }
458 
459     QString p{note->getOutline()->getKey().c_str()};
460     p += "#";
461     p += note->getMangledName().c_str();
462     mainPresenter->getStatusBar()->showInfo(QString(tr("Note '%1'   %2")).arg(note->getName().c_str()).arg(p));
463 }
464 
showFacetNoteEdit(Note * note)465 void OrlojPresenter::showFacetNoteEdit(Note* note)
466 {
467     if(activeFacet == OrlojPresenterFacets::FACET_NAVIGATOR) {
468         outlineViewPresenter->refresh(note->getOutline());
469     }
470 
471     noteEditPresenter->setNote(note);
472     view->showFacetNoteEdit();
473     setFacet(OrlojPresenterFacets::FACET_EDIT_NOTE);
474     mainPresenter->getMainMenu()->showFacetNoteEdit();
475 
476     // refresh live preview to ensure on/off autolinking, full O vs. header, ...
477     if(config.isUiLiveNotePreview()) {
478         noteViewPresenter->refreshLivePreview();
479     }
480 }
481 
showFacetOutlineHeaderEdit(Outline * outline)482 void OrlojPresenter::showFacetOutlineHeaderEdit(Outline* outline)
483 {
484     if(activeFacet == OrlojPresenterFacets::FACET_NAVIGATOR) {
485         outlineViewPresenter->refresh(outline);
486     }
487 
488     outlineHeaderEditPresenter->setOutline(outline);
489     view->showFacetOutlineHeaderEdit();
490     setFacet(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER);
491     mainPresenter->getMainMenu()->showFacetNoteEdit();
492 
493     // refresh live preview to ensure on/off autolinking, full O vs. header, ...
494     if(config.isUiLiveNotePreview()) {
495         outlineHeaderViewPresenter->refreshLivePreview();
496     }
497 }
498 
applyFacetHoisting()499 bool OrlojPresenter::applyFacetHoisting()
500 {
501     if(Configuration::getInstance().isUiHoistedMode()) {
502         if(isFacetActive(OrlojPresenterFacets::FACET_VIEW_OUTLINE)
503              ||
504            isFacetActive(OrlojPresenterFacets::FACET_VIEW_OUTLINE_HEADER))
505         {
506             view->showFacetHoistedOutlineHeaderView();
507         } else if(isFacetActive(OrlojPresenterFacets::FACET_VIEW_NOTE)) {
508             view->showFacetHoistedNoteView();
509         } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_NOTE)) {
510             view->showFacetHoistedNoteEdit();
511         } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) {
512             view->showFacetHoistedOutlineHeaderEdit();
513         }
514 
515         return false;
516     } else {
517         if(isFacetActive(OrlojPresenterFacets::FACET_VIEW_OUTLINE)
518              ||
519            isFacetActive(OrlojPresenterFacets::FACET_VIEW_OUTLINE_HEADER))
520         {
521             view->showFacetOutlineHeaderView();
522         } else if(isFacetActive(OrlojPresenterFacets::FACET_VIEW_NOTE)) {
523             view->showFacetNoteView();
524         } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_NOTE)) {
525             view->showFacetNoteEdit();
526         } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) {
527             view->showFacetOutlineHeaderEdit();
528         }
529 
530         return true;
531     }
532 }
533 
fromOutlineHeaderEditBackToView(Outline * outline)534 void OrlojPresenter::fromOutlineHeaderEditBackToView(Outline* outline)
535 {
536     // LEFT: update O name above Ns tree
537     outlineViewPresenter->refresh(outline->getName());
538     // RIGHT
539     outlineHeaderViewPresenter->refresh(outline);
540     view->showFacetOutlineHeaderView();
541     setFacet(OrlojPresenterFacets::FACET_VIEW_OUTLINE_HEADER);
542     mainPresenter->getMainMenu()->showFacetOutlineView();
543 }
544 
fromNoteEditBackToView(Note * note)545 void OrlojPresenter::fromNoteEditBackToView(Note* note)
546 {
547     noteViewPresenter->clearSearchExpression();
548     noteViewPresenter->refresh(note);
549     // update N in the tree
550     outlineViewPresenter->refresh(note);
551 
552     showFacetNoteView();
553 }
554 
avoidDataLossOnNoteEdit()555 bool OrlojPresenter::avoidDataLossOnNoteEdit()
556 {
557     // avoid lost of N editor changes
558     if(skipEditNoteCheck) {
559         skipEditNoteCheck=false;
560         if(activeFacet == OrlojPresenterFacets::FACET_EDIT_NOTE) {
561             noteEditPresenter->getView()->getNoteEditor()->setFocus();
562         } else if(activeFacet == OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER) {
563             outlineHeaderEditPresenter->getView()->getHeaderEditor()->setFocus();
564         }
565         return true;
566     } else {
567         if(activeFacet == OrlojPresenterFacets::FACET_EDIT_NOTE) {
568             int decision{};
569             if(Configuration::getInstance().isUiEditorAutosave()) {
570                 decision = OrlojButtonRoles::SAVE_ROLE;
571             } else {
572                 decision = dialogSaveOrCancel();
573             }
574 
575             switch(decision) {
576             case OrlojButtonRoles::DISCARD_ROLE:
577                 // do nothing
578                 break;
579             case OrlojButtonRoles::AUTOSAVE_ROLE:
580                 Configuration::getInstance().setUiEditorAutosave(true);
581                 mainPresenter->getConfigRepresentation()->save(Configuration::getInstance());
582                 MF_FALL_THROUGH;
583             case OrlojButtonRoles::SAVE_ROLE:
584                 noteEditPresenter->slotSaveNote();
585                 break;
586             case OrlojButtonRoles::EDIT_ROLE:
587                 MF_FALL_THROUGH;
588             default:
589                 // rollback ~ select previous N and continue
590                 // ugly & stupid hack to disable signal emitted on N selection in O tree
591                 skipEditNoteCheck=true;
592                 outlineViewPresenter->selectRowByNote(noteEditPresenter->getCurrentNote());
593                 return true;
594             }
595         } else if(activeFacet == OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER) {
596             int decision = dialogSaveOrCancel();
597             switch(decision) {
598             case OrlojButtonRoles::DISCARD_ROLE:
599                 // do nothing
600                 break;
601             case OrlojButtonRoles::AUTOSAVE_ROLE:
602                 Configuration::getInstance().setUiEditorAutosave(true);
603                 mainPresenter->getConfigRepresentation()->save(Configuration::getInstance());
604                 MF_FALL_THROUGH;
605             case OrlojButtonRoles::SAVE_ROLE:
606                 outlineHeaderEditPresenter->slotSaveOutlineHeader();
607                 break;
608             case OrlojButtonRoles::EDIT_ROLE:
609                 MF_FALL_THROUGH;
610             default:
611                 // rollback ~ select previous N and continue
612                 // ugly & stupid hack to disable signal emitted on N selection in O tree
613                 skipEditNoteCheck=true;
614                 outlineViewPresenter->getOutlineTree()->clearSelection();
615                 return true;
616             }
617         }
618     }
619 
620     return false;
621 }
622 
slotShowOutlineHeader()623 void OrlojPresenter::slotShowOutlineHeader()
624 {
625     if(avoidDataLossOnNoteEdit()) return;
626 
627     // refresh header
628     outlineHeaderViewPresenter->refresh(outlineViewPresenter->getCurrentOutline());
629     setFacet(OrlojPresenterFacets::FACET_VIEW_OUTLINE_HEADER);
630     view->showFacetOutlineHeaderView();
631 }
632 
slotShowNote(const QItemSelection & selected,const QItemSelection & deselected)633 void OrlojPresenter::slotShowNote(const QItemSelection& selected, const QItemSelection& deselected)
634 {
635     Q_UNUSED(deselected);
636 
637     if(avoidDataLossOnNoteEdit()) return;
638 
639     QModelIndexList indices = selected.indexes();
640     if(indices.size()) {
641         const QModelIndex& index = indices.at(0);
642         QStandardItem* item
643             = outlineViewPresenter->getOutlineTree()->getModel()->itemFromIndex(index);
644         // IMPROVE make my role constant
645         Note* note = item->data(Qt::UserRole + 1).value<Note*>();
646 
647         note->incReads();
648         note->makeDirty();
649 
650         showFacetNoteView(note);
651     } else {
652         Outline* outline = outlineViewPresenter->getCurrentOutline();
653         mainPresenter->getStatusBar()->showInfo(QString("Notebook '%1'   %2").arg(outline->getName().c_str()).arg(outline->getKey().c_str()));
654     }
655 }
656 
slotShowNavigator()657 void OrlojPresenter::slotShowNavigator()
658 {
659     view->showFacetNavigator();
660 }
661 
slotShowNoteNavigator(Note * note)662 void OrlojPresenter::slotShowNoteNavigator(Note* note)
663 {
664     if(note) {
665         note->incReads();
666         note->makeDirty();
667 
668         showFacetNoteView(note);
669     }
670 }
671 
slotShowOutlineNavigator(Outline * outline)672 void OrlojPresenter::slotShowOutlineNavigator(Outline* outline)
673 {
674     if(outline) {
675         // timestamps are updated by O header view
676         showFacetOutline(outline);
677     }
678 }
679 
680 /**
681  * @brief Return MD links for given O/N name prefix (pattern).
682  *
683  * For example 'Mi' > { '[MindForger](mf/projects.md#mind-forger)', '[Middle](mf/places.md#middle) }'
684  */
slotGetLinksForPattern(const QString & pattern)685 void OrlojPresenter::slotGetLinksForPattern(const QString& pattern)
686 {
687     vector<Thing*> allThings{};
688     vector<string> thingsNames = vector<string>{};
689     string prefix{pattern.toStdString()};
690 
691     Outline* currentOutline;
692     if(activeFacet == OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER) {
693         currentOutline = outlineHeaderEditPresenter->getCurrentOutline();
694     } else {
695         currentOutline = noteEditPresenter->getCurrentNote()->getOutline();
696     }
697 
698     mind->getAllThings(
699         allThings,
700         &thingsNames,
701         &prefix,
702         ThingNameSerialization::LINK,
703         currentOutline);
704 
705     vector<string>* links = new vector<string>{};
706     *links = thingsNames;
707 
708     if(activeFacet == OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER) {
709         emit signalLinksForHeaderPattern(pattern, links);
710     } else {
711         emit signalLinksForPattern(pattern, links);
712     }
713 }
714 
slotShowSelectedRecentNote()715 void OrlojPresenter::slotShowSelectedRecentNote()
716 {
717     if(activeFacet == OrlojPresenterFacets::FACET_RECENT_NOTES
718          ||
719        activeFacet == OrlojPresenterFacets::FACET_DASHBOARD
720       )
721     {
722         int row;
723         if(activeFacet==OrlojPresenterFacets::FACET_DASHBOARD) {
724             row = dashboardPresenter->getRecentNotesPresenter()->getCurrentRow();
725         } else {
726             row = recentNotesTablePresenter->getCurrentRow();
727         }
728         if(row != RecentNotesTablePresenter::NO_ROW) {
729             QStandardItem* item;
730             switch(activeFacet) {
731             case OrlojPresenterFacets::FACET_RECENT_NOTES:
732                 item = recentNotesTablePresenter->getModel()->item(row);
733                 break;
734             case OrlojPresenterFacets::FACET_DASHBOARD:
735                 item = dashboardPresenter->getRecentNotesPresenter()->getModel()->item(row);
736                 break;
737             default:
738                 item = nullptr;
739             }
740             // TODO make my role constant
741             if(item) {
742                 const Note* note = item->data(Qt::UserRole + 1).value<const Note*>();
743 
744                 showFacetOutline(note->getOutline());
745                 if(note->getType() != note->getOutline()->getOutlineDescriptorNoteType()) {
746                     // IMPROVE make this more efficient
747                     showFacetNoteView();
748                     getOutlineView()->selectRowByNote(note);
749                 }
750                 mainPresenter->getStatusBar()->showInfo(QString(tr("Note "))+QString::fromStdString(note->getName()));
751             } else {
752                 mainPresenter->getStatusBar()->showInfo(QString(tr("Selected Notebook/Note not found!")));
753             }
754         } else {
755             mainPresenter->getStatusBar()->showInfo(QString(tr("No Note selected!")));
756         }
757     }
758 }
759 
slotShowRecentNote(const QItemSelection & selected,const QItemSelection & deselected)760 void OrlojPresenter::slotShowRecentNote(const QItemSelection& selected, const QItemSelection& deselected)
761 {
762     Q_UNUSED(deselected);
763 
764     if(activeFacet == OrlojPresenterFacets::FACET_RECENT_NOTES
765          ||
766        activeFacet == OrlojPresenterFacets::FACET_DASHBOARD
767       )
768     {
769         QModelIndexList indices = selected.indexes();
770         if(indices.size()) {
771             const QModelIndex& index = indices.at(0);
772             QStandardItem* item;
773             if(activeFacet == OrlojPresenterFacets::FACET_RECENT_NOTES) {
774                 item = recentNotesTablePresenter->getModel()->itemFromIndex(index);
775             } else {
776                 item = dashboardPresenter->getRecentNotesPresenter()->getModel()->itemFromIndex(index);
777             }
778             // TODO make my role constant
779             const Note* note = item->data(Qt::UserRole + 1).value<const Note*>();
780 
781             showFacetOutline(note->getOutline());
782             if(note->getType() != note->getOutline()->getOutlineDescriptorNoteType()) {
783                 // IMPROVE make this more efficient
784                 showFacetNoteView();
785                 getOutlineView()->selectRowByNote(note);
786             }
787             mainPresenter->getStatusBar()->showInfo(QString(tr("Note "))+QString::fromStdString(note->getName()));
788         } else {
789             mainPresenter->getStatusBar()->showInfo(QString(tr("No Note selected!")));
790         }
791     }
792 }
793 
refreshLiveNotePreview()794 void OrlojPresenter::refreshLiveNotePreview()
795 {
796     if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_NOTE)) {
797         view->showFacetNoteEdit();
798     } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) {
799         view->showFacetOutlineHeaderEdit();
800     }
801 }
802 
slotEditStartLinkCompletion()803 void OrlojPresenter::slotEditStartLinkCompletion()
804 {
805     if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_NOTE)) {
806         view->getNoteEdit()->getNoteEditor()->slotStartLinkCompletion();
807     } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) {
808         view->getOutlineHeaderEdit()->getHeaderEditor()->slotStartLinkCompletion();
809     }
810 }
811 
slotRefreshCurrentNotePreview()812 void OrlojPresenter::slotRefreshCurrentNotePreview()
813 {
814     MF_DEBUG("Slot to refresh live preview: " << getFacet() << " hoist: " << config.isUiHoistedMode() << endl);
815     if(!config.isUiHoistedMode()) {
816         if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_NOTE)) {
817             noteViewPresenter->refreshLivePreview();
818 #if defined(__APPLE__) || defined(_WIN32)
819             getNoteEdit()->getView()->getNoteEditor()->setFocus();
820 #endif
821         } else if(isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) {
822             outlineHeaderViewPresenter->refreshLivePreview();
823 #if defined(__APPLE__) || defined(_WIN32)
824             getOutlineHeaderEdit()->getView()->getHeaderEditor()->setFocus();
825 #endif
826         }
827     }
828 }
829 
slotOutlinesTableSorted(int column)830 void OrlojPresenter::slotOutlinesTableSorted(int column)
831 {
832     Qt::SortOrder order
833         = view->getOutlinesTable()->horizontalHeader()->sortIndicatorOrder();
834     MF_DEBUG("Os table sorted: " << column << " descending: " << order << endl);
835 
836     config.setUiOsTableSortColumn(column);
837     config.setUiOsTableSortOrder(order==Qt::SortOrder::AscendingOrder?true:false);
838     mainPresenter->getConfigRepresentation()->save(config);
839 }
840 
slotToggleFullOutlinePreview()841 void OrlojPresenter::slotToggleFullOutlinePreview()
842 {
843     config.setUiFullOPreview(!config.isUiFullOPreview());
844     mainPresenter->getConfigRepresentation()->save(config);
845 
846     // refresh O header view
847     getOutlineHeaderView()->refreshCurrent();
848 }
849 
850 } // m8r namespace
851