diff --git a/src/libs/ui/kptdependencyeditor.cpp b/src/libs/ui/kptdependencyeditor.cpp index 97c02539..8f909f2b 100644 --- a/src/libs/ui/kptdependencyeditor.cpp +++ b/src/libs/ui/kptdependencyeditor.cpp @@ -1,2470 +1,2470 @@ /* This file is part of the KDE project Copyright (C) 2007 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; 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 "kptdependencyeditor.h" #include "PlanMacros.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptitemmodelbase.h" #include "kptcommand.h" #include "kptproject.h" #include "kptrelation.h" #include "kptschedule.h" #include "kptdebug.h" #include "config.h" #include "Help.h" #include "RelationEditorDialog.h" #include "KoPageLayoutWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ConnectCursor Qt::DragLinkCursor namespace KPlato { void plan_paintFocusSelectedItem( QPainter *painter, const QStyleOptionGraphicsItem *option ) { if ( option->state & ( QStyle::State_Selected | QStyle::State_HasFocus ) ) { painter->save(); if (option->state & QStyle::State_Selected) { debugPlanDepEditor<<"selected"; QPalette::ColorGroup cg = option->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option->state & QStyle::State_Active)) cg = QPalette::Inactive; QLinearGradient g( 0.0, option->rect.top(), 0.0, option->rect.bottom() ); QColor col = option->palette.brush(cg, QPalette::Highlight).color(); g.setColorAt( 0.0, col.lighter( 125 ) ); g.setColorAt( 1.0, col.lighter( 60 ) ); painter->setPen( Qt::NoPen ); painter->setBrush( QBrush( g ) ); painter->drawRect( option->exposedRect ); } if ( option->state & QStyle::State_HasFocus ) { debugPlanDepEditor<<"has focus"; QPalette::ColorGroup cg = option->state & QStyle::State_Enabled ? QPalette::Active : QPalette::Disabled; if (cg == QPalette::Active && !(option->state & QStyle::State_Active)) cg = QPalette::Inactive; QPen p( Qt::DotLine ); p.setWidthF( 2. ); if ( option->state & QStyle::State_Selected ) { p.setColor( option->palette.color( cg, QPalette::Shadow ) ); debugPlanDepEditor<<"focus: selected"<palette.color( cg, QPalette::Highlight ) ); debugPlanDepEditor<<"focus: not selected"<setPen( p ); painter->setBrush( Qt::NoBrush ); painter->drawRect( option->exposedRect ); } painter->restore(); } } //---------------------- DependecyViewPrintingDialog::DependecyViewPrintingDialog( ViewBase *parent, DependencyView *view ) : PrintingDialog( parent ), m_depview( view ) { debugPlanDepEditor< DependecyViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void DependecyViewPrintingDialog::printPage( int page, QPainter &painter ) { painter.save(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); debugPlanDepEditor<project()) ); int gap = 8; int pageHeight = pageRect.height(); if ( hRect.isValid() ) { pageHeight -= ( hRect.height() + gap ); } if ( fRect.isValid() ) { pageHeight -= ( fRect.height() + gap ); } painter.translate( 0, hRect.height() + gap ); QRect r( 0, 0, pageRect.width(), pageHeight ); m_depview->itemScene()->render( &painter, r ); painter.restore(); } DependencyLinkItemBase::DependencyLinkItemBase( QGraphicsItem *parent ) : QGraphicsPathItem( parent ), m_editable( false ), predItem( 0 ), succItem( 0 ), relation( 0 ), m_arrow( new QGraphicsPathItem( this ) ) { } DependencyLinkItemBase::DependencyLinkItemBase( DependencyNodeItem *predecessor, DependencyNodeItem *successor, Relation *rel, QGraphicsItem *parent ) : QGraphicsPathItem( parent ), m_editable( false ), predItem( predecessor ), succItem( successor ), relation( rel ), m_arrow( new QGraphicsPathItem( this ) ) { } DependencyLinkItemBase::~DependencyLinkItemBase() { } DependencyScene *DependencyLinkItemBase::itemScene() const { return static_cast( scene() ); } void DependencyLinkItemBase::createPath( const QPointF &sp, int starttype, const QPointF &ep, int endtype ) { //if ( predItem && succItem ) debugPlanDepEditor<text()<<" ->"<text()<<" visible="<horizontalGap(); bool up = sp.y() > ep.y(); bool right = sp.x() < ep.x(); bool same = sp.x() == ep.x(); QPainterPath link( sp ); qreal x = sp.x(); qreal y = sp.y(); if ( right && starttype == DependencyNodeItem::Finish) { x = ep.x(); x += endtype == DependencyNodeItem::Start ? - hgap/2 - 6 : hgap/2 - 6; link.lineTo( x, y ); x += 6; QPointF cp( x, y ); y += up ? -6 : +6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); x += endtype == DependencyNodeItem::Start ? 6 : -6; link.quadTo( cp, QPointF( x, y ) ); } else if ( right && starttype == DependencyNodeItem::Start ) { x = sp.x() - hgap/2 + 6; link.lineTo( x, y ); x -= 6; QPointF cp( x, y ); y += up ? -6 : +6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); x += endtype == DependencyNodeItem::Start ? 6 : -6; link.quadTo( cp, QPointF( x, y ) ); } else if ( same ) { x = ep.x(); x += endtype == DependencyNodeItem::Start ? - hgap/2 + 6 : hgap/2 - 6; link.lineTo( x, y ); x += endtype == DependencyNodeItem::Start ? -6 : +6; QPointF cp( x, y ); y += up ? -6 : 6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); if ( endtype == DependencyNodeItem::Start ) { x += 6; } else { x -= 6; } link.quadTo( cp, QPointF( x, y ) ); } else { x = ep.x(); x += endtype == DependencyNodeItem::Start ? - hgap/2 + 6 : hgap/2 + 6; link.lineTo( x, y ); x -= 6; QPointF cp( x, y ); y += up ? -6 : 6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); x += endtype == DependencyNodeItem::Start ? 6 : -6; link.quadTo( cp, QPointF( x, y ) ); } link.lineTo( ep ); setPath( link ); QPainterPath arrow; x = endtype == DependencyNodeItem::Start ? -6 : 6; arrow.moveTo( ep ); arrow.lineTo( ep.x() + x, ep.y() - 3 ); arrow.lineTo( ep.x() + x, ep.y() + 3 ); arrow.lineTo( ep ); m_arrow->setPath( arrow ); m_arrow->show(); } //-------------------------------- DependencyLinkItem::DependencyLinkItem( DependencyNodeItem *predecessor, DependencyNodeItem *successor, Relation *rel, QGraphicsItem *parent ) : DependencyLinkItemBase( predecessor, successor, rel, parent ) { setZValue( 100.0 ); setAcceptHoverEvents( true ); //debugPlanDepEditor<text()<<"("<column()<<") -"<text(); predItem->addChildRelation( this ); succItem->addParentRelation( this ); succItem->setColumn(); m_arrow->setBrush( Qt::black ); m_pen = pen(); } DependencyLinkItem::~DependencyLinkItem() { if ( predItem ) { predItem->takeChildRelation( this ); } if ( succItem ) { succItem->takeParentRelation( this ); } } int DependencyLinkItem::newChildColumn() const { int col = predItem->column(); if ( relation->type() == Relation::FinishStart ) { ++col; } //debugPlanDepEditor<<"new col="<isVisible() && succItem->isVisible() ); } void DependencyLinkItem::createPath() { setVisible( predItem->isVisible() && succItem->isVisible() ); if ( ! isVisible() ) { //debugPlanDepEditor<<"Visible="<node()->name()<<" -"<node()->name(); return; } QPointF sp = startPoint(); QPointF ep = endPoint(); int stype = 0, etype = 0; switch ( relation->type() ) { case Relation::StartStart: stype = DependencyNodeItem::Start; etype = DependencyNodeItem::Start; break; case Relation::FinishStart: stype = DependencyNodeItem::Finish; etype = DependencyNodeItem::Start; break; case Relation::FinishFinish: stype = DependencyNodeItem::Finish; etype = DependencyNodeItem::Finish; break; default: break; } DependencyLinkItemBase::createPath( sp, stype, ep, etype ); } QPointF DependencyLinkItem::startPoint() const { if ( relation->type() == Relation::StartStart ) { return predItem->connectorPoint( DependencyNodeItem::Start ); } return predItem->connectorPoint( DependencyNodeItem::Finish ); } QPointF DependencyLinkItem::endPoint() const { if ( relation->type() == Relation::FinishFinish ) { return succItem->connectorPoint( DependencyNodeItem::Finish ); } return succItem->connectorPoint( DependencyNodeItem::Start ); } void DependencyLinkItem::hoverEnterEvent( QGraphicsSceneHoverEvent * /*event*/ ) { setZValue( zValue() + 1 ); setPen( QPen( Qt::black, 2 ) ); m_arrow->setPen( pen() ); update(); } void DependencyLinkItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * /*event*/ ) { resetHooverIndication(); } void DependencyLinkItem::resetHooverIndication() { setZValue( zValue() - 1 ); setPen( m_pen ); m_arrow->setPen( m_pen ); update(); } void DependencyLinkItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { //debugPlanDepEditor; QGraphicsItem::GraphicsItemFlags f = flags(); if ( isEditable() && itemScene()->connectionMode() ) { itemScene()->clearConnection(); setFlags( f & ~QGraphicsItem::ItemIsSelectable ); } QGraphicsPathItem::mousePressEvent( event ); if ( f != flags() ) { setFlags( f ); } } //-------------------- DependencyCreatorItem::DependencyCreatorItem( QGraphicsItem *parent ) : DependencyLinkItemBase( parent ), predConnector( 0 ), succConnector( 0 ), m_editable( false ) { setZValue( 1000.0 ); clear(); setPen( QPen( Qt::blue, 2 ) ); m_arrow->setBrush( Qt::blue ); m_arrow->setPen( QPen( Qt::blue, 2 ) ); } void DependencyCreatorItem::clear() { hide(); if ( predConnector && predConnector->parentItem() ) { static_cast( predConnector->parentItem() )->setConnectorHoverMode( true ); } else if ( succConnector && succConnector->parentItem() ) { static_cast( succConnector->parentItem() )->setConnectorHoverMode( true ); } predConnector = 0; succConnector = 0; setPath( QPainterPath() ); m_arrow->setPath( QPainterPath() ); } void DependencyCreatorItem::setPredConnector( DependencyConnectorItem *item ) { predConnector = item; //static_cast( item->parentItem() )->setConnectorHoverMode( false ); } void DependencyCreatorItem::setSuccConnector( DependencyConnectorItem *item ) { succConnector = item; } void DependencyCreatorItem::createPath() { if ( predConnector == 0 ) { return; } if ( succConnector == 0 ) { return; } QPointF sp = predConnector->connectorPoint(); QPointF ep = succConnector->connectorPoint(); DependencyLinkItemBase::createPath( sp, predConnector->ctype(), ep, succConnector->ctype() ); } void DependencyCreatorItem::createPath( const QPointF &ep ) { m_arrow->hide(); if ( succConnector ) { return createPath(); } if ( predConnector == 0 ) { return; } QPointF sp = predConnector->connectorPoint(); QPainterPath link( sp ); link.lineTo( ep ); setPath( link ); } QPointF DependencyCreatorItem::startPoint() const { return predConnector == 0 ? QPointF() : predConnector->connectorPoint(); } QPointF DependencyCreatorItem::endPoint() const { return succConnector == 0 ? QPointF() : succConnector->connectorPoint(); } //-------------------- DependencyConnectorItem::DependencyConnectorItem( DependencyNodeItem::ConnectorType type, DependencyNodeItem *parent ) : QGraphicsRectItem( parent ), m_ctype( type ), m_editable( false ) { setCursor( ConnectCursor); setAcceptHoverEvents( true ); setZValue( 500.0 ); setFlag( QGraphicsItem::ItemIsFocusable ); } DependencyScene *DependencyConnectorItem::itemScene() const { return static_cast( scene() ); } DependencyNodeItem *DependencyConnectorItem::nodeItem() const { return static_cast( parentItem() ); } Node *DependencyConnectorItem::node() const { return static_cast( parentItem() )->node(); } QPointF DependencyConnectorItem::connectorPoint() const { QRectF r = rect(); return QPointF( r.x()+r.width(), r.y() + r.height()/2 ); } void DependencyConnectorItem::hoverEnterEvent( QGraphicsSceneHoverEvent * /*event*/ ) { itemScene()->connectorEntered( this, true ); } void DependencyConnectorItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * /*event*/ ) { itemScene()->connectorEntered( this, false ); } void DependencyConnectorItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { if ( ! isEditable() ) { event->ignore(); return; } if (event->button() == Qt::LeftButton ) { m_mousePressPos = event->pos(); } else { event->ignore(); } } void DependencyConnectorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { m_mousePressPos = QPointF(); if (event->button() != Qt::LeftButton ) { event->ignore(); return; } if ( rect().contains( event->scenePos() ) ) { // user clicked on this item bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0; if (multiSelect) { itemScene()->multiConnectorClicked( this ); } else { itemScene()->singleConnectorClicked( this ); } return; } QGraphicsItem *item = 0; foreach ( QGraphicsItem *i, itemScene()->items( event->scenePos() ) ) { if ( i->type() == DependencyConnectorItem::Type ) { item = i; break; } } if ( item == 0 || item == itemScene()->fromItem() ) { itemScene()->setFromItem( 0 ); return; } itemScene()->singleConnectorClicked( static_cast( item ) ); } void DependencyConnectorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (event->buttons() == Qt::LeftButton ) { if ( ! m_mousePressPos.isNull() ) { itemScene()->setFromItem( this ); m_mousePressPos = QPointF(); } QGraphicsItem *item = 0; foreach ( QGraphicsItem *i, itemScene()->items( event->scenePos() ) ) { if ( i->type() == DependencyConnectorItem::Type ) { item = i; break; } } if ( item != this ) { itemScene()->connectorEntered( this, false ); } if ( item != 0 ) { itemScene()->connectorEntered( static_cast( item ), true ); } } else { event->ignore(); } } void DependencyConnectorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget */*widget*/) { //debugPlanDepEditor; QStyleOptionGraphicsItem opt( *option ); opt.exposedRect = rect(); if ( itemScene()->fromItem() == this ) { opt.state |= QStyle::State_Selected; } if ( itemScene()->focusItem() == this ) { opt.state |= QStyle::State_HasFocus; } plan_paintFocusSelectedItem( painter, &opt ); QRectF r = rect(); if ( ctype() == DependencyNodeItem::Start ) { r.setRect( r.right() - (r.width()/2.0) + 1.0, r.y() + ( r.height() * 0.33 ), r.width() / 2.0, r.height() * 0.33 ); } else { r.setRect( r.right() - (r.width()/2.0) - 1.0, r.y() + ( r.height() * 0.33 ), r.width() / 2.0, r.height() * 0.33 ); } painter->fillRect( r, Qt::black ); } QList DependencyConnectorItem::predecessorItems() const { return nodeItem()->predecessorItems( m_ctype ); } QList DependencyConnectorItem::successorItems() const { return nodeItem()->successorItems( m_ctype ); } //-------------------- DependencyNodeItem::DependencyNodeItem( Node *node, DependencyNodeItem *parent ) : QGraphicsRectItem( parent ), m_node( node ), m_parent( 0 ), m_editable( false ) { setAcceptHoverEvents( true ); setZValue( 400.0 ); setParentItem( parent ); m_start = new DependencyConnectorItem( DependencyNodeItem::Start, this ); m_finish = new DependencyConnectorItem( DependencyNodeItem::Finish, this ); m_text = new QGraphicsTextItem( this ); m_textFont = m_text->font(); m_textFont.setPointSize( 10 ); m_text->setFont( m_textFont ); setText(); setFlags( QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable ); // do not attach this item to the scene as it gives continuous paint events when a node item is selected m_symbol = new DependencyNodeSymbolItem(); m_symbol->setZValue( zValue() + 10.0 ); setSymbol(); m_treeIndicator = new QGraphicsPathItem( this ); m_treeIndicator->setPen( QPen( Qt::gray ) ); } DependencyNodeItem::~DependencyNodeItem() { qDeleteAll( m_childrelations ); qDeleteAll( m_parentrelations ); //qDeleteAll( m_children ); delete m_symbol; } void DependencyNodeItem::setText() { m_text->setPlainText( m_node == 0 ? QString() : QString( "%1 %2").arg( m_node->wbsCode() ).arg(m_node->name() ) ); } DependencyScene *DependencyNodeItem::itemScene() const { return static_cast( scene() ); } void DependencyNodeItem::setSymbol() { m_symbol->setSymbol( m_node->type(), itemScene()->symbolRect() ); } QPointF DependencyNodeItem::connectorPoint( DependencyNodeItem::ConnectorType type ) const { QRectF r; if ( type == Start ) { return m_start->connectorPoint(); } return m_finish->connectorPoint(); } void DependencyNodeItem::setConnectorHoverMode( bool mode ) { m_start->setAcceptHoverEvents( mode ); m_finish->setAcceptHoverEvents( mode ); } void DependencyNodeItem::setParentItem( DependencyNodeItem *parent ) { if ( m_parent ) { m_parent->takeChild( this ); } m_parent = parent; if ( m_parent ) { m_parent->addChild( this ); } } void DependencyNodeItem::setExpanded( bool mode ) { foreach ( DependencyNodeItem *ch, m_children ) { itemScene()->setItemVisible( ch, mode ); ch->setExpanded( mode ); } } void DependencyNodeItem::setItemVisible( bool show ) { setVisible( show ); //debugPlanDepEditor<name(); foreach ( DependencyLinkItem *i, m_parentrelations ) { i->setItemVisible( show ); } foreach ( DependencyLinkItem *i, m_childrelations ) { i->setItemVisible( show ); } } DependencyNodeItem *DependencyNodeItem::takeChild( DependencyNodeItem *ch ) { int i = m_children.indexOf( ch ); if ( i == -1 ) { return 0; } return m_children.takeAt( i ); } void DependencyNodeItem::setRectangle( const QRectF &rect ) { //debugPlanDepEditor<( scene() )->connectorWidth(); m_start->setRect( rect.x() + connection, rect.y(), -connection, rect.height() ); m_finish->setRect( rect.right() - connection, rect.y(), connection, rect.height() ); m_text->setPos( m_finish->rect().right() + 2.0, itemScene()->gridY( row() ) ); m_symbol->setPos( rect.topLeft() + QPointF( connection, 0 ) + QPointF( 2.0, 2.0 ) ); } void DependencyNodeItem::moveToY( qreal y ) { QRectF r = rect(); r. moveTop( y ); setRectangle( r ); //debugPlanDepEditor<createPath(); } foreach ( DependencyLinkItem *i, m_childrelations ) { i->createPath(); } DependencyNodeItem *par = this; while ( par->parentItem() ) { par = par->parentItem(); } par->setTreeIndicator( true ); } void DependencyNodeItem::setRow( int row ) { moveToY( itemScene()->itemY( row ) ); } int DependencyNodeItem::row() const { return itemScene()->row( rect().y() ); } void DependencyNodeItem::moveToX( qreal x ) { QRectF r = rect(); r. moveLeft( x ); setRectangle( r ); //debugPlanDepEditor<toPlainText()<<" to="<createPath(); } foreach ( DependencyLinkItem *i, m_childrelations ) { i->createPath(); } DependencyNodeItem *par = this; while ( par->parentItem() ) { par = par->parentItem(); } par->setTreeIndicator( true ); } void DependencyNodeItem::setColumn() { int col = m_parent == 0 ? 0 : m_parent->column() + 1; //debugPlanDepEditor<newChildColumn() ); } if ( col != column() ) { setColumn( col ); foreach ( DependencyLinkItem *i, m_childrelations ) { i->succItem->setColumn(); } //debugPlanDepEditor<setColumn(); } } } void DependencyNodeItem::setColumn( int col ) { moveToX( itemScene()->itemX( col ) ); } int DependencyNodeItem::column() const { return itemScene()->column( rect().x() ); } DependencyLinkItem *DependencyNodeItem::takeParentRelation( DependencyLinkItem *r ) { int i = m_parentrelations.indexOf( r ); if ( i == -1 ) { return 0; } DependencyLinkItem *dep = m_parentrelations.takeAt( i ); setColumn(); return dep; } DependencyLinkItem *DependencyNodeItem::takeChildRelation( DependencyLinkItem *r ) { int i = m_childrelations.indexOf( r ); if ( i == -1 ) { return 0; } return m_childrelations.takeAt( i ); } void DependencyNodeItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { debugPlanDepEditor; QGraphicsItem::GraphicsItemFlags f = flags(); if ( itemScene()->connectionMode() ) { itemScene()->clearConnection(); setFlags( f & ~QGraphicsItem::ItemIsSelectable ); } QGraphicsRectItem::mousePressEvent( event ); if ( f != flags() ) { setFlags( f ); } } void DependencyNodeItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * ) { //debugPlanDepEditor; QLinearGradient g( 0.0, rect().top(), 0.0, rect().bottom() ); g.setColorAt( 0.0, option->palette.color( QPalette::Midlight ) ); g.setColorAt( 1.0, option->palette.color( QPalette::Dark ) ); QBrush b( g ); painter->setBrush( b ); painter->setPen( QPen( Qt::NoPen ) ); painter->drawRect( rect() ); QStyleOptionGraphicsItem opt( *option ); opt.exposedRect = rect().adjusted( -m_start->rect().width(), 0.0, -m_finish->rect().width(), 0.0 ); if ( this == itemScene()->focusItem() ) { opt.state |= QStyle::State_HasFocus; } plan_paintFocusSelectedItem( painter, &opt ); // paint the symbol m_symbol->paint( itemScene()->project(), painter, &opt ); } DependencyConnectorItem *DependencyNodeItem::connectorItem( ConnectorType ctype ) const { switch ( ctype ) { case Start: return m_start; case Finish: return m_finish; default: break; } return 0; } QList DependencyNodeItem::predecessorItems( ConnectorType ctype ) const { QList lst; foreach ( DependencyLinkItem *i, m_parentrelations ) { if ( ctype == Start && ( i->relation->type() == Relation::StartStart || i->relation->type() == Relation::FinishStart ) ) { lst << i; } if ( ctype == Finish && i->relation->type() == Relation::FinishFinish ) { lst << i; } } return lst; } QList DependencyNodeItem::successorItems( ConnectorType ctype ) const { QList lst; foreach ( DependencyLinkItem *i, m_childrelations ) { if ( ctype == Start && i->relation->type() == Relation::StartStart ) { lst << i; } if ( ctype == Finish && ( i->relation->type() == Relation::FinishFinish || i->relation->type() == Relation::FinishStart ) ) { lst << i; } } return lst; } qreal DependencyNodeItem::treeIndicatorX() const { return rect().x() + 18.0; } void DependencyNodeItem::setTreeIndicator( bool on ) { paintTreeIndicator( on ); foreach ( DependencyNodeItem *i, m_children ) { if ( i->isVisible() ) { i->setTreeIndicator( on ); } } } void DependencyNodeItem::paintTreeIndicator( bool on ) { if ( ! on ) { m_treeIndicator->hide(); return; } QPainterPath p; qreal y1 = itemScene()->gridY( row() ); qreal y2 = itemScene()->gridY( row() + 1 ); for ( DependencyNodeItem *par = m_parent; par; par = par->parentItem() ) { qreal x = par->treeIndicatorX(); p.moveTo( x, y1 ); if ( par == m_parent ) { p.lineTo( x, (y1 + y2) / 2.0 ); p.lineTo( x + 6, (y1 + y2) / 2.0 ); if ( m_node->siblingAfter() ) { p.moveTo( x, (y1 + y2) / 2.0 ); p.lineTo( x, y2 ); } } else { const QList &children = par->children(); if ( children.last()->rect().y() > rect().y() ) { p.lineTo( x, (y1 + y2) / 2.0 ); p.lineTo( x, y2 ); } } } if ( ! m_children.isEmpty() ) { qreal x = treeIndicatorX(); qreal y = rect().bottom(); p.moveTo( x, y ); p.lineTo( x, itemScene()->gridY( row() + 1 ) ); } if ( p.isEmpty() ) { m_treeIndicator->hide(); } else { m_treeIndicator->setPath( p ); m_treeIndicator->show(); } //debugPlanDepEditor<setBrush( p->config().summaryTaskDefaultColor() ); break; case Node::Type_Task: painter->setBrush( p->config().taskNormalColor() ); break; case Node::Type_Milestone: painter->setBrush( p->config().milestoneNormalColor() ); break; default: painter->setBrush( m_delegate.defaultBrush( m_itemtype ) ); break; } } else { painter->setBrush( m_delegate.defaultBrush( m_itemtype ) ); } painter->setPen( Qt::NoPen ); painter->translate( option->exposedRect.x() + 2.0, option->exposedRect.y() + 2.0 ); painter->drawPath( path() ); } //-------------------- DependencyScene::DependencyScene( QWidget *parent ) : QGraphicsScene( parent ), m_model( 0 ), m_readwrite( false ) { setSceneRect( QRectF() ); m_connectionitem = new DependencyCreatorItem(); addItem( m_connectionitem ); //debugPlanDepEditor; m_connectionitem->hide(); } DependencyScene::~DependencyScene() { //debugPlanDepEditor<<" DELETED"; clearScene(); } void DependencyScene::setFromItem( DependencyConnectorItem *item ) { DependencyConnectorItem *old = fromItem(); m_connectionitem->clear(); if ( old && old->parentItem() ) { old->parentItem()->update(); } if ( item ) { foreach ( QGraphicsItem *i, items() ) { if ( i != m_connectionitem && i->type() != DependencyConnectorItem::Type ) { i->setAcceptHoverEvents( false ); if ( i->type() == DependencyLinkItem::Type ) { static_cast( i )->resetHooverIndication(); } } } item->setCursor( ConnectCursor ); m_connectionitem->setPredConnector( item ); m_connectionitem->show(); } else { foreach ( QGraphicsItem *i, items() ) { if ( i != m_connectionitem && i->type() != DependencyConnectorItem::Type ) i->setAcceptHoverEvents( true ); } } if ( item && item->parentItem() ) { item->parentItem()->update(); } } bool DependencyScene::connectionIsValid( DependencyConnectorItem *pred, DependencyConnectorItem *succ ) { if ( pred->ctype() == DependencyNodeItem::Start && succ->ctype() == DependencyNodeItem::Finish ) { return false; } Node *par = static_cast( pred->parentItem() )->node(); Node *ch = static_cast( succ->parentItem() )->node(); return m_project->linkExists( par, ch ) || m_project->legalToLink( par, ch ); } void DependencyScene::connectorEntered( DependencyConnectorItem *item, bool entered ) { //debugPlanDepEditor<setCursor( ConnectCursor ); if ( ! entered ) { // when we leave a connector we don't have a successor m_connectionitem->setSuccConnector( 0 ); return; } if ( m_connectionitem->predConnector == item ) { // when inside the predecessor, clicking is allowed (deselects connector) item->setCursor( ConnectCursor ); return; } if ( ! m_connectionitem->isVisible() ) { // we are not in connection mode return; } if ( m_connectionitem->predConnector == 0 ) { // nothing we can do if we don't have a predecessor (shouldn't happen) return; } if ( item->parentItem() == m_connectionitem->predConnector->parentItem() ) { // not allowed to connect to the same node item->setCursor( Qt::ForbiddenCursor ); return; } if ( ! ( connectionIsValid( m_connectionitem->predConnector, item ) ) ) { // invalid connection (circular dependency, connecting to parent node, etc) item->setCursor( Qt::ForbiddenCursor ); return; } m_connectionitem->setSuccConnector( item ); m_connectionitem->createPath(); } void DependencyScene::drawBackground ( QPainter *painter, const QRectF &rect ) { QGraphicsScene::drawBackground( painter, rect ); QStyleOptionViewItem opt; QBrush br( opt.palette.brush( QPalette::AlternateBase ) ); int first = row( rect.y() ); int last = row( rect.bottom() ); for ( int r = first; r <= last; ++r ) { if ( r % 2 == 1 ) { qreal oy = gridY( r ); QRectF rct( rect.x(), oy, rect.width(), gridHeight() ); painter->fillRect( rct, br ); //debugPlanDepEditor< DependencyScene::itemList( int type ) const { QList lst; foreach ( QGraphicsItem *i, items() ) { if ( i->type() == type ) { lst << i; } } return lst; } void DependencyScene::clearScene() { m_connectionitem->clear(); QList its, deps; foreach ( QGraphicsItem *i, items() ) { if ( i->type() == DependencyNodeItem::Type && i->parentItem() == 0 ) { its << i; } else if ( i->type() == DependencyLinkItem::Type ) { deps << i; } } qDeleteAll( deps ); qDeleteAll( its ); removeItem( m_connectionitem ); qDeleteAll( items() ); setSceneRect( QRectF() ); addItem( m_connectionitem ); //debugPlanDepEditor; } QList DependencyScene::removeChildItems( DependencyNodeItem *item ) { QList lst; foreach ( DependencyNodeItem *i, item->children() ) { m_allItems.removeAt( m_allItems.indexOf( i ) ); lst << i; lst += removeChildItems( i ); } return lst; } void DependencyScene::moveItem( DependencyNodeItem *item, const QList &lst ) { //debugPlanDepEditor<text(); int idx = m_allItems.indexOf( item ); int ndx = lst.indexOf( item->node() ); Q_ASSERT( idx != -1 && ndx != -1 ); Node *oldParent = item->parentItem() == 0 ? 0 : item->parentItem()->node(); Node *newParent = item->node()->parentNode(); if ( newParent == m_project ) { newParent = 0; } else debugPlanDepEditor<name()<level(); if ( idx != ndx || oldParent != newParent ) { // If I have children, these must be moved too. QList items = removeChildItems( item ); m_allItems.removeAt( idx ); m_allItems.insert( ndx, item ); item->setParentItem( m_allItems.value( lst.indexOf( newParent ) ) ); item->setColumn(); //debugPlanDepEditor<text()<<":"<"<column()<setColumn(); //debugPlanDepEditor<text()<<": ->"<column()<flatNodeList() ); // might have been moved } m_hiddenItems.clear(); m_visibleItems.clear(); int viewrow = 0; for ( int i = 0; i < m_allItems.count(); ++i ) { DependencyNodeItem *itm = m_allItems[ i ]; if ( itm->isVisible() ) { m_visibleItems.insert( i, itm ); //debugPlanDepEditor<text()<<":"<setRow( viewrow ); ++viewrow; } else { m_hiddenItems.insert( i, itm ); } } } DependencyNodeItem *DependencyScene::findPrevItem( Node *node ) const { if ( node->numChildren() == 0 ) { return findItem( node ); } return findPrevItem( node->childNodeIterator().last() ); } DependencyNodeItem *DependencyScene::itemBefore( DependencyNodeItem *parent, Node *node ) const { Node *sib = node->siblingBefore(); DependencyNodeItem *bef = parent; if ( sib ) { bef = findPrevItem( sib ); } return bef; } DependencyNodeItem *DependencyScene::createItem( Node *node ) { DependencyNodeItem *parent = findItem( node->parentNode() ); DependencyNodeItem *after = itemBefore( parent, node ); int i = m_allItems.count()-1; if ( after ) { i = m_allItems.indexOf( after ); //debugPlanDepEditor<<"after="<node()->name()<<" pos="<scene() != this ) { addItem( item ); } item->setEditable( m_readwrite ); item->startConnector()->setEditable( m_readwrite ); item->finishConnector()->setEditable( m_readwrite ); //debugPlanDepEditor<text()<column() + 1; } item->setRectangle( QRectF( itemX( col ), itemY(), itemWidth(), itemHeight() ) ); m_allItems.insert( i+1, item ); setItemVisible( item, true ); return item; } DependencyLinkItem *DependencyScene::findItem( const Relation* rel ) const { foreach ( QGraphicsItem *i, itemList( DependencyLinkItem::Type ) ) { if ( static_cast( i )->relation == rel ) { return static_cast( i ); } } return 0; } DependencyLinkItem *DependencyScene::findItem( const DependencyConnectorItem *c1, const DependencyConnectorItem *c2, bool exact ) const { DependencyNodeItem *n1 = c1->nodeItem(); DependencyNodeItem *n2 = c2->nodeItem(); foreach ( QGraphicsItem *i, itemList( DependencyLinkItem::Type ) ) { DependencyLinkItem *link = static_cast( i ); if ( link->predItem == n1 && link->succItem == n2 ) { switch ( link->relation->type() ) { case Relation::StartStart: if ( c1->ctype() == DependencyNodeItem::Start && c2->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishStart: if ( c1->ctype() == DependencyNodeItem::Finish && c2->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishFinish: if ( c1->ctype() == DependencyNodeItem::Finish && c2->ctype() == DependencyNodeItem::Finish ) { return link; } break; default: break; } return 0; } if ( link->predItem == n2 && link->succItem == n1 ) { if ( exact ) { return 0; } switch ( link->relation->type() ) { case Relation::StartStart: if ( c2->ctype() == DependencyNodeItem::Start && c1->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishStart: if ( c2->ctype() == DependencyNodeItem::Finish && c1->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishFinish: if ( c2->ctype() == DependencyNodeItem::Finish && c1->ctype() == DependencyNodeItem::Finish ) { return link; } break; default: break; } return 0; } } return 0; } DependencyNodeItem *DependencyScene::findItem( const Node *node ) const { foreach ( QGraphicsItem *i, itemList( DependencyNodeItem::Type ) ) { if ( static_cast( i )->node() == node ) { return static_cast( i ); } } return 0; } void DependencyScene::createLinks() { foreach ( DependencyNodeItem *i, m_allItems ) { createLinks( i ); } } void DependencyScene::createLinks( DependencyNodeItem *item ) { foreach ( Relation *rel, item->node()->dependChildNodes() ) { createLink( item, rel ); } } void DependencyScene::createLink( DependencyNodeItem *parent, Relation *rel ) { DependencyNodeItem *child = findItem( rel->child() ); if ( parent == 0 || child == 0 ) { return; } DependencyLinkItem *dep = new DependencyLinkItem( parent, child, rel ); dep->setEditable( m_readwrite ); addItem( dep ); //debugPlanDepEditor; dep->createPath(); } void DependencyScene::mouseMoveEvent( QGraphicsSceneMouseEvent *mouseEvent ) { if ( m_connectionitem->isVisible() ) { int x = qMin( qMax( sceneRect().left() + 2, mouseEvent->scenePos().x() ), sceneRect().right() - 4 ); int y = qMin( qMax( sceneRect().top() + 2, mouseEvent->scenePos().y() ), sceneRect().bottom() - 4 ); m_connectionitem->createPath( QPoint( x, y ) ); } QGraphicsScene::mouseMoveEvent( mouseEvent ); //debugPlanDepEditor<scenePos()<<","<isAccepted(); } void DependencyScene::keyPressEvent( QKeyEvent *keyEvent ) { //debugPlanDepEditor<update(); } emit focusItemChanged( focusItem() ); return; } switch ( keyEvent->key() ) { case Qt::Key_Left: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyConnectorItem *item = static_cast( fitem )->startConnector(); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); if ( citem->ctype() == DependencyNodeItem::Start ) { //Goto prev nodes finishConnector DependencyNodeItem *nitem = static_cast( citem->parentItem() ); DependencyNodeItem *item = nodeItem( nitem->row() - 1 ); if ( item ) { setFocusItem( item->finishConnector() ); } } else { // Goto node item (parent) setFocusItem( citem->parentItem() ); } } break; } case Qt::Key_Right: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyConnectorItem *item = static_cast( fitem )->finishConnector(); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); if ( citem->ctype() == DependencyNodeItem::Finish ) { //Goto prev nodes startConnector DependencyNodeItem *nitem = static_cast( citem->parentItem() ); DependencyNodeItem *item = nodeItem( nitem->row() + 1 ); if ( item ) { setFocusItem( item->startConnector() ); } } else { // Goto node item (parent) setFocusItem( citem->parentItem() ); } } break; } case Qt::Key_Up: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() - 1 ); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); DependencyNodeItem *nitem = static_cast( citem->parentItem() ); if ( citem->ctype() == DependencyNodeItem::Finish ) { DependencyNodeItem *item = nodeItem( nitem->row() - 1 ); if ( item ) { setFocusItem( item->finishConnector() ); } } else { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() - 1 ); if ( item ) { setFocusItem( item->startConnector() ); } } } break; } case Qt::Key_Down: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() + 1 ); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); DependencyNodeItem *nitem = static_cast( citem->parentItem() ); if ( citem->ctype() == DependencyNodeItem::Finish ) { DependencyNodeItem *item = nodeItem( nitem->row() + 1 ); if ( item ) { setFocusItem( item->finishConnector() ); } } else { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() + 1 ); if ( item ) { setFocusItem( item->startConnector() ); } } } break; } case Qt::Key_Space: case Qt::Key_Select: { if ( fitem->type() == DependencyConnectorItem::Type ) { singleConnectorClicked( static_cast( fitem ) ); } else if ( fitem->type() == DependencyNodeItem::Type ) { singleConnectorClicked( 0 ); foreach ( QGraphicsItem *i, selectedItems() ) { i->setSelected( false ); } fitem->setSelected( true ); } return; } default: QGraphicsScene::keyPressEvent( keyEvent ); } if ( fitem ) { fitem->parentItem() ? fitem->parentItem()->update() : fitem->update(); } if ( focusItem() ) { focusItem()->parentItem() ? focusItem()->parentItem()->update() : focusItem()->update(); } if ( fitem != focusItem() ) { emit focusItemChanged( focusItem() ); } } DependencyNodeItem *DependencyScene::nodeItem( int row ) const { if ( row < 0 || m_visibleItems.isEmpty() ) { return 0; } foreach ( DependencyNodeItem *i, m_visibleItems ) { if ( i->row() == row ) { return i; } } return 0; } void DependencyScene::singleConnectorClicked( DependencyConnectorItem *item ) { //debugPlanDepEditor; clearSelection(); QList lst; if ( item == 0 || item == fromItem() ) { setFromItem( 0 ); m_clickedItems = lst; } else if ( fromItem() == 0 ) { setFromItem( item ); } else if ( connectionIsValid( fromItem(), item ) ) { emit connectItems( fromItem(), item ); setFromItem( 0 ); } else { setFromItem( 0 ); } emit connectorClicked( item ); } void DependencyScene::multiConnectorClicked( DependencyConnectorItem *item ) { //debugPlanDepEditor; singleConnectorClicked( item ); } void DependencyScene::clearConnection() { setFromItem( 0 ); m_clickedItems.clear(); } void DependencyScene::mousePressEvent( QGraphicsSceneMouseEvent *mouseEvent ) { //debugPlanDepEditor; QGraphicsScene::mousePressEvent( mouseEvent ); if ( ! mouseEvent->isAccepted() ) { clearConnection(); } } void DependencyScene::mouseDoubleClickEvent ( QGraphicsSceneMouseEvent *event ) { //debugPlanDepEditor<pos()<scenePos()<screenPos(); QGraphicsScene::mouseDoubleClickEvent( event ); emit itemDoubleClicked( itemAt( event->scenePos(), QTransform() ) ); } void DependencyScene::contextMenuEvent ( QGraphicsSceneContextMenuEvent *event ) { if ( event->reason() == QGraphicsSceneContextMenuEvent::Mouse ) { debugPlanDepEditor<<"Mouse:"<scenePos(), QTransform())<pos()<scenePos()<screenPos(); emit contextMenuRequested( itemAt( event->scenePos(), QTransform() ), event->screenPos() ); return; } if ( focusItem() ) { if ( focusItem()->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *to = static_cast( focusItem() ); DependencyConnectorItem *from = fromItem(); debugPlanDepEditor<<"DependencyConnectorItem:"<type() == DependencyConnectorItem::Type ) { static_cast( i )->setEditable( on ); } else if ( i->type() == DependencyLinkItem::Type ) { static_cast( i )->setEditable( on ); } } } //-------------------- DependencyView::DependencyView( QWidget *parent ) : QGraphicsView( parent ), m_project( 0 ), m_dirty( false ), m_active( false ) { setItemScene( new DependencyScene( this ) ); setAlignment( Qt::AlignLeft | Qt::AlignTop ); connect(scene(), &QGraphicsScene::selectionChanged, this, &DependencyView::slotSelectionChanged); connect(itemScene(), &DependencyScene::connectItems, this, &DependencyView::makeConnection); connect(itemScene(), static_cast(&DependencyScene::contextMenuRequested), this, &DependencyView::slotContextMenuRequested); connect(itemScene(), &DependencyScene::dependencyContextMenuRequested, this, &DependencyView::slotDependencyContextMenuRequested ); connect(itemScene(), SIGNAL(contextMenuRequested(QGraphicsItem*,QPoint)), this, SIGNAL(contextMenuRequested(QGraphicsItem*,QPoint))); // clazy:exclude=old-style-connect connect(itemScene(), static_cast(&DependencyScene::focusItemChanged), this, &DependencyView::slotFocusItemChanged); m_autoScrollTimer.start( 100 ); connect( &m_autoScrollTimer, &QTimer::timeout, this, &DependencyView::slotAutoScroll ); } void DependencyView::slotContextMenuRequested( QGraphicsItem *item ) { if ( item ) { debugPlanDepEditor<boundingRect()<<(item->mapToScene( item->pos() ).toPoint())<<(mapToGlobal( item->mapToParent( item->pos() ).toPoint())); emit contextMenuRequested( item, mapToGlobal( item->mapToScene( item->boundingRect().topRight() ).toPoint() ) ); } } void DependencyView::slotDependencyContextMenuRequested( DependencyLinkItem *item, DependencyConnectorItem */*connector */) { if ( item ) { debugPlanDepEditor<boundingRect()<<(item->mapToScene( item->pos() ).toPoint())<<(mapToGlobal( item->mapToParent( item->pos() ).toPoint())); emit contextMenuRequested( item, mapToGlobal( item->mapToScene( item->boundingRect().topRight() ).toPoint() ) ); } } void DependencyView::slotConnectorClicked( DependencyConnectorItem *item ) { if ( itemScene()->fromItem() == 0 ) { itemScene()->setFromItem( item ); } else { //debugPlanDepEditor<<"emit makeConnection:"<( item->parentItem() )->text(); emit makeConnection( itemScene()->fromItem(), item ); } } void DependencyView::slotSelectionChanged() { //HACK because of tt bug 160653 QTimer::singleShot(0, this, &DependencyView::slotSelectedItems); } void DependencyView::slotSelectedItems() { emit selectionChanged( itemScene()->selectedItems() ); } void DependencyView::slotFocusItemChanged( QGraphicsItem *item ) { ensureVisible( item, 10, 10 ); } void DependencyView::setItemScene( DependencyScene *scene ) { setScene( scene ); scene->setProject( m_project ); //slotResizeScene( m_treeview->viewport()->size() ); if ( m_project ) { createItems(); } } void DependencyView::setActive( bool activate ) { m_active = activate; if ( m_active && m_dirty ) { createItems(); } } void DependencyView::setProject( Project *project ) { if ( m_project ) { disconnect( m_project, &Project::relationAdded, this, &DependencyView::slotRelationAdded ); disconnect( m_project, &Project::relationRemoved, this, &DependencyView::slotRelationRemoved ); disconnect( m_project, &Project::relationModified, this, &DependencyView::slotRelationModified ); disconnect( m_project, &Project::nodeAdded, this, &DependencyView::slotNodeAdded ); disconnect( m_project, &Project::nodeRemoved, this, &DependencyView::slotNodeRemoved ); disconnect( m_project, &Project::nodeChanged, this, &DependencyView::slotNodeChanged ); disconnect( m_project, &Project::nodeMoved, this, &DependencyView::slotNodeMoved ); if ( itemScene() ) { itemScene()->clearScene(); } } m_project = project; if ( project ) { connect( m_project, &Project::relationAdded, this, &DependencyView::slotRelationAdded ); connect( m_project, &Project::relationRemoved, this, &DependencyView::slotRelationRemoved ); connect( m_project, &Project::relationModified, this, &DependencyView::slotRelationModified ); connect( m_project, &Project::nodeAdded, this, &DependencyView::slotNodeAdded ); connect( m_project, &Project::nodeRemoved, this, &DependencyView::slotNodeRemoved ); connect( m_project, &Project::nodeChanged, this, &DependencyView::slotNodeChanged ); connect( m_project, &Project::nodeMoved, this, &DependencyView::slotNodeMoved ); connect( m_project, &Project::wbsDefinitionChanged, this, &DependencyView::slotWbsCodeChanged ); if ( itemScene() ) { itemScene()->setProject( project ); if ( m_active ) { createItems(); } else { m_dirty = true; } } } } DependencyLinkItem *DependencyView::findItem( const Relation* rel ) const { return itemScene()->findItem( rel ); } DependencyNodeItem *DependencyView::findItem( const Node *node ) const { return itemScene()->findItem( node ); } void DependencyView::slotRelationAdded( Relation* rel ) { if ( m_dirty ) { return; } DependencyLinkItem *item = findItem( rel ); if ( item == 0 ) { DependencyNodeItem *p = findItem( rel->parent() ); DependencyNodeItem *c = findItem( rel->child() ); DependencyLinkItem *r = new DependencyLinkItem( p, c, rel ); scene()->addItem( r ); //debugPlanDepEditor; r->createPath(); r->setVisible( c->isVisible() && p->isVisible() ); } else debugPlanDepEditor<<"Relation already exists!"; } void DependencyView::slotRelationRemoved( Relation* rel ) { if ( m_dirty ) { return; } DependencyLinkItem *item = findItem( rel ); if ( item ) { scene()->removeItem( item ); delete item; } else debugPlanDepEditor<<"Relation does not exist!"; } void DependencyView::slotRelationModified( Relation* rel ) { //debugPlanDepEditor; if ( m_dirty ) { return; } slotRelationRemoved( rel ); slotRelationAdded( rel ); } void DependencyView::slotNodeAdded( Node *node ) { //debugPlanDepEditor; if ( m_dirty ) { return; } DependencyNodeItem *item = findItem( node ); if ( item == 0 ) { item = createItem( node ); } else { //debugPlanDepEditor<name(); itemScene()->setItemVisible( item, true ); } ensureVisible( item ); slotWbsCodeChanged(); } void DependencyView::slotNodeRemoved( Node *node ) { if ( m_dirty ) { return; } DependencyNodeItem *item = findItem( node ); if ( item ) { //debugPlanDepEditor<name(); itemScene()->setItemVisible( item, false ); } else debugPlanDepEditor<<"Node does not exist!"; slotWbsCodeChanged(); } void DependencyView::slotNodeChanged( Node *node ) { if ( m_dirty ) { return; } DependencyNodeItem *item = findItem( node ); if ( item && item->isVisible() ) { item->setText(); item->setSymbol(); } else debugPlanDepEditor<<"Node does not exist!"; } void DependencyView::slotWbsCodeChanged() { if ( m_dirty ) { return; } foreach( DependencyNodeItem *i, itemScene()->nodeItems() ) { if ( i->isVisible() ) { i->setText(); } } } void DependencyView::slotNodeMoved( Node *node ) { if ( m_dirty ) { return; } slotNodeRemoved( node ); slotNodeAdded( node ); } void DependencyView::setItemExpanded( int , bool ) { } void DependencyView::createItems() { itemScene()->clearScene(); m_dirty = false; if ( m_project == 0 ) { return; } scene()->addLine( 0.0, 0.0, 1.0, 0.0 ); createItems( m_project ); createLinks(); } DependencyNodeItem *DependencyView::createItem( Node *node ) { return itemScene()->createItem( node ); } void DependencyView::createItems( Node *node ) { if ( node != m_project ) { //debugPlanDepEditor<name()<<" ("<numChildren()<<")"; DependencyNodeItem *i = createItem( node ); if ( i == 0 ) { return; } } foreach ( Node *n, node->childNodeIterator() ) { createItems( n ); } } void DependencyView::createLinks() { //debugPlanDepEditor; itemScene()->createLinks(); } void DependencyView::keyPressEvent(QKeyEvent *event) { if ( event->modifiers() & Qt::ControlModifier ) { switch ( event->key() ) { case Qt::Key_Plus: return scale( 1.1, 1.1 ); case Qt::Key_Minus: return scale( 0.9, 0.9 ); default: break; } } QGraphicsView::keyPressEvent(event); } void DependencyView::mouseMoveEvent( QMouseEvent *mouseEvent ) { m_cursorPos = mouseEvent->pos(); if ( itemScene()->connectionMode() && itemScene()->mouseGrabberItem() ) { QPointF spos = mapToScene( m_cursorPos ); Qt::CursorShape c = Qt::ArrowCursor; foreach ( QGraphicsItem *i, itemScene()->items( spos ) ) { if ( i->type() == DependencyConnectorItem::Type ) { if ( i == itemScene()->fromItem() ) { c = ConnectCursor; } else { if ( itemScene()->connectionIsValid( itemScene()->fromItem(), static_cast( i ) ) ) { c = ConnectCursor; } else { c = Qt::ForbiddenCursor; } } } } if ( viewport()->cursor().shape() != c ) { viewport()->setCursor( c ); } } QGraphicsView::mouseMoveEvent( mouseEvent ); //debugPlanDepEditor<scenePos()<<","<isAccepted(); } void DependencyView::slotAutoScroll() { if ( itemScene()->connectionMode() ) { ensureVisible( QRectF( mapToScene( m_cursorPos ), QSizeF( 1, 1 ) ), 2, 2 ); } } //----------------------------------- DependencyeditorConfigDialog::DependencyeditorConfigDialog( ViewBase *view, QWidget *p, bool selectPrint) : KPageDialog(p), m_view( view ) { setWindowTitle( i18n("Settings") ); QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_headerfooter = ViewBase::createHeaderFooterWidget( view ); m_headerfooter->setOptions( view->printingOptions() ); tab->addTab( m_headerfooter, m_headerfooter->windowTitle() ); KPageWidgetItem *page = addPage( tab, i18n( "Printing" ) ); page->setHeader( i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, &QDialog::accepted, this, &DependencyeditorConfigDialog::slotOk); } void DependencyeditorConfigDialog::slotOk() { debugPlan; m_view->setPageLayout( m_pagelayout->pageLayout() ); m_view->setPrintingOptions( m_headerfooter->options() ); } //-------------------- DependencyEditor::DependencyEditor(KoPart *part, KoDocument *doc, QWidget *parent ) : ViewBase(part, doc, parent), m_currentnode( 0 ), m_manager( 0 ) { setXMLFile("DependencyEditorUi.rc"); setupGui(); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new DependencyView( this ); l->addWidget( m_view ); connect(m_view, &DependencyView::makeConnection, this, &DependencyEditor::slotCreateRelation ); connect(m_view, SIGNAL(selectionChanged(QList)), this, SLOT(slotSelectionChanged(QList))); // clazy::exclude=old-style-connect connect(m_view->itemScene(), &DependencyScene::itemDoubleClicked, this, &DependencyEditor::slotItemDoubleClicked ); connect(m_view, &DependencyView::contextMenuRequested, this, &DependencyEditor::slotContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Task Dependency Editor" "" "Edit dependencies between tasks." "Dependencies can be added by dragging a connection area (start or finish)" " from one task to a connection area of a different task." " You can edit or delete a dependency using the context menu." "" "This view supports printing using the context menu." "More..." "", Help::page("Manual/Task_Dependency_Editor_(Graphical)"))); } void DependencyEditor::updateReadWrite( bool on ) { m_view->itemScene()->setReadWrite( on ); ViewBase::updateReadWrite( on ); } void DependencyEditor::slotItemDoubleClicked( QGraphicsItem *item ) { //debugPlanDepEditor; if ( ! isReadWrite() ) { return; } if ( item && item->type() == DependencyLinkItem::Type ) { emit editRelation( static_cast( item )->relation ); return; } if ( item && item->type() == DependencyNodeItem::Type ) { emit editNode( static_cast( item )->node() ); return; } if ( item && item->type() == DependencyNodeSymbolItem::Type ) { emit editNode( static_cast( item->parentItem() )->node() ); return; } } void DependencyEditor::slotCreateRelation( DependencyConnectorItem *pred, DependencyConnectorItem *succ ) { //debugPlanDepEditor; if ( ! isReadWrite() ) { return; } Node *par = pred->node(); Node *ch = succ->node(); Relation::Type type = Relation::FinishStart; if ( pred->ctype() == DependencyNodeItem::Start ) { if ( succ->ctype() == DependencyNodeItem::Start ) { type = Relation::StartStart; } } else { if ( succ->ctype() == DependencyNodeItem::Start ) { type = Relation::FinishStart; } else { type = Relation::FinishFinish; } } Relation *rel = ch->findRelation( par ); if ( rel == 0 ) { //debugPlanDepEditor<<"New:"<name()<<" ->"<name()<<","<type() != type ) { //debugPlanDepEditor<<"Mod:"<name()<<" ->"<name()<<","<setProject( &project ); } void DependencyEditor::draw() { } void DependencyEditor::setGuiActive( bool activate ) { //debugPlanDepEditor<setActive( activate ); /* if ( activate && !m_view->selectionModel()->currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); }*/ } void DependencyEditor::slotCurrentChanged( const QModelIndex &, const QModelIndex & ) { //debugPlanDepEditor<& ) { //debugPlanDepEditor< DependencyEditor::selectedNodes() const { QList lst; foreach ( QGraphicsItem *i, m_view->itemScene()->selectedItems() ) { if ( i->type() == DependencyNodeItem::Type ) { lst << static_cast( i )->node(); } } return lst; } Node *DependencyEditor::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *DependencyEditor::currentNode() const { return m_currentnode; /* Node * n = 0; QGraphicsItem *i = m_view->itemScene()->focusItem(); if ( i && i->type() == DependencyNodeItem::Type ) { n = static_cast( i )->node(); } if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n;*/ } Relation *DependencyEditor::currentRelation() const { return m_currentrelation; } void DependencyEditor::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } void DependencyEditor::slotContextMenuRequested( QGraphicsItem *item, const QPoint& pos ) { //debugPlanDepEditor<type() == DependencyNodeSymbolItem::Type ) { item = item->parentItem(); } if ( item ) { if ( item->type() == DependencyNodeItem::Type ) { m_currentnode = static_cast( item )->node(); if ( m_currentnode == 0 ) { //debugPlanDepEditor<<"No node"; return; } bool scheduled = m_manager != 0 && m_currentnode->isScheduled( m_manager->scheduleId() ); switch ( m_currentnode->type() ) { case Node::Type_Task: name = scheduled ? "task_popup" : "task_edit_popup"; break; case Node::Type_Milestone: name = scheduled ? "taskeditor_milestone_popup" : "task_edit_popup"; break; case Node::Type_Summarytask: name = "summarytask_popup"; break; default: break; } //debugPlanDepEditor<name()<<" :"<type() == DependencyLinkItem::Type ) { m_currentrelation = static_cast( item )->relation; if ( m_currentrelation ) { name = "relation_popup"; } } else if ( item->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *c = static_cast( item ); QList items; QList actions; QMenu menu; foreach ( DependencyLinkItem *i, c->predecessorItems() ) { items << i; actions << menu.addAction(koIcon("document-properties"), i->predItem->text()); } menu.addSeparator(); foreach ( DependencyLinkItem *i, c->successorItems() ) { items << i; actions << menu.addAction(koIcon("document-properties"), i->succItem->text()); } if ( ! actions.isEmpty() ) { QAction *action = menu.exec( pos ); if ( action && actions.contains( action ) ) { emit editRelation( items[ actions.indexOf( action ) ]->relation ); return; } } } } //debugPlanDepEditor< lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } m_currentnode = 0; m_currentrelation = 0; } void DependencyEditor::slotEnableActions() { updateActionsEnabled( true ); } void DependencyEditor::updateActionsEnabled( bool on ) { if ( ! on || ! isReadWrite() ) { //FIXME: read-write is not set properly menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); return; } int selCount = selectedNodeCount(); if ( selCount == 0 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); return; } Node *n = selectedNode(); if ( n && n->type() != Node::Type_Task && n->type() != Node::Type_Milestone && n->type() != Node::Type_Summarytask ) { n = 0; } if ( selCount == 1 && n == 0 ) { // only project selected menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( true ); actionAddSubtask->setEnabled( true ); actionAddSubMilestone->setEnabled( true ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); return; } bool baselined = false; Project *p = m_view->project(); if ( p && p->isBaselined() ) { foreach ( Node *n, selectedNodes() ) { if ( n->isBaselined() ) { baselined = true; break; } } } if ( selCount == 1 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubtask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubMilestone->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( ! baselined ); return; } // selCount > 1 menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( false ); } void DependencyEditor::setupGui() { KActionCollection *coll = actionCollection(); menuAddTask = new KActionMenu(koIcon("view-task-add"), i18n("Add Task"), this); coll->addAction("add_task", menuAddTask ); connect( menuAddTask, &QAction::triggered, this, &DependencyEditor::slotAddTask ); actionAddTask = new QAction( i18n("Add Task..."), this); actionAddTask->setShortcut( Qt::CTRL + Qt::Key_I ); connect( actionAddTask, &QAction::triggered, this, &DependencyEditor::slotAddTask ); menuAddTask->addAction( actionAddTask ); actionAddMilestone = new QAction( i18n("Add Milestone..."), this ); actionAddMilestone->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_I ); connect( actionAddMilestone, &QAction::triggered, this, &DependencyEditor::slotAddMilestone ); menuAddTask->addAction( actionAddMilestone ); menuAddSubTask = new KActionMenu(koIcon("view-task-child-add"), i18n("Add Sub-Task"), this); - coll->addAction("add_subtask", menuAddTask ); + coll->addAction("add_subtask", menuAddSubTask ); connect( menuAddSubTask, &QAction::triggered, this, &DependencyEditor::slotAddSubtask ); actionAddSubtask = new QAction( i18n("Add Sub-Task..."), this ); actionAddSubtask->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_I ); connect( actionAddSubtask, &QAction::triggered, this, &DependencyEditor::slotAddSubtask ); menuAddSubTask->addAction( actionAddSubtask ); actionAddSubMilestone = new QAction( i18n("Add Sub-Milestone..."), this ); actionAddSubMilestone->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_I ); connect( actionAddSubMilestone, &QAction::triggered, this, &DependencyEditor::slotAddSubMilestone ); menuAddSubTask->addAction( actionAddSubMilestone ); actionDeleteTask = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); coll->addAction("delete_task", actionDeleteTask ); coll->setDefaultShortcut(actionDeleteTask, Qt::Key_Delete); connect( actionDeleteTask, &QAction::triggered, this, &DependencyEditor::slotDeleteTask ); actionLinkTask = new QAction(koIcon("link"), xi18nc("@action", "Link"), this); actionCollection()->setDefaultShortcut( actionLinkTask, Qt::CTRL + Qt::Key_L ); actionCollection()->addAction("link_task", actionLinkTask ); connect( actionLinkTask, &QAction::triggered, this, &DependencyEditor::slotLinkTask ); createOptionActions(ViewBase::OptionPrint | ViewBase::OptionPrintPreview | ViewBase::OptionPrintPdf | ViewBase::OptionPrintConfig); } void DependencyEditor::slotOptions() { debugPlan; DependencyeditorConfigDialog *dlg = new DependencyeditorConfigDialog( this, this, sender()->objectName() == "print_options" ); connect(dlg, &QDialog::finished, this, &DependencyEditor::slotOptionsFinished); dlg->open(); } void DependencyEditor::slotAddTask() { //debugPlanDepEditor; m_currentnode = selectedNode(); emit addTask(); m_currentnode = 0; } void DependencyEditor::slotAddMilestone() { //debugPlanDepEditor; m_currentnode = selectedNode(); // sibling emit addMilestone(); m_currentnode = 0; } void DependencyEditor::slotAddSubtask() { //debugPlanDepEditor; m_currentnode = selectedNode(); if ( m_currentnode == 0 ) { return; } emit addSubtask(); m_currentnode = 0; } void DependencyEditor::slotAddSubMilestone() { debugPlanDepEditor; m_currentnode = selectedNode(); if ( m_currentnode == 0 ) { return; } emit addSubMilestone(); m_currentnode = 0; } void DependencyEditor::edit( const QModelIndex &i ) { if ( i.isValid() ) { /* QModelIndex p = m_view->itemModel()->parent( i ); m_view->treeView()->setExpanded( p, true ); m_view->treeView()->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->treeView()->edit( i );*/ } } void DependencyEditor::slotDeleteTask() { //debugPlanDepEditor; QList lst = selectedNodes(); while ( true ) { // remove children of selected tasks, as parents delete their children Node *ch = 0; foreach ( Node *n1, lst ) { foreach ( Node *n2, lst ) { if ( n2->isChildOf( n1 ) ) { ch = n2; break; } } if ( ch != 0 ) { break; } } if ( ch == 0 ) { break; } lst.removeAt( lst.indexOf( ch ) ); } foreach ( Node* n, lst ) { debugPlanDepEditor<name(); } emit deleteTaskList( lst ); } void DependencyEditor::slotLinkTask() { //debugPlan; Node *n = selectedNode(); if (n && project()) { RelationEditorDialog dlg(project(), n); if (dlg.exec()) { KUndo2Command *cmd = dlg.buildCommand(); if (cmd) { koDocument()->addCommand(cmd); } } } } KoPrintJob *DependencyEditor::createPrintJob() { DependecyViewPrintingDialog *dia = new DependecyViewPrintingDialog( this, m_view ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } } // namespace KPlato diff --git a/src/libs/ui/kpttaskeditor.cpp b/src/libs/ui/kpttaskeditor.cpp index 76e083fc..e76f7555 100644 --- a/src/libs/ui/kpttaskeditor.cpp +++ b/src/libs/ui/kpttaskeditor.cpp @@ -1,1810 +1,1810 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2010, 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 "kpttaskeditor.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptnodeitemmodel.h" #include "kptcommand.h" #include "kptproject.h" #include "kptitemviewsettup.h" #include "kptworkpackagesenddialog.h" #include "kptworkpackagesendpanel.h" #include "kptdatetime.h" #include "kptdebug.h" #include "kptresourcemodel.h" #include "kptresourceallocationmodel.h" #include "ResourceAllocationView.h" #include "kpttaskdialog.h" #include "TasksEditController.h" #include "Help.h" #include "kpttaskdescriptiondialog.h" #include "RelationEditorDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------- TaskEditorItemModel::TaskEditorItemModel( QObject *parent ) : NodeItemModel( parent ) { } void TaskEditorItemModel::setScheduleManager(ScheduleManager *sm) { if (sm != manager()) { NodeItemModel::setScheduleManager(sm); } } Qt::ItemFlags TaskEditorItemModel::flags( const QModelIndex &index ) const { if ( index.column() == NodeModel::NodeType ) { if ( ! m_readWrite || isColumnReadOnly( index.column() ) ) { return QAbstractItemModel::flags( index ); } Node *n = node( index ); bool baselined = n ? n->isBaselined() : false; if ( n && ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { return QAbstractItemModel::flags( index ) | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; } return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled; } return NodeItemModel::flags( index ); } QVariant TaskEditorItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && section == NodeModel::NodeType ) { if ( role == Qt::ToolTipRole ) { return xi18nc( "@info:tooltip", "The type of task or the estimate type of the task" ); } else if ( role == Qt::WhatsThisRole ) { return xi18nc( "@info:whatsthis", "

Indicates the type of task or the estimate type of the task.

" "The type can be set to Milestone, Effort or Duration." "If the type is Summary or Project the type is not editable."); } } return NodeItemModel::headerData(section, orientation, role); } QVariant TaskEditorItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return NodeItemModel::data( index, role ); } Node *n = node( index ); if ( n != 0 && index.column() == NodeModel::NodeType ) { return type( n, role ); } return NodeItemModel::data( index, role ); } bool TaskEditorItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { Node *n = node( index ); if ( n != 0 && role == Qt::EditRole && index.column() == NodeModel::NodeType ) { return setType( n, value, role ); } return NodeItemModel::setData( index, value, role ); } QVariant TaskEditorItemModel::type( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return node->typeToString( true ); } case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: { if ( node->type() == Node::Type_Task ) { return xi18nc( "@info:tooltip", "Task with estimate type: %1", node->estimate()->typeToString( true ) ); } return xi18nc( "@info:tooltip", "Task type: %1", node->typeToString( true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::EnumListValue: { if ( node->type() == Node::Type_Milestone ) { return 0; } if ( node->type() == Node::Type_Task ) { return node->estimate()->type() + 1; } return -1; } case Role::EnumList: { QStringList lst; lst << Node::typeToString( Node::Type_Milestone, true ); lst += Estimate::typeToStringList( true ); return lst; } } return QVariant(); } bool TaskEditorItemModel::setType( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( node->type() == Node::Type_Summarytask ) { return false; } int v = value.toInt(); switch ( v ) { case 0: { // Milestone NamedCommand *cmd = 0; if ( node->constraint() == Node::FixedInterval ) { cmd = new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime(), kundo2_i18n( "Set type to Milestone" ) ); } else { cmd = new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 0.0, kundo2_i18n( "Set type to Milestone" ) ); } emit executeCommand( cmd ); return true; } default: { // Estimate --v; MacroCommand *m = new MacroCommand( kundo2_i18n( "Set type to %1", Estimate::typeToString( (Estimate::Type)v, true ) ) ); m->addCommand( new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v ) ); if ( node->type() == Node::Type_Milestone ) { if ( node->constraint() == Node::FixedInterval ) { m->addCommand( new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime().addDays( 1 ) ) ); } else { m->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), Duration::Unit_d ) ); m->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 1.0 ) ); } } emit executeCommand( m ); return true; } } break; } default: break; } return false; } //-------------------- TaskEditorTreeView::TaskEditorTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("view-task").pixmap(32)); TaskEditorItemModel *m = new TaskEditorItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setDefaultDropAction(Qt::CopyAction); createItemDelegates( m ); setItemDelegateForColumn( NodeModel::NodeType, new EnumDelegate( this ) ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &TaskEditorTreeView::slotDropAllowed ); } NodeItemModel *TaskEditorTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void TaskEditorTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } void TaskEditorTreeView::editPaste() { QModelIndex idx = m_leftview->currentIndex(); const QMimeData *data = QGuiApplication::clipboard()->mimeData(); model()->dropMimeData(data, Qt::CopyAction, idx.row()+1, 0, idx.parent()); } //-------------------- NodeTreeView::NodeTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("view-task").pixmap(32)); NodeItemModel *m = new NodeItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &NodeTreeView::slotDropAllowed ); } NodeItemModel *NodeTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void NodeTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } //----------------------------------- TaskEditor::TaskEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ) { debugPlan<<"----------------- Create TaskEditor ----------------------"; setXMLFile("TaskEditorUi.rc"); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new TaskEditorTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); debugPlan<actionSplitView(); setupGui(); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); m_view->setAcceptDropsOnView( true ); QList lst1; lst1 << 1 << -1; // only display column 0 (NodeName) in left view QList show; show << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeType << NodeModel::NodePriority << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; QList lst2; for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } for ( int i = 0; i < show.count(); ++i ) { int sec = m_view->slaveView()->header()->visualIndex( show[ i ] ); //debugPlan<<"move section:"<slaveView()->header()->moveSection( sec, i ); } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << NodeModel::NodeName ); m_view->slaveView()->setDefaultColumns( show ); connect( model(), SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskEditor::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskEditor::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskEditor::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_view->masterView(), &TreeViewBase::doubleClicked, this, &TaskEditor::itemDoubleClicked); connect(m_view->slaveView(), &TreeViewBase::doubleClicked, this, &TaskEditor::itemDoubleClicked); connect(baseModel(), &NodeItemModel::projectShownChanged, this, &TaskEditor::slotProjectShown); connect(model(), &QAbstractItemModel::rowsMoved, this, &TaskEditor::slotEnableActions); Help::add(this, xi18nc("@info:whatsthis", "Task Editor" "" "The Task Editor is used to create, edit, and delete tasks. " "Tasks are organized into a Work Breakdown Structure (WBS) to any depth." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Editor"))); } void TaskEditor::itemDoubleClicked(const QPersistentModelIndex &idx) { if (idx.column() == NodeModel::NodeDescription) { emit openTaskDescription(isReadWrite() && (idx.flags() & Qt::ItemIsEditable)); } } void TaskEditor::slotProjectShown( bool on ) { debugPlan<rowCount() > 0 ) { idx = proxyModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } } else if ( baseModel() && baseModel()->rowCount() > 0 ) { idx = baseModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } if ( on && idx.isValid() ) { m_view->masterView()->expand( idx ); } slotEnableActions(); } void TaskEditor::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskEditor::setProject( Project *project ) { debugPlan<setProject( project ); ViewBase::setProject( project ); } void TaskEditor::createDockers() { // Add dockers DockWidget *ds = 0; { ds = new DockWidget( this, "Allocations", xi18nc( "@title resource allocations", "Allocations" ) ); QTreeView *x = new QTreeView( ds ); AllocatedResourceItemModel *m1 = new AllocatedResourceItemModel( x ); x->setModel( m1 ); m1->setProject( project() ); // x->setHeaderHidden( true ); x->setSelectionBehavior( QAbstractItemView::SelectRows ); x->setSelectionMode( QAbstractItemView::ExtendedSelection ); x->expandAll(); x->resizeColumnToContents( 0 ); x->setDragDropMode( QAbstractItemView::DragOnly ); x->setDragEnabled ( true ); ds->setWidget( x ); connect(this, &ViewBase::projectChanged, m1, &AllocatedResourceItemModel::setProject); connect(this, &TaskEditor::taskSelected, m1, &AllocatedResourceItemModel::setTask); connect(m1, &AllocatedResourceItemModel::expandAll, x, &QTreeView::expandAll); connect(m1, &AllocatedResourceItemModel::resizeColumnToContents, x, &QTreeView::resizeColumnToContents); addDocker( ds ); } { ds = new DockWidget( this, "Resources", xi18nc( "@title", "Resources" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag resources into the Task Editor" " and drop into the allocations- or responsible column" ) ); ResourceAllocationView *e = new ResourceAllocationView(part(), ds ); ResourceItemModel *m = new ResourceItemModel( e ); e->setModel( m ); m->setProject( project() ); m->setReadWrite( isReadWrite() ); QList show; show << ResourceModel::ResourceName; for ( int i = m->columnCount() - 1; i >= 0; --i ) { e->setColumnHidden( i, ! show.contains( i ) ); } e->setHeaderHidden( true ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::ExtendedSelection ); e->expandAll(); e->resizeColumnToContents( ResourceModel::ResourceName ); e->setDragDropMode( QAbstractItemView::DragOnly ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, e, &ResourceAllocationView::setSelectedTasks); connect(this, SIGNAL(projectChanged(KPlato::Project*)), m, SLOT(setProject(KPlato::Project*))); connect(this, &ViewBase::readWriteChanged, m, &ItemModelBase::setReadWrite); connect(m, &ItemModelBase::executeCommand, part(), &KoDocument::addCommand); addDocker( ds ); } { ds = new DockWidget( this, "Taskmodules", xi18nc( "@title", "Task Modules" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag a task module into the Task Editor to add it to the project" ) ); ds->setLocation( Qt::LeftDockWidgetArea ); ds->setShown(false); // hide by default QTreeView *e = new QTreeView( ds ); QSortFilterProxyModel *sf = new QSortFilterProxyModel(e); TaskModuleModel *m = new TaskModuleModel(sf); sf->setSourceModel(m); e->setModel(sf); e->sortByColumn(0, Qt::AscendingOrder); e->setSortingEnabled(true); e->setHeaderHidden( true ); e->setRootIsDecorated( false ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::SingleSelection ); // e->resizeColumnToContents( 0 ); e->setDragDropMode( QAbstractItemView::DragDrop ); e->setAcceptDrops( true ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(e, &QAbstractItemView::doubleClicked, this, &TaskEditor::taskModuleDoubleClicked); connect(this, &TaskEditor::loadTaskModules, m, &TaskModuleModel::loadTaskModules); connect(m, &TaskModuleModel::saveTaskModule, this, &TaskEditor::saveTaskModule); connect(m, &TaskModuleModel::removeTaskModule, this, &TaskEditor::removeTaskModule); addDocker( ds ); } } void TaskEditor::taskModuleDoubleClicked(QModelIndex idx) { QUrl url = idx.data(Qt::UserRole).toUrl(); if (url.isValid()) { emit openDocument(url); } } void TaskEditor::setTaskModules(const QStringList& files) { emit loadTaskModules( files ); } void TaskEditor::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskEditor::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<( selectedNode() ) ); } QModelIndexList TaskEditor::selectedRows() const { #if 0 // Qt bug? return m_view->selectionModel()->selectedRows(); #else QModelIndexList lst; foreach ( QModelIndex i, m_view->selectionModel()->selectedIndexes() ) { if ( i.column() == 0 ) { lst << i; } } return lst; #endif } int TaskEditor::selectedRowCount() const { return selectedRows().count(); } QList TaskEditor::selectedNodes() const { QList lst; foreach ( const QModelIndex &i, selectedRows() ) { Node * n = m_view->baseModel()->node( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskEditor::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskEditor::currentNode() const { Node * n = m_view->baseModel()->node( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskEditor::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos, const QModelIndexList &rows ) { QString name; if (rows.count() > 1) { debugPlan< summarytasks; QList tasks; QList milestones; for (const QModelIndex &idx : rows) { Node *node = m_view->baseModel()->node( idx ); if (node) { switch ( node->type() ) { case Node::Type_Task: tasks << static_cast(node); break; case Node::Type_Milestone: milestones << static_cast(node); break; case Node::Type_Summarytask: summarytasks << static_cast(node); break; default: break; } } } if (!tasks.isEmpty()) { editTasks(tasks, pos); return; } return; } Node *node = m_view->baseModel()->node( index ); if ( node == 0 ) { return; } debugPlan<name()<<" :"<type() ) { case Node::Type_Project: name = "task_edit_popup"; break; case Node::Type_Task: name = node->isScheduled( baseModel()->id() ) ? "task_popup" : "task_edit_popup"; break; case Node::Type_Milestone: name = node->isScheduled( baseModel()->id() ) ? "taskeditor_milestone_popup" : "task_edit_popup"; break; case Node::Type_Summarytask: name = "summarytask_popup"; break; default: name = "node_popup"; break; } m_view->setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } debugPlan<setContextMenuIndex(QModelIndex()); } void TaskEditor::editTasks(const QList &tasks, const QPoint &pos) { QList lst; QAction tasksEdit(i18n( "Edit..."), nullptr); if (!tasks.isEmpty()) { TasksEditController *ted = new TasksEditController(*project(), tasks, this); connect(&tasksEdit, &QAction::triggered, ted, &TasksEditController::activate); connect(ted, &TasksEditController::addCommand, koDocument(), &KoDocument::addCommand); lst << &tasksEdit; } lst += contextActionList(); if (!lst.isEmpty()) { QMenu::exec( lst, pos, lst.first() ); } } void TaskEditor::setScheduleManager( ScheduleManager *sm ) { if (!sm && scheduleManager()) { // we should only get here if the only schedule manager is scheduled, // or when last schedule manager is deleted m_domdoc.clear(); QDomElement element = m_domdoc.createElement("expanded"); m_domdoc.appendChild(element); m_view->masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskEditor::slotEnableActions() { updateActionsEnabled( isReadWrite() ); } void TaskEditor::updateActionsEnabled( bool on ) { // debugPlan<setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } int selCount = selectedRowCount(); if ( selCount == 0 ) { if ( currentNode() ) { // there are tasks but none is selected menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } else { // we need to be able to add the first task menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } return; } Node *n = selectedNode(); // 0 if not a single task, summarytask or milestone if ( selCount == 1 && n == 0 ) { // only project selected menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( true ); actionAddSubtask->setEnabled( true ); actionAddSubMilestone->setEnabled( true ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } bool baselined = false; Project *p = m_view->project(); if ( p && p->isBaselined() ) { foreach ( Node *n, selectedNodes() ) { if ( n->isBaselined() ) { baselined = true; break; } } } if ( selCount == 1 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubtask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubMilestone->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( ! baselined ); Node *s = n->siblingBefore(); actionMoveTaskUp->setEnabled( s ); actionMoveTaskDown->setEnabled( n->siblingAfter() ); s = n->siblingBefore(); actionIndentTask->setEnabled( ! baselined && s && ! s->isBaselined() ); actionUnindentTask->setEnabled( ! baselined && n->level() > 1 ); return; } // selCount > 1 menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } void TaskEditor::setupGui() { menuAddTask = new KActionMenu(koIcon("view-task-add"), i18n("Add Task"), this); actionCollection()->addAction("add_task", menuAddTask ); connect( menuAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); actionAddTask = new QAction( i18n( "Add Task" ), this); actionAddTask->setShortcut( Qt::CTRL + Qt::Key_I ); connect( actionAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); menuAddTask->addAction( actionAddTask ); actionAddMilestone = new QAction( i18n( "Add Milestone" ), this ); actionAddMilestone->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_I ); connect( actionAddMilestone, &QAction::triggered, this, &TaskEditor::slotAddMilestone ); menuAddTask->addAction( actionAddMilestone ); menuAddSubTask = new KActionMenu(koIcon("view-task-child-add"), i18n("Add Sub-Task"), this); - actionCollection()->addAction("add_subtask", menuAddTask ); + actionCollection()->addAction("add_subtask", menuAddSubTask ); connect( menuAddSubTask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); actionAddSubtask = new QAction( i18n( "Add Sub-Task" ), this ); actionAddSubtask->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_I ); connect( actionAddSubtask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); menuAddSubTask->addAction( actionAddSubtask ); actionAddSubMilestone = new QAction( i18n( "Add Sub-Milestone" ), this ); actionAddSubMilestone->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_I ); connect( actionAddSubMilestone, &QAction::triggered, this, &TaskEditor::slotAddSubMilestone ); menuAddSubTask->addAction( actionAddSubMilestone ); actionDeleteTask = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); actionCollection()->setDefaultShortcut( actionDeleteTask, Qt::Key_Delete ); actionCollection()->addAction("delete_task", actionDeleteTask ); connect( actionDeleteTask, &QAction::triggered, this, &TaskEditor::slotDeleteTask ); actionLinkTask = new QAction(koIcon("link"), xi18nc("@action", "Link"), this); actionCollection()->setDefaultShortcut( actionLinkTask, Qt::CTRL + Qt::Key_L ); actionCollection()->addAction("link_task", actionLinkTask ); connect( actionLinkTask, &QAction::triggered, this, &TaskEditor::slotLinkTask ); actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask ); connect(actionIndentTask, &QAction::triggered, this, &TaskEditor::slotIndentTask); actionUnindentTask = new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask ); connect(actionUnindentTask, &QAction::triggered, this, &TaskEditor::slotUnindentTask); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp ); connect(actionMoveTaskUp, &QAction::triggered, this, &TaskEditor::slotMoveTaskUp); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown ); connect(actionMoveTaskDown, &QAction::triggered, this, &TaskEditor::slotMoveTaskDown); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); actionCollection()->addAction("show_project", actionShowProject); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction(actionShowProject); actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskEditor::slotSplitView); addContextAction(m_view->actionSplitView()); createOptionActions(ViewBase::OptionAll); createDockers(); } void TaskEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } void TaskEditor::slotAddTask() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddMilestone() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { // None selected or only project selected: insert under main project m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); // sibling if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubMilestone() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubtask() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::edit( const QModelIndex &i ) { if ( i.isValid() ) { m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->setParentsExpanded( i, true ); // in case treeview does not have focus m_view->edit( i ); } } void TaskEditor::slotDeleteTask() { //debugPlan; QList lst = selectedNodes(); while ( true ) { // remove children of selected tasks, as parents delete their children Node *ch = 0; foreach ( Node *n1, lst ) { foreach ( Node *n2, lst ) { if ( n2->isChildOf( n1 ) ) { ch = n2; break; } } if ( ch != 0 ) { break; } } if ( ch == 0 ) { break; } lst.removeAt( lst.indexOf( ch ) ); } //foreach ( Node* n, lst ) { debugPlan<name(); } emit deleteTaskList( lst ); QModelIndex i = m_view->selectionModel()->currentIndex(); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotLinkTask() { //debugPlan; Node *n = currentNode(); if (n && project()) { RelationEditorDialog dlg(project(), n); if (dlg.exec()) { KUndo2Command *cmd = dlg.buildCommand(); if (cmd) { koDocument()->addCommand(cmd); } } } } void TaskEditor::slotIndentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit indentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->setParentsExpanded( i, true ); } } void TaskEditor::slotUnindentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit unindentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskUp() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskUp(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskDown() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskDown(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } bool TaskEditor::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? bool res = m_view->loadContext( baseModel()->columnMap(), context ); return res; } void TaskEditor::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( baseModel()->columnMap(), context ); } KoPrintJob *TaskEditor::createPrintJob() { return m_view->createPrintJob( this ); } void TaskEditor::slotEditCopy() { m_view->editCopy(); } void TaskEditor::slotEditPaste() { m_view->editPaste(); } //----------------------------------- TaskView::TaskView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { setXMLFile("TaskViewUi.rc"); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new NodeTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); NodeSortFilterProxyModel *p = new NodeSortFilterProxyModel( m_view->baseModel(), m_view ); m_view->setModel( p ); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode(QAbstractItemView::DragOnly); m_view->setDropIndicatorShown( false ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( false ); m_view->setAcceptDropsOnView( false ); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodePerformanceIndex << NodeModel::NodeBCWS << NodeModel::NodeBCWP << NodeModel::NodeACWP << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_view->masterView(), &TreeViewBase::doubleClicked, this, &TaskView::itemDoubleClicked); connect(m_view->slaveView(), &TreeViewBase::doubleClicked, this, &TaskView::itemDoubleClicked); Help::add(this, xi18nc("@info:whatsthis", "Task Execution View" "" "The view is used to edit and inspect task progress during project execution." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Execution_View"))); } void TaskView::itemDoubleClicked(const QPersistentModelIndex &idx) { if (idx.column() == NodeModel::NodeDescription) { emit openTaskDescription(isReadWrite() && (idx.flags() & Qt::ItemIsEditable)); } } void TaskView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskView::draw( Project &project ) { m_view->setProject( &project ); } void TaskView::draw() { } void TaskView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( i ) ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskView::currentNode() const { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( m_view->selectionModel()->currentIndex() ) ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = m_view->baseModel()->node( proxyModel()->mapToSource( index ) ); if ( node ) { switch ( node->type() ) { case Node::Type_Project: name = "taskview_project_popup"; break; case Node::Type_Task: name = "taskview_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager() && sm != scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskView::slotEnableActions() { updateActionsEnabled( true ); } void TaskView::updateActionsEnabled( bool /*on*/ ) { } void TaskView::setupGui() { // KActionCollection *coll = actionCollection(); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); actionCollection()->addAction("show_project", actionShowProject); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction( actionShowProject ); actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } bool TaskView::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskView::createPrintJob() { return m_view->createPrintJob( this ); } void TaskView::slotEditCopy() { m_view->editCopy(); } //--------------------------------- WorkPackageTreeView::WorkPackageTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { debugPlan<<"----------"<baseModel(); } void WorkPackageTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { Q_UNUSED(index); Q_UNUSED(dropIndicatorPosition); Q_UNUSED(event); /* QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); }*/ } //-------------------------------- TaskWorkPackageView::TaskWorkPackageView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ), m_cmd( 0 ) { setXMLFile("WorkPackageViewUi.rc"); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new WorkPackageTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); m_view->setAcceptDropsOnView( true ); m_view->setDefaultDropAction(Qt::CopyAction); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskWorkPackageView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskWorkPackageView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskWorkPackageView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_view->proxyModel(), &WorkPackageProxyModel::loadWorkPackage, this, &TaskWorkPackageView::slotLoadWorkPackage); connect(m_view->masterView(), &TreeViewBase::doubleClicked, this, &TaskWorkPackageView::itemDoubleClicked); connect(m_view->slaveView(), &TreeViewBase::doubleClicked, this, &TaskWorkPackageView::itemDoubleClicked); } void TaskWorkPackageView::itemDoubleClicked(const QPersistentModelIndex &idx) { if (idx.column() == NodeModel::NodeDescription) { emit openTaskDescription(isReadWrite() && (idx.flags() & Qt::ItemIsEditable)); } } Project *TaskWorkPackageView::project() const { return m_view->project(); } void TaskWorkPackageView::setProject( Project *project ) { m_view->setProject( project ); } WorkPackageProxyModel *TaskWorkPackageView::proxyModel() const { return m_view->proxyModel(); } void TaskWorkPackageView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskWorkPackageView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskWorkPackageView::slotRefreshView() { emit checkForWorkPackages(false); } void TaskWorkPackageView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskWorkPackageView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = proxyModel()->taskFromIndex( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskWorkPackageView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskWorkPackageView::currentNode() const { Node * n = proxyModel()->taskFromIndex( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskWorkPackageView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = proxyModel()->taskFromIndex( index ); if ( node ) { switch ( node->type() ) { case Node::Type_Task: name = "workpackage_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu(name, pos); m_view->setContextMenuIndex(QModelIndex()); } void TaskWorkPackageView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<baseModel()->setScheduleManager( sm ); } void TaskWorkPackageView::slotEnableActions() { updateActionsEnabled( true ); } void TaskWorkPackageView::updateActionsEnabled( bool on ) { bool o = ! selectedNodes().isEmpty(); actionMailWorkpackage->setEnabled( o && on ); } void TaskWorkPackageView::setupGui() { actionMailWorkpackage = new QAction(koIcon("cloud-upload"), i18n("Publish..."), this); actionCollection()->addAction("send_workpackage", actionMailWorkpackage ); connect( actionMailWorkpackage, &QAction::triggered, this, &TaskWorkPackageView::slotMailWorkpackage ); actionOpenWorkpackages = new QAction(koIcon("view-task"), i18n("Work Packages..."), this); actionCollection()->addAction("open_workpackages", actionOpenWorkpackages ); actionOpenWorkpackages->setEnabled(false); connect( actionOpenWorkpackages, &QAction::triggered, this, &TaskWorkPackageView::openWorkpackages ); // Add the context menu actions for the view options addContextAction( m_view->actionSplitView() ); actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskWorkPackageView::slotSplitView); createOptionActions(ViewBase::OptionAll); } void TaskWorkPackageView::slotMailWorkpackage() { QList lst = selectedNodes(); if ( ! lst.isEmpty() ) { // TODO find a better way to log to avoid undo/redo m_cmd = new MacroCommand( kundo2_i18n( "Log Send Workpackage" ) ); QPointer dlg = new WorkPackageSendDialog( lst, scheduleManager(), this ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::publishWorkpackages ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::slotWorkPackageSent ); dlg->exec(); delete dlg; if ( ! m_cmd->isEmpty() ) { part()->addCommand( m_cmd ); m_cmd = 0; } delete m_cmd; m_cmd = 0; } } void TaskWorkPackageView::slotWorkPackageSent( const QList &nodes, Resource *resource ) { foreach ( Node *n, nodes ) { WorkPackage *wp = new WorkPackage( static_cast( n )->workPackage() ); wp->setOwnerName( resource->name() ); wp->setOwnerId( resource->id() ); wp->setTransmitionTime( DateTime::currentDateTime() ); wp->setTransmitionStatus( WorkPackage::TS_Send ); m_cmd->addCommand( new WorkPackageAddCmd( static_cast( n->projectNode() ), n, wp ) ); } } void TaskWorkPackageView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskWorkPackageView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } bool TaskWorkPackageView::loadContext( const KoXmlElement &context ) { debugPlan; ViewBase::loadContext( context ); return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskWorkPackageView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskWorkPackageView::createPrintJob() { return m_view->createPrintJob( this ); } void TaskWorkPackageView::slotEditCopy() { m_view->editCopy(); } void TaskWorkPackageView::slotWorkpackagesAvailable(bool value) { actionOpenWorkpackages->setEnabled(value); } void TaskWorkPackageView::slotLoadWorkPackage(QList files) { QList urls; for (const QString &f : files) { QUrl url = QUrl(f); if (url.isValid()) { urls << url; } } emit loadWorkPackageUrl(m_view->project(), urls); } } // namespace KPlato