1 /*
2  outline_tree_presenter.cpp     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 "outline_tree_presenter.h"
20 
21 namespace m8r {
22 
23 using namespace std;
24 
OutlineTreePresenter(OutlineTreeView * view,MainWindowPresenter * mwp,QObject * parent)25 OutlineTreePresenter::OutlineTreePresenter(OutlineTreeView* view, MainWindowPresenter* mwp, QObject* parent)
26     : QObject(parent)
27 {
28     this->view = view;
29     this->model = new OutlineTreeModel{view, mwp->getHtmlRepresentation()};
30     this->view->setModel(this->model);
31     this->mind = mwp->getMind();
32 
33     // ensure HTML cells rendering
34     HtmlDelegate* delegate = new HtmlDelegate{};
35     this->view->setItemDelegate(delegate);
36 
37     // signals
38     QObject::connect(view, SIGNAL(signalSelectNextRow()), this, SLOT(slotSelectNextRow()));
39     QObject::connect(view, SIGNAL(signalSelectPreviousRow()), this, SLOT(slotSelectPreviousRow()));
40 
41     QObject::connect(view, SIGNAL(signalOutlineShow()), mwp, SLOT(doActionOutlineShow()));
42 
43     QObject::connect(view, SIGNAL(signalChangePromote()), mwp, SLOT(doActionNotePromote()));
44     QObject::connect(view, SIGNAL(signalChangeDemote()), mwp, SLOT(doActionNoteDemote()));
45     QObject::connect(view, SIGNAL(signalChangeFirst()), mwp, SLOT(doActionNoteFirst()));
46     QObject::connect(view, SIGNAL(signalChangeUp()), mwp, SLOT(doActionNoteUp()));
47     QObject::connect(view, SIGNAL(signalChangeDown()), mwp, SLOT(doActionNoteDown()));
48     QObject::connect(view, SIGNAL(signalChangeLast()), mwp, SLOT(doActionNoteLast()));
49 
50     QObject::connect(view, SIGNAL(signalOutlineOrNoteEdit()), mwp, SLOT(doActionOutlineOrNoteEdit()));
51     QObject::connect(view, SIGNAL(signalEdit()), mwp, SLOT(doActionNoteEdit()));
52     QObject::connect(view, SIGNAL(signalForget()), mwp, SLOT(doActionNoteForget()));
53 }
54 
~OutlineTreePresenter()55 OutlineTreePresenter::~OutlineTreePresenter()
56 {
57     if(model) delete model;
58 }
59 
refresh(Outline * outline,Outline::Patch * patch)60 void OutlineTreePresenter::refresh(Outline* outline, Outline::Patch* patch)
61 {
62     // If FORGET aspect is used, then only VIEW is filtered, but operations are performed
63     // on the non-filtered O's Ns. UI view serves just as a FILTER of what can be changed.
64     // Therefore anything special is needed on the backend.
65     //   Unfortunately PATCH will NOT help if VIEW is filtered and everyhing must be
66     // refreshed.
67     if(outline) {
68         if(patch) {
69             const vector<Note*>& notes = outline->getNotes();
70             switch(patch->diff) {
71             case Outline::Patch::Diff::CHANGE:
72                 for(unsigned int i=patch->start; i<=patch->start+patch->count; i++) {
73                     model->refresh(notes[i], i, false);
74                 }
75                 break;
76             case Outline::Patch::Diff::MOVE:
77                 for(unsigned int i=patch->start; i<=patch->start+patch->count; i++) {
78                     model->refresh(notes[i], i, true);
79                 }
80                 break;
81             default:
82                 break;
83             }
84         } else {
85             model->removeAllRows();
86             for(Note* note:outline->getNotes()) {
87                 model->addNote(note);
88             }
89         }
90 
91         // forget / time scope: hide view rows ~ there is full model, I just hide what's visible > patch should work
92         if(mind->getScopeAspect().isEnabled()) {
93             vector<int> parents;
94             for(size_t i=0; i<outline->getNotesCount(); i++) {
95                 if(mind->getScopeAspect().isInScope(outline->getNotes()[i])) {
96                     // N's parents
97                     parents.clear();
98                     outline->getNotePathToRoot(i, parents);
99                     if(parents.size()) {
100                         for(size_t p=0; p<parents.size(); p++) {
101                             view->showRow(parents[p]);
102                         }
103                     }
104                     // N
105                     view->showRow(i);
106                 } else {
107                     view->hideRow(i);
108                 }
109             }
110         }
111     }
112 }
113 
refresh(Note * note)114 void OutlineTreePresenter::refresh(Note* note)
115 {
116     QItemSelectionModel *select = view->selectionModel();
117     if(select->hasSelection()) {
118         model->refresh(note, select->selectedRows());
119     } else {
120         model->refresh(note);
121     }
122 }
123 
insertAndSelect(Note * note)124 void OutlineTreePresenter::insertAndSelect(Note* note)
125 {
126     int row = model->insertNote(note);
127     view->scrollTo(model->index(row, 0));
128     view->selectRow(row);
129 }
130 
clearSelection()131 void OutlineTreePresenter::clearSelection()
132 {
133     view->clearSelection();
134 }
135 
selectRowByNote(const Note * note)136 void OutlineTreePresenter::selectRowByNote(const Note* note)
137 {
138     if(note) {
139         int row = model->getRowByNote(note);
140         if(row >= 0) {
141             view->selectRow(row);
142             view->scrollTo(model->index(row, 0));
143             return;
144         }
145     }
146     view->clearSelection();
147 }
148 
getCurrentRow() const149 int OutlineTreePresenter::getCurrentRow() const
150 {
151     QModelIndexList indexes = view->selectionModel()->selection().indexes();
152     for(int i=0; i<indexes.count(); i++) {
153         return indexes.at(i).row();
154     }
155     return NO_ROW;
156 }
157 
getCurrentNote() const158 Note* OutlineTreePresenter::getCurrentNote() const
159 {
160     int row = getCurrentRow();
161     // IMPROVE constant w/ a name
162     if(row != -1) {
163         return model->item(row)->data().value<Note*>();
164     } else {
165         return nullptr;
166     }
167 }
168 
slotSelectNextRow()169 void OutlineTreePresenter::slotSelectNextRow()
170 {
171     int row = getCurrentRow();
172     if(row < model->rowCount()-1) {
173         QModelIndex nextIndex = model->index(row+1, 0);
174         view->setCurrentIndex(nextIndex);
175     }
176 }
177 
slotSelectPreviousRow()178 void OutlineTreePresenter::slotSelectPreviousRow()
179 {
180     int row = getCurrentRow();
181     if(row) {
182         QModelIndex previousIndex = model->index(row-1, 0);
183         view->setCurrentIndex(previousIndex);
184     }
185 }
186 
187 } // m8r namespace
188