]> git.sven.stormbind.net Git - sven/vym.git/blob - src/taskeditor.cpp
New upstream version 2.9.22
[sven/vym.git] / src / taskeditor.cpp
1 #include "taskeditor.h"
2
3 #include <QAbstractTableModel>
4 #include <QAction>
5 #include <QDebug>
6 #include <QHeaderView>
7 #include <QMenu>
8 #include <QSortFilterProxyModel>
9 #include <QTextEdit>
10 #include <QToolBar>
11 #include <QVBoxLayout>
12
13 #include "branchitem.h"
14 #include "mainwindow.h"
15 #include "task.h"
16 #include "taskmodel.h"
17 #include "vymmodel.h"
18
19 extern Main *mainWindow;
20 extern Settings settings;
21 extern QMenu *taskContextMenu;
22 extern TaskModel *taskModel;
23
24 extern QString editorFocusStyle;
25
26 TaskEditor::TaskEditor(QWidget *)
27 {
28     // Creat Table view
29     view = new QTableView;
30
31     setMinimumWidth(350);
32
33     QVBoxLayout *mainLayout = new QVBoxLayout;
34
35     QToolBar *tb = new QToolBar("TaskEditor filters");
36     tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
37     mainLayout->addWidget(tb);
38
39     // Original icon from KDE:
40     // /usr/share/icons/oxygen/16x16/actions/view-filter.png
41
42     QIcon icon = QIcon(":/taskfilter-currentmap.png");
43     QAction *a =
44         new QAction(icon, "", this); // tr( "Current map","TaskEditor" ),this );
45     a->setToolTip(
46         tr("Show only tasks from current map", "Filters in task Editor"));
47     a->setCheckable(true);
48     a->setChecked(settings.value("/taskeditor/filterMap", true).toBool());
49     tb->addAction(a);
50     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterMap()));
51     actionToggleFilterMap = a;
52
53     icon = QIcon(":/taskfilter-activetask.png");
54     a = new QAction(icon, "", this); // tr( "Active tasks","TaskEditor" ),this
55                                      // );
56     a->setToolTip(tr("Show only active tasks", "Filters in task Editor"));
57     a->setCheckable(true);
58     a->setChecked(settings.value("/taskeditor/filterActive", true).toBool());
59     tb->addAction(a);
60     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterActive()));
61     actionToggleFilterActive = a;
62
63     icon = QIcon(":/taskfilter-newtask.png");
64     a = new QAction(icon, "", this); // tr( "New tasks","TaskEditor" ),this );
65     a->setToolTip(tr("Show only new tasks", "Filters in task Editor"));
66     a->setCheckable(true);
67     a->setChecked(settings.value("/taskeditor/filterNew", false).toBool());
68     tb->addAction(a);
69     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterNew()));
70     actionToggleFilterNew = a;
71
72     icon = QIcon(":/flag-stopsign.svg");
73     a = new QAction(icon, "", this); // tr( "New tasks","TaskEditor" ),this );
74     a->setToolTip(tr("Show only blocker tasks", "Filters in task Editor"));
75     a->setCheckable(true);
76     a->setChecked(settings.value("/taskeditor/filterBlocker", false).toBool());
77     tb->addAction(a);
78     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterBlocker()));
79     actionToggleFilterBlocker = a;
80
81     icon = QIcon(":/flag-arrow-up.svg");
82     a = new QAction(icon, "", this);
83     a->setToolTip(tr("Show only tasks marked with this arrow-up flag",
84                      "Filters in task Editor"));
85     a->setCheckable(true);
86     a->setChecked(settings.value("/taskeditor/filterFlags1", false).toBool());
87     tb->addAction(a);
88     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterFlags1()));
89     actionToggleFilterFlags1 = a;
90
91     icon = QIcon(":/flag-arrow-2up.svg");
92     a = new QAction(icon, "", this);
93     a->setToolTip(tr("Show only tasks marked with this arrow-up flag",
94                      "Filters in task Editor"));
95     a->setCheckable(true);
96     a->setChecked(settings.value("/taskeditor/filterFlags2", false).toBool());
97     tb->addAction(a);
98     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterFlags2()));
99     actionToggleFilterFlags2 = a;
100
101     icon = QIcon(":/flag-no-arrow-up.png");
102     a = new QAction(icon, "", this);
103     a->setToolTip(tr("Show only tasks marked without any arrow-up flag",
104                      "Filters in task Editor"));
105     a->setCheckable(true);
106     a->setChecked(settings.value("/taskeditor/filterFlags3", false).toBool());
107     tb->addAction(a);
108     connect(a, SIGNAL(triggered()), this, SLOT(toggleFilterFlags3()));
109     actionToggleFilterFlags3 = a;
110
111     // Clone actions defined in MainWindow
112     foreach (QAction *qa, mainWindow->taskEditorActions) {
113         a = new QAction(this);
114         a->setShortcut(qa->shortcut());
115         a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
116         connect(a, SIGNAL(triggered()), qa, SLOT(trigger()));
117         addAction(a);
118     }
119
120     mainLayout->addWidget(view);
121     setLayout(mainLayout);
122
123     view->setSelectionBehavior(QAbstractItemView::SelectRows);
124     view->horizontalHeader()->setStretchLastSection(true);
125     view->verticalHeader()->hide();
126     // view->setEditTriggers(QAbstractItemView::NoEditTriggers);
127
128     filterActiveModel = new TaskFilterModel;
129     filterActiveModel->setSourceModel(taskModel);
130     filterActiveModel->setDynamicSortFilter(true);
131
132     view->setModel(filterActiveModel);
133     view->setSortingEnabled(true);
134     view->setIconSize(QSize(64, 64));
135
136     QHeaderView *hv = view->horizontalHeader();
137     hv->setSortIndicator(0, Qt::AscendingOrder);
138     view->sortByColumn(hv->sortIndicatorSection(), hv->sortIndicatorOrder());
139
140     view->setSelectionMode(QAbstractItemView::ExtendedSelection);
141     view->setDragEnabled(true);
142     view->setAcceptDrops(true);
143     view->setDropIndicatorShown(true);
144     view->setAutoScroll(false);
145
146     blockExternalSelect = false;
147
148     connect(view->selectionModel(),
149             SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this,
150             SLOT(selectionChanged(QItemSelection, QItemSelection)));
151
152     connect(view, SIGNAL(clicked(QModelIndex)),
153             this, SLOT(cellClicked(QModelIndex)));
154
155     // Enable wordwrap when data changes
156     if (settings.value("/taskeditor/wordWrap", true)
157             .toBool()) // FIXME-3 not working or only sometimes?
158     {
159         connect(taskModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), view,
160                 SLOT(resizeRowsToContents()));
161         connect(view->horizontalHeader(), SIGNAL(sectionResized(int, int, int)),
162                 view, SLOT(resizeRowsToContents()));
163     }
164
165     // Initialize view filters according to previous settings
166     setFilterMap();
167     setFilterActive();
168     setFilterNew();
169     setFilterBlocker();
170     setFilterFlags1();
171     setFilterFlags2();
172     setFilterFlags3();
173
174     // Initialize display of parents of a task
175     bool ok;
176     int i = settings.value("/taskeditor/showParentsLevel", 0).toInt(&ok);
177     if (ok)
178         taskModel->setShowParentsLevel(i);
179
180     view->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
181     connect(view->horizontalHeader(),
182             SIGNAL(customContextMenuRequested(QPoint)),
183             SLOT(headerContextMenu()));
184
185     view->setStyleSheet("QTableView:focus {" + editorFocusStyle + "}");
186
187     updateColumnLayout();
188 }
189
190 TaskEditor::~TaskEditor()
191 {
192     settings.setValue("/taskeditor/filterMap",
193                       actionToggleFilterMap->isChecked());
194     settings.setValue("/taskeditor/filterActive",
195                       actionToggleFilterActive->isChecked());
196     settings.setValue("/taskeditor/filterNew",
197                       actionToggleFilterNew->isChecked());
198     settings.setValue("/taskeditor/filterBlocker",
199                       actionToggleFilterBlocker->isChecked());
200     settings.setValue("/taskeditor/filterFlags1",
201                       actionToggleFilterFlags1->isChecked());
202     settings.setValue("/taskeditor/filterFlags2",
203                       actionToggleFilterFlags2->isChecked());
204     settings.setValue("/taskeditor/filterFlags3",
205                       actionToggleFilterFlags3->isChecked());
206     settings.setValue("/taskeditor/showParentsLevel",
207                       taskModel->getShowParentsLevel());
208
209     for (int i = 0; i <= 8; i++) {
210         settings.setValue(QString("/taskeditor/column/%1/width").arg(i),
211                           view->columnWidth(i));
212         settings.setValue(QString("/taskeditor/column/%1/hidden").arg(i),
213                           view->isColumnHidden(i));
214     }
215 }
216
217 void TaskEditor::setMapName(const QString &n)
218 {
219     currentMapName = n;
220
221     setFilterMap();
222 }
223
224 bool TaskEditor::isUsedFilterMap()
225 {
226     return actionToggleFilterMap->isChecked();
227 }
228
229 void TaskEditor::setFilterMap()
230 {
231     if (isUsedFilterMap())
232         filterActiveModel->setMapFilter(currentMapName);
233     else
234         filterActiveModel->setMapFilter(QString());
235     updateFilters();
236 }
237
238 bool TaskEditor::isUsedFilterActive()
239 {
240     return actionToggleFilterActive->isChecked();
241 }
242
243 void TaskEditor::setFilterActive()
244 {
245     filterActiveModel->setFilter(actionToggleFilterActive->isChecked());
246     updateFilters();
247 }
248
249 void TaskEditor::setFilterNew()
250 {
251     filterActiveModel->setFilterNew(actionToggleFilterNew->isChecked());
252     updateFilters();
253 }
254
255 void TaskEditor::setFilterBlocker()
256 {
257     filterActiveModel->setFilterBlocker(actionToggleFilterBlocker->isChecked());
258     updateFilters();
259 }
260
261 void TaskEditor::setFilterFlags1()
262 {
263     filterActiveModel->setFilterFlags1(actionToggleFilterFlags1->isChecked());
264     updateFilters();
265 }
266
267 void TaskEditor::setFilterFlags2()
268 {
269     filterActiveModel->setFilterFlags2(actionToggleFilterFlags2->isChecked());
270     updateFilters();
271 }
272
273 void TaskEditor::setFilterFlags3()
274 {
275     filterActiveModel->setFilterFlags3(actionToggleFilterFlags3->isChecked());
276     updateFilters();
277 }
278
279 void TaskEditor::updateFilters()
280 {
281     // ugly, but calling twice updates rows as expected  // FIXME-3 review...
282     filterActiveModel->invalidate();
283     filterActiveModel->invalidate();
284 }
285
286 bool TaskEditor::taskVisible(Task *task)
287 {
288     return filterActiveModel->taskVisible(task);
289 }
290
291 void TaskEditor::showSelection()
292 {
293     QModelIndexList list = view->selectionModel()->selectedIndexes();
294     if (list.count() > 0)
295         // Usually whole row is selected, so just go for first cell
296         view->scrollTo(taskModel->index(taskModel->getTask(list.first())),
297                        QAbstractItemView::EnsureVisible);
298 }
299
300 bool TaskEditor::select(Task *task)
301 {
302     if (task) {
303         blockExternalSelect = true;
304         QModelIndex i0b = taskModel->index(task);
305         QModelIndex i0e = taskModel->indexRowEnd(task);
306
307         QModelIndex i1b = filterActiveModel->mapFromSource(i0b);
308         QModelIndex i1e = filterActiveModel->mapFromSource(i0e);
309
310         QItemSelection sel(i1b, i1e);
311
312         view->selectionModel()->select(sel,
313                                        QItemSelectionModel::ClearAndSelect);
314         blockExternalSelect = false;
315         return true;
316     }
317     return false;
318 }
319
320 void TaskEditor::clearSelection() { view->selectionModel()->clearSelection(); }
321
322 void TaskEditor::cellClicked(QModelIndex ix_unmapped)
323 {
324     QModelIndex ix = filterActiveModel->mapToSource(ix_unmapped);
325     if (ix.isValid() && ix.column() == 2)
326         taskModel->getTask(ix)->getBranch()->getModel()->cycleTaskStatus();
327 }
328
329 void TaskEditor::headerContextMenu()
330 {
331     // qDebug() << "TE::headerContextMenu()";
332     // Trying to workaround https://bugreports.qt.io/browse/QTBUG-52307
333     // view->horizontalHeader()->setStretchLastSection(true);
334     // view->resizeColumnsToContents();
335     // updateGeometry();
336     // show();
337 }
338
339 void TaskEditor::updateColumnLayout()
340 {
341     // Update column widths and visibility
342
343     QString s = "/taskeditor/column/%1/";
344
345     // Priority
346     int i = 0;
347     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 80).toInt());
348     view->setColumnHidden(i,
349                           settings.value(s.arg(i) + "hidden", false).toBool());
350
351     // Delta
352     i = 1;
353     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 50).toInt());
354     view->setColumnHidden(i,
355                           settings.value(s.arg(i) + "hidden", false).toBool());
356
357     // Status
358     i = 2;
359     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 50).toInt());
360     view->setColumnHidden(i,
361                           settings.value(s.arg(i) + "hidden", false).toBool());
362
363     // Age total
364     i = 3;
365     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 50).toInt());
366     view->setColumnHidden(i,
367                           settings.value(s.arg(i) + "hidden", true).toBool());
368
369     // Age mod
370     i = 4;
371     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 50).toInt());
372     view->setColumnHidden(i,
373                           settings.value(s.arg(i) + "hidden", true).toBool());
374
375     // Sleep
376     i = 5;
377     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 80).toInt());
378     view->setColumnHidden(i,
379                           settings.value(s.arg(i) + "hidden", true).toBool());
380
381     // Map
382     i = 6;
383     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 100).toInt());
384     view->setColumnHidden(i,
385                           settings.value(s.arg(i) + "hidden", true).toBool());
386
387     // Flags
388     i = 7;
389     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 80).toInt());
390     view->setColumnHidden(i,
391                           settings.value(s.arg(i) + "hidden", false).toBool());
392
393     // Taskname
394     i = 8;
395     view->setColumnWidth(i, settings.value(s.arg(i) + "width", 80).toInt());
396     view->setColumnHidden(i,
397                           settings.value(s.arg(i) + "hidden", false).toBool());
398 }
399
400 void TaskEditor::selectionChanged(const QItemSelection &selected,
401                                   const QItemSelection &)
402 {
403     // FIXME-3 what, if multiple selection in MapEditor?
404
405     if (selected.indexes().isEmpty())
406         // Avoid segfault on quit, when selected is empty
407         return;
408
409     QItemSelection sel0 = filterActiveModel->mapSelectionToSource(selected);
410     QModelIndex ix = sel0.indexes().first();
411     Task *t = taskModel->getTask(ix);
412     if (t) {
413         BranchItem *bi = t->getBranch();
414         if (bi) {
415             VymModel *m = bi->getModel();
416             if (!blockExternalSelect)
417                 m->select(bi);
418             if (m != mainWindow->currentModel())
419                 mainWindow->gotoModel(m);
420             view->setStyleSheet(
421                 "QTableView {selection-background-color: " +
422                 m->getSelectionBrushColor().name() +
423                 "; selection-color:" + bi->getHeadingColor().name() + "}" +
424                 "QTableView:focus {" + editorFocusStyle + "}");
425             view->scrollTo(selected.indexes().first());
426         }
427     }
428 }
429
430 void TaskEditor::contextMenuEvent(QContextMenuEvent *e)
431 {
432     taskContextMenu->popup(e->globalPos());
433 }
434
435 void TaskEditor::toggleFilterMap() { setFilterMap(); }
436
437 void TaskEditor::toggleFilterActive() { setFilterActive(); }
438 void TaskEditor::toggleFilterNew() { setFilterNew(); }
439 void TaskEditor::toggleFilterBlocker() { setFilterBlocker(); }
440
441 void TaskEditor::toggleFilterFlags1() { setFilterFlags1(); }
442 void TaskEditor::toggleFilterFlags2() { setFilterFlags2(); }
443 void TaskEditor::toggleFilterFlags3() { setFilterFlags3(); }