1 /* This file is part of the KDE project
2  *  Copyright (C) 2019 Dag Andersen <danders@get2net.dk>
3  *  Copyright (C) 2006-2011, 2012 Dag Andersen <danders@get2net.dk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 // clazy:excludeall=qstring-arg
22 #include "kptscheduleeditor.h"
23 
24 #include "kptcommand.h"
25 #include "kptcalendar.h"
26 #include "kptduration.h"
27 #include "kptnode.h"
28 #include "kptproject.h"
29 #include "kpttask.h"
30 #include "kptschedule.h"
31 #include "kptdatetime.h"
32 #include "kptpertresult.h"
33 #include "kptitemviewsettup.h"
34 #include "kptrecalculatedialog.h"
35 #include "Help.h"
36 #include "kptdebug.h"
37 
38 #include <KoDocument.h>
39 #include <KoIcon.h>
40 
41 #include <QList>
42 #include <QVBoxLayout>
43 #include <QHeaderView>
44 #include <QApplication>
45 #include <QClipboard>
46 #include <QContextMenuEvent>
47 #include <QAction>
48 #include <QMenu>
49 
50 #include <KLocalizedString>
51 #include <kactioncollection.h>
52 
53 #include <ktoggleaction.h>
54 
55 
56 namespace KPlato
57 {
58 
ScheduleTreeView(QWidget * parent)59 ScheduleTreeView::ScheduleTreeView(QWidget *parent)
60     : TreeViewBase(parent)
61 {
62     header()->setStretchLastSection (false);
63 
64     ScheduleItemModel *m = new ScheduleItemModel(this);
65     setModel(m);
66     //setSelectionBehavior(QAbstractItemView::SelectItems);
67     setSelectionMode(QAbstractItemView::SingleSelection);
68     setSelectionBehavior(QAbstractItemView::SelectRows);
69     setTreePosition(-1); // always visual index 0
70 
71     createItemDelegates(m);
72 }
73 
selectionChanged(const QItemSelection & sel,const QItemSelection & desel)74 void ScheduleTreeView::selectionChanged(const QItemSelection &sel, const QItemSelection &desel)
75 {
76     //debugPlan<<sel.indexes().count();
77     foreach(const QModelIndex &i, selectionModel()->selectedIndexes()) {
78         Q_UNUSED(i);
79         //debugPlan<<i.row()<<","<<i.column();
80     }
81     QTreeView::selectionChanged(sel, desel);
82     emit selectionChanged(selectionModel()->selectedIndexes());
83 }
84 
currentChanged(const QModelIndex & current,const QModelIndex & previous)85 void ScheduleTreeView::currentChanged(const QModelIndex & current, const QModelIndex & previous)
86 {
87     //debugPlan<<current.row()<<","<<current.column();
88     QTreeView::currentChanged(current, previous);
89     emit currentChanged(current);
90     // possible bug in qt: in QAbstractItemView::SingleSelection you can select multiple items/rows
91     selectionModel()->select(current, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
92 }
93 
manager(const QModelIndex & idx) const94 ScheduleManager *ScheduleTreeView::manager(const QModelIndex &idx) const
95 {
96     return model()->manager(idx);
97 }
98 
currentManager() const99 ScheduleManager *ScheduleTreeView::currentManager() const
100 {
101     return model()->manager(currentIndex());
102 }
103 
selectedRows() const104 QModelIndexList ScheduleTreeView::selectedRows() const
105 {
106     QModelIndexList lst = selectionModel()->selectedRows();
107     debugPlan<<lst;
108     return lst;
109 }
110 
selectedManager() const111 ScheduleManager *ScheduleTreeView::selectedManager() const
112 {
113     ScheduleManager *sm = 0;
114     QModelIndexList lst = selectedRows();
115     if (lst.count() == 1) {
116         sm = model()->manager(lst.first());
117     }
118     return sm;
119 }
120 
121 //-----------------------------------
ScheduleEditor(KoPart * part,KoDocument * doc,QWidget * parent)122 ScheduleEditor::ScheduleEditor(KoPart *part, KoDocument *doc, QWidget *parent)
123     : ViewBase(part, doc, parent)
124 {
125     if (doc && doc->isReadWrite()) {
126         setXMLFile("ScheduleEditorUi.rc");
127     } else {
128         setXMLFile("ScheduleEditorUi_readonly.rc");
129     }
130 
131     setupGui();
132     slotEnableActions();
133 
134     QVBoxLayout * l = new QVBoxLayout(this);
135     l->setMargin(0);
136     m_schedulingRange = new SchedulingRange(doc, this);
137     l->addWidget(m_schedulingRange);
138     m_view = new ScheduleTreeView(this);
139     connect(this, &ViewBase::expandAll, m_view, &TreeViewBase::slotExpand);
140     connect(this, &ViewBase::collapseAll, m_view, &TreeViewBase::slotCollapse);
141 
142     l->addWidget(m_view);
143     m_view->setEditTriggers(m_view->editTriggers() | QAbstractItemView::EditKeyPressed);
144 
145     QList<int> show;
146     show << ScheduleModel::ScheduleName
147         << ScheduleModel::ScheduleState
148         << ScheduleModel::ScheduleDirection
149         << ScheduleModel::ScheduleOverbooking
150         << ScheduleModel::ScheduleDistribution
151         << ScheduleModel::SchedulePlannedStart
152         << ScheduleModel::SchedulePlannedFinish
153         << ScheduleModel::ScheduleScheduler
154         << ScheduleModel::ScheduleGranularity
155         << ScheduleModel::ScheduleMode
156         ;
157 
158     QList<int> lst;
159     for (int c = 0; c < model()->columnCount(); ++c) {
160         if (! show.contains(c)) {
161             lst << c;
162         }
163     }
164     m_view->setColumnsHidden(lst);
165     m_view->setDefaultColumns(show);
166 
167 
168     connect(model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand);
169 
170     connect(m_view, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentChanged(QModelIndex)));
171 
172     connect(m_view, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotSelectionChanged(QModelIndexList)));
173 
174     connect(model(), &QAbstractItemModel::dataChanged, this, &ScheduleEditor::updateActionsEnabled);
175 
176     connect(m_view, &TreeViewBase::contextMenuRequested, this, &ScheduleEditor::slotContextMenuRequested);
177 
178     connect(m_view, &TreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested);
179 
180     Help::add(this,
181         xi18nc("@info:whatsthis",
182                "<title>Schedule Editor</title>"
183                "<para>"
184                "The Schedule Editor is used to create, edit, calculate and delete schedules."
185                "</para><para>"
186                "If <emphasis>Mode</emphasis> is set to <emphasis>Auto</emphasis>, the schedule is calculated automatically."
187                " <note>Only one schedule can be in <emphasis>Auto Mode</emphasis> simultaneously"
188                " and it must be a top level schedule without sub-schedules.</note>"
189                "</para><para>"
190                "A schedule can have sub-schedules. A sub-schedule can use the projects progress data"
191                " in order to reschedule only tasks that are not yet finished."
192                " Rescheduling will then use e.g. actual start and remaining effort for the tasks."
193                "<nl/><link url='%1'>More...</link>"
194                "</para>", Help::page("Schedules_Editor")));
195 }
196 
draw(Project & project)197 void ScheduleEditor::draw(Project &project)
198 {
199     m_view->setProject(&project);
200     m_schedulingRange->setProject(&project);
201 }
202 
draw()203 void ScheduleEditor::draw()
204 {
205 }
206 
setGuiActive(bool activate)207 void ScheduleEditor::setGuiActive(bool activate)
208 {
209     //debugPlan<<activate;
210     ViewBase::setGuiActive(activate);
211     if (activate && !m_view->currentIndex().isValid()) {
212         m_view->selectionModel()->setCurrentIndex(m_view->model()->index(0, 0), QItemSelectionModel::NoUpdate);
213     }
214 }
215 
slotContextMenuRequested(const QModelIndex & index,const QPoint & pos)216 void ScheduleEditor::slotContextMenuRequested(const QModelIndex &index, const QPoint& pos)
217 {
218     debugPlan<<index.row()<<","<<index.column()<<":"<<pos;
219     QString name;
220     m_view->setContextMenuIndex(index);
221     if (name.isEmpty()) {
222         slotHeaderContextMenuRequested(pos);
223         m_view->setContextMenuIndex(QModelIndex());
224         return;
225     }
226     debugPlan<<name;
227     emit requestPopupMenu(name, pos);
228     m_view->setContextMenuIndex(QModelIndex());
229 }
230 
slotCurrentChanged(const QModelIndex &)231 void ScheduleEditor::slotCurrentChanged(const QModelIndex &)
232 {
233     //debugPlan<<curr.row()<<","<<curr.column();
234 }
235 
slotSelectionChanged(const QModelIndexList &)236 void ScheduleEditor::slotSelectionChanged(const QModelIndexList &/*list*/)
237 {
238     //debugPlan<<list.count();
239     // Note: Don't use list as it includes all columns in a row
240     QModelIndexList lst = m_view->selectedRows(); // gets column 0 in each row (should be 1 or 0 rows)
241     if (lst.count() == 1) {
242         ScheduleManager *sm = m_view->model()->manager(lst.first());
243         emit scheduleSelectionChanged(sm);
244     } else {
245         emit scheduleSelectionChanged(0);
246     }
247     slotEnableActions();
248 
249 }
250 
updateActionsEnabled(const QModelIndex & index)251 void ScheduleEditor::updateActionsEnabled(const QModelIndex &index)
252 {
253     debugPlan<<index;
254     slotEnableActions();
255 }
256 
slotEnableActions()257 void ScheduleEditor::slotEnableActions()
258 {
259     if (! isReadWrite()) {
260         actionAddSchedule->setEnabled(false);
261         actionAddSubSchedule->setEnabled(false);
262         actionDeleteSelection->setEnabled(false);
263         actionCalculateSchedule->setEnabled(false);
264         actionBaselineSchedule->setEnabled(false);
265         actionMoveLeft->setEnabled(false);
266         return;
267     }
268     QModelIndexList lst = m_view->selectedRows();
269     if (lst.isEmpty()) {
270         actionAddSchedule->setEnabled(true);
271         actionAddSubSchedule->setEnabled(false);
272         actionDeleteSelection->setEnabled(false);
273         actionCalculateSchedule->setEnabled(false);
274         actionBaselineSchedule->setEnabled(false);
275         actionMoveLeft->setEnabled(false);
276         return;
277     }
278     if (lst.count() > 1) {
279         actionAddSchedule->setEnabled(false);
280         actionAddSubSchedule->setEnabled(false);
281         actionDeleteSelection->setEnabled(false);
282         actionCalculateSchedule->setEnabled(false);
283         actionBaselineSchedule->setEnabled(false);
284         actionMoveLeft->setEnabled(false);
285         return;
286     }
287     // one and only one manager selected
288     ScheduleManager *sm = m_view->manager(lst.first());
289     Q_ASSERT(sm);
290     actionAddSchedule->setEnabled(true);
291     actionAddSubSchedule->setEnabled(sm->isScheduled());
292     actionDeleteSelection->setEnabled(! (sm->isBaselined() || sm->isChildBaselined()));
293     actionCalculateSchedule->setEnabled(! sm->scheduling() && sm->childCount() == 0 && ! (sm->isBaselined() || sm->isChildBaselined()));
294 
295     const char *const actionBaselineScheduleIconName =
296         sm->isBaselined() ? koIconNameCStr("view-time-schedule-baselined-remove") : koIconNameCStr("view-time-schedule-baselined-add");
297     actionBaselineSchedule->setIcon(QIcon::fromTheme(QLatin1String(actionBaselineScheduleIconName)));
298 
299     // enable if scheduled and no one else is baselined
300     bool en = sm->isScheduled() && (sm->isBaselined() || ! m_view->project()->isBaselined());
301     actionBaselineSchedule->setEnabled(en);
302 
303     actionMoveLeft->setEnabled(sm->parentManager());
304 }
305 
setupGui()306 void ScheduleEditor::setupGui()
307 {
308     actionAddSchedule  = new QAction(koIcon("view-time-schedule-insert"), i18n("Add Schedule"), this);
309     actionCollection()->setDefaultShortcut(actionAddSchedule, Qt::CTRL + Qt::Key_I);
310     actionCollection()->addAction("add_schedule", actionAddSchedule);
311     connect(actionAddSchedule, &QAction::triggered, this, &ScheduleEditor::slotAddSchedule);
312 
313     actionAddSubSchedule  = new QAction(koIcon("view-time-schedule-child-insert"), i18n("Add Sub-schedule"), this);
314     actionCollection()->setDefaultShortcut(actionAddSubSchedule, Qt::CTRL + Qt::SHIFT + Qt::Key_I);
315     actionCollection()->addAction("add_subschedule", actionAddSubSchedule);
316     connect(actionAddSubSchedule, &QAction::triggered, this, &ScheduleEditor::slotAddSubSchedule);
317 
318     actionDeleteSelection  = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this);
319     actionCollection()->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete);
320     actionCollection()->addAction("delete_selection", actionDeleteSelection);
321     connect(actionDeleteSelection, &QAction::triggered, this, &ScheduleEditor::slotDeleteSelection);
322 
323     actionCalculateSchedule  = new QAction(koIcon("view-time-schedule-calculus"), i18n("Calculate"), this);
324 //    actionCollection()->setDefaultShortcut(actionCalculateSchedule, Qt::CTRL + Qt::Key_C);
325     actionCollection()->addAction("calculate_schedule", actionCalculateSchedule);
326     connect(actionCalculateSchedule, &QAction::triggered, this, &ScheduleEditor::slotCalculateSchedule);
327 
328     actionBaselineSchedule  = new QAction(koIcon("view-time-schedule-baselined-add"), i18n("Baseline"), this);
329 //    actionCollection()->setDefaultShortcut(actionBaselineSchedule, Qt::CTRL + Qt::Key_B);
330     actionCollection()->addAction("schedule_baseline", actionBaselineSchedule);
331     connect(actionBaselineSchedule, &QAction::triggered, this, &ScheduleEditor::slotBaselineSchedule);
332 
333     actionMoveLeft  = new QAction(koIcon("go-first"), xi18nc("@action", "Detach"), this);
334     actionCollection()->addAction("schedule_move_left", actionMoveLeft);
335     connect(actionMoveLeft, &QAction::triggered, this, &ScheduleEditor::slotMoveLeft);
336 
337     // Add the context menu actions for the view options
338     createOptionActions(ViewBase::OptionExpand | ViewBase::OptionCollapse | ViewBase::OptionViewConfig);
339 }
340 
updateReadWrite(bool readwrite)341 void ScheduleEditor::updateReadWrite(bool readwrite)
342 {
343     debugPlan<<readwrite;
344     ViewBase::updateReadWrite(readwrite);
345     m_view->setReadWrite(readwrite);
346     m_schedulingRange->setReadWrite(readwrite);
347     slotEnableActions();
348 }
349 
slotOptions()350 void ScheduleEditor::slotOptions()
351 {
352     debugPlan;
353     ItemViewSettupDialog *dlg = new ItemViewSettupDialog(this, m_view, true, this);
354     connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int)));
355     dlg->open();
356 }
357 
slotCalculateSchedule()358 void ScheduleEditor::slotCalculateSchedule()
359 {
360     //debugPlan;
361     ScheduleManager *sm = m_view->selectedManager();
362     if (sm == 0) {
363         return;
364     }
365     if (sm->parentManager() || (sm->isScheduled() && project()->isStarted())) {
366         RecalculateDialog dlg;
367         if (dlg.exec() == QDialog::Rejected) {
368             return;
369         }
370         sm->setRecalculate(true);
371         sm->setRecalculateFrom(DateTime(dlg.dateTime()));
372     }
373     emit calculateSchedule(m_view->project(), sm);
374 }
375 
slotAddSchedule()376 void ScheduleEditor::slotAddSchedule()
377 {
378     //debugPlan;
379     int idx = -1;
380     ScheduleManager *sm = m_view->selectedManager();
381     if (sm) {
382         idx = sm->parentManager() ? sm->parentManager()->indexOf(sm) : m_view->project()->indexOf(sm);
383         if (idx >= 0) {
384             ++idx;
385         }
386     }
387     if (sm && sm->parentManager()) {
388         sm = sm->parentManager();
389         ScheduleManager *m = m_view->project()->createScheduleManager(sm->name() + QString(".%1").arg(sm->children().count() + 1));
390         part()->addCommand(new AddScheduleManagerCmd(sm, m, idx, kundo2_i18n("Create sub-schedule")));
391         QModelIndex idx = model()->index(m);
392         if (idx.isValid()) {
393             m_view->setFocus();
394             m_view->scrollTo(idx);
395             m_view->selectionModel()->select(idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
396             m_view->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate);
397         }
398     } else {
399         Project *p = m_view->project();
400         ScheduleManager *m = p->createScheduleManager();
401         AddScheduleManagerCmd *cmd =  new AddScheduleManagerCmd(*p, m, idx, kundo2_i18n("Add schedule %1", m->name()));
402         part() ->addCommand(cmd);
403         QModelIndex idx = model()->index(m);
404         if (idx.isValid()) {
405             m_view->setFocus();
406             m_view->scrollTo(idx);
407             m_view->selectionModel()->select(idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
408             m_view->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate);
409         }
410     }
411 }
412 
slotAddSubSchedule()413 void ScheduleEditor::slotAddSubSchedule()
414 {
415     //debugPlan;
416     ScheduleManager *sm = m_view->selectedManager();
417     if (sm) {
418         int row = sm->parentManager() ? sm->parentManager()->indexOf(sm) : m_view->project()->indexOf(sm);
419         if (row >= 0) {
420             ++row;
421         }
422         ScheduleManager *m = m_view->project()->createScheduleManager(sm->name() + QString(".%1").arg(sm->children().count() + 1));
423         part()->addCommand(new AddScheduleManagerCmd(sm, m, row, kundo2_i18n("Create sub-schedule")));
424         m_view->expand(model()->index(sm));
425         QModelIndex idx = model()->index(m);
426         if (idx.isValid()) {
427             m_view->selectionModel()->select(idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
428             m_view->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate);
429         }
430     } else {
431         slotAddSchedule();
432     }
433 }
434 
slotBaselineSchedule()435 void ScheduleEditor::slotBaselineSchedule()
436 {
437     //debugPlan;
438     ScheduleManager *sm = m_view->selectedManager();
439     if (sm) {
440         emit baselineSchedule(m_view->project(), sm);
441     }
442 }
443 
slotDeleteSelection()444 void ScheduleEditor::slotDeleteSelection()
445 {
446     //debugPlan;
447     ScheduleManager *sm = m_view->selectedManager();
448     if (sm) {
449         emit deleteScheduleManager(m_view->project(), sm);
450     }
451 }
452 
slotMoveLeft()453 void ScheduleEditor::slotMoveLeft()
454 {
455     ScheduleManager *sm = m_view->selectedManager();
456     if (sm) {
457         int index = -1;
458         for (ScheduleManager *m = sm; m != 0; m = m->parentManager()) {
459             if (m->parentManager() == 0) {
460                  index = m->project().indexOf(m) + 1;
461             }
462         }
463         debugPlan<<sm->name()<<index;
464         emit moveScheduleManager(sm, 0, index);
465     }
466 }
467 
loadContext(const KoXmlElement & context)468 bool ScheduleEditor::loadContext(const KoXmlElement &context)
469 {
470     debugPlan;
471     return m_view->loadContext(model()->columnMap(), context);
472 }
473 
saveContext(QDomElement & context) const474 void ScheduleEditor::saveContext(QDomElement &context) const
475 {
476     m_view->saveContext(model()->columnMap(), context);
477 }
478 
createPrintJob()479 KoPrintJob *ScheduleEditor::createPrintJob()
480 {
481     return m_view->createPrintJob(this);
482 }
483 
484 //-----------------------------------------
ScheduleLogTreeView(QWidget * parent)485 ScheduleLogTreeView::ScheduleLogTreeView(QWidget *parent)
486     : QTreeView(parent)
487 {
488     header()->setStretchLastSection (true);
489     header()->setContextMenuPolicy(Qt::CustomContextMenu);
490 
491     m_model = new QSortFilterProxyModel(this);
492     m_model->setFilterRole(Qt::UserRole+1);
493     m_model->setFilterKeyColumn (2); // severity
494     m_model->setFilterWildcard("[^0]"); // Filter out Debug
495 
496     m_model->setSourceModel(new ScheduleLogItemModel(this));
497     setModel(m_model);
498 
499     setRootIsDecorated(false);
500     setSelectionMode(QAbstractItemView::ExtendedSelection);
501     setSelectionBehavior(QAbstractItemView::SelectRows);
502     setAlternatingRowColors(true);
503 
504     connect(header(), &QWidget::customContextMenuRequested, this, &ScheduleLogTreeView::headerContextMenuRequested);
505 
506     actionShowDebug = new KToggleAction(xi18nc("@action", "Show Debug Information"), this);
507     connect(actionShowDebug, &QAction::toggled, this, &ScheduleLogTreeView::slotShowDebug);
508 }
509 
setFilterWildcard(const QString & filter)510 void ScheduleLogTreeView::setFilterWildcard(const QString &filter)
511 {
512     m_model->setFilterWildcard(filter);
513 }
514 
filterRegExp() const515 QRegExp ScheduleLogTreeView::filterRegExp() const
516 {
517     return m_model->filterRegExp();
518 }
519 
slotShowDebug(bool on)520 void ScheduleLogTreeView::slotShowDebug(bool on)
521 {
522     on ? setFilterWildcard(QString()) : setFilterWildcard("[^0]");
523 }
524 
contextMenuEvent(QContextMenuEvent * e)525 void ScheduleLogTreeView::contextMenuEvent (QContextMenuEvent *e)
526 {
527     debugPlan<<indexAt(e->pos())<<" at"<<e->pos();
528     emit contextMenuRequested(indexAt(e->pos()), e->globalPos());
529 }
530 
headerContextMenuRequested(const QPoint & pos)531 void ScheduleLogTreeView::headerContextMenuRequested(const QPoint &pos)
532 {
533     //debugPlan<<header()->logicalIndexAt(pos)<<" at"<<pos;
534     QMenu *m = new QMenu(this);
535     m->addAction(actionShowDebug);
536     m->exec(mapToGlobal(pos));
537     delete m;
538 }
539 
selectionChanged(const QItemSelection & sel,const QItemSelection & desel)540 void ScheduleLogTreeView::selectionChanged(const QItemSelection &sel, const QItemSelection &desel)
541 {
542     //debugPlan<<sel.indexes().count();
543     foreach(const QModelIndex &i, selectionModel()->selectedIndexes()) {
544         Q_UNUSED(i);
545         //debugPlan<<i.row()<<","<<i.column();
546     }
547     QTreeView::selectionChanged(sel, desel);
548     emit selectionChanged(selectionModel()->selectedIndexes());
549 }
550 
currentChanged(const QModelIndex & current,const QModelIndex & previous)551 void ScheduleLogTreeView::currentChanged(const QModelIndex & current, const QModelIndex & previous)
552 {
553     //debugPlan<<current.row()<<","<<current.column();
554     QTreeView::currentChanged(current, previous);
555     emit currentChanged(current);
556 //    selectionModel()->select(current, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
557 }
558 
slotEditCopy()559 void ScheduleLogTreeView::slotEditCopy()
560 {
561     QStringList lst;
562 //    int row = 0;
563     QString s;
564     QHeaderView *h = header();
565     foreach(const QModelIndex &i, selectionModel()->selectedRows()) {
566         QString s;
567         for (int section = 0; section < h->count(); ++section) {
568             QModelIndex idx = model()->index(i.row(), h->logicalIndex(section));
569             if (! idx.isValid() || isColumnHidden(idx.column())) {
570                 continue;
571             }
572             if (! s.isEmpty()) {
573                 s += ' ';
574             }
575             s = QString("%1%2").arg(s).arg(idx.data().toString(), -10);
576         }
577         if (! s.isEmpty()) {
578             lst << s;
579         }
580     }
581     if (! lst.isEmpty()) {
582         QApplication::clipboard()->setText(lst.join("\n"));
583     }
584 }
585 
586 //-----------------------------------
ScheduleLogView(KoPart * part,KoDocument * doc,QWidget * parent)587 ScheduleLogView::ScheduleLogView(KoPart *part, KoDocument *doc, QWidget *parent)
588     : ViewBase(part, doc, parent)
589 {
590     setupGui();
591     slotEnableActions(0);
592 
593     QVBoxLayout * l = new QVBoxLayout(this);
594     m_view = new ScheduleLogTreeView(this);
595     l->addWidget(m_view);
596 //    m_view->setEditTriggers(m_view->editTriggers() | QAbstractItemView::EditKeyPressed);
597 
598 
599     connect(m_view, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentChanged(QModelIndex)));
600 
601     connect(m_view, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotSelectionChanged(QModelIndexList)));
602 
603     connect(baseModel(), &QAbstractItemModel::dataChanged, this, &ScheduleLogView::updateActionsEnabled);
604 
605     connect(m_view, &ScheduleLogTreeView::contextMenuRequested, this, &ScheduleLogView::slotContextMenuRequested);
606 
607 }
608 
setProject(Project * project)609 void ScheduleLogView::setProject(Project *project)
610 {
611     m_view->setProject(project);
612 }
613 
draw(Project & project)614 void ScheduleLogView::draw(Project &project)
615 {
616     setProject(&project);
617 }
618 
setGuiActive(bool activate)619 void ScheduleLogView::setGuiActive(bool activate)
620 {
621     //debugPlan<<activate;
622     ViewBase::setGuiActive(activate);
623 /*    if (activate && !m_view->currentIndex().isValid()) {
624         m_view->selectionModel()->setCurrentIndex(m_view->model()->index(0, 0), QItemSelectionModel::NoUpdate);
625     }*/
626 }
627 
slotEdit()628 void ScheduleLogView::slotEdit()
629 {
630     QString id = sender()->property("p_identity").toString();
631     if (id.isEmpty()) {
632         emit editNode(project());
633         return;
634     }
635     Node *n = project()->findNode(id);
636     if (n) {
637         emit editNode(n);
638         return;
639     }
640     Resource *r = project()->findResource(id);
641     if (r) {
642         emit editResource(r);
643         return;
644     }
645     warnPlan<<"No object";
646 }
647 
slotContextMenuRequested(const QModelIndex & index,const QPoint & pos)648 void ScheduleLogView::slotContextMenuRequested(const QModelIndex &index, const QPoint& pos)
649 {
650     if (! isReadWrite() || ! index.isValid()) {
651         return;
652     }
653     QMenu *m = new QMenu(this);
654     QString id = index.data(ScheduleLogItemModel::IdentityRole).toString();
655     if (id.isEmpty()) {
656         return;
657     }
658     QAction *a = new QAction(koIcon("document-edit"), i18n("Edit..."), m);
659     a->setProperty("p_identity", id);
660     m->addAction(a);
661     connect(a, &QAction::triggered, this, &ScheduleLogView::slotEdit);
662     m->addSeparator();
663     m->exec(pos);
664     delete m;
665 }
666 
slotScheduleSelectionChanged(ScheduleManager * sm)667 void ScheduleLogView::slotScheduleSelectionChanged(ScheduleManager *sm)
668 {
669     baseModel()->setManager(sm);
670 }
671 
slotCurrentChanged(const QModelIndex &)672 void ScheduleLogView::slotCurrentChanged(const QModelIndex &)
673 {
674     //debugPlan<<curr.row()<<","<<curr.column();
675 }
676 
slotSelectionChanged(const QModelIndexList & list)677 void ScheduleLogView::slotSelectionChanged(const QModelIndexList &list)
678 {
679     debugPlan<<list.count();
680 }
681 
updateActionsEnabled(const QModelIndex & index)682 void ScheduleLogView::updateActionsEnabled(const QModelIndex &index)
683 {
684     debugPlan<<index;
685 }
686 
slotEnableActions(const ScheduleManager *)687 void ScheduleLogView::slotEnableActions(const ScheduleManager *)
688 {
689 }
690 
setupGui()691 void ScheduleLogView::setupGui()
692 {
693     // Add the context menu actions for the view options
694     createOptionActions(0);
695 }
696 
updateReadWrite(bool readwrite)697 void ScheduleLogView::updateReadWrite(bool readwrite)
698 {
699     debugPlan<<readwrite;
700     ViewBase::updateReadWrite(readwrite);
701 //    m_view->setReadWrite(readwrite);
702     //slotEnableActions(m_view->currentManager());
703 }
704 
slotOptions()705 void ScheduleLogView::slotOptions()
706 {
707     debugPlan;
708 }
709 
slotEditCopy()710 void ScheduleLogView::slotEditCopy()
711 {
712     m_view->slotEditCopy();
713 }
714 
loadContext(const KoXmlElement &)715 bool ScheduleLogView::loadContext(const KoXmlElement &/*context */)
716 {
717     debugPlan;
718     return true;//m_view->loadContext(model()->columnMap(), context);
719 }
720 
saveContext(QDomElement &) const721 void ScheduleLogView::saveContext(QDomElement &/*context */) const
722 {
723     //m_view->saveContext(model()->columnMap(), context);
724 }
725 
726 
727 //---------------------------
728 
ScheduleHandlerView(KoPart * part,KoDocument * doc,QWidget * parent)729 ScheduleHandlerView::ScheduleHandlerView(KoPart *part, KoDocument *doc, QWidget *parent)
730     : SplitterView(part, doc, parent)
731 {
732     debugPlan<<"---------------- Create ScheduleHandlerView ------------------";
733     m_scheduleEditor = new ScheduleEditor(part, doc, this);
734     m_scheduleEditor->setObjectName("ScheduleEditor");
735     addView(m_scheduleEditor);
736     insertChildClient(m_scheduleEditor);
737 
738     QTabWidget *tab = addTabWidget();
739 
740     PertResult *p = new PertResult(part, doc, tab);
741     p->setObjectName("PertResult");
742     addView(p, tab, i18n("Result"));
743     connect(m_scheduleEditor, &ScheduleEditor::scheduleSelectionChanged, p, &PertResult::slotScheduleSelectionChanged);
744 
745     PertCpmView *c = new PertCpmView(part, doc, tab);
746     c->setObjectName("PertCpmView");
747     addView(c, tab, i18n("Critical Path"));
748     connect(m_scheduleEditor, &ScheduleEditor::scheduleSelectionChanged, c, &PertCpmView::slotScheduleSelectionChanged);
749 
750     ScheduleLogView *v = new ScheduleLogView(part, doc, tab);
751     v->setObjectName("ScheduleLogView");
752     addView(v, tab, i18n("Scheduling Log"));
753     connect(m_scheduleEditor, SIGNAL(scheduleSelectionChanged(KPlato::ScheduleManager*)), v, SLOT(slotScheduleSelectionChanged(KPlato::ScheduleManager*)));
754     connect(v, &ScheduleLogView::editNode, this, &ScheduleHandlerView::editNode);
755     connect(v, &ScheduleLogView::editResource, this, &ScheduleHandlerView::editResource);
756 }
757 
currentTabChanged(int)758 void ScheduleHandlerView::currentTabChanged(int)
759 {
760 }
761 
hitView(const QPoint &)762 ViewBase *ScheduleHandlerView::hitView(const QPoint &/*glpos */)
763 {
764     //debugPlan<<this<<glpos<<"->"<<mapFromGlobal(glpos)<<"in"<<frameGeometry();
765     return this;
766 }
767 
768 
setGuiActive(bool active)769 void ScheduleHandlerView::setGuiActive(bool active) // virtual slot
770 {
771     foreach (ViewBase *v, findChildren<ViewBase*>()) {
772         v->setGuiActive(active);
773     }
774     m_activeview = active ? this : 0;
775     emit guiActivated(this, active);
776 }
777 
slotGuiActivated(ViewBase *,bool)778 void ScheduleHandlerView::slotGuiActivated(ViewBase *, bool)
779 {
780 }
781 
SchedulingRange(KoDocument * doc,QWidget * parent)782 SchedulingRange::SchedulingRange(KoDocument *doc, QWidget *parent)
783     : QWidget(parent)
784     , m_doc(doc)
785     , m_project(0)
786 {
787     setupUi(this);
788 
789     connect(targetStartTime, &QAbstractSpinBox::editingFinished, this, &SchedulingRange::slotStartChanged);
790     connect(targetEndTime, &QAbstractSpinBox::editingFinished, this, &SchedulingRange::slotEndChanged);
791 }
792 
setReadWrite(bool rw)793 void SchedulingRange::setReadWrite(bool rw)
794 {
795     targetStartTime->setEnabled(rw);
796     targetEndTime->setEnabled(rw);
797 }
798 
setProject(Project * project)799 void SchedulingRange::setProject(Project *project)
800 {
801     if (m_project) {
802         disconnect(m_project, &Project::nodeChanged, this, &SchedulingRange::slotProjectChanged);
803     }
804     m_project = project;
805     if (m_project) {
806         connect(m_project, &Project::nodeChanged, this, &SchedulingRange::slotProjectChanged);
807         slotProjectChanged(m_project);
808     }
809 }
810 
slotProjectChanged(Node * node)811 void SchedulingRange::slotProjectChanged(Node *node)
812 {
813     if (node != m_project) {
814         return;
815     }
816     if (targetStartTime->dateTime() != m_project->constraintStartTime()) {
817         targetStartTime->setDateTime(m_project->constraintStartTime());
818     }
819     if (targetEndTime->dateTime() != m_project->constraintEndTime()) {
820         targetEndTime->setDateTime(m_project->constraintEndTime());
821     }
822 }
823 
slotStartChanged()824 void SchedulingRange::slotStartChanged()
825 {
826     if (!m_project || !m_doc) {
827         return;
828     }
829     if (targetStartTime->dateTime() == m_project->constraintStartTime()) {
830         return;
831     }
832     ProjectModifyStartTimeCmd *cmd = new ProjectModifyStartTimeCmd(*m_project, targetStartTime->dateTime(), kundo2_i18n("Modify project target start time"));
833     m_doc->addCommand(cmd);
834 }
835 
slotEndChanged()836 void SchedulingRange::slotEndChanged()
837 {
838     if (!m_project || !m_doc) {
839         return;
840     }
841     if (targetEndTime->dateTime() == m_project->constraintEndTime()) {
842         return;
843     }
844     ProjectModifyEndTimeCmd *cmd = new ProjectModifyEndTimeCmd(*m_project, targetEndTime->dateTime(), kundo2_i18n("Modify project target end time"));
845     m_doc->addCommand(cmd);
846 }
847 
848 } // namespace KPlato
849