diff --git a/src/libs/models/kptnodeitemmodel.cpp b/src/libs/models/kptnodeitemmodel.cpp index b153efd6..20bd8b1a 100644 --- a/src/libs/models/kptnodeitemmodel.cpp +++ b/src/libs/models/kptnodeitemmodel.cpp @@ -1,5250 +1,5269 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2009, 2012 Dag Andersen Copyright (C) 2016 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 "kptnodeitemmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptduration.h" #include "kptproject.h" #include "kptnode.h" #include "kpttaskcompletedelegate.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "InsertProjectXmlCommand.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------------------------- NodeModel::NodeModel() : QObject(), m_project( 0 ), m_manager( 0 ), m_now( QDate::currentDate() ), m_prec( 1 ) { } const QMetaEnum NodeModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } void NodeModel::setProject( Project *project ) { debugPlan<"<"<name(); case Qt::ToolTipRole: { QTextEdit w(node->description(), nullptr); QString description = w.toPlainText(); if (description.length() > 200) { description = description.left(200) + " ..."; description.replace('\n', "
"); } else { description = node->description(); } w.setHtml(i18n("

%1: %2

%3

", node->wbsCode(), node->name(), description)); return w.toHtml(); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::DecorationRole: if ( node->isBaselined() ) { return koIcon("view-time-schedule-baselined"); } break; case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return static_cast( node )->completion().isFinished() ? m_project->config().taskFinishedColor() : m_project->config().taskNormalColor(); case Node::Type_Milestone: return static_cast( node )->completion().isFinished() ? m_project->config().milestoneFinishedColor() : m_project->config().milestoneNormalColor(); case Node::Type_Summarytask: return m_project->config().summaryTaskLevelColor( node->level() ); default: break; } break; } } return QVariant(); } QVariant NodeModel::leader( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return node->leader(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::allocation( const Node *node, int role ) const { if ( node->type() == Node::Type_Task ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->requests().requestNameList().join( "," ); case Qt::ToolTipRole: { QMap lst; foreach ( ResourceRequest *rr, node->requests().resourceRequests( false ) ) { QStringList sl; foreach( Resource *r, rr->requiredResources() ) { sl << r->name(); } lst.insert( rr->resource()->name(), sl ); } if ( lst.isEmpty() ) { return xi18nc( "@info:tooltip", "No resources has been allocated" ); } QStringList sl; for ( QMap::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) { if ( it.value().isEmpty() ) { sl << it.key(); } else { sl << xi18nc( "@info:tooltip 1=resource name, 2=list of required resources", "%1 (%2)", it.key(), it.value().join(", ") ); } } if ( sl.count() == 1 ) { return xi18nc( "@info:tooltip 1=resource name", "Allocated resource:%1", sl.first() ); } KLocalizedString ks = kxi18nc( "@info:tooltip 1=list of resources", "Allocated resources:%1"); // Hack to get around ks escaping '<' and '>' QString s = ks.subs(sl.join("#¤#")).toString(); return s.replace("#¤#", "
"); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::description( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); QString s = w.textOrHtml(); int i = s.indexOf( '\n' ); s = s.left( i ); if ( i > 0 ) { s += "..."; } return s; } case Qt::ToolTipRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); if ( w.textOrHtml().isEmpty() ) { return QVariant(); } return node->description(); } case Qt::EditRole: return node->description(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::type( const Node *node, int role ) const { //debugPlan<name()<<", "<typeToString( true ); case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return (int)(Qt::AlignLeft|Qt::AlignVCenter); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::constraint( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: return i18n( "Target times" ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Earliest start and latest finish" ); case Role::EnumList: case Qt::EditRole: case Role::EnumListValue: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return node->constraintToString( true ); case Role::EnumList: return Node::constraintList( true ); case Qt::EditRole: return node->constraint(); case Role::EnumListValue: return (int)node->constraint(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintStartTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::StartNotEarlier: case Node::MustStartOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintEndTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::FinishNotLater: case Node::MustFinishOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::FinishNotLater || c == Node::MustFinishOn || c == Node::FixedInterval ) { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::estimateType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return QString(); case Role::EnumList: return Estimate::typeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->type(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimateCalendar( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return i18n( "None" ); } return QString(); case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->type() == Estimate::Type_Effort ) { return xi18nc( "@info:tooltip", "Not applicable, estimate type is Effort" ); } if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return QVariant(); } return QString(); case Role::EnumList: { QStringList lst; lst << i18n( "None" ); const Node *n = const_cast( node )->projectNode(); if ( n ) { lst += static_cast( n )->calendarNames(); } return lst; } case Qt::EditRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() == 0 ) { return i18n( "None" ); } return node->estimate()->calendar()->name(); } return QString(); case Role::EnumListValue: { if ( node->estimate()->calendar() == 0 ) { return 0; } QStringList lst; const Node *n = const_cast( node )->projectNode(); if ( n ) { lst = static_cast( n )->calendarNames(); } return lst.indexOf( node->estimate()->calendar()->name() ) + 1; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimate( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); if ( node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { s = '(' + s + ')'; } return s; } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Estimated effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Estimated duration: %1", s ); } return s; } break; case Qt::EditRole: return node->estimate()->expectedEstimate(); case Role::DurationUnit: return static_cast( node->estimate()->unit() ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->optimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Optimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Optimistic duration: %1", s ); } return s; } break; case Role::Minimum: return -99; case Role::Maximum: return 0; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->pessimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Pessimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Pessimistic duration: %1", s ); } return s; } break; case Role::Minimum: return 0; case Role::Maximum: return INT_MAX; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::riskType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString( true ); } return QString(); case Role::EnumList: return Estimate::risktypeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->risktype(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::priority( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->priority(); case Qt::ToolTipRole: break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::runningAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a ? xi18nc( "@info:tooltip", "Account for resource cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for resource cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->runningAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name() ) : xi18nc( "@info:tooltip", "Account for task startup cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->startupAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->startupCost() ); } break; case Qt::EditRole: return node->startupCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a ? xi18nc( "@info:tooltip", "Account for task shutdown cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for task shutdown cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->shutdownAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->shutdownCost() ); } break; case Qt::EditRole: return node->shutdownCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->startTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<startTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->startTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::endTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->endTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<endTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->endTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::duration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } break; case Qt::EditRole: { return node->duration( id() ).toDouble( Duration::Unit_h ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return QLocale().toString( v, 'f', 2 ); } break; case Qt::EditRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); return node->variance( id(), unit ); } return 0.0; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return xi18nc( "@info:tooltip", "PERT duration variance: %1", QLocale().toString( v ,'f', 2 ) ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceEstimate( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); //debugPlan<name()<<": "<variance( est->unit() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); return xi18nc( "@info:tooltip", "PERT estimate variance: %1", QLocale().toString( v, 'f', 2 ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); return d.toDouble( unit ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->optimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Optimistic estimate: %1", QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pertExpected( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return Estimate::scale( est->pertExpected(), est->unit(), est->scales() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return xi18nc( "@info:tooltip", "PERT expected estimate: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; return d.toDouble( node->estimate()->unit() ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->pessimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Pessimistic estimate: %1", QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::positiveFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->positiveFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::freeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->freeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->freeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->freeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::negativeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->negativeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->startFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->startFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->startFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->finishFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->finishFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->finishFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::assignedResources( const Node *node, int role ) const { if ( node->type() != Node::Type_Task ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->assignedNameList( id() ).join(","); case Qt::ToolTipRole: { QStringList lst = node->assignedNameList( id() ); if ( ! lst.isEmpty() ) { return xi18nc( "@info:tooltip 1=list of resources", "Assigned resources:%1", node->assignedNameList( id() ).join(", ") ); } break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::completed( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->completion().percentFinished(); case Qt::EditRole: return t->completion().percentFinished(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task is %1% completed", t->completion().percentFinished() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::status( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { return i18n( "Finished late" ); } if ( st & Node::State_FinishedEarly ) { return i18n( "Finished early" ); } return i18n( "Finished" ); } if ( st & Node::State_Running ) { if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Running" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { return i18n( "Started late" ); } if ( st & Node::State_StartedEarly ) { return i18n( "Started early" ); } if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Started" ); } if ( st & Node::State_ReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Not started" ); } return i18n( "Can start" ); } if ( st & Node::State_NotReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Delayed" ); } return i18n( "Cannot start" ); } return i18n( "Not started" ); break; } case Qt::ToolTipRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { Duration d = t->completion().finishTime() - t->endTime( id() ); return xi18nc( "@info:tooltip", "Finished %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_FinishedEarly ) { Duration d = t->endTime( id() ) - t->completion().finishTime(); return xi18nc( "@info:tooltip", "Finished %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Finished" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { Duration d = t->completion().startTime() - t->startTime( id() ); return xi18nc( "@info:tooltip", "Started %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_StartedEarly ) { Duration d = t->startTime( id() ) - t->completion().startTime(); return xi18nc( "@info:tooltip", "Started %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Started" ); } if ( st & Node::State_Running ) { return xi18nc( "@info:tooltip", "Running" ); } if ( st & Node::State_ReadyToStart ) { return xi18nc( "@info:tooltip", "Can start" ); } if ( st & Node::State_NotReadyToStart ) { return xi18nc( "@info:tooltip", "Cannot start" ); } return xi18nc( "@info:tooltip", "Not started" ); break; } case Qt::EditRole: return t->state( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isStarted() ) { return QLocale().toString( t->completion().startTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "Actual start: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isStarted() ) { return t->completion().startTime(); } return QDateTime::currentDateTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isStarted( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isStarted(); case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "The task started at: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not started" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isFinished() ) { return QLocale().toString( t->completion().finishTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "Actual finish: %1", QLocale().toString( t->completion().finishTime(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isFinished() ) { return t->completion().finishTime(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isFinished( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isFinished(); case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "The task finished at: %1", QLocale().toString( t->completion().finishTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not finished" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->plannedEffortTo( m_now, id() ).format(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned effort until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), node->plannedEffortTo( m_now, id() ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->plannedEffortTo( m_now, id() ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->actualEffortTo( m_now ).format(); case Qt::ToolTipRole: //debugPlan<actualEffortTo( m_now ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->actualEffortTo( m_now ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::remainingEffort( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t ) { return t->completion().remainingEffort().format(); } break; } case Qt::ToolTipRole: { const Task *t = dynamic_cast( node ); if ( t ) { return xi18nc( "@info:tooltip", "Remaining effort: %1", t->completion().remainingEffort().toString( Duration::Format_i18nHour ) ); } break; } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return t->completion().remainingEffort().toDouble( Duration::Unit_h ); } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->plannedCostTo( m_now, id() ) ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->plannedCostTo( m_now, id() ) ) ); case Qt::EditRole: return node->plannedCostTo( m_now ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->actualCostTo( id(), m_now ).cost() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->actualCostTo( id(), m_now ).cost() ) ); case Qt::EditRole: return node->actualCostTo( id(), m_now ).cost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::note( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Node *n = const_cast( node ); return static_cast( n )->completion().note(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeSchedulingStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->schedulingStatus( id(), true ).value( 0 ); case Qt::EditRole: return node->schedulingStatus( id(), false ).value( 0 ); case Qt::ToolTipRole: return node->schedulingStatus( id(), true ).join( "\n" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::resourceIsMissing( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceError( id() ); case Qt::ToolTipRole: if ( node->resourceError( id() ) ) { return xi18nc( "@info:tooltip", "Resource allocation is expected when the task estimate type is Effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsOverbooked( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceOverbooked( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceOverbooked( id() ); case Qt::ToolTipRole: if ( node->resourceOverbooked( id() ) ) { return xi18nc( "@info:tooltip", "A resource has been overbooked" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsNotAvailable( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceNotAvailable( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceNotAvailable( id() ); case Qt::ToolTipRole: if ( node->resourceNotAvailable( id() ) ) { return xi18nc( "@info:tooltip", "No resource is available for this task" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingConstraintsError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->constraintError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->constraintError( id() ); case Qt::ToolTipRole: if ( node->constraintError( id() ) ) { return xi18nc( "@info:tooltip", "Failed to comply with a timing constraint" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeIsNotScheduled( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->notScheduled( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->notScheduled( id() ); case Qt::ToolTipRole: if ( node->notScheduled( id() ) ) { return xi18nc( "@info:tooltip", "This task has not been scheduled" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::effortNotMet( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->effortMetError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->effortMetError( id() ); case Qt::ToolTipRole: if ( node->effortMetError( id() ) ) { return xi18nc( "@info:tooltip", "The assigned resources cannot deliver the required estimated effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->schedulingError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->schedulingError( id() ); case Qt::ToolTipRole: if ( node->schedulingError( id() ) ) { return xi18nc( "@info:tooltip", "Scheduling error" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wbsCode( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->wbsCode(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Work breakdown structure code: %1", node->wbsCode() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case SortableRole: return node->wbsCode(true); } return QVariant(); } QVariant NodeModel::nodeLevel( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->level(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task level: %1", node->level() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWS( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ); case Qt::EditRole: return node->bcws( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Scheduled at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ); case Qt::EditRole: return node->bcwp( id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeACWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost(), QString(), 0 ); case Qt::EditRole: return node->acwp( m_now, id() ).cost(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost() ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodePerformanceIndex( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ); case Qt::EditRole: return node->schedulePerformanceIndex( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Schedule Performance Index at %1: %2", m_now.toString(), QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::ForegroundRole: return QColor(node->schedulePerformanceIndex( m_now, id() ) < 1.0 ? Qt::red : Qt::black); } return QVariant(); } QVariant NodeModel::nodeIsCritical( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->isCritical( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeInCriticalPath( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->inCriticalPath( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wpOwnerName( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return t->wpOwnerName(); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent to %1 at %2", static_cast( node )->wpOwnerName(), t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received from %1 at %2", static_cast( node )->wpOwnerName(), t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), true ); } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), false ); } case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return QLocale().toString( t->wpTransmitionTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent: %1", t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received: %1", t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::data( const Node *n, int property, int role ) const { QVariant result; switch ( property ) { // Edited by user case NodeName: result = name( n, role ); break; case NodeType: result = type( n, role ); break; case NodeResponsible: result = leader( n, role ); break; case NodeAllocation: result = allocation( n, role ); break; case NodeEstimateType: result = estimateType( n, role ); break; case NodeEstimateCalendar: result = estimateCalendar( n, role ); break; case NodeEstimate: result = estimate( n, role ); break; case NodeOptimisticRatio: result = optimisticRatio( n, role ); break; case NodePessimisticRatio: result = pessimisticRatio( n, role ); break; case NodeRisk: result = riskType( n, role ); break; case NodePriority: result = priority( n, role ); break; case NodeConstraint: result = constraint( n, role ); break; case NodeConstraintStart: result = constraintStartTime( n, role ); break; case NodeConstraintEnd: result = constraintEndTime( n, role ); break; case NodeRunningAccount: result = runningAccount( n, role ); break; case NodeStartupAccount: result = startupAccount( n, role ); break; case NodeStartupCost: result = startupCost( n, role ); break; case NodeShutdownAccount: result = shutdownAccount( n, role ); break; case NodeShutdownCost: result = shutdownCost( n, role ); break; case NodeDescription: result = description( n, role ); break; // Based on edited values case NodeExpected: result = pertExpected( n->estimate(), role ); break; case NodeVarianceEstimate: result = varianceEstimate( n->estimate(), role ); break; case NodeOptimistic: result = optimisticEstimate( n->estimate(), role ); break; case NodePessimistic: result = pessimisticEstimate( n->estimate(), role ); break; // After scheduling case NodeStartTime: result = startTime( n, role ); break; case NodeEndTime: result = endTime( n, role ); break; case NodeEarlyStart: result = earlyStart( n, role ); break; case NodeEarlyFinish: result = earlyFinish( n, role ); break; case NodeLateStart: result = lateStart( n, role ); break; case NodeLateFinish: result = lateFinish( n, role ); break; case NodePositiveFloat: result = positiveFloat( n, role ); break; case NodeFreeFloat: result = freeFloat( n, role ); break; case NodeNegativeFloat: result = negativeFloat( n, role ); break; case NodeStartFloat: result = startFloat( n, role ); break; case NodeFinishFloat: result = finishFloat( n, role ); break; case NodeAssignments: result = assignedResources( n, role ); break; // Based on scheduled values case NodeDuration: result = duration( n, role ); break; case NodeVarianceDuration: result = varianceDuration( n, role ); break; case NodeOptimisticDuration: result = optimisticDuration( n, role ); break; case NodePessimisticDuration: result = pessimisticDuration( n, role ); break; // Completion case NodeStatus: result = status( n, role ); break; case NodeCompleted: result = completed( n, role ); break; case NodePlannedEffort: result = plannedEffortTo( n, role ); break; case NodeActualEffort: result = actualEffortTo( n, role ); break; case NodeRemainingEffort: result = remainingEffort( n, role ); break; case NodePlannedCost: result = plannedCostTo( n, role ); break; case NodeActualCost: result = actualCostTo( n, role ); break; case NodeActualStart: result = startedTime( n, role ); break; case NodeStarted: result = isStarted( n, role ); break; case NodeActualFinish: result = finishedTime( n, role ); break; case NodeFinished: result = isFinished( n, role ); break; case NodeStatusNote: result = note( n, role ); break; // Scheduling errors case NodeSchedulingStatus: result = nodeSchedulingStatus( n, role ); break; case NodeNotScheduled: result = nodeIsNotScheduled( n, role ); break; case NodeAssignmentMissing: result = resourceIsMissing( n, role ); break; case NodeResourceOverbooked: result = resourceIsOverbooked( n, role ); break; case NodeResourceUnavailable: result = resourceIsNotAvailable( n, role ); break; case NodeConstraintsError: result = schedulingConstraintsError( n, role ); break; case NodeEffortNotMet: result = effortNotMet( n, role ); break; case NodeSchedulingError: result = schedulingError( n, role ); break; case NodeWBSCode: result = wbsCode( n, role ); break; case NodeLevel: result = nodeLevel( n, role ); break; // Performance case NodeBCWS: result = nodeBCWS( n, role ); break; case NodeBCWP: result = nodeBCWP( n, role ); break; case NodeACWP: result = nodeACWP( n, role ); break; case NodePerformanceIndex: result = nodePerformanceIndex( n, role ); break; case NodeCritical: result = nodeIsCritical( n, role ); break; case NodeCriticalPath: result = nodeInCriticalPath( n, role ); break; case WPOwnerName: result = wpOwnerName( n, role ); break; case WPTransmitionStatus: result = wpTransmitionStatus( n, role ); break; case WPTransmitionTime: result = wpTransmitionTime( n, role ); break; default: //debugPlan<<"Invalid property number: "<name() ) { return 0; } KUndo2MagicString s = kundo2_i18n( "Modify name" ); switch ( node->type() ) { case Node::Type_Task: s = kundo2_i18n( "Modify task name" ); break; case Node::Type_Milestone: s = kundo2_i18n( "Modify milestone name" ); break; case Node::Type_Summarytask: s = kundo2_i18n( "Modify summarytask name" ); break; case Node::Type_Project: s = kundo2_i18n( "Modify project name" ); break; } return new NodeModifyNameCmd( *node, value.toString(), s ); } } return 0; } KUndo2Command *NodeModel::setLeader( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( value.toString() != node->leader() ) { return new NodeModifyLeaderCmd( *node, value.toString(), kundo2_i18n( "Modify responsible" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setAllocation( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setDescription( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == node->description() ) { return 0; } return new NodeModifyDescriptionCmd( *node, value.toString(), kundo2_i18n( "Modify task description" ) ); } return 0; } KUndo2Command *NodeModel::setType( Node *, const QVariant &, int ) { return 0; } KUndo2Command *NodeModel::setConstraint( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Node::ConstraintType v; QStringList lst = node->constraintList( false ); if ( lst.contains( value.toString() ) ) { v = Node::ConstraintType( lst.indexOf( value.toString() ) ); } else { v = Node::ConstraintType( value.toInt() ); } //debugPlan<constraint() ) { return new NodeModifyConstraintCmd( *node, v, kundo2_i18n( "Modify constraint type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintStartTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintStartTime() ) { return new NodeModifyConstraintStartTimeCmd( *node, dt, kundo2_i18n( "Modify constraint start time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintEndTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintEndTime() ) { return new NodeModifyConstraintEndTimeCmd( *node, dt, kundo2_i18n( "Modify constraint end time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Estimate::Type v; QStringList lst = node->estimate()->typeToStringList( false ); if ( lst.contains( value.toString() ) ) { v = Estimate::Type( lst.indexOf( value.toString() ) ); } else { v = Estimate::Type( value.toInt() ); } if ( v != node->estimate()->type() ) { return new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v, kundo2_i18n( "Modify estimate type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateCalendar( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Calendar *c = 0; Calendar *old = node->estimate()->calendar(); if ( value.toInt() > 0 ) { QStringList lst = estimateCalendar( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { c = m_project->calendarByName( lst.at( value.toInt() ) ); } } if ( c != old ) { return new ModifyEstimateCalendarCmd( *node, old, c, kundo2_i18n( "Modify estimate calendar" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimate( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { double d; Duration::Unit unit; if ( value.toList().count() == 2 ) { d = value.toList()[0].toDouble(); unit = static_cast( value.toList()[1].toInt() ); } else if ( value.canConvert() ) { bool ok = Duration::valueFromString( value.toString(), d, unit ); if ( ! ok ) { return 0; } } else { return 0; } //debugPlan<"<estimate()->expectedEstimate() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), d ) ); } if ( unit != node->estimate()->unit() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), unit ) ); } if ( cmd ) { return cmd; } break; } default: break; } return 0; } KUndo2Command *NodeModel::setOptimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->optimisticRatio() ) { return new EstimateModifyOptimisticRatioCmd( *node, node->estimate()->optimisticRatio(), value.toInt(), kundo2_i18n( "Modify optimistic estimate" ) ); } break; default: break; } return 0; } KUndo2Command *NodeModel::setPessimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->pessimisticRatio() ) { return new EstimateModifyPessimisticRatioCmd( *node, node->estimate()->pessimisticRatio(), value.toInt(), kundo2_i18n( "Modify pessimistic estimate" ) ); } default: break; } return 0; } KUndo2Command *NodeModel::setRiskType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { int val = 0; QStringList lst = node->estimate()->risktypeToStringList( false ); if ( lst.contains( value.toString() ) ) { val = lst.indexOf( value.toString() ); } else { val = value.toInt(); } if ( val != node->estimate()->risktype() ) { Estimate::Risktype v = Estimate::Risktype( val ); return new EstimateModifyRiskCmd( *node, node->estimate()->risktype(), v, kundo2_i18n( "Modify risk type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setPriority(Node *node, const QVariant &value, int role) { switch (role) { case Qt::EditRole: { if (value.toInt() != node->priority()) { return new NodeModifyPriorityCmd(*node, node->priority(), value.toInt(), kundo2_i18n("Modify priority")); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setRunningAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = runningAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->runningAccount(); if ( old != a ) { return new NodeModifyRunningAccountCmd( *node, old, a, kundo2_i18n( "Modify running account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setStartupAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = startupAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->startupAccount(); //debugPlan<<(value.toInt())<<";"<<(lst.at( value.toInt()))<<":"<startupCost() ) { return new NodeModifyStartupCostCmd( *node, v, kundo2_i18n( "Modify startup cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = shutdownAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->shutdownAccount(); if ( old != a ) { return new NodeModifyShutdownAccountCmd( *node, old, a, kundo2_i18n( "Modify shutdown account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownCost( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { double v = value.toDouble(); if ( v != node->shutdownCost() ) { return new NodeModifyShutdownCostCmd( *node, v, kundo2_i18n( "Modify shutdown cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setCompletion( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setRemainingEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); return new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ); } return 0; } KUndo2Command *NodeModel::setActualEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); return new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ); } return 0; } KUndo2Command *NodeModel::setStartedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual start time" ) ); if ( ! t->completion().isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); } m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } return m; } default: break; } return 0; } KUndo2Command *NodeModel::setFinishedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual finish time" ) ); if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); } return m; } default: break; } return 0; } //---------------------------- NodeItemModel::NodeItemModel( QObject *parent ) : ItemModelBase( parent ), m_node( 0 ), m_projectshown( false ) { setReadOnly( NodeModel::NodeDescription, true ); } NodeItemModel::~NodeItemModel() { } void NodeItemModel::setShowProject( bool on ) { beginResetModel(); m_projectshown = on; endResetModel(); emit projectShownChanged( on ); } void NodeItemModel::slotNodeToBeInserted( Node *parent, int row ) { //debugPlan<name()<<"; "<parentNode()->name()<<"-->"<name(); Q_ASSERT( node->parentNode() == m_node ); endInsertRows(); m_node = 0; emit nodeInserted( node ); } void NodeItemModel::slotNodeToBeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( m_node == 0 ); m_node = node; int row = index( node ).row(); beginRemoveRows( index( node->parentNode() ), row, row ); } void NodeItemModel::slotNodeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( node == m_node ); #ifdef NDEBUG Q_UNUSED(node) #endif endRemoveRows(); m_node = 0; } void NodeItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { //debugPlan<parentNode()->name()<name()<parentNode() ), pos, pos, index( newParent ), newPos ); } void NodeItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); //debugPlan<parentNode()->name()<parentNode()->indexOf( node ); endMoveRows(); } void NodeItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void NodeItemModel::slotProjectCalculated(ScheduleManager *sm) { debugPlan<allNodes() ) { int row = n->parentNode()->indexOf( n ); QModelIndex idx = createIndex( row, NodeModel::NodeWBSCode, n ); emit dataChanged( idx, idx ); } } void NodeItemModel::setProject( Project *project ) { beginResetModel(); if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &NodeItemModel::projectDeleted); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect( m_project, &Project::wbsDefinitionChanged, this, &NodeItemModel::slotWbsDefinitionChanged); disconnect( m_project, &Project::nodeChanged, this, &NodeItemModel::slotNodeChanged); disconnect( m_project, &Project::nodeToBeAdded, this, &NodeItemModel::slotNodeToBeInserted); disconnect( m_project, &Project::nodeToBeRemoved, this, &NodeItemModel::slotNodeToBeRemoved); disconnect( m_project, &Project::nodeToBeMoved, this, &NodeItemModel::slotNodeToBeMoved); disconnect( m_project, &Project::nodeMoved, this, &NodeItemModel::slotNodeMoved); disconnect( m_project, &Project::nodeAdded, this, &NodeItemModel::slotNodeInserted); disconnect( m_project, &Project::nodeRemoved, this, &NodeItemModel::slotNodeRemoved); disconnect( m_project, &Project::projectCalculated, this, &NodeItemModel::slotProjectCalculated); } m_project = project; debugPlan<"<isBaselined(); flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeAllocation: // allocation if ( n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeEstimateType: // estimateType { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimate: // estimate { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeOptimisticRatio: // optimisticRatio case NodeModel::NodePessimisticRatio: // pessimisticRatio { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimateCalendar: { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeRisk: // risktype { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodePriority: { if (!baselined) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraint: // constraint type if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeConstraintEnd: { // constraint end if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeRunningAccount: // running account if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description flags |= Qt::ItemIsEditable; break; default: break; } Task *t = static_cast( n ); if ( manager() && t->isScheduled( id() ) ) { if ( ! t->completion().isStarted() ) { switch ( index.column() ) { case NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualFinish: if ( t->type() == Node::Type_Milestone ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeCompleted: if ( t->state() & Node::State_ReadyToStart ) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if ( ! t->completion().isFinished() ) { switch ( index.column() ) { case NodeModel::NodeActualFinish: - case NodeModel::NodeCompleted: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; + case NodeModel::NodeCompleted: + flags |= Qt::ItemIsEditable; + break; case NodeModel::NodeActualEffort: if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) { flags |= Qt::ItemIsEditable; } break; default: break; } } } } return flags; } QModelIndex NodeItemModel::parent( const QModelIndex &index ) const { if ( ! index.isValid() ) { return QModelIndex(); } Node *n = node( index ); if ( n == 0 || n == m_project ) { return QModelIndex(); } Node *p = n->parentNode(); if ( p == m_project ) { return m_projectshown ? createIndex( 0, 0, p ) : QModelIndex(); } int row = p->parentNode()->indexOf( p ); if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, p ); } QModelIndex NodeItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { Q_ASSERT( parent.model() == this ); } //debugPlan<= columnCount() || row < 0 ) { //debugPlan<= p->numChildren() ) { errorPlan<name()<<" row too high"<childNode( row ); QModelIndex idx = createIndex(row, column, n); //debugPlan<parentNode(); if ( par ) { //debugPlan<"<indexOf( node ), column, const_cast(node) ); } if ( m_projectshown && node == m_project ) { return createIndex( 0, column, m_project ); } //debugPlan<( node ); if ( task == 0 ) { return false; } switch ( role ) { case Qt::EditRole: { MacroCommand *cmd = 0; QStringList res = m_project->resourceNameList(); QStringList req = node->requestNameList(); QStringList alloc; foreach ( const QString &s, value.toString().split( QRegExp(" *, *"), QString::SkipEmptyParts ) ) { alloc << s.trimmed(); } // first add all new resources (to "default" group) ResourceGroup *pargr = m_project->groupByName( i18n( "Resources" ) ); foreach ( const QString &s, alloc ) { Resource *r = m_project->resourceByName( s.trimmed() ); if ( r != 0 ) { continue; } if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Add resource" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s.trimmed() ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } KUndo2MagicString c = kundo2_i18n( "Modify resource allocations" ); // Handle deleted requests foreach ( const QString &s, req ) { // if a request is not in alloc, it must have been be removed by the user if ( alloc.indexOf( s ) == -1 ) { // remove removed resource request ResourceRequest *r = node->resourceRequest( s ); if ( r ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); //debugPlan<<"delete request:"<resource()->name()<<" group:"<parent()->group()->name(); cmd->addCommand( new RemoveResourceRequestCmd( r->parent(), r ) ); } } } // Handle new requests QHash groupmap; foreach ( const QString &s, alloc ) { // if an allocation is not in req, it must be added if ( req.indexOf( s ) == -1 ) { ResourceGroup *pargr = 0; Resource *r = m_project->resourceByName( s ); if ( r == 0 ) { // Handle request to non existing resource pargr = m_project->groupByName( i18n( "Resources" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } else { pargr = r->parentGroup(); //debugPlan<<"add '"<name()<<"' to group:"<resourceGroupRequest( pargr ); if ( g == 0 ) { g = groupmap.value( pargr ); } if ( g == 0 ) { // create a group request if ( cmd == 0 ) cmd = new MacroCommand( c ); g = new ResourceGroupRequest( pargr ); cmd->addCommand( new AddResourceGroupRequestCmd( *task, g ) ); groupmap.insert( pargr, g ); //debugPlan<<"add group request:"<addCommand( new AddResourceRequestCmd( g, new ResourceRequest( r, r->units() ) ) ); //debugPlan<<"add request:"<name()<<" group:"<name()<type() == Node::Type_Task ) { Completion &c = static_cast( node )->completion(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) ); if ( ! c.isStarted() ) { m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); } m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) ); if ( value.toInt() == 100 ) { m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); } emit executeCommand( m ); // also adds a new entry if necessary if (c.entrymode() != Completion::EnterEffortPerResource) { Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlan<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); cmd->execute(); m->addCommand( cmd ); } return true; } if ( node->type() == Node::Type_Milestone ) { Completion &c = static_cast( node )->completion(); if ( value.toInt() > 0 ) { QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Set finished" ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) ); emit executeCommand( m ); // also adds a new entry if necessary return true; } return false; } return false; } QVariant NodeItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( role == Role::Object ) { return n ? QVariant::fromValue( static_cast( n ) ) : QVariant(); } QVariant result; if ( n != 0 ) { result = m_nodemodel.data( n, index.column(), role ); //debugPlan<name()<<": "<numChildren(); } Qt::DropActions NodeItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList NodeItemModel::mimeTypes() const { return ItemModelBase::mimeTypes() << "application/x-vnd.kde.plan.nodeitemmodel.internal" << "application/x-vnd.kde.plan.resourceitemmodel.internal" << "application/x-vnd.kde.plan.project" << "text/uri-list"; } QMimeData *NodeItemModel::mimeData( const QModelIndexList & indexes ) const { QMimeData *m = ItemModelBase::mimeData(indexes); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QList rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); QList nodes; foreach (const QModelIndex &index, indexes) { if (index.isValid()) { //debugPlan<setData("application/x-vnd.kde.plan.project", context.document.toByteArray()); } return m; } bool NodeItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { debugPlan<hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal") ) { switch ( dropIndicatorPosition ) { case ItemModelBase::OnItem: if ( index.column() == NodeModel::NodeAllocation ) { debugPlan<<"resource:"<type() == Node::Type_Task); return dn->type() == Node::Type_Task; } else if ( index.column() == NodeModel::NodeResponsible ) { debugPlan<<"resource:"<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal") || data->hasFormat( "application/x-vnd.kde.plan.project" ) || data->hasUrls() ) { switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling, if not project if ( dn == m_project ) { return dropAllowed( dn, data ); } return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } } return false; } QList NodeItemModel::resourceList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; debugPlan<<"id"<findResource( id ); if ( r ) { lst << r; } } debugPlan<isBaselined() && on->type() != Node::Type_Summarytask && on->type() != Node::Type_Project) { return false; } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( n->type() == Node::Type_Project || on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } } return true; } QList NodeItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList NodeItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool NodeItemModel::dropResourceMimeData( const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *n = node( parent ); debugPlan<name(); if ( parent.column() == NodeModel::NodeResponsible ) { QString s; foreach ( Resource *r, resourceList( stream ) ) { s += r->name(); } if ( ! s.isEmpty() ) { if ( action == Qt::CopyAction && ! n->leader().isEmpty() ) { s += ',' + n->leader(); } KUndo2Command *cmd = m_nodemodel.setLeader( n, s, Qt::EditRole ); if ( cmd ) { emit executeCommand( cmd ); } debugPlan<type() == Node::Type_Task ) { QList lst = resourceList( stream ); if ( action == Qt::CopyAction ) { lst += static_cast( n )->requestedResources(); } KUndo2Command *cmd = createAllocationCommand( static_cast( *n ), lst ); if ( cmd ) { emit executeCommand( cmd ); } return true; } return true; } bool NodeItemModel::dropProjectMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { Node *n = node( parent ); if ( n == 0 ) { n = m_project; } debugPlan<data("application/x-vnd.kde.plan.project"), n, n->childNode(row), kundo2_i18n("Insert tasks")); emit executeCommand( cmd ); return true; } bool NodeItemModel::dropUrlMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project project; XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( &project ); if ( ! project.load( element, status ) ) { debugPlan<<"Failed to load project from:"<childNode( row - 1 ), kundo2_i18n( "Insert %1", url.fileName() ) ); emit executeCommand( cmd ); return true; } KUndo2Command *NodeItemModel::createAllocationCommand( Task &task, const QList &lst ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Modify resource allocations" ) ); QHash groups; foreach ( Resource *r, lst ) { if ( ! groups.contains( r->parentGroup() ) && task.resourceGroupRequest( r->parentGroup() ) == 0 ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); groups[ r->parentGroup() ] = gr; cmd->addCommand( new AddResourceGroupRequestCmd( task, gr ) ); } } QList resources = task.requestedResources(); foreach ( Resource *r, lst ) { if ( resources.contains( r ) ) { continue; } ResourceGroupRequest *gr = groups.value( r->parentGroup() ); if ( gr == 0 ) { gr = task.resourceGroupRequest( r->parentGroup() ); } if ( gr == 0 ) { errorPlan<<"No group request found, cannot add resource request:"<name(); continue; } cmd->addCommand( new AddResourceRequestCmd( gr, new ResourceRequest( r, 100 ) ) ); } foreach ( Resource *r, resources ) { if ( ! lst.contains( r ) ) { ResourceGroupRequest *gr = task.resourceGroupRequest( r->parentGroup() ); ResourceRequest *rr = task.requests().find( r ); if ( gr && rr ) { cmd->addCommand( new RemoveResourceRequestCmd( gr, rr ) ); } } } if ( cmd->isEmpty() ) { delete cmd; return 0; } return cmd; } bool NodeItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { debugPlan<formats(); if (action == Qt::IgnoreAction) { return true; } if ( data->hasFormat( "application/x-vnd.kde.plan.resourceitemmodel.internal" ) ) { return dropResourceMimeData( data, action, row, column, parent ); } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; if ( pos >= 0 && n->parentNode() == par && par->indexOf( n ) < pos ) { --pos; } if ( n->parentNode() == par ) { // avoid drop into the same position, QAbstractItemModel does not like it int crow = par->indexOf( n ); if ( ( ( pos == -1 ) && ( crow == par->numChildren() - 1 ) ) || ( pos == crow ) ) { delete cmd; cmd = 0; continue; } } cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } } if ( data->hasFormat( "application/x-vnd.kde.plan.project" ) ) { debugPlan; return dropProjectMimeData( data, action, row, column, parent ); } if ( data->hasUrls() ) { return dropUrlMimeData( data, action, row, column, parent ); } return false; } Node *NodeItemModel::node( const QModelIndex &index ) const { Node *n = m_project; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); Q_ASSERT( n ); } return n; } void NodeItemModel::slotNodeChanged( Node *node ) { if ( node == 0 || ( ! m_projectshown && node->type() == Node::Type_Project ) ) { return; } if ( node->type() == Node::Type_Project ) { emit dataChanged( createIndex( 0, 0, node ), createIndex( 0, columnCount()-1, node ) ); return; } int row = node->parentNode()->findChildNode( node ); Q_ASSERT( row >= 0 ); emit dataChanged( createIndex( row, 0, node ), createIndex( row, columnCount()-1, node ) ); } QModelIndex NodeItemModel::insertTask( Node *node, Node *after ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Add task" ) ); cmd->addCommand( new TaskAddCmd( m_project, node, after ) ); if ( m_project && node->type() == Node::Type_Task ) { QHash groups; foreach ( Resource *r, m_project->autoAllocateResources() ) { if ( ! groups.contains( r->parentGroup() ) ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); cmd->addCommand( new AddResourceGroupRequestCmd( static_cast(*node), gr ) ); groups[ r->parentGroup() ] = gr; } ResourceRequest *rr = new ResourceRequest( r, 100 ); cmd->addCommand( new AddResourceRequestCmd( groups[ r->parentGroup() ], rr ) ); } } emit executeCommand( cmd ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<<"Inserted: "<name()<<"; "<name(); return QModelIndex(); } QModelIndex NodeItemModel::insertSubtask( Node *node, Node *parent ) { emit executeCommand( new SubtaskAddCmd( m_project, node, parent, kundo2_i18n( "Add sub-task" ) ) ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<parentNode()<<" inserted: "<name()<<"; "<name(); return QModelIndex(); } int NodeItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } debugPlan< lst = parentmap.values(); while ( ! lst.isEmpty() ) delete (int*)(lst.takeFirst()); } int GanttItemModel::rowCount( const QModelIndex &parent ) const { if ( m_showSpecial ) { if (parentmap.values().contains(parent.internalPointer())) { // clazy:exclude=container-anti-pattern return 0; } Node *n = node( parent ); if ( n && n->type() == Node::Type_Task ) { return 5; // the task + early start + late finish ++ } } return NodeItemModel::rowCount( parent ); } QModelIndex GanttItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( m_showSpecial && parent.isValid() ) { Node *p = node( parent ); if ( p->type() == Node::Type_Task ) { void *v = 0; foreach ( void *i, parentmap.values( p ) ) { // clazy:exclude=container-anti-pattern if ( *( (int*)( i ) ) == row ) { v = i; break; } } if ( v == 0 ) { v = new int( row ); const_cast( this )->parentmap.insertMulti( p, v ); } return createIndex( row, column, v ); } } return NodeItemModel::index( row, column, parent ); } QModelIndex GanttItemModel::parent( const QModelIndex &idx ) const { if ( m_showSpecial ) { QList lst = parentmap.keys( idx.internalPointer() ); if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); return index( lst.first() ); } } return NodeItemModel::parent( idx ); } QVariant GanttItemModel::data( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } QModelIndex idx = index; QList lst; if ( m_showSpecial ) { lst = parentmap.keys( idx.internalPointer() ); } if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); int row = *((int*)(idx.internalPointer())); Node *n = lst.first(); if ( role == SpecialItemTypeRole ) { return row; // 0=task, 1=early start, 2=late finish... } switch ( row ) { case 0: // the task if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { switch ( n->type() ) { case Node::Type_Task: return KGantt::TypeTask; default: break; } } break; case 1: { // early start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyStart( id() ); default: break; } return QVariant(); } case 2: { // late finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateFinish( id() ); default: break; } return QVariant(); } case 3: { // late start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateStart( id() ); default: break; } return QVariant(); } case 4: { // early finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyFinish( id() ); default: break; } return QVariant(); } default: return QVariant(); } idx = createIndex( idx.row(), idx.column(), n ); } else { if ( role == SpecialItemTypeRole ) { return 0; // task of some type } if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { QModelIndex notScheduled = idx.sibling(idx.row(), NodeModel::NodeNotScheduled); if (notScheduled.data(Qt::EditRole).toBool()) { return QVariant(); } QVariant result = NodeItemModel::data( idx, Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Project: return KGantt::TypeSummary; case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return m_showSpecial ? KGantt::TypeMulti : KGantt::TypeTask; } } } return NodeItemModel::data( idx, role ); } //---------------------------- MilestoneItemModel::MilestoneItemModel( QObject *parent ) : ItemModelBase( parent ) { } MilestoneItemModel::~MilestoneItemModel() { } QList MilestoneItemModel::mileStones() const { QList lst; foreach( Node* n, m_nodemap ) { if ( n->type() == Node::Type_Milestone ) { lst << n; } } return lst; } void MilestoneItemModel::slotNodeToBeInserted( Node *parent, int row ) { Q_UNUSED(parent); Q_UNUSED(row); } void MilestoneItemModel::slotNodeInserted( Node *node ) { Q_UNUSED(node); resetModel(); } void MilestoneItemModel::slotNodeToBeRemoved( Node *node ) { Q_UNUSED(node); //debugPlan<name(); /* int row = m_nodemap.values().indexOf( node ); if ( row != -1 ) { Q_ASSERT( m_nodemap.contains( node->wbsCode() ) ); Q_ASSERT( m_nodemap.keys().indexOf( node->wbsCode() ) == row ); beginRemoveRows( QModelIndex(), row, row ); m_nodemap.remove( node->wbsCode() ); endRemoveRows(); }*/ } void MilestoneItemModel::slotNodeRemoved( Node *node ) { Q_UNUSED(node); resetModel(); //endRemoveRows(); } void MilestoneItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void MilestoneItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { Q_UNUSED( node ); Q_UNUSED( pos ); Q_UNUSED( newParent ); Q_UNUSED( newPos ); } void MilestoneItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); resetModel(); } void MilestoneItemModel::setProject( Project *project ) { if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &MilestoneItemModel::projectDeleted); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect( m_project, &Project::wbsDefinitionChanged, this, &MilestoneItemModel::slotWbsDefinitionChanged); disconnect( m_project, &Project::nodeChanged, this, &MilestoneItemModel::slotNodeChanged); disconnect( m_project, &Project::nodeToBeAdded, this, &MilestoneItemModel::slotNodeToBeInserted); disconnect( m_project, &Project::nodeToBeRemoved, this, &MilestoneItemModel::slotNodeToBeRemoved); disconnect(m_project, &Project::nodeToBeMoved, this, &MilestoneItemModel::slotNodeToBeMoved); disconnect(m_project, &Project::nodeMoved, this, &MilestoneItemModel::slotNodeMoved); disconnect( m_project, &Project::nodeAdded, this, &MilestoneItemModel::slotNodeInserted); disconnect( m_project, &Project::nodeRemoved, this, &MilestoneItemModel::slotNodeRemoved); } m_project = project; //debugPlan<"<allNodes() ) { m_nodemap.insert( n->wbsCode(true), n ); } } return cnt != m_nodemap.count(); } void MilestoneItemModel::resetModel() { beginResetModel(); resetData(); endResetModel(); } Qt::ItemFlags MilestoneItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); if ( !index.isValid() ) { if ( m_readWrite ) { flags |= Qt::ItemIsDropEnabled; } return flags; } flags |= Qt::ItemIsDragEnabled; if ( m_readWrite ) { flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name - flags |= Qt::ItemIsEditable; + if (!isColumnReadOnly(index.column())) { + flags |= Qt::ItemIsEditable; + } break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible - flags |= Qt::ItemIsEditable; + if (!isColumnReadOnly(index.column())) { + flags |= Qt::ItemIsEditable; + } break; case NodeModel::NodeConstraint: // constraint type - flags |= Qt::ItemIsEditable; + if (!isColumnReadOnly(index.column())) { + flags |= Qt::ItemIsEditable; + } break; case NodeModel::NodeConstraintStart: { // constraint start - Node *n = node( index ); - if ( n == 0 ) - break; - int c = n->constraint(); - if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { - flags |= Qt::ItemIsEditable; + if (!isColumnReadOnly(index.column())) { + Node *n = node( index ); + if ( n == 0 ) + break; + int c = n->constraint(); + if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { + flags |= Qt::ItemIsEditable; + } } break; } case NodeModel::NodeConstraintEnd: { // constraint end - Node *n = node( index ); - if ( n == 0 ) - break; - int c = n->constraint(); - if ( c == Node::MustFinishOn || c == Node::FinishNotLater || c == Node::FixedInterval ) { - flags |= Qt::ItemIsEditable; + if (!isColumnReadOnly(index.column())) { + Node *n = node( index ); + if ( n == 0 ) + break; + int c = n->constraint(); + if ( c == Node::MustFinishOn || c == Node::FinishNotLater || c == Node::FixedInterval ) { + flags |= Qt::ItemIsEditable; + } } break; } case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost - Node *n = node( index ); - if ( n && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) ) { - flags |= Qt::ItemIsEditable; + if (!isColumnReadOnly(index.column())) { + Node *n = node( index ); + if ( n && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) ) { + flags |= Qt::ItemIsEditable; + } } break; } case NodeModel::NodeDescription: // description break; + case NodeModel::NodeCompleted: + if (!isColumnReadOnly(index.column())) { + flags |= Qt::ItemIsEditable; + } + break; default: flags &= ~Qt::ItemIsEditable; } } return flags; } QModelIndex MilestoneItemModel::parent( const QModelIndex &index ) const { Q_UNUSED(index); return QModelIndex(); } QModelIndex MilestoneItemModel::index( int row, int column, const QModelIndex &parent ) const { //debugPlan<= m_nodemap.count() ) { //debugPlan<<"No index for"<( node ) ), 0, const_cast(node) ); // clazy:exclude=container-anti-pattern } QVariant MilestoneItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( n != 0 ) { if ( index.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { result = m_nodemodel.data( n, index.column(), Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return KGantt::TypeTask; } return result; } } result = m_nodemodel.data( n, index.column(), role ); return result; } bool MilestoneItemModel::setData( const QModelIndex &index, const QVariant &/*value*/, int role ) { if ( ( flags(index) &Qt::ItemIsEditable ) == 0 || role != Qt::EditRole ) { return false; } // Node *n = node( index ); switch (index.column()) { default: qWarning("data: invalid display value column %d", index.column()); return false; } return false; } QVariant MilestoneItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole || role == Qt::EditRole) { return m_nodemodel.headerData(section, role); } } if ( role == Qt::ToolTipRole ) { return NodeModel::headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *MilestoneItemModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeModel::NodeEstimateType: return new EnumDelegate( parent ); case NodeModel::NodeEstimateCalendar: return new EnumDelegate( parent ); case NodeModel::NodeEstimate: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeOptimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodePessimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodeRisk: return new EnumDelegate( parent ); case NodeModel::NodeConstraint: return new EnumDelegate( parent ); case NodeModel::NodeRunningAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupCost: return new MoneyDelegate( parent ); case NodeModel::NodeShutdownAccount: return new EnumDelegate( parent ); case NodeModel::NodeShutdownCost: return new MoneyDelegate( parent ); case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent ); default: return 0; } return 0; } int MilestoneItemModel::columnCount( const QModelIndex &/*parent*/ ) const { return m_nodemodel.propertyCount(); } int MilestoneItemModel::rowCount( const QModelIndex &parent ) const { //debugPlan< rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool MilestoneItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { //debugPlan; Node *dn = node( index ); if ( dn == 0 ) { errorPlan<<"no node to drop on!"; return false; // hmmm } switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } return false; } bool MilestoneItemModel::dropAllowed( Node *on, const QMimeData *data ) { if ( !data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal") ) { return false; } if ( on == m_project ) { return true; } QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } return true; } QList MilestoneItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList MilestoneItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool MilestoneItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { //debugPlan<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { return false; } if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } return false; } Node *MilestoneItemModel::node( const QModelIndex &index ) const { Node *n = 0; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); } return n; } void MilestoneItemModel::slotNodeChanged( Node *node ) { //debugPlan<name(); if ( node == 0 ) { return; } beginResetModel(); resetData(); endResetModel(); } void MilestoneItemModel::slotWbsDefinitionChanged() { //debugPlan; if ( m_project == 0 ) { return; } if ( ! m_nodemap.isEmpty() ) { beginResetModel(); resetData(); endResetModel(); } } int MilestoneItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } return v; } //-------------- NodeSortFilterProxyModel::NodeSortFilterProxyModel( ItemModelBase* model, QObject *parent, bool filterUnscheduled ) : QSortFilterProxyModel( parent ), m_filterUnscheduled( filterUnscheduled ) { setSourceModel( model ); setDynamicSortFilter( true ); } ItemModelBase *NodeSortFilterProxyModel::itemModel() const { return static_cast( sourceModel() ); } void NodeSortFilterProxyModel::setFilterUnscheduled( bool on ) { m_filterUnscheduled = on; invalidateFilter(); } bool NodeSortFilterProxyModel::filterAcceptsRow ( int row, const QModelIndex & parent ) const { //debugPlan<project() == 0 ) { //debugPlan<project(); return false; } if ( m_filterUnscheduled ) { QString s = sourceModel()->data( sourceModel()->index( row, NodeModel::NodeNotScheduled, parent ), Qt::EditRole ).toString(); if ( s == "true" ) { //debugPlan<<"Filtered unscheduled:"<index( row, 0, parent ); return false; } } bool accepted = QSortFilterProxyModel::filterAcceptsRow( row, parent ); //debugPlan<index( row, 0, parent )<<"accepted ="<sortRole(column)); QSortFilterProxyModel::sort(column, order); } //------------------ TaskModuleModel::TaskModuleModel( QObject *parent ) : QAbstractItemModel( parent ) { } void TaskModuleModel::addTaskModule( Project *project, const QUrl &url ) { beginInsertRows( QModelIndex(), m_modules.count(), m_modules.count() ); m_modules << project; m_urls << url; endInsertRows(); } Qt::ItemFlags TaskModuleModel::flags( const QModelIndex &idx ) const { Qt::ItemFlags f = QAbstractItemModel::flags( idx ) | Qt::ItemIsDropEnabled; if ( idx.isValid() ) { f |= Qt::ItemIsDragEnabled; } return f; } int TaskModuleModel::columnCount (const QModelIndex &/*idx*/ ) const { return 1; } int TaskModuleModel::rowCount( const QModelIndex &idx ) const { return idx.isValid() ? 0 : m_modules.count(); } QVariant TaskModuleModel::data( const QModelIndex& idx, int role ) const { if (!idx.isValid() || idx.row() >= m_modules.count()) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_modules.value( idx.row() )->name(); case Qt::ToolTipRole: return m_modules.value( idx.row() )->description(); case Qt::WhatsThisRole: return m_modules.value( idx.row() )->description(); case Qt::UserRole: return m_urls.value(idx.row()); default: break; } return QVariant(); } QVariant TaskModuleModel::headerData( int /*section*/, Qt::Orientation orientation , int role ) const { if ( orientation == Qt::Horizontal ) { switch ( role ) { case Qt::DisplayRole: return xi18nc( "@title:column", "Name" ); default: break; } } return QVariant(); } QModelIndex TaskModuleModel::parent( const QModelIndex& /*idx*/ ) const { return QModelIndex(); } QModelIndex TaskModuleModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column, m_modules.value( row ) ); } QStringList TaskModuleModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan" << "text/uri-list"; } bool TaskModuleModel::dropMimeData( const QMimeData *data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex &/*parent*/ ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project *project = new Project(); XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( project ); if ( project->load( element, status ) ) { stripProject( project ); addTaskModule( project, url ); if ( emitsignal ) { // FIXME: save destroys the project, so give it a copy (see kptview.cpp) Project p; status.setProject( &p ); p.load( element, status ); emit saveTaskModule( url, &p ); } } else { debugPlan<<"Failed to load project from:"<setData( "application/x-vnd.kde.plan.project", context.document.toByteArray() ); } } return mime; } void TaskModuleModel::stripProject( Project *project ) const { foreach ( ScheduleManager *sm, project->scheduleManagers() ) { DeleteScheduleManagerCmd c( *project, sm ); } } void TaskModuleModel::loadTaskModules( const QStringList &files ) { debugPlan< Copyright (C) 2006 Raphael Langerhorst Copyright (C) 2016 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 "kptganttview.h" #include "kptnodeitemmodel.h" #include "kptappointment.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptrelation.h" #include "kptschedule.h" #include "kptviewbase.h" #include "kptitemviewsettup.h" #include "kptduration.h" #include "kptdatetime.h" #include "kptresourceappointmentsmodel.h" #include "Help.h" #include "kptdebug.h" #include "gantt/DateTimeTimeLine.h" #include "gantt/DateTimeGrid.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// The main namespace namespace KPlato { class GanttItemDelegate; //------------------------------------------------- GanttChartDisplayOptionsPanel::GanttChartDisplayOptionsPanel( GanttViewBase *gantt, GanttItemDelegate *delegate, QWidget *parent ) : QWidget( parent ) , m_delegate( delegate ) , m_gantt(gantt) { setupUi( this ); setValues( *delegate ); connect( ui_showTaskName, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showResourceNames, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showDependencies, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showPositiveFloat, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showNegativeFloat, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showCriticalPath, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showCriticalTasks, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showCompletion, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showSchedulingError, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showTimeConstraint, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); } void GanttChartDisplayOptionsPanel::slotOk() { m_delegate->showTaskName = ui_showTaskName->checkState() == Qt::Checked; m_delegate->showResources = ui_showResourceNames->checkState() == Qt::Checked; m_delegate->showTaskLinks = ui_showDependencies->checkState() == Qt::Checked; m_delegate->showPositiveFloat = ui_showPositiveFloat->checkState() == Qt::Checked; m_delegate->showNegativeFloat = ui_showNegativeFloat->checkState() == Qt::Checked; m_delegate->showCriticalPath = ui_showCriticalPath->checkState() == Qt::Checked; m_delegate->showCriticalTasks = ui_showCriticalTasks->checkState() == Qt::Checked; m_delegate->showProgress = ui_showCompletion->checkState() == Qt::Checked; m_delegate->showSchedulingError = ui_showSchedulingError->checkState() == Qt::Checked; m_delegate->showTimeConstraint = ui_showTimeConstraint->checkState() == Qt::Checked; DateTimeTimeLine *timeline = m_gantt->timeLine(); timeline->setInterval(ui_timeLineInterval->value() * 60000); QPen pen; pen.setWidth(ui_timeLineStroke->value()); pen.setColor(ui_timeLineColor->color()); timeline->setPen(pen); DateTimeTimeLine::Options opt = timeline->options(); opt.setFlag(DateTimeTimeLine::Foreground, ui_timeLineForeground->isChecked()); opt.setFlag(DateTimeTimeLine::Background, ui_timeLineBackground->isChecked()); opt.setFlag(DateTimeTimeLine::UseCustomPen, ui_timeLineUseCustom->isChecked()); timeline->setOptions(opt); } void GanttChartDisplayOptionsPanel::setValues( const GanttItemDelegate &del ) { ui_showTaskName->setCheckState( del.showTaskName ? Qt::Checked : Qt::Unchecked ); ui_showResourceNames->setCheckState( del.showResources ? Qt::Checked : Qt::Unchecked ); ui_showDependencies->setCheckState( del.showTaskLinks ? Qt::Checked : Qt::Unchecked ); ui_showPositiveFloat->setCheckState( del.showPositiveFloat ? Qt::Checked : Qt::Unchecked ); ui_showNegativeFloat->setCheckState( del.showNegativeFloat ? Qt::Checked : Qt::Unchecked ); ui_showCriticalPath->setCheckState( del.showCriticalPath ? Qt::Checked : Qt::Unchecked ); ui_showCriticalTasks->setCheckState( del.showCriticalTasks ? Qt::Checked : Qt::Unchecked ); ui_showCompletion->setCheckState( del.showProgress ? Qt::Checked : Qt::Unchecked ); ui_showSchedulingError->setCheckState( del.showSchedulingError ? Qt::Checked : Qt::Unchecked ); ui_showTimeConstraint->setCheckState( del.showTimeConstraint ? Qt::Checked : Qt::Unchecked ); DateTimeTimeLine *timeline = m_gantt->timeLine(); ui_timeLineInterval->setValue(timeline->interval() / 60000); QPen pen = timeline->pen(); ui_timeLineStroke->setValue(pen.width()); ui_timeLineColor->setColor(pen.color()); ui_timeLineHide->setChecked(true); DateTimeTimeLine::Options opt = timeline->options(); ui_timeLineBackground->setChecked(opt & DateTimeTimeLine::Background); ui_timeLineForeground->setChecked(opt & DateTimeTimeLine::Foreground); ui_timeLineUseCustom->setChecked(opt & DateTimeTimeLine::UseCustomPen); } void GanttChartDisplayOptionsPanel::setDefault() { GanttItemDelegate del; setValues( del ); } //---- GanttViewSettingsDialog::GanttViewSettingsDialog( GanttViewBase *gantt, GanttItemDelegate *delegate, ViewBase *view, bool selectPrint ) : ItemViewSettupDialog( view, gantt->treeView(), true, view ), m_gantt( gantt ) { GanttChartDisplayOptionsPanel *panel = new GanttChartDisplayOptionsPanel( gantt, delegate ); /*KPageWidgetItem *page = */insertWidget( 1, panel, i18n( "Chart" ), i18n( "Gantt Chart Settings" ) ); QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_printingoptions = new GanttPrintingOptionsWidget( this ); m_printingoptions->setOptions( gantt->printingOptions() ); tab->addTab( m_printingoptions, m_printingoptions->windowTitle() ); KPageWidgetItem *page = insertWidget( 2, tab, i18n( "Printing" ), i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, SIGNAL(accepted()), this, SLOT(slotOk()) ); connect( this, &QDialog::accepted, panel, &GanttChartDisplayOptionsPanel::slotOk ); connect( button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, panel, &GanttChartDisplayOptionsPanel::setDefault ); } void GanttViewSettingsDialog::slotOk() { debugPlan; m_gantt->setPrintingOptions( m_printingoptions->options()); ItemViewSettupDialog::slotOk(); } //------------------------- GanttPrintingOptions::GanttPrintingOptions() : printRowLabels( true ), singlePage( true ) { } bool GanttPrintingOptions::loadContext( const KoXmlElement &settings ) { KoXmlElement e = settings.namedItem( "print-options" ).toElement(); if ( ! e.isNull() ) { printRowLabels = (bool)( e.attribute( "print-rowlabels", "0" ).toInt() ); singlePage = (bool)( e.attribute( "print-singlepage", "0" ).toInt() ); } debugPlan <<"..........."<treeView()->header()->height(); // same header hight m_sceneRect = gantt->graphicsView()->sceneRect(); m_horPages = 1; qreal c = m_sceneRect.width() - printer().pageRect().width(); while ( c > 0 ) { ++m_horPages; c -= printer().pageRect().width(); } m_vertPages = 1; c = m_sceneRect.height() - printer().pageRect().height() - m_headerHeight; while ( c > 0 ) { ++m_vertPages; c -= printer().pageRect().height(); } debugPlan< pages; if ( printer().fromPage() > 0 ) { pages << printer().fromPage(); if ( ! m_gantt->m_printOptions.singlePage ) { int last = printer().toPage(); for ( int i = pages.first() + 1; i <= last; ++i ) { pages << i; } if (m_vertPages > 1) { m_image = QImage(m_sceneRect.width(), m_sceneRect.height() + m_headerHeight, QImage::Format_ARGB32); m_image.fill(Qt::white); QPainter p(&m_image); m_gantt->print(&p, m_image.rect(), m_gantt->m_printOptions.printRowLabels, true); } } } setPageRange( pages ); PrintingDialog::startPrinting( removePolicy ); } QList GanttPrintingDialog::createOptionWidgets() const { //debugPlan; GanttPrintingOptionsWidget *w = new GanttPrintingOptionsWidget(); w->setPrintRowLabels( m_gantt->m_printOptions.printRowLabels ); connect(w->ui_printRowLabels, &QAbstractButton::toggled, this, &GanttPrintingDialog::slotPrintRowLabelsToogled); w->setSinglePage( m_gantt->m_printOptions.singlePage ); connect(w->ui_singlePage, &QAbstractButton::toggled, this, &GanttPrintingDialog::slotSinglePageToogled); const_cast( this )->m_options = w; return QList() << createPageLayoutWidget() << m_options; } void GanttPrintingDialog::slotPrintRowLabelsToogled( bool on ) { m_gantt->m_printOptions.printRowLabels = on; } void GanttPrintingDialog::slotSinglePageToogled( bool on ) { m_gantt->m_printOptions.singlePage = on; printer().setFromTo( documentFirstPage(), documentLastPage() ); } int GanttPrintingDialog::documentLastPage() const { //debugPlan<m_printOptions.singlePage<m_printOptions.singlePage ? documentFirstPage() : m_horPages * m_vertPages; } void GanttPrintingDialog::printPage( int page, QPainter &painter ) { debugPlan<<"page:"<m_printOptions.singlePage; int vert = singlePage ? 0 : p / m_horPages; int hor = singlePage ? 0 : p % m_horPages; // painter.setClipRect( pageRect.adjusted( -1.0, -1.0, 1.0, 1.0 ) ); if (singlePage) { // single page: use KGantt m_gantt->print( &painter, m_sceneRect.left(), m_sceneRect.right(), pageRect, m_gantt->m_printOptions.printRowLabels, true ); } else if (m_vertPages == 1) { // single vertical page: use KGantt qreal hh = vert == 0 ? m_headerHeight : 0; qreal ho = vert > 0 ? m_headerHeight : 0; QRectF sourceRect = QRectF( m_sceneRect.x() + ( pageRect.width() * hor ), m_sceneRect.y() + ( ( pageRect.height() * vert ) - ho ), pageRect.width(), pageRect.height() - hh ); debugPlan<print( &painter, sourceRect.left(), sourceRect.right(), pageRect, hor == 0 && m_gantt->m_printOptions.printRowLabels, true ); } else { // print on multiple vertical pages: use pixmap // QT5TODO Make KGantt able to print multiple pages vertically QRectF sourceRect = m_image.rect(); qreal hh = vert == 0 ? m_headerHeight : 0; qreal ho = vert > 0 ? m_headerHeight : 0; sourceRect = QRectF( sourceRect.x() + ( pageRect.width() * hor ), sourceRect.y() + ( ( pageRect.height() * vert ) - ho ), pageRect.width(), pageRect.height() - hh ); debugPlan<setContextMenuPolicy( Qt::CustomContextMenu ); connect( header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderContextMenuRequested(QPoint)) ); } //------------------------------------------- GanttZoomWidget::GanttZoomWidget( QWidget *parent ) : QSlider( parent ), m_hide( true ), m_grid( 0 ) { setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); setGeometry( 0, 0, 200, minimumSizeHint().height() ); setContextMenuPolicy( Qt::PreventContextMenu ); setOrientation( Qt::Horizontal ); setPageStep( 5 ); setMaximum( 125 ); connect(this, &QAbstractSlider::valueChanged, this, &GanttZoomWidget::sliderValueChanged); } void GanttZoomWidget::setEnableHideOnLeave( bool hide ) { m_hide = hide; } void GanttZoomWidget::setGrid( KGantt::DateTimeGrid *grid ) { m_grid = grid; if ( grid ) { int pos = -1; // daywidth always >= 0.1 for ( qreal dw = grid->dayWidth(); dw >= 0.1 && pos < maximum(); ++pos ) { dw *= 1.0 / 1.1; } blockSignals( true ); setValue( pos ); blockSignals( false ); } } void GanttZoomWidget::leaveEvent( QEvent *e ) { if ( m_hide ) { setVisible( false ); } QSlider::leaveEvent( e ); } void GanttZoomWidget::sliderValueChanged( int value ) { //debugPlan<setDayWidth( v ); } } //------------------------------------------- GanttViewBase::GanttViewBase( QWidget *parent ) : KGantt::View( parent ) { setGrid(new DateTimeGrid()); DateTimeGrid *g = static_cast( grid() ); g->setUserDefinedUpperScale( new KGantt::DateTimeScaleFormatter(KGantt::DateTimeScaleFormatter::Month, QString::fromLatin1("yyyy-MMMM"))); g->setUserDefinedLowerScale( new KGantt::DateTimeScaleFormatter(KGantt::DateTimeScaleFormatter::Day, QString::fromLatin1("ddd"))); g->timeNow()->setInterval(5000); QLocale locale; g->setWeekStart( locale.firstDayOfWeek() ); const QList weekdays = locale.weekdays(); QSet fd; for ( int i = Qt::Monday; i <= Qt::Sunday; ++i ) { if (!weekdays.contains(static_cast(i))) { fd << static_cast( i ); } } g->setFreeDays( fd ); m_zoomwidget = new GanttZoomWidget( graphicsView() ); m_zoomwidget->setGrid( g ); m_zoomwidget->setEnableHideOnLeave( true ); m_zoomwidget->hide(); m_zoomwidget->move( 6, 6 ); graphicsView()->installEventFilter(this); graphicsView()->setMouseTracking(true); } GanttViewBase::~GanttViewBase() { // HACK: avoid crash due to access of graphicsview scrollbar after death // KGantt tries to sync leftview scrollbar with graphicsview scrollbar // and seems sometimes graphicsview has already been deleted. // Note: this will be fixed in next KGantt release leftView()->verticalScrollBar()->disconnect(); } DateTimeTimeLine *GanttViewBase::timeLine() const { DateTimeGrid *g = static_cast( grid() ); return g->timeNow(); } GanttTreeView *GanttViewBase::treeView() const { GanttTreeView *tv = qobject_cast(const_cast(leftView())); Q_ASSERT(tv); return tv; } bool GanttViewBase::eventFilter(QObject *obj, QEvent *event) { if (obj != graphicsView()) { return false; } if (event->type() == QEvent::HoverMove) { QHoverEvent *e = static_cast( event ); if (e->pos().y() > 7 && e->pos().y() < m_zoomwidget->height() + 5 && e->pos().x() > 7 && e->pos().x() < m_zoomwidget->width() + 5 ) { if ( !m_zoomwidget->isVisible()) { m_zoomwidget->show(); m_zoomwidget->setFocus(); } return true; } } return false; } bool GanttViewBase::loadContext( const KoXmlElement &settings ) { KGantt::DateTimeGrid *g = static_cast( grid() ); g->setScale( static_cast( settings.attribute( "chart-scale", "0" ).toInt() ) ); g->setDayWidth( settings.attribute( "chart-daywidth", "30" ).toDouble() ); DateTimeTimeLine::Options opt; opt.setFlag(DateTimeTimeLine::Foreground, settings.attribute("timeline-foreground").toInt()); opt.setFlag(DateTimeTimeLine::Background, settings.attribute("timeline-background").toInt()); opt.setFlag(DateTimeTimeLine::UseCustomPen, settings.attribute("timeline-custom").toInt()); timeLine()->setOptions(opt); timeLine()->setInterval(settings.attribute("timeline-interval", 0).toInt() * 60000); QPen pen; pen.setWidth(settings.attribute("timeline-width").toInt()); pen.setColor(QColor(settings.attribute("timeline-color"))); timeLine()->setPen(pen); return true; } void GanttViewBase::saveContext( QDomElement &settings ) const { KGantt::DateTimeGrid *g = static_cast( grid() ); settings.setAttribute( "chart-scale", QString::number(g->scale()) ); settings.setAttribute( "chart-daywidth", QString::number(g->dayWidth()) ); settings.setAttribute("timeline-foreground", timeLine()->options() & DateTimeTimeLine::Foreground); settings.setAttribute("timeline-background", timeLine()->options() & DateTimeTimeLine::Background); settings.setAttribute("timeline-interval", timeLine()->interval() / 60000); settings.setAttribute("timeline-custom", timeLine()->options() & DateTimeTimeLine::UseCustomPen); settings.setAttribute("timeline-width", timeLine()->pen().width()); settings.setAttribute("timeline-color", timeLine()->pen().color().name()); } void GanttViewBase::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_dragStartPosition = event->pos(); } KGantt::View::mousePressEvent(event); } void GanttViewBase::mouseMoveEvent(QMouseEvent *event) { if (!(event->buttons() & Qt::LeftButton)) { KGantt::View::mouseMoveEvent(event); return; } if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance()) { KGantt::View::mouseMoveEvent(event); return; } QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; QPixmap pixmap(size()); render(&pixmap); mimeData->setImageData(pixmap); drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); } //------------------------------------------- NodeGanttViewBase::NodeGanttViewBase( QWidget *parent ) : GanttViewBase( parent ), m_project( 0 ), m_ganttdelegate( new GanttItemDelegate( this ) ) { debugPlan<<"------------------- create NodeGanttViewBase -----------------------"; graphicsView()->setItemDelegate( m_ganttdelegate ); GanttTreeView *tv = new GanttTreeView( this ); tv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); tv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tv->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); // needed since qt 4.2 setLeftView( tv ); m_rowController = new KGantt::TreeViewRowController( tv, ganttProxyModel() ); setRowController( m_rowController ); tv->header()->setStretchLastSection( true ); NodeSortFilterProxyModel *m = new NodeSortFilterProxyModel(&m_defaultModel, this, true); KGantt::View::setModel( m ); } NodeGanttViewBase::~NodeGanttViewBase() { delete m_rowController; } NodeSortFilterProxyModel *NodeGanttViewBase::sfModel() const { return static_cast( KGantt::View::model() ); } void NodeGanttViewBase::setItemModel( ItemModelBase *model ) { sfModel()->setSourceModel( model ); } ItemModelBase *NodeGanttViewBase::model() const { return sfModel()->itemModel(); } void NodeGanttViewBase::setProject( Project *project ) { model()->setProject( project ); m_project = project; } bool NodeGanttViewBase::loadContext( const KoXmlElement &settings ) { treeView()->loadContext( model()->columnMap(), settings ); KoXmlElement e = settings.namedItem( "ganttchart" ).toElement(); if ( ! e.isNull() ) { m_ganttdelegate->showTaskLinks = (bool)( e.attribute( "show-dependencies", "0" ).toInt() ); m_ganttdelegate->showTaskName = (bool)( e.attribute( "show-taskname", "0" ).toInt() ); m_ganttdelegate->showResources = (bool)( e.attribute( "show-resourcenames", "0" ).toInt() ); m_ganttdelegate->showProgress = (bool)( e.attribute( "show-completion", "0" ).toInt() ); m_ganttdelegate->showCriticalPath = (bool)( e.attribute( "show-criticalpath", "0" ).toInt() ); m_ganttdelegate->showCriticalTasks = (bool)( e.attribute( "show-criticaltasks", "0" ).toInt() ); m_ganttdelegate->showPositiveFloat = (bool)( e.attribute( "show-positivefloat", "0" ).toInt() ); m_ganttdelegate->showSchedulingError = (bool)( e.attribute( "show-schedulingerror", "0" ).toInt() ); m_ganttdelegate->showTimeConstraint = (bool)( e.attribute( "show-timeconstraint", "0" ).toInt() ); m_ganttdelegate->showNegativeFloat = (bool)( e.attribute( "show-negativefloat", "0" ).toInt() ); GanttViewBase::loadContext( e ); m_printOptions.loadContext( e ); } return true; } void NodeGanttViewBase::saveContext( QDomElement &settings ) const { debugPlan; treeView()->saveContext( model()->columnMap(), settings ); QDomElement e = settings.ownerDocument().createElement( "ganttchart" ); settings.appendChild( e ); e.setAttribute( "show-dependencies", QString::number(m_ganttdelegate->showTaskLinks) ); e.setAttribute( "show-taskname", QString::number(m_ganttdelegate->showTaskName) ); e.setAttribute( "show-resourcenames", QString::number(m_ganttdelegate->showResources) ); e.setAttribute( "show-completion", QString::number(m_ganttdelegate->showProgress) ); e.setAttribute( "show-criticalpath", QString::number(m_ganttdelegate->showCriticalPath) ); e.setAttribute( "show-criticaltasks",QString::number(m_ganttdelegate->showCriticalTasks) ); e.setAttribute( "show-positivefloat", QString::number(m_ganttdelegate->showPositiveFloat) ); e.setAttribute( "show-schedulingerror", QString::number(m_ganttdelegate->showSchedulingError) ); e.setAttribute( "show-timeconstraint", QString::number(m_ganttdelegate->showTimeConstraint) ); e.setAttribute( "show-negativefloat", QString::number(m_ganttdelegate->showNegativeFloat) ); GanttViewBase::saveContext( e ); m_printOptions.saveContext( e ); } void NodeGanttViewBase::setShowUnscheduledTasks(bool show) { NodeSortFilterProxyModel *m = qobject_cast(KGantt::View::model()); if (m) { m->setFilterUnscheduled(!show); } } //------------------------------------------- MyKGanttView::MyKGanttView( QWidget *parent ) : NodeGanttViewBase( parent ), m_manager( 0 ) { debugPlan<<"------------------- create MyKGanttView -----------------------"; GanttItemModel *gm = new GanttItemModel( this ); + for (int i = 0; i < gm->columnCount(); ++i) { + if (i != NodeModel::NodeCompleted) { + gm->setReadOnly(i, true); + } + } setItemModel( gm ); treeView()->createItemDelegates( gm ); QList show; show << NodeModel::NodeName << NodeModel::NodeCompleted << NodeModel::NodeStartTime << NodeModel::NodeEndTime; treeView()->setDefaultColumns( show ); for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { treeView()->hideColumn( i ); } } setConstraintModel( new KGantt::ConstraintModel( this ) ); KGantt::ProxyModel *m = static_cast( ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); // To provide correct format m->setRole( KGantt::StartTimeRole, Qt::EditRole ); // To provide correct format m->setRole( KGantt::EndTimeRole, Qt::EditRole ); // To provide correct format m->removeColumn( Qt::DisplayRole ); m->setColumn( KGantt::ItemTypeRole, NodeModel::NodeType ); m->setColumn( KGantt::StartTimeRole, NodeModel::NodeStartTime ); m->setColumn( KGantt::EndTimeRole, NodeModel::NodeEndTime ); m->setColumn( KGantt::TaskCompletionRole, NodeModel::NodeCompleted ); KGantt::DateTimeGrid *g = static_cast( grid() ); g->setDayWidth( 30 ); // TODO: extend QLocale/KGantt to support formats for hourly time display // see bug #349030 // removed custom code here connect( model(), &NodeItemModel::nodeInserted, this, &MyKGanttView::slotNodeInserted ); } GanttItemModel *MyKGanttView::model() const { return static_cast( NodeGanttViewBase::model() ); } void MyKGanttView::setProject( Project *proj ) { clearDependencies(); if ( project() ) { disconnect( project(), &Project::relationToBeModified, this, &MyKGanttView::removeDependency); disconnect( project(), &Project::relationModified, this, &MyKGanttView::addDependency); disconnect( project(), &Project::relationAdded, this, &MyKGanttView::addDependency ); disconnect( project(), &Project::relationToBeRemoved, this, &MyKGanttView::removeDependency ); disconnect( project(), &Project::projectCalculated, this, &MyKGanttView::slotProjectCalculated ); } NodeGanttViewBase::setProject( proj ); if ( proj ) { connect( project(), &Project::relationToBeModified, this, &MyKGanttView::removeDependency); connect( project(), &Project::relationModified, this, &MyKGanttView::addDependency); connect( proj, &Project::relationAdded, this, &MyKGanttView::addDependency ); connect( proj, &Project::relationToBeRemoved, this, &MyKGanttView::removeDependency ); connect( proj, &Project::projectCalculated, this, &MyKGanttView::slotProjectCalculated ); } createDependencies(); } void MyKGanttView::slotProjectCalculated( ScheduleManager *sm ) { if ( m_manager == sm ) { setScheduleManager( sm ); } } void MyKGanttView::setScheduleManager( ScheduleManager *sm ) { constraintModel()->clear(); m_manager = sm; KGantt::DateTimeGrid *g = static_cast( grid() ); if ( sm && project() ) { QDateTime start = project()->startTime( sm->scheduleId() ); if ( start.isValid() && g->startDateTime() != start ) { g->setStartDateTime( start ); } } if ( ! g->startDateTime().isValid() ) { g->setStartDateTime( QDateTime::currentDateTime() ); } model()->setScheduleManager( sm ); createDependencies(); } void MyKGanttView::slotNodeInserted( Node *node ) { foreach( Relation *r, node->dependChildNodes() ) { addDependency( r ); } foreach( Relation *r, node->dependParentNodes() ) { addDependency( r ); } } void MyKGanttView::addDependency( Relation *rel ) { QModelIndex par = sfModel()->mapFromSource( model()->index( rel->parent() ) ); QModelIndex ch = sfModel()->mapFromSource( model()->index( rel->child() ) ); // debugPlan<<"addDependency() "<( rel->type() )/*NOTE!!*/ ); if ( ! constraintModel()->hasConstraint( con ) ) { constraintModel()->addConstraint( con ); } } } void MyKGanttView::removeDependency( Relation *rel ) { QModelIndex par = sfModel()->mapFromSource( model()->index( rel->parent() ) ); QModelIndex ch = sfModel()->mapFromSource( model()->index( rel->child() ) ); KGantt::Constraint con( par, ch, KGantt::Constraint::TypeSoft, static_cast( rel->type() )/*NOTE!!*/ ); constraintModel()->removeConstraint( con ); } void MyKGanttView::clearDependencies() { constraintModel()->clear(); // Remove old deps from view // NOTE: This should be handled by KGantt graphicsView()->updateScene(); } void MyKGanttView::createDependencies() { clearDependencies(); if ( project() == 0 || m_manager == 0 ) { return; } foreach ( Node* n, project()->allNodes() ) { foreach ( Relation *r, n->dependChildNodes() ) { addDependency( r ); } } } //------------------------------------------ GanttView::GanttView(KoPart *part, KoDocument *doc, QWidget *parent, bool readWrite) : ViewBase(part, doc, parent), m_readWrite( readWrite ), m_project( 0 ) { debugPlan <<" ---------------- KPlato: Creating GanttView ----------------"; setXMLFile("GanttViewUi.rc"); QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); m_splitter = new QSplitter( this ); l->addWidget( m_splitter ); m_splitter->setOrientation( Qt::Vertical ); m_gantt = new MyKGanttView( m_splitter ); m_gantt->graphicsView()->setHeaderContextMenuPolicy(Qt::CustomContextMenu); connect(this, &ViewBase::expandAll, m_gantt->treeView(), &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_gantt->treeView(), &TreeViewBase::slotCollapse); setupGui(); updateReadWrite( readWrite ); //connect( m_gantt->constraintModel(), SIGNAL(constraintAdded(Constraint)), this, SLOT(update()) ); debugPlan <constraintModel(); connect( m_gantt->treeView(), &TreeViewBase::contextMenuRequested, this, &GanttView::slotContextMenuRequested ); connect( m_gantt->treeView(), &TreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_gantt->graphicsView(), &KGantt::GraphicsView::headerContextMenuRequested, this, &GanttView::slotGanttHeaderContextMenuRequested); connect(qobject_cast(m_gantt->graphicsView()->grid()), &KGantt::DateTimeGrid::gridChanged, this, &GanttView::slotDateTimeGridChanged); Help::add(this, xi18nc("@info:whatsthis", "Gantt View" "" "Displays scheduled tasks in a Gantt diagram." " The chart area can be zoomed in and out with a slider" " positioned in the upper left corner of the time scale." " You need to hoover over it with the mouse for it to show." "" "This view supports configuration and printing using the context menu of the tree view." "More..." "", Help::page("Manual/Task_Gantt_View"))); } void GanttView::slotGanttHeaderContextMenuRequested(const QPoint &pt) { QMenu *menu = popupMenu("gantt_datetimegrid_popup"); if (menu) { menu->exec(pt); } } KoPrintJob *GanttView::createPrintJob() { return new GanttPrintingDialog( this, m_gantt ); } void GanttView::setZoom( double ) { //debugPlan <<"setting gantt zoom:" << zoom; //m_gantt->setZoomFactor(zoom,true); NO!!! setZoomFactor() is something else } void GanttView::setupGui() { // create context menu actions actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); actionCollection()->addAction("show_project", actionShowProject); // FIXME: Dependencies depend on these methods being called in the correct order connect(actionShowProject, &QAction::triggered, m_gantt, &MyKGanttView::clearDependencies); connect(actionShowProject, &QAction::triggered, m_gantt->model(), &NodeItemModel::setShowProject); connect(actionShowProject, &QAction::triggered, m_gantt, &MyKGanttView::createDependencies); addContextAction( actionShowProject ); actionShowUnscheduled = new KToggleAction( i18n( "Show Unscheduled Tasks" ), this ); actionCollection()->addAction("show_unscheduled_tasks", actionShowUnscheduled); connect(actionShowUnscheduled, &QAction::triggered, m_gantt, &MyKGanttView::setShowUnscheduledTasks); addContextAction(actionShowUnscheduled); createOptionActions(ViewBase::OptionAll); for (QAction *a : contextActionList()) { actionCollection()->addAction(a->objectName(), a); } m_scalegroup = new QActionGroup(this); QAction *a = new QAction(i18nc("@action:inmenu", "Auto"), this); a->setCheckable(true); a->setChecked(true); actionCollection()->addAction("scale_auto", a); connect(a, &QAction::triggered, this, &GanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Month"), this); actionCollection()->addAction("scale_month", a); a->setCheckable(true); connect(a, &QAction::triggered, this, &GanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Week"), this); actionCollection()->addAction("scale_week", a); a->setCheckable(true); connect(a, &QAction::triggered, this, &GanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Day"), this); a->setCheckable(true); actionCollection()->addAction("scale_day", a); connect(a, &QAction::triggered, this, &GanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Hour"), this); a->setCheckable(true); actionCollection()->addAction("scale_hour", a); connect(a, &QAction::triggered, this, &GanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Zoom In"), this); a->setIcon(koIcon("zoom-in")); actionCollection()->addAction("zoom_in", a); connect(a, &QAction::triggered, this, &GanttView::ganttActions); a = new QAction(i18nc("@action:inmenu", "Zoom Out"), this); a->setIcon(koIcon("zoom-out")); actionCollection()->addAction("zoom_out", a); connect(a, &QAction::triggered, this, &GanttView::ganttActions); } void GanttView::slotDateTimeGridChanged() { DateTimeGrid *grid = qobject_cast(m_gantt->grid()); Q_ASSERT(grid); if (!grid) { return; } QAction *a = m_scalegroup->checkedAction(); switch (grid->scale()) { case KGantt::DateTimeGrid::ScaleAuto: actionCollection()->action("scale_auto")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleHour: actionCollection()->action("scale_hour")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleDay: actionCollection()->action("scale_day")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleWeek: actionCollection()->action("scale_week")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleMonth: actionCollection()->action("scale_month")->setChecked(true); break; default: warnPlan<<"Unused scale:"<scale(); break; } } void GanttView::ganttActions() { QAction *action = qobject_cast(sender()); if (!action) { return; } DateTimeGrid *grid = qobject_cast(m_gantt->grid()); Q_ASSERT(grid); if (!grid) { return; } if (action->objectName() == "scale_auto") { grid->setScale(DateTimeGrid::ScaleAuto); } else if (action->objectName() == "scale_month") { grid->setScale(DateTimeGrid::ScaleMonth); } else if (action->objectName() == "scale_week") { grid->setScale( DateTimeGrid::ScaleWeek); } else if (action->objectName() == "scale_day") { grid->setScale( DateTimeGrid::ScaleDay); } else if (action->objectName() == "scale_hour") { grid->setScale( DateTimeGrid::ScaleHour); } else if (action->objectName() == "zoom_in") { grid->setDayWidth(grid->dayWidth() * 1.25); } else if (action->objectName() == "zoom_out") { // daywidth *MUST NOT* go below 1.0, it is used as an integer later on grid->setDayWidth(qMax(1.0, grid->dayWidth() * 0.8)); } else { warnPlan<<"Unknown gantt action:"<delegate(), this, sender()->objectName() == "print options" ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void GanttView::slotOptionsFinished( int result ) { GanttViewSettingsDialog *dlg = qobject_cast( sender() ); if ( dlg && result == QDialog::Accepted ) { m_gantt->graphicsView()->updateScene(); } ViewBase::slotOptionsFinished( result ); } void GanttView::clear() { // m_gantt->clear(); } void GanttView::setShowResources( bool on ) { m_gantt->delegate()->showResources = on; } void GanttView::setShowTaskName( bool on ) { m_gantt->delegate()->showTaskName = on; } void GanttView::setShowProgress( bool on ) { m_gantt->delegate()->showProgress = on; } void GanttView::setShowPositiveFloat( bool on ) { m_gantt->delegate()->showPositiveFloat = on; } void GanttView::setShowCriticalTasks( bool on ) { m_gantt->delegate()->showCriticalTasks = on; } void GanttView::setShowCriticalPath( bool on ) { m_gantt->delegate()->showCriticalPath = on; } void GanttView::setShowNoInformation( bool on ) { m_gantt->delegate()->showNoInformation = on; } void GanttView::setShowAppointments( bool on ) { m_gantt->delegate()->showAppointments = on; } void GanttView::setShowTaskLinks( bool on ) { m_gantt->delegate()->showTaskLinks = on; } void GanttView::setProject( Project *project ) { m_gantt->setProject( project ); } void GanttView::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_gantt->treeView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); bool expand = sm && scheduleManager() && sm != scheduleManager(); QDomDocument doc; if (expand) { QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_gantt->treeView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_gantt->setScheduleManager( sm ); if (expand) { m_gantt->treeView()->doExpand(doc); } else if (tryexpand) { m_gantt->treeView()->doExpand(m_domdoc); } } void GanttView::draw( Project &project ) { setProject( &project ); } void GanttView::drawChanges( Project &project ) { if ( m_project != &project ) { setProject( &project ); } } Node *GanttView::currentNode() const { QModelIndex idx = m_gantt->treeView()->selectionModel()->currentIndex(); return m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); } void GanttView::slotContextMenuRequested( const QModelIndex &idx, const QPoint &pos ) { debugPlan; QString name; Node *node = m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); 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"; m_gantt->treeView()->setContextMenuIndex(idx); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); debugPlan<<"No menu"; return; } emit requestPopupMenu( name, pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); } bool GanttView::loadContext( const KoXmlElement &settings ) { debugPlan; ViewBase::loadContext( settings ); bool show = (bool)(settings.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); m_gantt->model()->setShowProject( show ); // why is this not called by the action? show = (bool)(settings.attribute( "show-unscheduled", "1" ).toInt() ); actionShowUnscheduled->setChecked(show); m_gantt->setShowUnscheduledTasks(show); return m_gantt->loadContext( settings ); } void GanttView::saveContext( QDomElement &settings ) const { debugPlan; ViewBase::saveContext( settings ); settings.setAttribute( "show-project", QString::number(actionShowProject->isChecked()) ); settings.setAttribute( "show-unscheduled", QString::number(actionShowUnscheduled->isChecked()) ); m_gantt->saveContext( settings ); } void GanttView::updateReadWrite( bool on ) { // TODO: KGanttView needs read/write mode m_readWrite = on; + if (m_gantt->model()) { + m_gantt->model()->setReadWrite(on); + } } //---- MilestoneGanttViewSettingsDialog::MilestoneGanttViewSettingsDialog( GanttViewBase *gantt, ViewBase *view, bool selectPrint ) : ItemViewSettupDialog( view, gantt->treeView(), true, view ), m_gantt( gantt ) { QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_printingoptions = new GanttPrintingOptionsWidget( this ); m_printingoptions->setOptions( gantt->printingOptions() ); tab->addTab( m_printingoptions, m_printingoptions->windowTitle() ); KPageWidgetItem *page = insertWidget( -1, tab, i18n( "Printing" ), i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, SIGNAL(accepted()), this, SLOT(slotOk()) ); } void MilestoneGanttViewSettingsDialog::slotOk() { debugPlan; m_gantt->setPrintingOptions( m_printingoptions->options()); ItemViewSettupDialog::slotOk(); } //------------------------ MilestoneKGanttView::MilestoneKGanttView( QWidget *parent ) : NodeGanttViewBase( parent ), m_manager( 0 ) { debugPlan<<"------------------- create MilestoneKGanttView -----------------------"; MilestoneItemModel *mm = new MilestoneItemModel( this ); + for (int i = 0; i < mm->columnCount(); ++i) { + if (i != NodeModel::NodeCompleted) { + mm->setReadOnly(i, true); + } + } setItemModel( mm ); treeView()->createItemDelegates( mm ); sfModel()->setFilterRole ( Qt::EditRole ); sfModel()->setFilterFixedString( QString::number( Node::Type_Milestone ) ); sfModel()->setFilterKeyColumn( NodeModel::NodeType ); QList show; show << NodeModel::NodeWBSCode << NodeModel::NodeName << NodeModel::NodeStartTime; treeView()->setDefaultColumns( show ); for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { treeView()->hideColumn( i ); } } treeView()->header()->moveSection(NodeModel::NodeWBSCode, show.indexOf(NodeModel::NodeWBSCode)); treeView()->setRootIsDecorated ( false ); KGantt::ProxyModel *m = static_cast( ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); // To provide correct format m->setRole( KGantt::StartTimeRole, Qt::EditRole ); // To provide correct format m->setRole( KGantt::EndTimeRole, Qt::EditRole ); // To provide correct format m->removeColumn( Qt::DisplayRole ); m->setColumn( KGantt::ItemTypeRole, NodeModel::NodeType ); m->setColumn( KGantt::StartTimeRole, NodeModel::NodeStartTime ); m->setColumn( KGantt::EndTimeRole, NodeModel::NodeEndTime ); m->setColumn( KGantt::TaskCompletionRole, NodeModel::NodeCompleted ); KGantt::DateTimeGrid *g = static_cast( grid() ); g->setDayWidth( 30 ); // TODO: extend QLocale/KGantt to support formats for hourly time display // see bug #349030 // removed custom code here // TODO: add to context treeView()->sortByColumn(NodeModel::NodeWBSCode, Qt::AscendingOrder); treeView()->setSortingEnabled(true); Help::add(this, xi18nc("@info:whatsthis", "Milestone Gantt View" "" "Displays scheduled milestones in a Gantt diagram." " The chart area can be zoomed in and out with a slider" " positioned in the upper left corner of the time scale." " You need to hoover over it with the mouse for it to show." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Milestone_Gantt_View"))); } MilestoneItemModel *MilestoneKGanttView::model() const { return static_cast( NodeGanttViewBase::model() ); } void MilestoneKGanttView::setProject( Project *proj ) { if ( project() ) { disconnect( project(), &Project::projectCalculated, this, &MilestoneKGanttView::slotProjectCalculated ); } NodeGanttViewBase::setProject( proj ); if ( proj ) { connect( proj, &Project::projectCalculated, this, &MilestoneKGanttView::slotProjectCalculated ); } } void MilestoneKGanttView::slotProjectCalculated( ScheduleManager *sm ) { if ( m_manager == sm ) { setScheduleManager( sm ); } } void MilestoneKGanttView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<setScheduleManager( 0 ); m_manager = sm; KGantt::DateTimeGrid *g = static_cast( grid() ); if ( sm && m_project ) { QDateTime start; foreach ( const Node *n, model()->mileStones() ) { QDateTime nt = n->startTime( sm->scheduleId() ); if ( ! nt.isValid() ) { continue; } if ( ! start.isValid() || start > nt ) { start = nt; debugPlan<name()<startTime( sm->scheduleId() ); } if (start.isValid() && g->startDateTime() != start ) { g->setStartDateTime( start ); } } if ( ! g->startDateTime().isValid() ) { g->setStartDateTime( QDateTime::currentDateTime() ); } model()->setScheduleManager( sm ); } //------------------------------------------ MilestoneGanttView::MilestoneGanttView(KoPart *part, KoDocument *doc, QWidget *parent, bool readWrite) : ViewBase(part, doc, parent), m_readWrite( readWrite ), m_project( 0 ) { debugPlan <<" ---------------- Plan: Creating Milesone GanttView ----------------"; setXMLFile("GanttViewUi.rc"); QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); m_splitter = new QSplitter( this ); l->addWidget( m_splitter ); m_splitter->setOrientation( Qt::Vertical ); setupGui(); m_gantt = new MilestoneKGanttView( m_splitter ); m_gantt->graphicsView()->setHeaderContextMenuPolicy(Qt::CustomContextMenu); m_showTaskName = false; // FIXME m_showProgress = false; //FIXME m_showPositiveFloat = false; //FIXME m_showCriticalTasks = false; //FIXME m_showNoInformation = false; //FIXME updateReadWrite( readWrite ); connect( m_gantt->treeView(), &TreeViewBase::contextMenuRequested, this, &MilestoneGanttView::slotContextMenuRequested ); connect( m_gantt->treeView(), &TreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_gantt->graphicsView(), &KGantt::GraphicsView::headerContextMenuRequested, this, &MilestoneGanttView::slotGanttHeaderContextMenuRequested); connect(qobject_cast(m_gantt->graphicsView()->grid()), &KGantt::DateTimeGrid::gridChanged, this, &MilestoneGanttView::slotDateTimeGridChanged); } void MilestoneGanttView::slotGanttHeaderContextMenuRequested(const QPoint &pt) { QMenu *menu = popupMenu("gantt_datetimegrid_popup"); if (menu) { menu->exec(pt); } } void MilestoneGanttView::setZoom( double ) { //debugPlan <<"setting gantt zoom:" << zoom; //m_gantt->setZoomFactor(zoom,true); NO!!! setZoomFactor() is something else } void MilestoneGanttView::show() { } void MilestoneGanttView::clear() { } void MilestoneGanttView::setProject( Project *project ) { m_gantt->setProject( project ); } void MilestoneGanttView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<setScheduleManager( sm ); } void MilestoneGanttView::draw( Project &project ) { setProject( &project ); } void MilestoneGanttView::drawChanges( Project &project ) { if ( m_project != &project ) { setProject( &project ); } } Node *MilestoneGanttView::currentNode() const { QModelIndex idx = m_gantt->treeView()->selectionModel()->currentIndex(); return m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); } void MilestoneGanttView::setupGui() { createOptionActions(ViewBase::OptionAll & ~(ViewBase::OptionExpand|ViewBase::OptionCollapse)); for (QAction *a : contextActionList()) { actionCollection()->addAction(a->objectName(), a); } m_scalegroup = new QActionGroup(this); QAction *a = new QAction(i18nc("@action:inmenu", "Auto"), this); a->setCheckable(true); a->setChecked(true); actionCollection()->addAction("scale_auto", a); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Month"), this); actionCollection()->addAction("scale_month", a); a->setCheckable(true); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Week"), this); actionCollection()->addAction("scale_week", a); a->setCheckable(true); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Day"), this); a->setCheckable(true); actionCollection()->addAction("scale_day", a); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Hour"), this); a->setCheckable(true); actionCollection()->addAction("scale_hour", a); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Zoom In"), this); a->setIcon(koIcon("zoom-in")); actionCollection()->addAction("zoom_in", a); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); a = new QAction(i18nc("@action:inmenu", "Zoom Out"), this); a->setIcon(koIcon("zoom-out")); actionCollection()->addAction("zoom_out", a); connect(a, &QAction::triggered, this, &MilestoneGanttView::ganttActions); } void MilestoneGanttView::slotDateTimeGridChanged() { DateTimeGrid *grid = qobject_cast(m_gantt->grid()); Q_ASSERT(grid); if (!grid) { return; } QAction *a = m_scalegroup->checkedAction(); switch (grid->scale()) { case KGantt::DateTimeGrid::ScaleAuto: actionCollection()->action("scale_auto")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleHour: actionCollection()->action("scale_hour")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleDay: actionCollection()->action("scale_day")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleWeek: actionCollection()->action("scale_week")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleMonth: actionCollection()->action("scale_month")->setChecked(true); break; default: warnPlan<<"Unused scale:"<scale(); break; } } void MilestoneGanttView::ganttActions() { QAction *action = qobject_cast(sender()); if (!action) { return; } DateTimeGrid *grid = qobject_cast(m_gantt->grid()); Q_ASSERT(grid); if (!grid) { return; } if (action->objectName() == "scale_auto") { grid->setScale(DateTimeGrid::ScaleAuto); } else if (action->objectName() == "scale_month") { grid->setScale(DateTimeGrid::ScaleMonth); } else if (action->objectName() == "scale_week") { grid->setScale( DateTimeGrid::ScaleWeek); } else if (action->objectName() == "scale_day") { grid->setScale( DateTimeGrid::ScaleDay); } else if (action->objectName() == "scale_hour") { grid->setScale( DateTimeGrid::ScaleHour); } else if (action->objectName() == "zoom_in") { grid->setDayWidth(grid->dayWidth() * 1.25); } else if (action->objectName() == "zoom_out") { // daywidth *MUST NOT* go below 1.0, it is used as an integer later on grid->setDayWidth(qMax(1.0, grid->dayWidth() * 0.8)); } else { warnPlan<<"Unknown gantt action:"<model()->node( m_gantt->sfModel()->mapToSource( idx ) ); if ( node ) { switch ( node->type() ) { 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"; m_gantt->treeView()->setContextMenuIndex(idx); if ( name.isEmpty() ) { debugPlan<<"No menu"; slotHeaderContextMenuRequested( pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); } void MilestoneGanttView::slotOptions() { debugPlan; MilestoneGanttViewSettingsDialog *dlg = new MilestoneGanttViewSettingsDialog( m_gantt, this, sender()->objectName() == "print options" ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool MilestoneGanttView::loadContext( const KoXmlElement &settings ) { debugPlan; ViewBase::loadContext( settings ); return m_gantt->loadContext( settings ); } void MilestoneGanttView::saveContext( QDomElement &settings ) const { debugPlan; ViewBase::saveContext( settings ); return m_gantt->saveContext( settings ); } void MilestoneGanttView::updateReadWrite( bool on ) { m_readWrite = on; + if (m_gantt->model()) { + m_gantt->model()->setReadWrite(on); + } } KoPrintJob *MilestoneGanttView::createPrintJob() { return new GanttPrintingDialog( this, m_gantt ); } //-------------------- ResourceAppointmentsGanttViewSettingsDialog::ResourceAppointmentsGanttViewSettingsDialog( GanttViewBase *gantt, ViewBase *view, bool selectPrint ) : ItemViewSettupDialog( view, gantt->treeView(), true, view ) , m_gantt(gantt) { QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_printingoptions = new GanttPrintingOptionsWidget( this ); m_printingoptions->setOptions( gantt->printingOptions() ); tab->addTab( m_printingoptions, m_printingoptions->windowTitle() ); KPageWidgetItem *page = insertWidget( -1, tab, i18n( "Printing" ), i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, SIGNAL(accepted()), this, SLOT(slotOk()) ); } void ResourceAppointmentsGanttViewSettingsDialog::slotOk() { debugPlan; m_gantt->setPrintingOptions( m_printingoptions->options()); ItemViewSettupDialog::slotOk(); } //------------------------------------------ ResourceAppointmentsGanttView::ResourceAppointmentsGanttView(KoPart *part, KoDocument *doc, QWidget *parent, bool readWrite) : ViewBase(part, doc, parent), m_project( 0 ), m_model( new ResourceAppointmentsGanttModel( this ) ) { debugPlan <<" ---------------- KPlato: Creating ResourceAppointmentsGanttView ----------------"; setXMLFile("GanttViewUi.rc"); m_gantt = new GanttViewBase( this ); m_gantt->graphicsView()->setHeaderContextMenuPolicy(Qt::CustomContextMenu); m_gantt->graphicsView()->setItemDelegate( new ResourceGanttItemDelegate( m_gantt ) ); GanttTreeView *tv = new GanttTreeView( m_gantt ); tv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); tv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tv->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); // needed since qt 4.2 m_gantt->setLeftView( tv ); connect(this, &ViewBase::expandAll, tv, &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, tv, &TreeViewBase::slotCollapse); m_rowController = new KGantt::TreeViewRowController( tv, m_gantt->ganttProxyModel() ); m_gantt->setRowController( m_rowController ); tv->header()->setStretchLastSection( true ); tv->setTreePosition(-1); KGantt::ProxyModel *m = static_cast( m_gantt->ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); m->setRole( KGantt::StartTimeRole, KGantt::StartTimeRole ); m->setRole( KGantt::EndTimeRole, KGantt::EndTimeRole ); m->setRole( KGantt::TaskCompletionRole, KGantt::TaskCompletionRole ); m_gantt->setModel( m_model ); QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); l->addWidget( m_gantt ); setupGui(); updateReadWrite( readWrite ); connect( m_gantt->leftView(), SIGNAL(contextMenuRequested(QModelIndex,QPoint,QModelIndexList)), SLOT(slotContextMenuRequested(QModelIndex,QPoint)) ); connect( m_gantt->leftView(), SIGNAL(headerContextMenuRequested(QPoint)), SLOT(slotHeaderContextMenuRequested(QPoint)) ); connect(m_gantt->graphicsView(), &KGantt::GraphicsView::headerContextMenuRequested, this, &ResourceAppointmentsGanttView::slotGanttHeaderContextMenuRequested); connect(qobject_cast(m_gantt->graphicsView()->grid()), &KGantt::DateTimeGrid::gridChanged, this, &ResourceAppointmentsGanttView::slotDateTimeGridChanged); Help::add(this, xi18nc("@info:whatsthis", "Resource Assignments (Gantt)" "" "Displays the scheduled resource - task assignments in a Gantt diagram." " The chart area can be zoomed in and out with a slider" " positioned in the upper left corner of the time scale." " You need to hoover over it with the mouse for it to show." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Resource_Assignment_Gantt_View"))); } ResourceAppointmentsGanttView::~ResourceAppointmentsGanttView() { delete m_rowController; } void ResourceAppointmentsGanttView::slotGanttHeaderContextMenuRequested(const QPoint &pt) { QMenu *menu = popupMenu("gantt_datetimegrid_popup"); if (menu) { menu->exec(pt); } } void ResourceAppointmentsGanttView::setZoom( double ) { //debugPlan <<"setting gantt zoom:" << zoom; //m_gantt->setZoomFactor(zoom,true); NO!!! setZoomFactor() is something else } Project *ResourceAppointmentsGanttView::project() const { return m_model->project(); } void ResourceAppointmentsGanttView::setProject( Project *project ) { m_model->setProject( project ); } void ResourceAppointmentsGanttView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); bool expand = sm && scheduleManager() && sm != scheduleManager(); QDomDocument doc; if (expand) { QDomElement element = doc.createElement("expanded"); doc.appendChild(element); treeView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_model->setScheduleManager( sm ); if (expand) { treeView()->doExpand(doc); } else if (tryexpand) { treeView()->doExpand(m_domdoc); } } void ResourceAppointmentsGanttView::setupGui() { createOptionActions(ViewBase::OptionAll); for (QAction *a : contextActionList()) { actionCollection()->addAction(a->objectName(), a); } m_scalegroup = new QActionGroup(this); QAction *a = new QAction(i18nc("@action:inmenu", "Auto"), this); a->setCheckable(true); a->setChecked(true); actionCollection()->addAction("scale_auto", a); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Month"), this); actionCollection()->addAction("scale_month", a); a->setCheckable(true); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Week"), this); actionCollection()->addAction("scale_week", a); a->setCheckable(true); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Day"), this); a->setCheckable(true); actionCollection()->addAction("scale_day", a); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Hour"), this); a->setCheckable(true); actionCollection()->addAction("scale_hour", a); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); m_scalegroup->addAction(a); a = new QAction(i18nc("@action:inmenu", "Zoom In"), this); a->setIcon(koIcon("zoom-in")); actionCollection()->addAction("zoom_in", a); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); a = new QAction(i18nc("@action:inmenu", "Zoom Out"), this); a->setIcon(koIcon("zoom-out")); actionCollection()->addAction("zoom_out", a); connect(a, &QAction::triggered, this, &ResourceAppointmentsGanttView::ganttActions); } void ResourceAppointmentsGanttView::slotDateTimeGridChanged() { DateTimeGrid *grid = qobject_cast(m_gantt->grid()); Q_ASSERT(grid); if (!grid) { return; } QAction *a = m_scalegroup->checkedAction(); switch (grid->scale()) { case KGantt::DateTimeGrid::ScaleAuto: actionCollection()->action("scale_auto")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleHour: actionCollection()->action("scale_hour")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleDay: actionCollection()->action("scale_day")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleWeek: actionCollection()->action("scale_week")->setChecked(true); break; case KGantt::DateTimeGrid::ScaleMonth: actionCollection()->action("scale_month")->setChecked(true); break; default: warnPlan<<"Unused scale:"<scale(); break; } } void ResourceAppointmentsGanttView::ganttActions() { QAction *action = qobject_cast(sender()); if (!action) { return; } DateTimeGrid *grid = qobject_cast(m_gantt->grid()); Q_ASSERT(grid); if (!grid) { return; } if (action->objectName() == "scale_auto") { grid->setScale(DateTimeGrid::ScaleAuto); } else if (action->objectName() == "scale_month") { grid->setScale(DateTimeGrid::ScaleMonth); } else if (action->objectName() == "scale_week") { grid->setScale( DateTimeGrid::ScaleWeek); } else if (action->objectName() == "scale_day") { grid->setScale( DateTimeGrid::ScaleDay); } else if (action->objectName() == "scale_hour") { grid->setScale( DateTimeGrid::ScaleHour); } else if (action->objectName() == "zoom_in") { grid->setDayWidth(grid->dayWidth() * 1.25); } else if (action->objectName() == "zoom_out") { // daywidth *MUST NOT* go below 1.0, it is used as an integer later on grid->setDayWidth(qMax(1.0, grid->dayWidth() * 0.8)); } else { warnPlan<<"Unknown gantt action:"<selectionModel()->currentIndex(); return m_model->node( idx ); } void ResourceAppointmentsGanttView::slotContextMenuRequested( const QModelIndex &idx, const QPoint &pos ) { debugPlan<node( idx ); if ( n ) { name = "taskview_popup"; } } m_gantt->treeView()->setContextMenuIndex(idx); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); } void ResourceAppointmentsGanttView::slotOptions() { debugPlan; ItemViewSettupDialog *dlg = new ResourceAppointmentsGanttViewSettingsDialog(m_gantt, this, sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool ResourceAppointmentsGanttView::loadContext( const KoXmlElement &settings ) { debugPlan; ViewBase::loadContext( settings ); m_gantt->loadContext( settings ); return treeView()->loadContext( m_model->columnMap(), settings ); } void ResourceAppointmentsGanttView::saveContext( QDomElement &settings ) const { debugPlan; ViewBase::saveContext( settings ); m_gantt->saveContext( settings ); treeView()->saveContext( m_model->columnMap(), settings ); } void ResourceAppointmentsGanttView::updateReadWrite( bool on ) { m_readWrite = on; } KoPrintJob *ResourceAppointmentsGanttView::createPrintJob() { return new GanttPrintingDialog( this, m_gantt ); } } //KPlato namespace #include "moc_kptganttview.cpp"