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