diff --git a/src/workpackage/taskworkpackageview.cpp b/src/workpackage/taskworkpackageview.cpp index c177888a..902a3d38 100644 --- a/src/workpackage/taskworkpackageview.cpp +++ b/src/workpackage/taskworkpackageview.cpp @@ -1,959 +1,969 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2009, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "taskworkpackageview.h" #include "taskworkpackagemodel.h" #include "workpackage.h" #include "part.h" #include "kptglobal.h" #include "kptcommand.h" #include "kptproject.h" #include "kptschedule.h" #include "kpteffortcostmap.h" #include "kptitemviewsettup.h" #include "calligraplanworksettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debugarea.h" using namespace KPlato; namespace KPlatoWork { TaskWorkPackageTreeView::TaskWorkPackageTreeView( Part *part, QWidget *parent ) : DoubleTreeViewBase( parent ) { setContextMenuPolicy( Qt::CustomContextMenu ); masterView()->header()->setSortIndicatorShown( true ); masterView()->header()->setSectionsClickable( true ); slaveView()->header()->setSortIndicatorShown( true ); slaveView()->header()->setSectionsClickable( true ); QSortFilterProxyModel *sf = new QSortFilterProxyModel( this ); TaskWorkPackageModel *m = new TaskWorkPackageModel( part, sf ); sf->setSourceModel( m ); setModel( sf ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::SingleSelection ); setStretchLastSection( false ); createItemDelegates( m ); QList lst1; lst1 << 1 << -1; // display column 0 (NodeName) in left view masterView()->setDefaultColumns( QList() << TaskWorkPackageModel::NodeName ); QList show; show << TaskWorkPackageModel::NodeCompleted << TaskWorkPackageModel::NodeActualEffort << TaskWorkPackageModel::NodeRemainingEffort << TaskWorkPackageModel::NodePlannedEffort << TaskWorkPackageModel::NodeStartTime << TaskWorkPackageModel::NodeActualStart << TaskWorkPackageModel::NodeEndTime << TaskWorkPackageModel::NodeActualFinish << TaskWorkPackageModel::ProjectName << TaskWorkPackageModel::ProjectManager; QList lst2; for ( int i = 0; i < m->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } hideColumns( lst1, lst2 ); slaveView()->setDefaultColumns( show ); setViewSplitMode(false); masterView()->setFocus(); debugPlanWork<taskWorkPackageView(); connect(masterView()->header(), &QHeaderView::sortIndicatorChanged, this, &TaskWorkPackageTreeView::setSortOrder); connect(slaveView()->header(), &QHeaderView::sortIndicatorChanged, this, &TaskWorkPackageTreeView::setSortOrder); connect(masterView()->header(), &QHeaderView::sectionMoved, this, &TaskWorkPackageTreeView::sectionsMoved); connect(slaveView()->header(), &QHeaderView::sectionMoved, this, &TaskWorkPackageTreeView::sectionsMoved); masterView()->header()->setSortIndicator( TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder ); sf->sort(TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder); } void TaskWorkPackageTreeView::setSortOrder( int col, Qt::SortOrder order ) { static_cast(model())->setSortRole(Qt::EditRole); model()->sort( col, order ); } TaskWorkPackageModel *TaskWorkPackageTreeView::itemModel() const { return static_cast( static_cast( model() )->sourceModel() ); } Project *TaskWorkPackageTreeView::project() const { return itemModel()->project(); } Document *TaskWorkPackageTreeView::currentDocument() const { QSortFilterProxyModel *sf = qobject_cast( model() ); Q_ASSERT( sf ); if ( sf == 0 ) { return 0; } return itemModel()->documentForIndex( sf->mapToSource( selectionModel()->currentIndex() ) ); } Node *TaskWorkPackageTreeView::currentNode() const { QSortFilterProxyModel *sf = qobject_cast( model() ); Q_ASSERT( sf ); if ( sf == 0 ) { return 0; } return itemModel()->nodeForIndex( sf->mapToSource( selectionModel()->currentIndex() ) ); } QList TaskWorkPackageTreeView::selectedNodes() const { QList lst; QSortFilterProxyModel *sf = qobject_cast( model() ); Q_ASSERT( sf ); if ( sf == 0 ) { return lst; } foreach( const QModelIndex &idx, selectionModel()->selectedIndexes() ) { QModelIndex i = sf->mapToSource( idx ); Q_ASSERT( i.isValid() && i.model() == itemModel() ); Node *n = itemModel()->nodeForIndex( i ); if ( n && ! lst.contains( n ) ) { lst << n; } } return lst; } void TaskWorkPackageTreeView::setProject( Project *project ) { itemModel()->setProject( project ); } void TaskWorkPackageTreeView::slotActivated( const QModelIndex &index ) { debugPlanWork<source() != this || !(event->possibleActions() & Qt::MoveAction))) return; TreeViewBase::dragMoveEvent( event ); if ( ! event->isAccepted() ) { return; } //QTreeView thinks it's ok to drop event->ignore(); QModelIndex index = indexAt( event->pos() ); if ( ! index.isValid() ) { event->accept(); return; // always ok to drop on main project } Node *dn = model()->node( index ); if ( dn == 0 ) { errorPlanWork<<"no node to drop on!" return; // hmmm } switch ( dropIndicatorPosition() ) { case AboveItem: case BelowItem: //dn == sibling if ( model()->dropAllowed( dn->parentNode(), event->mimeData() ) ) { event->accept(); } break; case OnItem: //dn == new parent if ( model()->dropAllowed( dn, event->mimeData() ) ) { event->accept(); } break; default: break; }*/ } //----------------------------------- AbstractView::AbstractView( Part *part, QWidget *parent ) : QWidget( parent ), m_part( part ) { } void AbstractView::updateReadWrite( bool /*rw*/ ) { } QList AbstractView::selectedNodes() const { return QList(); } Node *AbstractView::currentNode() const { return 0; } Document *AbstractView::currentDocument() const { return 0; } void AbstractView::slotHeaderContextMenuRequested( const QPoint &pos ) { debugPlanWork; QList lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } void AbstractView::slotContextMenuRequested( const QModelIndex &/*index*/, const QPoint& pos ) { return slotHeaderContextMenuRequested( pos ); } void AbstractView::slotContextMenuRequested( Node *node, const QPoint& pos ) { debugPlanWork<name()<<" :"<type() ) { case Node::Type_Task: name = "taskstatus_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } debugPlanWork<url()<<" :"<type() ) { case Document::Type_Product: name = "editdocument_popup"; break; default: name = "viewdocument_popup"; break; } debugPlanWork<setMargin( 0 ); m_view = new TaskWorkPackageTreeView( part, this ); l->addWidget( m_view ); setupGui(); connect( itemModel(), &KPlato::ItemModelBase::executeCommand, part, &Part::addCommand ); connect( m_view, SIGNAL(contextMenuRequested(QModelIndex,QPoint,QModelIndexList)), SLOT(slotContextMenuRequested(QModelIndex,QPoint)) ); connect( m_view, &KPlato::DoubleTreeViewBase::headerContextMenuRequested, this, &TaskWorkPackageView::slotHeaderContextMenuRequested ); connect( m_view, &KPlato::DoubleTreeViewBase::selectionChanged, this, &TaskWorkPackageView::slotSelectionChanged ); loadContext(); connect(m_view, &TaskWorkPackageTreeView::sectionsMoved, this, &TaskWorkPackageView::sectionsMoved); } void TaskWorkPackageView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); } void TaskWorkPackageView::slotSelectionChanged( const QModelIndexList &/*lst*/ ) { emit selectionChanged(); } QList TaskWorkPackageView::selectedNodes() const { return m_view->selectedNodes(); } Node *TaskWorkPackageView::currentNode() const { return m_view->currentNode(); } Document *TaskWorkPackageView::currentDocument() const { return m_view->currentDocument(); } void TaskWorkPackageView::slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ) { debugPlanWork<( m_view->model() ); Q_ASSERT( sf ); if ( sf == 0 ) { return; } QModelIndex idx = sf->mapToSource( index ); if ( ! idx.isValid() ) { slotHeaderContextMenuRequested( pos ); return; } Node *node = itemModel()->nodeForIndex( idx ); if ( node ) { return slotContextMenuRequested( node, pos ); } Document *doc = itemModel()->documentForIndex( idx ); if ( doc ) { return slotContextMenuRequested( doc, pos ); } return slotHeaderContextMenuRequested( pos ); } void TaskWorkPackageView::setupGui() { // Add the context menu actions for the view options connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskWorkPackageView::slotSplitView); addContextAction( m_view->actionSplitView() ); actionOptions = new QAction(koIcon("configure"), i18n("Configure View..."), this); connect(actionOptions, &QAction::triggered, this, &TaskWorkPackageView::slotOptions); addContextAction( actionOptions ); } void TaskWorkPackageView::slotSplitView() { debugPlanWork; m_view->setViewSplitMode( ! m_view->isViewSplit() ); saveContext(); } void TaskWorkPackageView::slotOptions() { debugPlanWork; QPointer dlg = new SplitItemViewSettupDialog( 0, m_view, this ); dlg->exec(); delete dlg; saveContext(); } bool TaskWorkPackageView::loadContext() { KoXmlDocument doc; doc.setContent( PlanWorkSettings::self()->taskWorkPackageView() ); KoXmlElement context = doc.namedItem( "TaskWorkPackageViewSettings" ).toElement(); if ( context.isNull() ) { debugPlanWork<<"No settings"; return false; } return m_view->loadContext( itemModel()->columnMap(), context ); } void TaskWorkPackageView::saveContext() { QDomDocument doc ( "TaskWorkPackageView" ); QDomElement context = doc.createElement( "TaskWorkPackageViewSettings" ); doc.appendChild( context ); m_view->saveContext( itemModel()->columnMap(), context ); PlanWorkSettings::self()->setTaskWorkPackageView( doc.toString() ); PlanWorkSettings::self()->save(); debugPlanWork<( idx.data( KGantt::ItemTypeRole ).toInt() ); QString txt = itemText( idx, typ ); QRectF itemRect = opt.itemRect; // painter->save(); // painter->setPen( Qt::blue ); // painter->drawRect( opt.boundingRect.adjusted( -1., -1., 1., 1. ) ); // painter->setPen( Qt::red ); // painter->drawRect( itemRect ); // painter->restore(); QRectF textRect = itemRect; if ( ! txt.isEmpty() ) { int tw = opt.fontMetrics.width( txt ) + static_cast( itemRect.height()/1.5 ); switch( opt.displayPosition ) { case KGantt::StyleOptionGanttItem::Left: textRect.adjust( -tw, 0.0, 0.0, 0.0 ); break; case KGantt::StyleOptionGanttItem::Right: textRect.adjust( 0.0, 0.0, tw, 0.0 ); break; default: break; } } painter->save(); QPen pen = defaultPen( typ ); if ( opt.state & QStyle::State_Selected ) pen.setWidth( 2*pen.width() ); painter->setPen( pen ); qreal pw = painter->pen().width()/2.; switch( typ ) { case KGantt::TypeTask: if ( itemRect.isValid() ) { pw-=1; QRectF r = itemRect; r.translate( 0., r.height()/6. ); r.setHeight( 2.*r.height()/3. ); painter->save(); painter->setBrushOrigin( itemRect.topLeft() ); painter->translate( 0.5, 0.5 ); bool normal = true; if ( showStatus ) { int state = data( idx, TaskWorkPackageModel::NodeStatus, Qt::EditRole ).toInt(); if ( state & Node::State_NotScheduled ) { painter->setBrush( m_brushes[ Brush_NotScheduled ] ); normal = false; } else if ( state & Node::State_Finished ) { painter->setBrush( m_brushes[ Brush_Finished ] ); normal = false; } else if ( state & Node::State_Started ) { if ( state & Node::State_Late ) { painter->setBrush( m_brushes[ Brush_Late ] ); normal = false; } } else { // scheduled, not started, not finished if ( state & Node::State_Late ) { painter->setBrush( m_brushes[ Brush_Late ] ); normal = false; } else if ( state & Node::State_NotReadyToStart ) { painter->setBrush( m_brushes[ Brush_NotReadyToStart ] ); normal = false; } else if ( state & Node::State_ReadyToStart ) { painter->setBrush( m_brushes[ Brush_ReadyToStart ] ); normal = false; } } } else if ( showCriticalTasks ) { bool critical = data( idx, NodeModel::NodeCritical, Qt::DisplayRole ).toBool(); if ( ! critical && showCriticalPath ) { critical = data( idx, NodeModel::NodeCriticalPath, Qt::DisplayRole ).toBool(); } if ( critical ) { QVariant br = data( idx, NodeModel::NodeCritical, Role::Foreground ); painter->setBrush( br.isValid() ? br.value() : m_criticalBrush ); normal = false; } } if ( normal ) { painter->setBrush( m_brushes[ Brush_Normal ] ); } painter->drawRect( r ); if ( showProgress ) { bool ok; qreal completion = idx.model()->data( idx, KGantt::TaskCompletionRole ).toDouble( &ok ); if ( ok ) { qreal h = r.height(); QRectF cr( r.x(), r.y()+h/4. + 1, r.width()*completion/100., h/2. - 2 ); painter->fillRect( cr, painter->pen().brush() ); } } painter->restore(); // only Left/Center/Right used const Qt::Alignment ta = (opt.displayPosition == KGantt::StyleOptionGanttItem::Left) ? Qt::AlignLeft : (opt.displayPosition == KGantt::StyleOptionGanttItem::Right) ? Qt::AlignRight : /* KGantt::StyleOptionGanttItem::Center*/ Qt::AlignCenter; painter->drawText( textRect, ta, txt ); } break; default: break; } painter->restore(); } QModelIndex mapToSource(const QModelIndex &index) { QModelIndex idx = index; const QAbstractProxyModel *proxy = qobject_cast(idx.model()); while (proxy) { idx = proxy->mapToSource(idx); proxy = qobject_cast(idx.model()); } return idx; } QString GanttItemDelegate::toolTip( const QModelIndex &index ) const { if (!index.isValid()) { return QString(); } // map to source manually, gantt models do some tricks so we only get column 0 QModelIndex idx = mapToSource(index); Q_ASSERT(idx.isValid()); if ( data( idx, TaskWorkPackageModel::NodeFinished, Qt::EditRole ).toBool() ) { // finished return xi18nc( "@info:tooltip", "Task: %1" "Actual finish: %2" "Planned finish: %3" "Status: %4" "Project: %5", idx.data().toString(), data( idx, TaskWorkPackageModel::NodeActualFinish, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeEndTime, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeStatus, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::ProjectName, Qt::DisplayRole ).toString() ); } if ( data( idx, TaskWorkPackageModel::NodeStarted, Qt::EditRole ).toBool() ) { // started return xi18nc( "@info:tooltip", "Task: %1" "Completion: %2 %" "Actual start: %3" "Planned: %4 - %5" "Status: %6" "Project: %7", idx.data().toString(), data( idx, TaskWorkPackageModel::NodeCompleted, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeActualStart, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeStartTime, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeEndTime, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeStatus, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::ProjectName, Qt::DisplayRole ).toString() ); } // Planned KGantt::StyleOptionGanttItem opt; int typ = data( idx, NodeModel::NodeType, Qt::EditRole ).toInt(); switch ( typ ) { case Node::Type_Task: return xi18nc( "@info:tooltip", "Task: %1" "Planned: %2 - %3" "Status: %4" "Project: %5", idx.data().toString(), data( idx, TaskWorkPackageModel::NodeStartTime, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeEndTime, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::NodeStatus, Qt::DisplayRole ).toString(), data( idx, TaskWorkPackageModel::ProjectName, Qt::DisplayRole ).toString() ); } return QString(); } GanttView::GanttView( Part *part, QWidget *parent ) : KPlato::GanttViewBase( parent ), m_part( part ), m_project( 0 ), m_ganttdelegate( new GanttItemDelegate( this ) ), m_itemmodel( new TaskWorkPackageModel( part, this ) ) { debugPlanWork<<"------------------- create GanttView -----------------------"; m_itemmodel->setObjectName( "Gantt model" ); graphicsView()->setItemDelegate( m_ganttdelegate ); GanttTreeView *tv = new GanttTreeView( this ); tv->setSortingEnabled(true); tv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); tv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tv->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); // needed since qt 4.2 setLeftView( tv ); m_rowController = new KGantt::TreeViewRowController( tv, ganttProxyModel() ); setRowController( m_rowController ); tv->header()->setStretchLastSection( true ); QSortFilterProxyModel *sf = new QSortFilterProxyModel(tv); sf->setSortRole(Qt::EditRole); sf->setSourceModel(m_itemmodel); KGantt::View::setModel(sf); QList show; show << TaskWorkPackageModel::NodeName << TaskWorkPackageModel::NodeDescription; tv->setDefaultColumns( show ); for ( int i = 0; i < m_itemmodel->columnCount(); ++i ) { if ( ! show.contains( i ) ) { tv->hideColumn( i ); } } debugPlanWork<<"mapping roles"; KGantt::ProxyModel *m = static_cast( ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); // To provide correct format m->setRole( KGantt::StartTimeRole, Qt::EditRole ); // To provide correct format m->setRole( KGantt::EndTimeRole, Qt::EditRole ); // To provide correct format m->setColumn( KGantt::ItemTypeRole, TaskWorkPackageModel::NodeType ); m->setColumn( KGantt::StartTimeRole, TaskWorkPackageModel::NodeStartTime ); m->setColumn( KGantt::EndTimeRole, TaskWorkPackageModel::NodeEndTime ); m->setColumn( KGantt::TaskCompletionRole, TaskWorkPackageModel::NodeCompleted ); debugPlanWork<<"roles mapped"; KGantt::DateTimeGrid *g = static_cast( grid() ); g->setDayWidth( 30 ); // TODO: extend QLocale/KGantt to support formats for hourly time display // see bug #349030 // removed custom code here for ( int i = 0; i < part->workPackageCount(); ++i ) { updateDateTimeGrid( part->workPackage( i ) ); } connect( m_itemmodel, &QAbstractItemModel::rowsInserted, this, &GanttView::slotRowsInserted); connect( m_itemmodel, &QAbstractItemModel::rowsRemoved, this, &GanttView::slotRowsRemoved); connect(tv, &KPlato::TreeViewBase::contextMenuRequested, this, &GanttView::contextMenuRequested); connect(tv, &KPlato::TreeViewBase::headerContextMenuRequested, this, &GanttView::headerContextMenuRequested); connect(tv->selectionModel(), &QItemSelectionModel::selectionChanged, this, &GanttView::slotSelectionChanged); connect(tv->header(), &QHeaderView::sectionMoved, this, &GanttView::sectionsMoved); tv->header()->setSortIndicator( TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder ); sf->sort(TaskWorkPackageModel::NodeStartTime, Qt::AscendingOrder); } GanttView::~GanttView() { delete m_rowController; } void GanttView::slotSelectionChanged( const QItemSelection &selected, const QItemSelection& ) { emit selectionChanged( selected.indexes() ); } void GanttView::slotRowsInserted( const QModelIndex &parent, int start, int end ) { debugPlanWork<workPackage( i ) ); } } } void GanttView::slotRowsRemoved( const QModelIndex &/*parent*/, int /*start*/, int /*end*/ ) { KGantt::DateTimeGrid *g = static_cast( grid() ); QDateTime newStart; for ( int i = 0; i < m_part->workPackageCount(); ++i ) { WorkPackage *wp = m_part->workPackage(i); Task *task = static_cast(wp->project()->childNode(0)); if (!newStart.isValid() || newStart > task->startTime()) { newStart = task->startTime(); } } if (newStart.isValid()) { g->setStartDateTime(newStart); } } void GanttView::updateDateTimeGrid( WorkPackage *wp ) { debugPlanWork<project() || ! wp->project()->childNode( 0 ) ) { return; } Task *task = static_cast( wp->project()->childNode( 0 ) ); DateTime st = task->startTime(); if ( ! st.isValid() && task->completion().startTime().isValid() ) { st = qMin( st, task->completion().startTime() ); } if ( ! st.isValid() ) { return; } KGantt::DateTimeGrid *g = static_cast( grid() ); QDateTime gst = g->startDateTime(); if ( ! gst.isValid() || gst > st ) { st.setTime(QTime(0, 0, 0, 0)); g->setStartDateTime( st ); } } TaskWorkPackageModel *GanttView::itemModel() const { return m_itemmodel; } void GanttView::setProject( Project *project ) { itemModel()->setProject( project ); m_project = project; } QList GanttView::selectedNodes() const { QList nodes; foreach( const QModelIndex &idx, treeView()->selectionModel()->selectedRows() ) { nodes << itemModel()->nodeForIndex( idx ); } return nodes; } Node *GanttView::currentNode() const { return itemModel()->nodeForIndex( treeView()->selectionModel()->currentIndex() ); } +Document *GanttView::currentDocument() const +{ + return itemModel()->documentForIndex( treeView()->selectionModel()->currentIndex() ); +} + bool GanttView::loadContext( const KoXmlElement &context ) { KoXmlElement e = context.namedItem( "itemview" ).toElement(); if ( ! e.isNull() ) { treeView()->loadContext( itemModel()->columnMap(), e ); } e = context.namedItem( "ganttview" ).toElement(); if ( ! e.isNull() ) { KPlato::GanttViewBase::loadContext( e ); } return true; } void GanttView::saveContext( QDomElement &context ) const { QDomElement e = context.ownerDocument().createElement( "itemview" ); context.appendChild( e ); treeView()->saveContext( itemModel()->columnMap(), e ); e = context.ownerDocument().createElement( "ganttview" ); context.appendChild( e ); KPlato::GanttViewBase::saveContext( e ); } //----------------------------------- TaskWPGanttView::TaskWPGanttView( Part *part, QWidget *parent ) : AbstractView( part, parent ) { debugPlanWork<<"-------------------- creating TaskWPGanttView -------------------"; QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new GanttView( part, this ); l->addWidget( m_view ); setupGui(); connect(itemModel(), &KPlato::ItemModelBase::executeCommand, part, &Part::addCommand); connect( m_view, SIGNAL(contextMenuRequested(QModelIndex,QPoint)), SLOT(slotContextMenuRequested(QModelIndex,QPoint))); connect( m_view, &GanttView::headerContextMenuRequested, this, &TaskWPGanttView::slotHeaderContextMenuRequested); connect( m_view, &GanttView::selectionChanged, this, &TaskWPGanttView::slotSelectionChanged); connect(m_view, &GanttView::sectionsMoved, this, &TaskWPGanttView::sectionsMoved); } void TaskWPGanttView::slotSelectionChanged( const QModelIndexList& /*lst*/ ) { emit selectionChanged(); } QList TaskWPGanttView::selectedNodes() const { return m_view->selectedNodes(); } Node *TaskWPGanttView::currentNode() const { return m_view->currentNode(); } +Document *TaskWPGanttView::currentDocument() const +{ + return m_view->currentDocument(); +} + void TaskWPGanttView::slotContextMenuRequested( const QModelIndex &idx, const QPoint& pos ) { debugPlanWork<nodeForIndex( idx ); if ( node ) { return slotContextMenuRequested( node, pos ); } Document *doc = itemModel()->documentForIndex( idx ); if ( doc ) { return slotContextMenuRequested( doc, pos ); } return slotHeaderContextMenuRequested( pos ); } void TaskWPGanttView::setupGui() { actionOptions = new QAction(koIcon("configure"), i18n("Configure View..."), this); connect(actionOptions, &QAction::triggered, this, &TaskWPGanttView::slotOptions); addContextAction( actionOptions ); } void TaskWPGanttView::slotOptions() { debugPlanWork; QPointer dlg = new ItemViewSettupDialog( 0, m_view->treeView(), true, this ); dlg->exec(); delete dlg; saveContext(); } bool TaskWPGanttView::loadContext() { KoXmlDocument doc; doc.setContent( PlanWorkSettings::self()->taskWPGanttView() ); KoXmlElement context = doc.namedItem( "TaskWPGanttViewSettings" ).toElement(); if ( context.isNull() ) { debugPlanWork<<"No settings"; return false; } return m_view->loadContext( context ); } void TaskWPGanttView::saveContext() { QDomDocument doc ( "TaskWPGanttView" ); QDomElement context = doc.createElement( "TaskWPGanttViewSettings" ); doc.appendChild( context ); m_view->saveContext( context ); PlanWorkSettings::self()->setTaskWPGanttView( doc.toString() ); PlanWorkSettings::self()->save(); debugPlanWork< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef TASKWORKPACKAGEVIEW_H #define TASKWORKPACKAGEVIEW_H #include "planwork_export.h" #include "kptitemmodelbase.h" #include "kptviewbase.h" #include "kptganttview.h" #include #include class QItemSelection; namespace KPlato { class Project; class Node; class Document; } using namespace KPlato; namespace KPlatoWork { class Part; class WorkPackage; class TaskWorkPackageModel; class PLANWORK_EXPORT TaskWorkPackageTreeView : public DoubleTreeViewBase { Q_OBJECT public: TaskWorkPackageTreeView( Part *part, QWidget *parent ); //void setSelectionModel( QItemSelectionModel *selectionModel ); TaskWorkPackageModel *itemModel() const; Project *project() const; void setProject( Project *project ); Document *currentDocument() const; Node *currentNode() const; QList selectedNodes() const; Q_SIGNALS: void sectionsMoved(); protected Q_SLOTS: void slotActivated( const QModelIndex &index ); void setSortOrder( int col, Qt::SortOrder order ); protected: void dragMoveEvent(QDragMoveEvent *event); }; class PLANWORK_EXPORT AbstractView : public QWidget, public ViewActionLists { Q_OBJECT public: AbstractView( Part *part, QWidget *parent ); /// reimplement virtual void updateReadWrite( bool readwrite ); /// reimplement virtual Node *currentNode() const; /// reimplement virtual Document *currentDocument() const; /// reimplement virtual QList selectedNodes() const; /// Loads context info into this view. Reimplement. virtual bool loadContext(); /// Save context info from this view. Reimplement. virtual void saveContext(); /// reimplement virtual KoPrintJob *createPrintJob(); Q_SIGNALS: void requestPopupMenu( const QString& name, const QPoint &pos ); void selectionChanged(); protected Q_SLOTS: /// Builds menu from action list virtual void slotHeaderContextMenuRequested( const QPoint &pos ); /// Reimplement if you have index specific context menu, standard calls slotHeaderContextMenuRequested() void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); /// Should not need to be reimplemented virtual void slotContextMenuRequested(KPlato::Node *node, const QPoint& pos); /// Should not need to be reimplemented virtual void slotContextMenuRequested(KPlato::Document *doc, const QPoint& pos); /// Calls saveContext(), connect to this to have configuration saved virtual void sectionsMoved(); protected: Part *m_part; }; class PLANWORK_EXPORT TaskWorkPackageView : public AbstractView { Q_OBJECT public: TaskWorkPackageView( Part *part, QWidget *parent ); void setupGui(); TaskWorkPackageModel *itemModel() const { return m_view->itemModel(); } void updateReadWrite( bool readwrite ); Node *currentNode() const; Document *currentDocument() const; QList selectedNodes() const; /// Loads context info into this view. Reimplement. virtual bool loadContext(); /// Save context info from this view. Reimplement. virtual void saveContext(); using AbstractView::slotContextMenuRequested; protected Q_SLOTS: void slotOptions(); void slotSplitView(); void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotSelectionChanged( const QModelIndexList &lst ); protected: void updateActionsEnabled( bool on ); private: TaskWorkPackageTreeView *m_view; }; //------------- class GanttItemDelegate : public KPlato::GanttItemDelegate { Q_OBJECT public: enum Brushes { Brush_Normal, Brush_Late, Brush_NotScheduled, Brush_Finished, Brush_NotReadyToStart, Brush_ReadyToStart }; explicit GanttItemDelegate(QObject *parent = 0); void paintGanttItem( QPainter* painter, const KGantt::StyleOptionGanttItem& opt, const QModelIndex& idx ); QString toolTip( const QModelIndex &idx ) const; protected: bool showStatus; QMap m_brushes; }; class GanttView : public KPlato::GanttViewBase { Q_OBJECT public: GanttView( Part *part, QWidget *parent ); ~GanttView(); TaskWorkPackageModel *itemModel() const; void setProject( Project *project ); Project *project() const { return m_project; } GanttItemDelegate *delegate() const { return m_ganttdelegate; } QList selectedNodes() const; Node *currentNode() const; + Document *currentDocument() const; /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &context ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &context ) const; Q_SIGNALS: void headerContextMenuRequested( const QPoint& ); void contextMenuRequested( const QModelIndex&, const QPoint& ); void selectionChanged( const QModelIndexList& ); void sectionsMoved(); protected Q_SLOTS: void slotSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected ); void slotRowsInserted( const QModelIndex &parent, int start, int end ); void slotRowsRemoved( const QModelIndex &parent, int start, int end ); void updateDateTimeGrid(KPlatoWork::WorkPackage *wp); protected: Part *m_part; Project *m_project; GanttItemDelegate *m_ganttdelegate; TaskWorkPackageModel *m_itemmodel; KGantt::TreeViewRowController *m_rowController; }; class PLANWORK_EXPORT TaskWPGanttView : public AbstractView { Q_OBJECT public: TaskWPGanttView( Part *part, QWidget *parent ); void setupGui(); TaskWorkPackageModel *itemModel() const { return m_view->itemModel(); } Node *currentNode() const; QList selectedNodes() const; + Document *currentDocument() const; /// Loads context info into this view. Reimplement. virtual bool loadContext(); /// Save context info from this view. Reimplement. virtual void saveContext(); using AbstractView::slotContextMenuRequested; protected Q_SLOTS: void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotSelectionChanged( const QModelIndexList &lst ); void slotOptions(); private: GanttView *m_view; }; } //namespace KPlatoWork #endif diff --git a/src/workpackage/view.cpp b/src/workpackage/view.cpp index 08761c38..3e7e9c15 100644 --- a/src/workpackage/view.cpp +++ b/src/workpackage/view.cpp @@ -1,485 +1,486 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2009, 2011, 2012 Dag Andersen Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "view.h" #include "mainwindow.h" #include "taskworkpackageview.h" #include "workpackage.h" #include "taskcompletiondialog.h" #include "calligraplanworksettings.h" #include "kpttaskeditor.h" #include "kpttaskdescriptiondialog.h" #include "kptcommonstrings.h" #include "KoDocumentInfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "part.h" #include "factory.h" #include "kptviewbase.h" #include "kptdocumentseditor.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptcommand.h" #include "kptdocuments.h" #include "kpttaskprogressdialog.h" #include "kptcalendar.h" #include #include "debugarea.h" namespace KPlatoWork { View::View( Part *part, QWidget *parent, KActionCollection *collection ) : QStackedWidget( parent ), m_part( part ), m_scheduleActionGroup( new QActionGroup( this ) ), m_manager( 0 ) { m_readWrite = part->isReadWrite(); debugPlanWork<addAction("package_remove_selected", actionRemoveSelectedPackages ); connect( actionRemoveSelectedPackages, &QAction::triggered, this, &View::slotRemoveSelectedPackages ); actionRemoveCurrentPackage = new QAction(koIcon("edit-delete"), i18n("Remove Package"), this); collection->addAction("package_remove_current", actionRemoveCurrentPackage ); connect( actionRemoveCurrentPackage, &QAction::triggered, this, &View::slotRemoveCurrentPackage ); actionViewList = new QAction(koIcon("view-list-tree"), i18n("List"), this); actionViewList->setToolTip( i18nc( "@info:tooltip", "Select task list" ) ); collection->addAction("view_list", actionViewList ); connect( actionViewList, &QAction::triggered, this, &View::slotViewList ); actionViewGantt = new QAction(koIcon("view-time-schedule"), i18n("Gantt"), this); actionViewGantt->setToolTip( i18nc( "@info:tooltip", "Select timeline" ) ); collection->addAction("view_gantt", actionViewGantt ); connect( actionViewGantt, &QAction::triggered, this, &View::slotViewGantt ); // actionTaskProgress = new QAction(koIcon("document-edit"), i18n("Progress..."), this); // collection->addAction("task_progress", actionTaskProgress ); // connect( actionTaskProgress, SIGNAL(triggered(bool)), SLOT(slotTaskProgress()) ); //------ Settings actionConfigure = new QAction(koIcon("configure"), i18n("Configure PlanWork..."), this); collection->addAction("configure", actionConfigure ); connect( actionConfigure, &QAction::triggered, this, &View::slotConfigure ); //------ Popups actionEditDocument = new QAction(koIcon("document-edit"), i18n("Edit..."), this); collection->addAction("edit_document", actionEditDocument ); connect( actionEditDocument, SIGNAL(triggered(bool)), SLOT(slotEditDocument()) ); actionViewDocument = new QAction(koIcon("document-preview"), i18nc( "@verb", "View..."), this); collection->addAction("view_document", actionViewDocument ); connect( actionViewDocument, &QAction::triggered, this, &View::slotViewDocument ); // FIXME remove UndoText::removeDocument() when string freeze is lifted actionRemoveDocument = new QAction(koIcon("list-remove"), UndoText::removeDocument().toString(), this); collection->addAction("remove_document", actionRemoveDocument ); connect( actionRemoveDocument, &QAction::triggered, this, &View::slotRemoveDocument ); actionSendPackage = new QAction(koIcon("mail-send"), i18n("Send Package..."), this); collection->addAction("edit_sendpackage", actionSendPackage ); connect( actionSendPackage, &QAction::triggered, this, &View::slotSendPackage ); actionTaskCompletion = new QAction(koIcon("document-edit"), i18n("Edit Progress..."), this); collection->addAction("task_progress", actionTaskCompletion ); connect( actionTaskCompletion, &QAction::triggered, this, &View::slotTaskCompletion ); actionViewDescription = new QAction(/*koIcon("document_view"),*/ i18n("View Description..."), this); collection->addAction("task_description", actionViewDescription ); connect( actionViewDescription, &QAction::triggered, this, &View::slotTaskDescription ); updateReadWrite( m_readWrite ); //debugPlanWork<<" end"; loadContext(); slotCurrentChanged( currentIndex() ); connect( this, &QStackedWidget::currentChanged, this, &View::slotCurrentChanged ); slotSelectionChanged(); } View::~View() { saveContext(); } void View::slotCurrentChanged( int index ) { actionViewList->setEnabled( index != 0 ); actionViewGantt->setEnabled( index != 1 ); saveContext(); } void View::slotViewList() { debugPlanWork; setCurrentIndex( 0 ); } void View::slotViewGantt() { debugPlanWork; setCurrentIndex( 1 ); } void View::createViews() { QWidget *v = createTaskWorkPackageView(); addWidget( v ); v = createGanttView(); addWidget( v ); } TaskWorkPackageView *View::createTaskWorkPackageView() { TaskWorkPackageView *v = new TaskWorkPackageView( part(), this ); connect( v, &AbstractView::requestPopupMenu, this, &View::slotPopupMenu ); connect( v, &AbstractView::selectionChanged, this, &View::slotSelectionChanged ); v->updateReadWrite( m_readWrite ); v->loadContext(); return v; } TaskWPGanttView *View::createGanttView() { TaskWPGanttView *v = new TaskWPGanttView( part(), this ); connect( v, &AbstractView::requestPopupMenu, this, &View::slotPopupMenu ); connect( v, &AbstractView::selectionChanged, this, &View::slotSelectionChanged ); v->updateReadWrite( m_readWrite ); v->loadContext(); return v; } void View::setupPrinter( QPrinter &/*printer*/, QPrintDialog &/*printDialog */) { //debugPlanWork; } void View::print( QPrinter &/*printer*/, QPrintDialog &/*printDialog*/ ) { } void View::slotSelectionChanged() { bool enable = ! currentView()->selectedNodes().isEmpty(); actionRemoveSelectedPackages->setEnabled( enable ); actionRemoveCurrentPackage->setEnabled( enable ); } void View::slotEditCut() { //debugPlanWork; } void View::slotEditCopy() { //debugPlanWork; } void View::slotEditPaste() { //debugPlanWork; } void View::slotProgressChanged( int ) { } void View::slotConfigure() { } ScheduleManager *View::currentScheduleManager() const { WorkPackage *wp = m_part->findWorkPackage( currentNode() ); return wp ? wp->project()->scheduleManagers().value(0) : nullptr; } void View::updateReadWrite( bool readwrite ) { debugPlanWork<"<setEnabled( readwrite ); emit sigUpdateReadWrite( readwrite ); } Part *View::part() const { return m_part; } void View::slotPopupMenu( const QString& name, const QPoint & pos ) { + debugPlanWork<factory() ); if ( m_part->factory() == 0 ) { return; } QMenu *menu = ( ( QMenu* ) m_part->factory() ->container( name, m_part ) ); if ( menu == 0 ) { return; } QList lst; AbstractView *v = currentView(); if ( v ) { lst = v->contextActionList(); debugPlanWork<addSeparator(); foreach ( QAction *a, lst ) { menu->addAction( a ); } } } menu->exec( pos ); foreach ( QAction *a, lst ) { menu->removeAction( a ); } } bool View::loadContext() { debugPlanWork; setCurrentIndex( PlanWorkSettings::self()->currentView() ); return true; } void View::saveContext() const { debugPlanWork; PlanWorkSettings::self()->setCurrentView( currentIndex() ); PlanWorkSettings::self()->save(); } void View::slotEditDocument() { slotEditDocument( currentDocument() ); } void View::slotEditDocument( Document *doc ) { debugPlanWork<type() != Document::Type_Product ) { KMessageBox::error( 0, i18n( "This file is not editable" ) ); return; } part()->editWorkpackageDocument( doc ); } void View::slotViewDocument() { emit viewDocument( currentDocument() ); } void View::slotRemoveDocument() { part()->removeDocument( currentDocument() ); } void View::slotSendPackage() { Node *node = currentNode(); if ( node == 0 ) { KMessageBox::error(0, i18n("No work package is selected" ) ); return; } debugPlanWork<name(); WorkPackage *wp = part()->findWorkPackage( node ); if ( wp == 0 ) { KMessageBox::error(0, i18n("Cannot find work package" ) ); return; } /* if ( wp->isModified() ) { int r = KMessageBox::questionYesNoCancel( 0, i18n("This work package has been modified.\nDo you want to save it before sending?" ), node->name() ); switch ( r ) { case KMessageBox::Cancel: return; case KMessageBox::Yes: wp->saveToProjects( part() ); break; default: break; } }*/ bool wasmodified = wp->isModified(); if (wp->sendUrl().isValid()) { QTemporaryFile temp(wp->sendUrl().toLocalFile() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); temp.setAutoRemove( false ); if ( ! temp.open() ) { KMessageBox::error( 0, i18n("Could not open file. Sending is aborted." ) ); return; } wp->saveNativeFormat( part(), temp.fileName() ); } else { QTemporaryFile temp(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); temp.setAutoRemove( false ); if ( ! temp.open() ) { KMessageBox::error( 0, i18n("Could not open temporary file. Sending is aborted." ) ); return; } wp->saveNativeFormat( part(), temp.fileName() ); QStringList attachURLs; attachURLs << temp.fileName(); QString to = node->projectNode()->leader(); QString cc; QString bcc; QString subject = i18n( "Work Package: %1", node->name() ); QString body = node->projectNode()->name(); QString messageFile; KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); } wp->setModified( wasmodified ); } void View::slotTaskDescription() { Task *node = qobject_cast( currentNode() ); if ( node == 0 ) { return; } QPointer dlg = new TaskDescriptionDialog( *node, this, true ); dlg->exec(); delete dlg; } AbstractView *View::currentView() const { return qobject_cast( currentWidget() ); } Node *View::currentNode() const { AbstractView *v = currentView(); return v ? v->currentNode() : 0; } Document *View::currentDocument() const { AbstractView *v = currentView(); return v ? v->currentDocument() : 0; } void View::slotTaskProgress() { debugPlanWork; Task *n = qobject_cast( currentNode() ); if ( n == 0 ) { return; } StandardWorktime *w = qobject_cast( n->projectNode() )->standardWorktime(); QPointer dlg = new TaskProgressDialog( *n, currentScheduleManager(), w, this ); if ( dlg->exec() == QDialog::Accepted && dlg ) { KUndo2Command *cmd = dlg->buildCommand(); if ( cmd ) { cmd->redo(); //FIXME m_part->addCommand( cmd ); } } } void View::slotTaskCompletion() { debugPlanWork; WorkPackage *wp = m_part->findWorkPackage( currentNode() ); if ( wp == 0 ) { return; } QPointer dlg = new TaskCompletionDialog( *wp, currentScheduleManager(), this ); if ( dlg->exec() == QDialog::Accepted && dlg ) { KUndo2Command *cmd = dlg->buildCommand(); if ( cmd ) { m_part->addCommand( cmd ); } } delete dlg; } void View::slotRemoveSelectedPackages() { debugPlanWork; QList lst = currentView()->selectedNodes(); if ( lst.isEmpty() ) { return; } m_part->removeWorkPackages( lst ); } void View::slotRemoveCurrentPackage() { debugPlanWork; Node *n = currentNode(); if ( n == 0 ) { return; } m_part->removeWorkPackage( n ); } } //KPlatoWork namespace