File indexing completed on 2024-05-12 16:39:26

0001 /* This file is part of the KDE project
0002   Copyright (C) 2007 - 2009, 2012 Dag Andersen <danders@get2net.dk>
0003 
0004   This library is free software; you can redistribute it and/or
0005   modify it under the terms of the GNU Library General Public
0006   License as published by the Free Software Foundation; either
0007   version 2 of the License, or (at your option) any later version.
0008 
0009   This library is distributed in the hope that it will be useful,
0010   but WITHOUT ANY WARRANTY; without even the implied warranty of
0011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012   Library General Public License for more details.
0013 
0014   You should have received a copy of the GNU Library General Public License
0015   along with this library; see the file COPYING.LIB.  If not, write to
0016   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017 * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 // clazy:excludeall=qstring-arg
0021 #include "taskworkpackageview.h"
0022 #include "taskworkpackagemodel.h"
0023 #include "workpackage.h"
0024 
0025 #include "part.h"
0026 #include "kptglobal.h"
0027 #include "kptcommand.h"
0028 #include "kptproject.h"
0029 #include "kptschedule.h"
0030 #include "kpteffortcostmap.h"
0031 #include "kptitemviewsettup.h"
0032 #include "calligraplanworksettings.h"
0033 
0034 #include <KGanttGraphicsView>
0035 #include <KGanttTreeViewRowController>
0036 #include <KGanttProxyModel>
0037 #include <KGanttDateTimeGrid>
0038 #include <KGanttStyleOptionGanttItem>
0039 
0040 #include <KoIcon.h>
0041 #include <KoXmlReader.h>
0042 
0043 #include <QDragMoveEvent>
0044 #include <QMenu>
0045 #include <QModelIndex>
0046 #include <QWidget>
0047 #include <QSortFilterProxyModel>
0048 #include <QHeaderView>
0049 #include <QPointer>
0050 #include <QAction>
0051 
0052 #include "debugarea.h"
0053 
0054 using namespace KPlato;
0055 
0056 namespace KPlatoWork
0057 {
0058 
0059 
0060 TaskWorkPackageTreeView::TaskWorkPackageTreeView(Part *part, QWidget *parent)
0061     : DoubleTreeViewBase(parent)
0062 {
0063     setContextMenuPolicy(Qt::CustomContextMenu);
0064     masterView()->header()->setSortIndicatorShown(true);
0065     masterView()->header()->setSectionsClickable(true);
0066     slaveView()->header()->setSortIndicatorShown(true);
0067     slaveView()->header()->setSectionsClickable(true);
0068 
0069     QSortFilterProxyModel *sf = new QSortFilterProxyModel(this);
0070     TaskWorkPackageModel *m = new TaskWorkPackageModel(part, sf);
0071     sf->setSourceModel(m);
0072     setModel(sf);
0073     //setSelectionBehavior(QAbstractItemView::SelectItems);
0074     setSelectionMode(QAbstractItemView::SingleSelection);
0075     setStretchLastSection(false);
0076 
0077     createItemDelegates(m);
0078 
0079     QList<int> lst1; lst1 << 1 << -1; // display column 0 (NodeName) in left view
0080     masterView()->setDefaultColumns(QList<int>() << TaskWorkPackageModel::NodeName);
0081     QList<int> show;
0082     show << TaskWorkPackageModel::NodeCompleted
0083             << TaskWorkPackageModel::NodeActualEffort
0084             << TaskWorkPackageModel::NodeRemainingEffort
0085             << TaskWorkPackageModel::NodePlannedEffort
0086             << TaskWorkPackageModel::NodeStartTime
0087             << TaskWorkPackageModel::NodeActualStart
0088             << TaskWorkPackageModel::NodeEndTime
0089             << TaskWorkPackageModel::NodeActualFinish
0090             << TaskWorkPackageModel::ProjectName
0091             << TaskWorkPackageModel::ProjectManager;
0092 
0093     QList<int> lst2;
0094     for (int i = 0; i < m->columnCount(); ++i) {
0095         if (! show.contains(i)) {
0096             lst2 << i;
0097         }
0098     }
0099     hideColumns(lst1, lst2);
0100     slaveView()->setDefaultColumns(show);
0101     setViewSplitMode(false);
0102     masterView()->setFocus();
0103 
0104     debugPlanWork<<PlanWorkSettings::self()->taskWorkPackageView();
0105 
0106     connect(masterView()->header(), &QHeaderView::sortIndicatorChanged, this, &TaskWorkPackageTreeView::setSortOrder);
0107     connect(slaveView()->header(), &QHeaderView::sortIndicatorChanged, this, &TaskWorkPackageTreeView::setSortOrder);
0108 
0109     connect(masterView()->header(), &QHeaderView::sectionMoved, this, &TaskWorkPackageTreeView::sectionsMoved);
0110     connect(slaveView()->header(), &QHeaderView::sectionMoved, this, &TaskWorkPackageTreeView::sectionsMoved);
0111 
0112     masterView()->header()->setSortIndicator(TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder);
0113     sf->sort(TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder);
0114 }
0115 
0116 void TaskWorkPackageTreeView::setSortOrder(int col, Qt::SortOrder order)
0117 {
0118     static_cast<QSortFilterProxyModel*>(model())->setSortRole(Qt::EditRole);
0119     model()->sort(col, order);
0120 }
0121 
0122 TaskWorkPackageModel *TaskWorkPackageTreeView::itemModel() const
0123 {
0124     return static_cast<TaskWorkPackageModel*>(static_cast<QSortFilterProxyModel*>(model())->sourceModel());
0125 }
0126 
0127 Project *TaskWorkPackageTreeView::project() const
0128 {
0129     return itemModel()->project();
0130 }
0131 
0132 Document *TaskWorkPackageTreeView::currentDocument() const
0133 {
0134     QSortFilterProxyModel *sf = qobject_cast<QSortFilterProxyModel*>(model());
0135     Q_ASSERT(sf);
0136     if (sf == 0) {
0137         return 0;
0138     }
0139     return itemModel()->documentForIndex(sf->mapToSource(selectionModel()->currentIndex()));
0140 }
0141 
0142 Node *TaskWorkPackageTreeView::currentNode() const
0143 {
0144     QSortFilterProxyModel *sf = qobject_cast<QSortFilterProxyModel*>(model());
0145     Q_ASSERT(sf);
0146     if (sf == 0) {
0147         return 0;
0148     }
0149     return itemModel()->nodeForIndex(sf->mapToSource(selectionModel()->currentIndex()));
0150 }
0151 
0152 QList<Node*> TaskWorkPackageTreeView::selectedNodes() const
0153 {
0154     QList<Node*> lst;
0155     QSortFilterProxyModel *sf = qobject_cast<QSortFilterProxyModel*>(model());
0156     Q_ASSERT(sf);
0157     if (sf == 0) {
0158         return lst;
0159     }
0160     foreach(const QModelIndex &idx, selectionModel()->selectedIndexes()) {
0161         QModelIndex i = sf->mapToSource(idx);
0162         Q_ASSERT(i.isValid() && i.model() == itemModel());
0163         Node *n = itemModel()->nodeForIndex(i);
0164         if (n && ! lst.contains(n)) {
0165             lst << n;
0166         }
0167     }
0168     return lst;
0169 }
0170 
0171 void TaskWorkPackageTreeView::setProject(Project *project)
0172 {
0173     itemModel()->setProject(project);
0174 }
0175 
0176 void TaskWorkPackageTreeView::slotActivated(const QModelIndex &index)
0177 {
0178     debugPlanWork<<index.column();
0179 }
0180 
0181 void TaskWorkPackageTreeView::dragMoveEvent(QDragMoveEvent */*event*/)
0182 {
0183 /*    if (dragDropMode() == InternalMove
0184         && (event->source() != this || !(event->possibleActions() & Qt::MoveAction)))
0185         return;
0186 
0187     TreeViewBase::dragMoveEvent(event);
0188     if (! event->isAccepted()) {
0189         return;
0190     }
0191     //QTreeView thinks it's ok to drop
0192     event->ignore();
0193     QModelIndex index = indexAt(event->pos());
0194     if (! index.isValid()) {
0195         event->accept();
0196         return; // always ok to drop on main project
0197     }
0198     Node *dn = model()->node(index);
0199     if (dn == 0) {
0200         errorPlanWork<<"no node to drop on!"
0201         return; // hmmm
0202     }
0203     switch (dropIndicatorPosition()) {
0204         case AboveItem:
0205         case BelowItem:
0206             //dn == sibling
0207             if (model()->dropAllowed(dn->parentNode(), event->mimeData())) {
0208                 event->accept();
0209             }
0210             break;
0211         case OnItem:
0212             //dn == new parent
0213             if (model()->dropAllowed(dn, event->mimeData())) {
0214                 event->accept();
0215             }
0216             break;
0217         default:
0218             break;
0219     }*/
0220 }
0221 
0222 
0223 //-----------------------------------
0224 AbstractView::AbstractView(Part *part, QWidget *parent)
0225     : QWidget(parent),
0226       m_part(part)
0227 {
0228 }
0229 
0230 void AbstractView::updateReadWrite(bool /*rw*/)
0231 {
0232 }
0233 
0234 QList<Node*> AbstractView::selectedNodes() const
0235 {
0236     return QList<Node*>();
0237 }
0238 
0239 Node *AbstractView::currentNode() const
0240 {
0241     return 0;
0242 }
0243 
0244 Document *AbstractView::currentDocument() const
0245 {
0246     return 0;
0247 }
0248 
0249 
0250 void AbstractView::slotHeaderContextMenuRequested(const QPoint &pos)
0251 {
0252     debugPlanWork;
0253     QList<QAction*> lst = contextActionList();
0254     if (! lst.isEmpty()) {
0255         QMenu::exec(lst, pos, lst.first());
0256     }
0257 }
0258 
0259 void AbstractView::slotContextMenuRequested(const QModelIndex &/*index*/, const QPoint& pos)
0260 {
0261     return slotHeaderContextMenuRequested(pos);
0262 }
0263 
0264 void AbstractView::slotContextMenuRequested(Node *node, const QPoint& pos)
0265 {
0266     debugPlanWork<<node->name()<<" :"<<pos;
0267     QString name;
0268     switch (node->type()) {
0269         case Node::Type_Task:
0270             name = "taskstatus_popup";
0271             break;
0272         case Node::Type_Milestone:
0273             name = "taskview_milestone_popup";
0274             break;
0275         case Node::Type_Summarytask:
0276             name = "taskview_summary_popup";
0277             break;
0278         default:
0279             break;
0280     }
0281     debugPlanWork<<name;
0282     if (name.isEmpty()) {
0283         slotHeaderContextMenuRequested(pos);
0284         return;
0285     }
0286     emit requestPopupMenu(name, pos);
0287 }
0288 
0289 void AbstractView::slotContextMenuRequested(Document *doc, const QPoint& pos)
0290 {
0291     debugPlanWork<<doc->url()<<" :"<<pos;
0292     QString name;
0293     switch (doc->type()) {
0294         case Document::Type_Product:
0295             name = "editdocument_popup";
0296             break;
0297         default:
0298             name = "viewdocument_popup";
0299             break;
0300     }
0301     debugPlanWork<<name;
0302     if (name.isEmpty()) {
0303         slotHeaderContextMenuRequested(pos);
0304         return;
0305     }
0306     emit requestPopupMenu(name, pos);
0307 }
0308 
0309 void AbstractView::sectionsMoved()
0310 {
0311 }
0312 
0313 bool AbstractView::loadContext()
0314 {
0315     return true;
0316 }
0317 
0318 void AbstractView::saveContext()
0319 {
0320 }
0321 
0322 KoPrintJob *AbstractView::createPrintJob()
0323 {
0324     return 0;
0325 }
0326 
0327 //-----------------------------------
0328 TaskWorkPackageView::TaskWorkPackageView(Part *part, QWidget *parent)
0329     : AbstractView(part, parent)
0330 {
0331     debugPlanWork<<"-------------------- creating TaskWorkPackageView -------------------";
0332     QVBoxLayout * l = new QVBoxLayout(this);
0333     l->setMargin(0);
0334     m_view = new TaskWorkPackageTreeView(part, this);
0335     l->addWidget(m_view);
0336     setupGui();
0337 
0338     connect(itemModel(), &KPlato::ItemModelBase::executeCommand, part, &Part::addCommand);
0339 
0340     connect(m_view, SIGNAL(contextMenuRequested(QModelIndex,QPoint,QModelIndexList)), SLOT(slotContextMenuRequested(QModelIndex,QPoint)));
0341 
0342     connect(m_view, &KPlato::DoubleTreeViewBase::headerContextMenuRequested, this, &TaskWorkPackageView::slotHeaderContextMenuRequested);
0343 
0344     connect(m_view, &KPlato::DoubleTreeViewBase::selectionChanged, this, &TaskWorkPackageView::slotSelectionChanged);
0345 
0346     loadContext();
0347 
0348     connect(m_view, &TaskWorkPackageTreeView::sectionsMoved, this, &TaskWorkPackageView::sectionsMoved);
0349 }
0350 
0351 TaskWorkPackageView::~TaskWorkPackageView()
0352 {
0353     saveContext();
0354 }
0355 
0356 void TaskWorkPackageView::updateReadWrite(bool rw)
0357 {
0358     m_view->setReadWrite(rw);
0359 }
0360 
0361 void TaskWorkPackageView::slotSelectionChanged(const QModelIndexList &/*lst*/)
0362 {
0363     emit selectionChanged();
0364 }
0365 
0366 QList<Node*> TaskWorkPackageView::selectedNodes() const
0367 {
0368     return m_view->selectedNodes();
0369 }
0370 
0371 Node *TaskWorkPackageView::currentNode() const
0372 {
0373     return m_view->currentNode();
0374 }
0375 
0376 Document *TaskWorkPackageView::currentDocument() const
0377 {
0378     return m_view->currentDocument();
0379 }
0380 
0381 void TaskWorkPackageView::slotContextMenuRequested(const QModelIndex &index, const QPoint& pos)
0382 {
0383     debugPlanWork<<index<<pos;
0384     if (! index.isValid()) {
0385         slotHeaderContextMenuRequested(pos);
0386         return;
0387     }
0388     QSortFilterProxyModel *sf = qobject_cast<QSortFilterProxyModel*>(m_view->model());
0389     Q_ASSERT(sf);
0390     if (sf == 0) {
0391         return;
0392     }
0393     QModelIndex idx = sf->mapToSource(index);
0394     if (! idx.isValid()) {
0395         slotHeaderContextMenuRequested(pos);
0396         return;
0397     }
0398 
0399     Node *node = itemModel()->nodeForIndex(idx);
0400     if (node) {
0401         return slotContextMenuRequested(node, pos);
0402     }
0403     Document *doc = itemModel()->documentForIndex(idx);
0404     if (doc) {
0405         return slotContextMenuRequested(doc, pos);
0406     }
0407     return slotHeaderContextMenuRequested(pos);
0408 }
0409 
0410 void TaskWorkPackageView::setupGui()
0411 {
0412     // Add the context menu actions for the view options
0413     connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskWorkPackageView::slotSplitView);
0414     addContextAction(m_view->actionSplitView());
0415 
0416     actionOptions = new QAction(koIcon("configure"), i18n("Configure View..."), this);
0417     connect(actionOptions, &QAction::triggered, this, &TaskWorkPackageView::slotOptions);
0418     addContextAction(actionOptions);
0419 }
0420 
0421 void TaskWorkPackageView::slotSplitView()
0422 {
0423     debugPlanWork;
0424     m_view->setViewSplitMode(! m_view->isViewSplit());
0425 }
0426 
0427 
0428 void TaskWorkPackageView::slotOptions()
0429 {
0430     debugPlanWork;
0431     QPointer<SplitItemViewSettupDialog> dlg = new SplitItemViewSettupDialog(0, m_view, this);
0432     dlg->exec();
0433     delete dlg;
0434 }
0435 
0436 bool TaskWorkPackageView::loadContext()
0437 {
0438     KoXmlDocument doc;
0439     doc.setContent(PlanWorkSettings::self()->taskWorkPackageView());
0440     KoXmlElement context = doc.namedItem("TaskWorkPackageViewSettings").toElement();
0441     if (context.isNull()) {
0442         debugPlanWork<<"No settings";
0443         return false;
0444     }
0445     debugPlanWork<<KoXml::asQDomDocument(doc).toString();
0446     return m_view->loadContext(itemModel()->columnMap(), context);
0447 }
0448 
0449 void TaskWorkPackageView::saveContext()
0450 {
0451     QDomDocument doc ("TaskWorkPackageView");
0452     QDomElement context = doc.createElement("TaskWorkPackageViewSettings");
0453     doc.appendChild(context);
0454     m_view->saveContext(itemModel()->columnMap(), context);
0455     PlanWorkSettings::self()->setTaskWorkPackageView(doc.toString());
0456     debugPlanWork<<"saved context:"<<endl<<doc.toString();
0457 }
0458 
0459 //-------------------------------------------
0460 GanttItemDelegate::GanttItemDelegate(QObject *parent)
0461     : KPlato::GanttItemDelegate(parent)
0462 {
0463     showResources = false;
0464     showTaskName = true;
0465     showTaskLinks = false;
0466     showProgress = true;
0467     showPositiveFloat = false;
0468     showNegativeFloat = false;
0469     showCriticalPath = false;
0470     showCriticalTasks = false;
0471     showAppointments = false;
0472     showNoInformation = false;
0473     showTimeConstraint = false;
0474     showSchedulingError = false;
0475     showStatus = true;
0476 
0477     QLinearGradient b(0., 0., 0., QApplication::fontMetrics().height());
0478     b.setColorAt(0., Qt::green);
0479     b.setColorAt(1., Qt::darkGreen);
0480     m_brushes.insert(Brush_Normal, QBrush(b));
0481 
0482     b.setColorAt(0., Qt::red);
0483     b.setColorAt(1., Qt::darkRed);
0484     m_brushes.insert(Brush_Late, QBrush(b));
0485 
0486     b.setColorAt(0., Qt::gray);
0487     b.setColorAt(1., Qt::darkGray);
0488     m_brushes.insert(Brush_Finished, QBrush(b));
0489 
0490     b.setColorAt(0., Qt::blue);
0491     b.setColorAt(1., Qt::darkBlue);
0492     m_brushes.insert(Brush_ReadyToStart, QBrush(b));
0493 
0494     b.setColorAt(0., Qt::white);
0495     b.setColorAt(1., Qt::gray);
0496     m_brushes.insert(Brush_NotReadyToStart, QBrush(b));
0497 
0498     b.setColorAt(0., Qt::white);
0499     b.setColorAt(1., Qt::white);
0500     m_brushes.insert(Brush_NotScheduled, QBrush(b));
0501 }
0502 
0503 void GanttItemDelegate::paintGanttItem(QPainter* painter, const KGantt::StyleOptionGanttItem& opt, const QModelIndex& idx)
0504 {
0505     if (!idx.isValid()) return;
0506 
0507     const KGantt::ItemType typ = static_cast<KGantt::ItemType>(idx.data(KGantt::ItemTypeRole).toInt());
0508 
0509     QString txt = itemText(idx, typ);
0510     QRectF itemRect = opt.itemRect;
0511 
0512 //     painter->save();
0513 //     painter->setPen(Qt::blue);
0514 //     painter->drawRect(opt.boundingRect.adjusted(-1., -1., 1., 1.));
0515 //     painter->setPen(Qt::red);
0516 //     painter->drawRect(itemRect);
0517 //     painter->restore();
0518 
0519     QRectF textRect = itemRect;
0520     if (! txt.isEmpty()) {
0521         int tw = opt.fontMetrics.width(txt) + static_cast<int>(itemRect.height()/1.5);
0522         switch(opt.displayPosition) {
0523             case KGantt::StyleOptionGanttItem::Left:
0524                 textRect.adjust(-tw, 0.0, 0.0, 0.0);
0525                 break;
0526             case KGantt::StyleOptionGanttItem::Right:
0527                 textRect.adjust(0.0, 0.0, tw, 0.0);
0528                 break;
0529             default:
0530                 break;
0531         }
0532     }
0533     painter->save();
0534 
0535     QPen pen = defaultPen(typ);
0536     if (opt.state & QStyle::State_Selected) pen.setWidth(2*pen.width());
0537     painter->setPen(pen);
0538 
0539     qreal pw = painter->pen().width()/2.;
0540     switch(typ) {
0541     case KGantt::TypeTask:
0542         if (itemRect.isValid()) {
0543             pw-=1;
0544             QRectF r = itemRect;
0545             r.translate(0., r.height()/6.);
0546             r.setHeight(2.*r.height()/3.);
0547             painter->save();
0548             painter->setBrushOrigin(itemRect.topLeft());
0549             painter->translate(0.5, 0.5);
0550             bool normal = true;
0551             if (showStatus) {
0552                 int state = data(idx, TaskWorkPackageModel::NodeStatus, Qt::EditRole).toInt();
0553                 if (state & Node::State_NotScheduled) {
0554                     painter->setBrush(m_brushes[ Brush_NotScheduled ]);
0555                     normal = false;
0556                 } else if (state & Node::State_Finished) {
0557                     painter->setBrush(m_brushes[ Brush_Finished ]);
0558                     normal = false;
0559                 } else if (state & Node::State_Started) {
0560                     if (state & Node::State_Late) {
0561                         painter->setBrush(m_brushes[ Brush_Late ]);
0562                         normal = false;
0563                     }
0564                 } else  {
0565                     // scheduled, not started, not finished
0566                     if (state & Node::State_Late) {
0567                         painter->setBrush(m_brushes[ Brush_Late ]);
0568                         normal = false;
0569                     } else if (state & Node::State_NotReadyToStart) {
0570                         painter->setBrush(m_brushes[ Brush_NotReadyToStart ]);
0571                         normal = false;
0572                     } else if (state & Node::State_ReadyToStart) {
0573                         painter->setBrush(m_brushes[ Brush_ReadyToStart ]);
0574                         normal = false;
0575                     }
0576                 }
0577             } else if (showCriticalTasks) {
0578                 bool critical = data(idx, NodeModel::NodeCritical, Qt::DisplayRole).toBool();
0579                 if (! critical && showCriticalPath) {
0580                     critical = data(idx, NodeModel::NodeCriticalPath, Qt::DisplayRole).toBool();
0581                 }
0582                 if (critical) {
0583                     QVariant br = data(idx, NodeModel::NodeCritical, Role::Foreground);
0584                     painter->setBrush(br.isValid() ? br.value<QBrush>() : m_criticalBrush);
0585                     normal = false;
0586                 }
0587             }
0588             if (normal) {
0589                 painter->setBrush(m_brushes[ Brush_Normal ]);
0590             }
0591             painter->drawRect(r);
0592 
0593             if (showProgress) {
0594                 bool ok;
0595                 qreal completion = idx.model()->data(idx, KGantt::TaskCompletionRole).toDouble(&ok);
0596                 if (ok) {
0597                     qreal h = r.height();
0598                     QRectF cr(r.x(), r.y()+h/4. + 1,
0599                             r.width()*completion/100., h/2. - 2);
0600                     painter->fillRect(cr, painter->pen().brush());
0601                 }
0602             }
0603             painter->restore();
0604 
0605             // only Left/Center/Right used
0606             const Qt::Alignment ta =
0607                 (opt.displayPosition == KGantt::StyleOptionGanttItem::Left)  ? Qt::AlignLeft :
0608                 (opt.displayPosition == KGantt::StyleOptionGanttItem::Right) ? Qt::AlignRight :
0609                 /*                      KGantt::StyleOptionGanttItem::Center*/ Qt::AlignCenter;
0610 
0611             painter->drawText(textRect, ta, txt);
0612         }
0613         break;
0614     default:
0615         break;
0616     }
0617     painter->restore();
0618 }
0619 
0620 QModelIndex mapToSource(const QModelIndex &index)
0621 {
0622     QModelIndex idx = index;
0623     const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel*>(idx.model());
0624     while (proxy) {
0625         idx = proxy->mapToSource(idx);
0626         proxy = qobject_cast<const QAbstractProxyModel*>(idx.model());
0627     }
0628     return idx;
0629 }
0630 
0631 QString GanttItemDelegate::toolTip(const QModelIndex &index) const
0632 {
0633     if (!index.isValid()) {
0634         return QString();
0635     }
0636     // map to source manually, gantt models do some tricks so we only get column 0
0637     QModelIndex idx = mapToSource(index);
0638     Q_ASSERT(idx.isValid());
0639     if (data(idx, TaskWorkPackageModel::NodeFinished, Qt::EditRole).toBool()) {
0640         // finished
0641         return xi18nc("@info:tooltip",
0642                       "Task: %1<nl/>"
0643                       "Actual finish: %2<nl/>"
0644                       "Planned finish: %3<nl/>"
0645                       "Status: %4<nl/>"
0646                       "Project: %5",
0647                       idx.data().toString(),
0648                       data(idx, TaskWorkPackageModel::NodeActualFinish, Qt::DisplayRole).toString(),
0649                       data(idx, TaskWorkPackageModel::NodeEndTime, Qt::DisplayRole).toString(),
0650                       data(idx, TaskWorkPackageModel::NodeStatus, Qt::DisplayRole).toString(),
0651                       data(idx, TaskWorkPackageModel::ProjectName, Qt::DisplayRole).toString()
0652                       );
0653     }
0654     if (data(idx, TaskWorkPackageModel::NodeStarted, Qt::EditRole).toBool()) {
0655         // started
0656         return xi18nc("@info:tooltip",
0657                       "Task: %1<nl/>"
0658                       "Completion: %2 %<nl/>"
0659                       "Actual start: %3<nl/>"
0660                       "Planned: %4 - %5<nl/>"
0661                       "Status: %6<nl/>"
0662                       "Project: %7",
0663                       idx.data().toString(),
0664                       data(idx, TaskWorkPackageModel::NodeCompleted, Qt::DisplayRole).toString(),
0665                       data(idx, TaskWorkPackageModel::NodeActualStart, Qt::DisplayRole).toString(),
0666                       data(idx, TaskWorkPackageModel::NodeStartTime, Qt::DisplayRole).toString(),
0667                       data(idx, TaskWorkPackageModel::NodeEndTime, Qt::DisplayRole).toString(),
0668                       data(idx, TaskWorkPackageModel::NodeStatus, Qt::DisplayRole).toString(),
0669                       data(idx, TaskWorkPackageModel::ProjectName, Qt::DisplayRole).toString()
0670                       );
0671     }
0672     // Planned
0673     KGantt::StyleOptionGanttItem opt;
0674     int typ = data(idx, NodeModel::NodeType, Qt::EditRole).toInt();
0675     switch (typ) {
0676         case Node::Type_Task:
0677             return xi18nc("@info:tooltip",
0678                           "Task: %1<nl/>"
0679                           "Planned: %2 - %3<nl/>"
0680                           "Status: %4<nl/>"
0681                           "Project: %5",
0682                           idx.data().toString(),
0683                           data(idx, TaskWorkPackageModel::NodeStartTime, Qt::DisplayRole).toString(),
0684                           data(idx, TaskWorkPackageModel::NodeEndTime, Qt::DisplayRole).toString(),
0685                           data(idx, TaskWorkPackageModel::NodeStatus, Qt::DisplayRole).toString(),
0686                           data(idx, TaskWorkPackageModel::ProjectName, Qt::DisplayRole).toString()
0687                           );
0688     }
0689     return QString();
0690 }
0691 
0692 GanttView::GanttView(Part *part, QWidget *parent)
0693     : KPlato::GanttViewBase(parent),
0694       m_part(part),
0695       m_project(0),
0696       m_ganttdelegate(new GanttItemDelegate(this)),
0697       m_itemmodel(new TaskWorkPackageModel(part, this))
0698 {
0699     debugPlanWork<<"------------------- create GanttView -----------------------";
0700     m_itemmodel->setObjectName("Gantt model");
0701     graphicsView()->setItemDelegate(m_ganttdelegate);
0702     GanttTreeView *tv = new GanttTreeView(this);
0703     tv->setSortingEnabled(true);
0704     tv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
0705     tv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0706     tv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); // needed since qt 4.2
0707     setLeftView(tv);
0708     m_rowController = new KGantt::TreeViewRowController(tv, ganttProxyModel());
0709     setRowController(m_rowController);
0710     tv->header()->setStretchLastSection(true);
0711 
0712     QSortFilterProxyModel *sf = new QSortFilterProxyModel(tv);
0713     sf->setSortRole(Qt::EditRole);
0714     sf->setSourceModel(m_itemmodel);
0715     KGantt::View::setModel(sf);
0716 
0717     QList<int> show;
0718     show << TaskWorkPackageModel::NodeName << TaskWorkPackageModel::NodeDescription;
0719     tv->setDefaultColumns(show);
0720     for (int i = 0; i < m_itemmodel->columnCount(); ++i) {
0721         if (! show.contains(i)) {
0722             tv->hideColumn(i);
0723         }
0724     }
0725     debugPlanWork<<"mapping roles";
0726     KGantt::ProxyModel *m = static_cast<KGantt::ProxyModel*>(ganttProxyModel());
0727 
0728     m->setRole(KGantt::ItemTypeRole, KGantt::ItemTypeRole); // To provide correct format
0729     m->setRole(KGantt::StartTimeRole, Qt::EditRole); // To provide correct format
0730     m->setRole(KGantt::EndTimeRole, Qt::EditRole); // To provide correct format
0731 
0732     m->setColumn(KGantt::ItemTypeRole, TaskWorkPackageModel::NodeType);
0733     m->setColumn(KGantt::StartTimeRole, TaskWorkPackageModel::NodeStartTime);
0734     m->setColumn(KGantt::EndTimeRole, TaskWorkPackageModel::NodeEndTime);
0735     m->setColumn(KGantt::TaskCompletionRole, TaskWorkPackageModel::NodeCompleted);
0736     debugPlanWork<<"roles mapped";
0737 
0738     KGantt::DateTimeGrid *g = static_cast<KGantt::DateTimeGrid*>(grid());
0739     g->setDayWidth(30);
0740     // TODO: extend QLocale/KGantt to support formats for hourly time display
0741     // see bug #349030
0742     // removed custom code here
0743 
0744     for (int i = 0; i < part->workPackageCount(); ++i) {
0745         updateDateTimeGrid(part->workPackage(i));
0746     }
0747     connect(m_itemmodel, &QAbstractItemModel::rowsInserted, this, &GanttView::slotRowsInserted);
0748     connect(m_itemmodel, &QAbstractItemModel::rowsRemoved, this, &GanttView::slotRowsRemoved);
0749 
0750     connect(tv, &KPlato::TreeViewBase::contextMenuRequested, this, &GanttView::contextMenuRequested);
0751     connect(tv, &KPlato::TreeViewBase::headerContextMenuRequested, this, &GanttView::headerContextMenuRequested);
0752 
0753     connect(tv->selectionModel(), &QItemSelectionModel::selectionChanged, this, &GanttView::slotSelectionChanged);
0754 
0755     connect(tv->header(), &QHeaderView::sectionMoved, this, &GanttView::sectionsMoved);
0756 
0757     tv->header()->setSortIndicator(TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder);
0758     sf->sort(TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder);
0759 }
0760 
0761 GanttView::~GanttView()
0762 {
0763     delete m_rowController;
0764 }
0765 
0766 void GanttView::slotSelectionChanged(const QItemSelection &selected, const QItemSelection&)
0767 {
0768     emit selectionChanged(selected.indexes());
0769 }
0770 
0771 void GanttView::slotRowsInserted(const QModelIndex &parent, int start, int end)
0772 {
0773     debugPlanWork<<parent<<start<<end;
0774     if (! parent.isValid()) {
0775         for (int i = start; i <= end; ++i) {
0776             updateDateTimeGrid(m_itemmodel->workPackage(i));
0777         }
0778     }
0779 }
0780 
0781 void GanttView::slotRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/)
0782 {
0783     KGantt::DateTimeGrid *g = static_cast<KGantt::DateTimeGrid*>(grid());
0784     QDateTime newStart;
0785     for (int i = 0; i < m_part->workPackageCount(); ++i) {
0786         WorkPackage *wp = m_part->workPackage(i);
0787         Task *task = static_cast<Task*>(wp->project()->childNode(0));
0788         if (!newStart.isValid() || newStart > task->startTime()) {
0789             newStart = task->startTime();
0790         }
0791     }
0792     if (newStart.isValid()) {
0793         g->setStartDateTime(newStart);
0794     }
0795 }
0796 
0797 void GanttView::updateDateTimeGrid(WorkPackage *wp)
0798 {
0799     debugPlanWork<<wp;
0800     if (! wp || ! wp->project() || ! wp->project()->childNode(0)) {
0801         return;
0802     }
0803     Task *task = static_cast<Task*>(wp->project()->childNode(0));
0804     DateTime st = task->startTime();
0805     if (! st.isValid() && task->completion().startTime().isValid()) {
0806         st = qMin(st, task->completion().startTime());
0807     }
0808     if (! st.isValid()) {
0809         return;
0810     }
0811     KGantt::DateTimeGrid *g = static_cast<KGantt::DateTimeGrid*>(grid());
0812     QDateTime gst = g->startDateTime();
0813     if (! gst.isValid() || gst > st) {
0814         st.setTime(QTime(0, 0, 0, 0));
0815         g->setStartDateTime(st);
0816     }
0817 }
0818 
0819 TaskWorkPackageModel *GanttView::itemModel() const
0820 {
0821     return m_itemmodel;
0822 }
0823 
0824 void GanttView::setProject(Project *project)
0825 {
0826     itemModel()->setProject(project);
0827     m_project = project;
0828 }
0829 
0830 QList<Node*> GanttView::selectedNodes() const
0831 {
0832     QList<Node*> nodes;
0833     foreach(const QModelIndex &idx, treeView()->selectionModel()->selectedRows()) {
0834         nodes << itemModel()->nodeForIndex(idx);
0835     }
0836     return nodes;
0837 }
0838 
0839 Node *GanttView::currentNode() const
0840 {
0841     return itemModel()->nodeForIndex(treeView()->selectionModel()->currentIndex());
0842 }
0843 
0844 Document *GanttView::currentDocument() const
0845 {
0846     return itemModel()->documentForIndex(treeView()->selectionModel()->currentIndex());
0847 }
0848 
0849 bool GanttView::loadContext(const KoXmlElement &context)
0850 {
0851     KoXmlElement e = context.namedItem("itemview").toElement();
0852     if (! e.isNull()) {
0853         treeView()->loadContext(itemModel()->columnMap(), e);
0854     }
0855     e = context.namedItem("ganttview").toElement();
0856     if (! e.isNull()) {
0857         KPlato::GanttViewBase::loadContext(e);
0858     }
0859     return true;
0860 }
0861 
0862 void GanttView::saveContext(QDomElement &context) const
0863 {
0864     QDomElement e = context.ownerDocument().createElement("itemview");
0865     context.appendChild(e);
0866     treeView()->saveContext(itemModel()->columnMap(), e);
0867     e = context.ownerDocument().createElement("ganttview");
0868     context.appendChild(e);
0869     KPlato::GanttViewBase::saveContext(e);
0870 }
0871 
0872 //-----------------------------------
0873 TaskWPGanttView::TaskWPGanttView(Part *part, QWidget *parent)
0874     : AbstractView(part, parent)
0875 {
0876     debugPlanWork<<"-------------------- creating TaskWPGanttView -------------------";
0877     QVBoxLayout * l = new QVBoxLayout(this);
0878     l->setMargin(0);
0879     m_view = new GanttView(part, this);
0880     l->addWidget(m_view);
0881 
0882     setupGui();
0883 
0884     connect(itemModel(), &KPlato::ItemModelBase::executeCommand, part, &Part::addCommand);
0885 
0886     connect(m_view, SIGNAL(contextMenuRequested(QModelIndex,QPoint)), SLOT(slotContextMenuRequested(QModelIndex,QPoint)));
0887     connect(m_view, &GanttView::headerContextMenuRequested, this, &TaskWPGanttView::slotHeaderContextMenuRequested);
0888 
0889     connect(m_view, &GanttView::selectionChanged, this, &TaskWPGanttView::slotSelectionChanged);
0890 
0891     connect(m_view, &GanttView::sectionsMoved, this, &TaskWPGanttView::sectionsMoved);
0892 }
0893 
0894 TaskWPGanttView::~TaskWPGanttView()
0895 {
0896     saveContext();
0897 }
0898 
0899 void TaskWPGanttView::slotSelectionChanged(const QModelIndexList& /*lst*/)
0900 {
0901     emit selectionChanged();
0902 }
0903 
0904 QList<Node*> TaskWPGanttView::selectedNodes() const
0905 {
0906     return m_view->selectedNodes();
0907 }
0908 
0909 Node *TaskWPGanttView::currentNode() const
0910 {
0911     return m_view->currentNode();
0912 }
0913 
0914 Document *TaskWPGanttView::currentDocument() const
0915 {
0916     return m_view->currentDocument();
0917 }
0918 
0919 void TaskWPGanttView::slotContextMenuRequested(const QModelIndex &idx, const QPoint& pos)
0920 {
0921     debugPlanWork<<idx<<pos;
0922     if (! idx.isValid()) {
0923         slotHeaderContextMenuRequested(pos);
0924         return;
0925     }
0926     Node *node = itemModel()->nodeForIndex(idx);
0927     if (node) {
0928         return slotContextMenuRequested(node, pos);
0929     }
0930     Document *doc = itemModel()->documentForIndex(idx);
0931     if (doc) {
0932         return slotContextMenuRequested(doc, pos);
0933     }
0934     return slotHeaderContextMenuRequested(pos);
0935 }
0936 
0937 void TaskWPGanttView::setupGui()
0938 {
0939     actionOptions = new QAction(koIcon("configure"), i18n("Configure View..."), this);
0940     connect(actionOptions, &QAction::triggered, this, &TaskWPGanttView::slotOptions);
0941     addContextAction(actionOptions);
0942 }
0943 
0944 void TaskWPGanttView::slotOptions()
0945 {
0946     debugPlanWork;
0947     QPointer<ItemViewSettupDialog> dlg = new ItemViewSettupDialog(0, m_view->treeView(), true, this);
0948     dlg->exec();
0949     delete dlg;
0950 }
0951 
0952 bool TaskWPGanttView::loadContext()
0953 {
0954     KoXmlDocument doc;
0955     doc.setContent(PlanWorkSettings::self()->taskWPGanttView());
0956     KoXmlElement context = doc.namedItem("TaskWPGanttViewSettings").toElement();
0957     if (context.isNull()) {
0958         debugPlanWork<<"No settings";
0959         return false;
0960     }
0961     return m_view->loadContext(context);
0962 }
0963 
0964 void TaskWPGanttView::saveContext()
0965 {
0966     QDomDocument doc ("TaskWPGanttView");
0967     QDomElement context = doc.createElement("TaskWPGanttViewSettings");
0968     doc.appendChild(context);
0969     m_view->saveContext(context);
0970     PlanWorkSettings::self()->setTaskWPGanttView(doc.toString());
0971     debugPlanWork<<endl<<doc.toString();
0972 }
0973 
0974 } // namespace KPlatoWork