diff --git a/src/calligraplansettings.kcfg b/src/calligraplansettings.kcfg index fac1b0af..ff82c4db 100644 --- a/src/calligraplansettings.kcfg +++ b/src/calligraplansettings.kcfg @@ -1,355 +1,358 @@ 1760 176 40 8 true NoneExists true InWeekCalendar Default true 08:00 16:00 true 08:00 16:00 true 08:00 16:00 true 08:00 16:00 true 08:00 16:00 false 08:00 16:00 false 08:00 16:00 false AsSoonAsPossible CurrentdateTime CurrentdateTime Effort Hour 8.0 -75 100 Linear false #0000ff #0000ff #0000ff #0000ff #0000ff #00ff00 #ff0000 #A0A0A0 #ffff00 #0000ff #ff0000 #A0A0A0 #ffff00 - + + + + EnumUnit::Hour EnumUnit::Month diff --git a/src/kptconfig.cpp b/src/kptconfig.cpp index a6264a72..a207110e 100644 --- a/src/kptconfig.cpp +++ b/src/kptconfig.cpp @@ -1,329 +1,328 @@ /* This file is part of the KDE project Copyright (C) 2004, 2007 Dag Andersen Copyright (C) 2011, 2012 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptconfig.h" #include "calligraplansettings.h" #include "kptconfigskeleton.h" #include "kptfactory.h" #include "kptproject.h" #include "kptdebug.h" #include namespace KPlato { Config::Config() : ConfigBase() { debugPlan<<"Leader:"<save(); } bool Config::isWorkingday(int day) const { switch (day) { case Qt::Monday: return KPlatoSettings::monday(); break; case Qt::Tuesday: return KPlatoSettings::tuesday(); break; case Qt::Wednesday: return KPlatoSettings::wednesday(); break; case Qt::Thursday: return KPlatoSettings::thursday(); break; case Qt::Friday: return KPlatoSettings::friday(); break; case Qt::Saturday: return KPlatoSettings::saturday(); break; case Qt::Sunday: return KPlatoSettings::sunday(); break; default: break; }; return false; } QTime Config::dayStartTime(int day) const { switch (day) { case Qt::Monday: return QTime::fromString(KPlatoSettings::mondayStart()); break; case Qt::Tuesday: return QTime::fromString(KPlatoSettings::tuesdayStart()); break; case Qt::Wednesday: return QTime::fromString(KPlatoSettings::wednesdayStart()); break; case Qt::Thursday: return QTime::fromString(KPlatoSettings::thursdayStart()); break; case Qt::Friday: return QTime::fromString(KPlatoSettings::fridayStart()); break; case Qt::Saturday: return QTime::fromString(KPlatoSettings::saturdayStart()); break; case Qt::Sunday: return QTime::fromString(KPlatoSettings::sundayStart()); break; default: break; }; return QTime(); } int Config::dayLength(int day) const { QTime start = dayStartTime(day); QTime end; int value = 0; switch (day) { case Qt::Monday: end = QTime::fromString(KPlatoSettings::mondayEnd()); break; case Qt::Tuesday: end = QTime::fromString(KPlatoSettings::tuesdayEnd()); break; case Qt::Wednesday: end = QTime::fromString(KPlatoSettings::wednesdayEnd()); break; case Qt::Thursday: end = QTime::fromString(KPlatoSettings::thursdayEnd()); break; case Qt::Friday: end = QTime::fromString(KPlatoSettings::fridayEnd()); break; case Qt::Saturday: end = QTime::fromString(KPlatoSettings::saturdayEnd()); break; case Qt::Sunday: end = QTime::fromString(KPlatoSettings::sundayEnd()); break; default: break; }; value = start.msecsTo(end); if (value < 0) { value = (24*60*60*1000) + value; } else if (value == 0 && start == QTime(0, 0)) { value = 24*60*60*1000; } return value; } void Config::setDefaultValues( Project &project ) { project.setLeader( KPlatoSettings::manager() ); project.setUseSharedResources( KPlatoSettings::useSharedResources() ); project.setSharedResourcesFile( KPlatoSettings::sharedResourcesFile() ); project.setSharedProjectsUrl( QUrl(KPlatoSettings::sharedProjectsPlace()) ); project.setDescription( KPlatoSettings::projectDescription() ); StandardWorktime *v = project.standardWorktime(); Q_ASSERT(v); if (v) { v->setYear(KPlatoSettings::hoursPrYear()); v->setMonth(KPlatoSettings::hoursPrMonth()); v->setWeek(KPlatoSettings::hoursPrWeek()); v->setDay(KPlatoSettings::hoursPrDay()); } + + Project::WorkPackageInfo wpi; + wpi.checkForWorkPackages = KPlatoSettings::checkForWorkPackages(); + wpi.retrieveUrl = KPlatoSettings::retrieveUrl(); + wpi.deleteAfterRetrieval = KPlatoSettings::deleteFile(); + wpi.archiveAfterRetrieval = !KPlatoSettings::deleteFile(); + wpi.archiveUrl = KPlatoSettings::saveUrl(); + wpi.publishUrl = KPlatoSettings::publishUrl(); + project.setWorkPackageInfo(wpi); } void Config::setDefaultValues( Task &task ) { task.setLeader( KPlatoSettings::leader() ); task.setDescription( KPlatoSettings::description() ); task.setConstraint( (Node::ConstraintType) KPlatoSettings::constraintType() ); // avoid problems with start <= end & end >= start task.setConstraintStartTime( DateTime() ); task.setConstraintEndTime( DateTime() ); switch ( KPlatoSettings::startTimeUsage() ) { case KPlatoSettings::EnumStartTimeUsage::CurrentdateTime: task.setConstraintStartTime( DateTime( QDateTime::currentDateTime() ) ); break; case KPlatoSettings::EnumStartTimeUsage::CurrentDate: task.setConstraintStartTime( DateTime( QDate::currentDate(), KPlatoSettings::constraintStartTime().time() ) ); break; case KPlatoSettings::EnumStartTimeUsage::SpecifiedDateTime: //fall through default: task.setConstraintStartTime( DateTime( KPlatoSettings::constraintStartTime() ) ); break; } switch ( KPlatoSettings::endTimeUsage() ) { case KPlatoSettings::EnumEndTimeUsage::CurrentdateTime: task.setConstraintEndTime( DateTime( QDateTime::currentDateTime() ) ); break; case KPlatoSettings::EnumEndTimeUsage::CurrentDate: task.setConstraintEndTime( DateTime( QDate::currentDate(), KPlatoSettings::constraintEndTime().time() ) ); break; case KPlatoSettings::EnumEndTimeUsage::SpecifiedDateTime: //fall through default: task.setConstraintEndTime( DateTime( KPlatoSettings::constraintEndTime() ) ); break; } task.estimate()->setType( (Estimate::Type) KPlatoSettings::estimateType() ); task.estimate()->setUnit( (Duration::Unit) KPlatoSettings::unit() ); task.estimate()->setExpectedEstimate( KPlatoSettings::expectedEstimate() ); task.estimate()->setPessimisticRatio( KPlatoSettings::pessimisticRatio() ); task.estimate()->setOptimisticRatio( KPlatoSettings::optimisticRatio() ); } int Config::minimumDurationUnit() const { return KPlatoSettings::minimumDurationUnit(); } int Config::maximumDurationUnit() const { return KPlatoSettings::maximumDurationUnit(); } -bool Config::checkForWorkPackages() const -{ - return KPlatoSettings::checkForWorkPackages(); -} - -QUrl Config::retrieveUrl() const -{ - return KPlatoSettings::retrieveUrl(); -} - - QBrush Config::summaryTaskDefaultColor() const { QColor c = KPlatoSettings::summaryTaskDefaultColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } bool Config::summaryTaskLevelColorsEnabled() const { return KPlatoSettings::summaryTaskLevelColorsEnabled(); } QBrush Config::summaryTaskLevelColor_1() const { QColor c = KPlatoSettings::summaryTaskLevelColor_1(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::summaryTaskLevelColor_2() const { QColor c = KPlatoSettings::summaryTaskLevelColor_2(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::summaryTaskLevelColor_3() const { QColor c = KPlatoSettings::summaryTaskLevelColor_3(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::summaryTaskLevelColor_4() const { QColor c = KPlatoSettings::summaryTaskLevelColor_4(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::taskNormalColor() const { QColor c = KPlatoSettings::taskNormalColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::taskErrorColor() const { QColor c = KPlatoSettings::taskErrorColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::taskCriticalColor() const { QColor c = KPlatoSettings::taskCriticalColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::taskFinishedColor() const { QColor c = KPlatoSettings::taskFinishedColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::milestoneNormalColor() const { QColor c = KPlatoSettings::milestoneNormalColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::milestoneErrorColor() const { QColor c = KPlatoSettings::milestoneErrorColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::milestoneCriticalColor() const { QColor c = KPlatoSettings::milestoneCriticalColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } QBrush Config::milestoneFinishedColor() const { QColor c = KPlatoSettings::milestoneFinishedColor(); if ( KPlatoSettings::colorGradientType() == KPlatoSettings::EnumColorGradientType::Linear ) { return gradientBrush( c ); } return c; } } //KPlato namespace diff --git a/src/kptconfig.h b/src/kptconfig.h index 8cf838e9..8f9ca381 100644 --- a/src/kptconfig.h +++ b/src/kptconfig.h @@ -1,74 +1,72 @@ /* This file is part of the KDE project Copyright (C) 2004, 2007 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTCONFIG_H #define KPTCONFIG_H #include "kptconfigbase.h" namespace KPlato { class Config : public ConfigBase { Q_OBJECT public: Config(); ~Config(); void readConfig(); void saveSettings(); bool isWorkingday(int day) const; QTime dayStartTime(int day) const; int dayLength(int day) const; virtual void setDefaultValues( Project &project ); virtual void setDefaultValues( Task &task ); - virtual bool checkForWorkPackages() const; - virtual QUrl retrieveUrl() const; - virtual int minimumDurationUnit() const; virtual int maximumDurationUnit() const; virtual bool summaryTaskLevelColorsEnabled() const; virtual QBrush summaryTaskDefaultColor() const; virtual QBrush summaryTaskLevelColor_1() const; virtual QBrush summaryTaskLevelColor_2() const; virtual QBrush summaryTaskLevelColor_3() const; virtual QBrush summaryTaskLevelColor_4() const; virtual QBrush taskNormalColor() const; virtual QBrush taskErrorColor() const; virtual QBrush taskCriticalColor() const; virtual QBrush taskFinishedColor() const; virtual QBrush milestoneNormalColor() const; virtual QBrush milestoneErrorColor() const; virtual QBrush milestoneCriticalColor() const; virtual QBrush milestoneFinishedColor() const; }; } //KPlato namespace #endif // CONFIG_H diff --git a/src/kptmaindocument.cpp b/src/kptmaindocument.cpp index ad010785..32589bba 100644 --- a/src/kptmaindocument.cpp +++ b/src/kptmaindocument.cpp @@ -1,1586 +1,1483 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2004, 2010, 2012 Dag Andersen Copyright (C) 2006 Raphael Langerhorst Copyright (C) 2007 Thorsten Zachmann - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptmaindocument.h" #include "kptpart.h" #include "kptview.h" #include "kptfactory.h" #include "kptproject.h" #include "kptlocale.h" #include "kptresource.h" #include "kptcontext.h" #include "kptschedulerpluginloader.h" #include "kptschedulerplugin.h" #include "kptbuiltinschedulerplugin.h" #include "kptcommand.h" #include "calligraplansettings.h" #include "kpttask.h" #include "KPlatoXmlLoader.h" #include "XmlSaveContext.h" #include "kptpackage.h" -#include "kptworkpackagemergedialog.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KHOLIDAYS #include #endif namespace KPlato { MainDocument::MainDocument(KoPart *part) : KoDocument(part), m_project( 0 ), m_context( 0 ), m_xmlLoader(), m_loadingTemplate( false ), m_loadingSharedResourcesTemplate( false ), m_viewlistModified( false ), m_checkingForWorkPackages( false ), m_loadingSharedProject(false), m_skipSharedProjects(false), m_isTaskModule(false) { Q_ASSERT(part); setAlwaysAllowSaving(true); m_config.setReadWrite( true ); loadSchedulerPlugins(); setProject( new Project( m_config ) ); // after config & plugins are loaded m_project->setId( m_project->uniqueNodeId() ); m_project->registerNodeId( m_project ); // register myself connect(this, &MainDocument::insertSharedProject, this, &MainDocument::slotInsertSharedProject); } MainDocument::~MainDocument() { qDeleteAll( m_schedulerPlugins ); if ( m_project ) { m_project->deref(); // deletes if last user } qDeleteAll( m_mergedPackages ); delete m_context; } void MainDocument::setReadWrite( bool rw ) { m_config.setReadWrite( rw ); KoDocument::setReadWrite( rw ); } void MainDocument::loadSchedulerPlugins() { // Add built-in scheduler addSchedulerPlugin( "Built-in", new BuiltinSchedulerPlugin( this ) ); // Add all real scheduler plugins SchedulerPluginLoader *loader = new SchedulerPluginLoader(this); connect(loader, &SchedulerPluginLoader::pluginLoaded, this, &MainDocument::addSchedulerPlugin); loader->loadAllPlugins(); } void MainDocument::addSchedulerPlugin( const QString &key, SchedulerPlugin *plugin) { debugPlan<setConfig( m_config ); } void MainDocument::setProject( Project *project ) { if ( m_project ) { disconnect( m_project, &Project::projectChanged, this, &MainDocument::changed ); delete m_project; } m_project = project; if ( m_project ) { connect( m_project, &Project::projectChanged, this, &MainDocument::changed ); // m_project->setConfig( config() ); m_project->setSchedulerPlugins( m_schedulerPlugins ); } m_aboutPage.setProject( project ); emit changed(); } bool MainDocument::loadOdf( KoOdfReadStore &odfStore ) { warnPlan<< "OpenDocument not supported, let's try native xml format"; return loadXML( odfStore.contentDoc(), 0 ); // We have only one format, so try to load that! } bool MainDocument::loadXML( const KoXmlDocument &document, KoStore* ) { QPointer updater; if (progressUpdater()) { updater = progressUpdater()->startSubtask(1, "Plan::Part::loadXML"); updater->setProgress(0); m_xmlLoader.setUpdater( updater ); } QString value; KoXmlElement plan = document.documentElement(); // Check if this is the right app value = plan.attribute( "mime", QString() ); if ( value.isEmpty() ) { errorPlan << "No mime type specified!"; setErrorMessage( i18n( "Invalid document. No mimetype specified." ) ); return false; } if ( value == "application/x-vnd.kde.kplato" ) { if (updater) { updater->setProgress(5); } m_xmlLoader.setMimetype( value ); QString message; Project *newProject = new Project(m_config, false); KPlatoXmlLoader loader( m_xmlLoader, newProject ); bool ok = loader.load( plan ); if ( ok ) { setProject( newProject ); setModified( false ); debugPlan<schedules(); // Cleanup after possible bug: // There should *not* be any deleted schedules (or with parent == 0) foreach ( Node *n, newProject->nodeDict()) { foreach ( Schedule *s, n->schedules()) { if ( s->isDeleted() ) { // true also if parent == 0 errorPlan<name()<takeSchedule( s ); delete s; } } } } else { setErrorMessage( loader.errorMessage() ); delete newProject; } if (updater) { updater->setProgress(100); // the rest is only processing, not loading } emit changed(); return ok; } if ( value != "application/x-vnd.kde.plan" ) { errorPlan << "Unknown mime type " << value; setErrorMessage( i18n( "Invalid document. Expected mimetype application/x-vnd.kde.plan, got %1", value ) ); return false; } QString syntaxVersion = plan.attribute( "version", PLAN_FILE_SYNTAX_VERSION ); m_xmlLoader.setVersion( syntaxVersion ); if ( syntaxVersion > PLAN_FILE_SYNTAX_VERSION ) { KMessageBox::ButtonCode ret = KMessageBox::warningContinueCancel( 0, i18n( "This document was created with a newer version of Plan (syntax version: %1)\n" "Opening it in this version of Plan will lose some information.", syntaxVersion ), i18n( "File-Format Mismatch" ), KGuiItem( i18n( "Continue" ) ) ); if ( ret == KMessageBox::Cancel ) { setErrorMessage( "USER_CANCELED" ); return false; } } if (updater) updater->setProgress(5); /* #ifdef KOXML_USE_QDOM int numNodes = plan.childNodes().count(); #else int numNodes = plan.childNodesCount(); #endif */ #if 0 This test does not work any longer. KoXml adds a couple of elements not present in the file!! if ( numNodes > 2 ) { //TODO: Make a proper bitching about this debugPlan <<"*** Error ***"; debugPlan <<" Children count should be maximum 2, but is" << numNodes; return false; } #endif m_xmlLoader.startLoad(); KoXmlNode n = plan.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "project" ) { - Project *newProject = new Project(m_config, false); + Project *newProject = new Project(m_config, true); m_xmlLoader.setProject( newProject ); if ( newProject->load( e, m_xmlLoader ) ) { if ( newProject->id().isEmpty() ) { newProject->setId( newProject->uniqueNodeId() ); newProject->registerNodeId( newProject ); } // The load went fine. Throw out the old project setProject( newProject ); // Cleanup after possible bug: // There should *not* be any deleted schedules (or with parent == 0) foreach ( Node *n, newProject->nodeDict()) { foreach ( Schedule *s, n->schedules()) { if ( s->isDeleted() ) { // true also if parent == 0 errorPlan<name()<takeSchedule( s ); delete s; } } } } else { delete newProject; m_xmlLoader.addMsg( XMLLoaderObject::Errors, "Loading of project failed" ); //TODO add some ui here } } } m_xmlLoader.stopLoad(); if (updater) updater->setProgress(100); // the rest is only processing, not loading setModified( false ); emit changed(); return true; } QDomDocument MainDocument::saveXML() { debugPlan; // Save the project XmlSaveContext context(m_project); context.save(); return context.document; } QDomDocument MainDocument::saveWorkPackageXML( const Node *node, long id, Resource *resource ) { - debugPlan; + debugPlanWp<name() ); wp.setAttribute( "owner-id", resource->id() ); } wp.setAttribute( "time-tag", QDateTime::currentDateTime().toString( Qt::ISODate ) ); + wp.setAttribute("save-url", m_project->workPackageInfo().retrieveUrl.toString(QUrl::None)); + wp.setAttribute("load-url", m_project->workPackageInfo().publishUrl.toString(QUrl::None)); + debugPlanWp<<"publish:"<workPackageInfo().publishUrl.toString(QUrl::None); + debugPlanWp<<"retrieve:"<workPackageInfo().retrieveUrl.toString(QUrl::None); doc.appendChild( wp ); // Save the project m_project->saveWorkPackageXML( doc, node, id ); return document; } bool MainDocument::saveWorkPackageToStream( QIODevice *dev, const Node *node, long id, Resource *resource ) { QDomDocument doc = saveWorkPackageXML( node, id, resource ); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open( QIODevice::WriteOnly ); int nwritten = dev->write( s.data(), s.size() ); if ( nwritten != (int)s.size() ) { - warnPlan<<"wrote:"<m_specialOutputFlag == SaveEncrypted ) { backend = KoStore::Encrypted; debugPlan <<"Saving using encrypted backend."; }*/ #endif QByteArray mimeType = "application/x-vnd.kde.plan.work"; - debugPlan <<"MimeType=" << mimeType; + debugPlanWp <<"MimeType=" << mimeType; KoStore *store = KoStore::createStore( file, KoStore::Write, mimeType, backend ); /* if ( d->m_specialOutputFlag == SaveEncrypted && !d->m_password.isNull( ) ) { store->setPassword( d->m_password ); }*/ if ( store->bad() ) { setErrorMessage( i18n( "Could not create the workpackage file for saving: %1", file ) ); // more details needed? delete store; return false; } // Tell KoStore not to touch the file names if ( ! store->open( "root" ) ) { setErrorMessage( i18n( "Not able to write '%1'. Partition full?", QString( "maindoc.xml") ) ); delete store; return false; } KoStoreDevice dev( store ); if ( !saveWorkPackageToStream( &dev, node, id, resource ) || !store->close() ) { - debugPlan <<"saveToStream failed"; + errorPlanWp <<"saveToStream failed"; delete store; return false; } node->documents().saveToStore( store ); - debugPlan <<"Saving done of url:" << file; + debugPlanWp <<"Saving done of url:" << file; if ( !store->finalize() ) { delete store; return false; } // Success delete store; return true; } bool MainDocument::saveWorkPackageUrl( const QUrl &_url, const Node *node, long id, Resource *resource ) { - //debugPlan<<_url; + debugPlanWp<<_url; QApplication::setOverrideCursor( Qt::WaitCursor ); emit statusBarMessage( i18n("Saving...") ); bool ret = false; ret = saveWorkPackageFormat( _url.path(), node, id, resource ); // kzip don't handle file:// QApplication::restoreOverrideCursor(); emit clearStatusBarMessage(); return ret; } bool MainDocument::loadWorkPackage( Project &project, const QUrl &url ) { - debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); - debugPlan<<"bad store"<open( "root" ) ) { // "old" file format (maindoc.xml) // i18n( "File does not have a maindoc.xml: %1", file ); - debugPlan<<"No root"<device(), &errorMsg, &errorLine, &errorColumn ); if ( ! ok ) { - errorPlan << "Parsing error in " << url.url() << "! Aborting!" << endl + errorPlanWp << "Parsing error in " << url.url() << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg; //d->lastErrorMessage = i18n( "Parsing error in %1 at line %2, column %3\nError message: %4",filename ,errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, QCoreApplication::UnicodeUTF8)); } else { package = loadWorkPackageXML( project, store->device(), doc, url ); if ( package ) { package->url = url; m_workpackages.insert( package->timeTag, package ); } else { ok = false; } } store->close(); //### if ( ok && package && package->settings.documents ) { ok = extractFiles( store, package ); } delete store; if ( ! ok ) { // QApplication::restoreOverrideCursor(); return false; } return true; } -Package *MainDocument::loadWorkPackageXML( Project &project, QIODevice *, const KoXmlDocument &document, const QUrl &/*url*/ ) +Package *MainDocument::loadWorkPackageXML( Project &project, QIODevice *, const KoXmlDocument &document, const QUrl &url ) { QString value; bool ok = true; Project *proj = 0; Package *package = 0; KoXmlElement plan = document.documentElement(); // Check if this is the right app value = plan.attribute( "mime", QString() ); if ( value.isEmpty() ) { - debugPlan << "No mime type specified!"; + errorPlanWp<timeTag = QDateTime::fromString( loader.timeTag(), Qt::ISODate ); } else if ( value != "application/x-vnd.kde.plan.work" ) { - debugPlan << "Unknown mime type " << value; + errorPlanWp << "Unknown mime type " << value; setErrorMessage( i18n( "Invalid document. Expected mimetype application/x-vnd.kde.plan.work, got %1", value ) ); return 0; } else { + if (plan.attribute("editor") != QStringLiteral("PlanWork")) { + warnPlanWp<<"Skipped work package file not generated with PlanWork:"< PLANWORK_FILE_SYNTAX_VERSION ) { KMessageBox::ButtonCode ret = KMessageBox::warningContinueCancel( 0, i18n( "This document was created with a newer version of PlanWork (syntax version: %1)\n" "Opening it in this version of PlanWork will lose some information.", syntaxVersion ), i18n( "File-Format Mismatch" ), KGuiItem( i18n( "Continue" ) ) ); if ( ret == KMessageBox::Cancel ) { setErrorMessage( "USER_CANCELED" ); return 0; } } m_xmlLoader.setVersion( plan.attribute( "plan-version", PLAN_FILE_SYNTAX_VERSION ) ); m_xmlLoader.startLoad(); proj = new Project(); package = new Package(); package->project = proj; KoXmlNode n = plan.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "project" ) { m_xmlLoader.setProject( proj ); ok = proj->load( e, m_xmlLoader ); if ( ! ok ) { m_xmlLoader.addMsg( XMLLoaderObject::Errors, "Loading of work package failed" ); + warnPlanWp<<"Skip workpackage:"<<"Loading project failed"; //TODO add some ui here } } else if ( e.tagName() == "workpackage" ) { package->timeTag = QDateTime::fromString( e.attribute( "time-tag" ), Qt::ISODate ); package->ownerId = e.attribute( "owner-id" ); package->ownerName = e.attribute( "owner" ); debugPlan<<"workpackage:"<timeTag<ownerId<ownerName; KoXmlElement elem; forEachElement( elem, e ) { if ( elem.tagName() != "settings" ) { continue; } package->settings.usedEffort = (bool)elem.attribute( "used-effort" ).toInt(); package->settings.progress = (bool)elem.attribute( "progress" ).toInt(); package->settings.documents = (bool)elem.attribute( "documents" ).toInt(); } } } if ( proj->numChildren() > 0 ) { package->task = static_cast( proj->childNode( 0 ) ); package->toTask = qobject_cast( m_project->findNode( package->task->id() ) ); WorkPackage &wp = package->task->workPackage(); if ( wp.ownerId().isEmpty() ) { wp.setOwnerId( package->ownerId ); wp.setOwnerName( package->ownerName ); } - debugPlan<<"Task set:"<task->name(); + if (wp.ownerId() != package->ownerId) { + warnPlanWp<<"Current owner:"<ownerName; + } + debugPlanWp<<"Task set:"<task->name(); } m_xmlLoader.stopLoad(); } - if ( ok && proj->id() == project.id() && proj->childNode( 0 ) ) { - ok = project.nodeDict().contains( proj->childNode( 0 )->id() ); - if ( ok && m_mergedPackages.contains( package->timeTag ) ) { - ok = false; // already merged - } - if ( ok && package->timeTag.isValid() && ! m_mergedPackages.contains( package->timeTag ) ) { - m_mergedPackages[ package->timeTag ] = proj; // register this for next time - } - if ( ok && ! package->timeTag.isValid() ) { - warnPlan<<"Work package is not time tagged:"<childNode( 0 )->name()<url; - ok = false; - } + if (ok && proj->id() != project.id()) { + debugPlanWp<<"Skip workpackage:"<<"Not the correct project"; + ok = false; } - if ( ! ok ) { + if (ok && (package->task == nullptr)) { + warnPlanWp<<"Skip workpackage:"<<"No task in workpackage file"; + ok = false; + } + if (ok && (package->toTask == nullptr)) { + warnPlanWp<<"Skip workpackage:"<<"Cannot find task:"<task->id()<task->name(); + ok = false; + } + if (ok && !package->timeTag.isValid()) { + warnPlanWp<<"Work package is not time tagged:"<task->name()<url; + ok = false; + } + if (ok && m_mergedPackages.contains(package->timeTag)) { + ok = false; // already merged + } + if (!ok) { delete proj; delete package; - return 0; + return nullptr; + } + if (package->timeTag.isValid() && !m_mergedPackages.contains( package->timeTag)) { + m_mergedPackages[package->timeTag] = proj; // register this for next time } - Q_ASSERT( package ); return package; } bool MainDocument::extractFiles( KoStore *store, Package *package ) { if ( package->task == 0 ) { errorPlan<<"No task!"; return false; } foreach ( Document *doc, package->task->documents().documents() ) { if ( ! doc->isValid() || doc->type() != Document::Type_Product || doc->sendAs() != Document::SendAs_Copy ) { continue; } if ( ! extractFile( store, package, doc ) ) { return false; } } return true; } bool MainDocument::extractFile( KoStore *store, Package *package, const Document *doc ) { QTemporaryFile tmpfile; if ( ! tmpfile.open() ) { errorPlan<<"Failed to open temporary file"; return false; } if ( ! store->extractFile( doc->url().fileName(), tmpfile.fileName() ) ) { errorPlan<<"Failed to extract file:"<url().fileName()<<"to:"<documents.insert( tmpfile.fileName(), doc->url() ); tmpfile.setAutoRemove( false ); debugPlan<<"extracted:"<url().fileName()<<"->"<(sender()); - if ( m_config.checkForWorkPackages() ) { + if (m_project && m_project->workPackageInfo().checkForWorkPackages) { checkForWorkPackages( true ); } if (timer && timer->interval() != 10000) { timer->stop(); timer->setInterval(10000); timer->start(); } } void MainDocument::checkForWorkPackages( bool keep ) { - if ( m_checkingForWorkPackages || m_config.retrieveUrl().isEmpty() || m_project == 0 || m_project->numChildren() == 0 ) { + if (m_checkingForWorkPackages || m_project == nullptr || m_project->numChildren() == 0 || m_project->workPackageInfo().retrieveUrl.isEmpty()) { return; } if ( ! keep ) { qDeleteAll( m_mergedPackages ); m_mergedPackages.clear(); } - QDir dir( m_config.retrieveUrl().path(), "*.planwork" ); + QDir dir( m_project->workPackageInfo().retrieveUrl.path(), "*.planwork" ); m_infoList = dir.entryInfoList( QDir::Files | QDir::Readable, QDir::Time ); checkForWorkPackage(); return; } void MainDocument::checkForWorkPackage() { if ( ! m_infoList.isEmpty() ) { m_checkingForWorkPackages = true; - loadWorkPackage( *m_project, QUrl::fromLocalFile( m_infoList.takeLast().absoluteFilePath() ) ); + QUrl url = QUrl::fromLocalFile( m_infoList.takeLast().absoluteFilePath() ); + if (m_skipUrls.contains(url)) { + return; + } + if (!loadWorkPackage( *m_project, url )) { + m_skipUrls << url; + } if ( ! m_infoList.isEmpty() ) { QTimer::singleShot ( 0, this, &MainDocument::checkForWorkPackage ); return; } // all files read // remove other projects QMutableMapIterator it( m_workpackages ); while ( it.hasNext() ) { it.next(); Package *package = it.value(); if ( package->project->id() != m_project->id() ) { delete package->project; delete package; it.remove(); } } // Merge our workpackages if ( ! m_workpackages.isEmpty() ) { - WorkPackageMergeDialog *dlg = new WorkPackageMergeDialog( i18n( "New work packages detected. Merge data with existing tasks?" ), m_workpackages ); - connect(dlg, &QDialog::finished, this, &MainDocument::workPackageMergeDialogFinished); - dlg->show(); - dlg->raise(); - dlg->activateWindow(); + emit workPackageLoaded(); } else { m_checkingForWorkPackages = false; } } } -void MainDocument::workPackageMergeDialogFinished( int result ) -{ - WorkPackageMergeDialog *dlg = qobject_cast( sender() ); - Q_ASSERT(dlg); - if ( dlg == 0 ) { - return; - } - if ( result == KoDialog::Yes ) { - // merge the oldest first - foreach( int i, dlg->checkedList() ) { - const QList &packages = m_workpackages.values(); - mergeWorkPackage(packages.at(i)); - } - // 'Yes' was hit so terminate all packages - foreach(const Package *p, m_workpackages) { - terminateWorkPackage( p ); - } - } - qDeleteAll( m_workpackages ); - m_workpackages.clear(); - m_checkingForWorkPackages = false; - dlg->deleteLater(); -} - -void MainDocument::mergeWorkPackages() -{ - foreach (const Package *package, m_workpackages) { - mergeWorkPackage( package ); - } -} - void MainDocument::terminateWorkPackage( const Package *package ) { + if (m_workpackages.value(package->timeTag) == package) { + m_workpackages.remove(package->timeTag); + } QFile file( package->url.path() ); if ( ! file.exists() ) { return; } - if ( KPlatoSettings::deleteFile() || KPlatoSettings::saveUrl().isEmpty() ) { - file.remove(); - } else if ( KPlatoSettings::saveFile() && ! KPlatoSettings::saveUrl().isEmpty() ) { - QDir dir( KPlatoSettings::saveUrl().path() ); + Project::WorkPackageInfo wpi = m_project->workPackageInfo(); + if (wpi.archiveAfterRetrieval && wpi.archiveUrl.isValid()) { + QDir dir(wpi.archiveUrl.path()); if ( ! dir.exists() ) { if ( ! dir.mkpath( dir.path() ) ) { //TODO message - debugPlan<<"Could not create directory:"<project); - if ( proj.id() == m_project->id() && proj.childNode( 0 ) ) { - const Task *from = package->task; - Task *to = package->toTask; - if ( to && from ) { - mergeWorkPackage( to, from, package ); - } - } -} - -void MainDocument::mergeWorkPackage( Task *to, const Task *from, const Package *package ) -{ - Resource *resource = m_project->findResource( package->ownerId ); - if ( resource == 0 ) { - KMessageBox::error( 0, i18n( "The package owner '%1' is not a resource in this project. You must handle this manually.", package->ownerName ) ); - return; - } - - MacroCommand *cmd = new MacroCommand( kundo2_noi18n("Merge workpackage") ); - Completion &org = to->completion(); - const Completion &curr = from->completion(); - - if ( package->settings.progress ) { - if ( org.isStarted() != curr.isStarted() ) { - cmd->addCommand( new ModifyCompletionStartedCmd(org, curr.isStarted() ) ); - } - if ( org.isFinished() != curr.isFinished() ) { - cmd->addCommand( new ModifyCompletionFinishedCmd( org, curr.isFinished() ) ); - } - if ( org.startTime() != curr.startTime() ) { - cmd->addCommand( new ModifyCompletionStartTimeCmd( org, curr.startTime() ) ); - } - if ( org.finishTime() != curr.finishTime() ) { - cmd->addCommand( new ModifyCompletionFinishTimeCmd( org, curr.finishTime() ) ); - } - // TODO: review how/if to merge data from different resources - // remove entries - const QList &dates = org.entries().keys(); - for (const QDate &d : dates) { - if ( ! curr.entries().contains( d ) ) { - debugPlan<<"remove entry "<addCommand( new RemoveCompletionEntryCmd( org, d ) ); + QString name = dir.absolutePath() + '/' + from.fileName(); + if (!file.rename(name)) { + // try to create a unique name in case name already existed + name = dir.absolutePath() + '/'; + name += from.completeBaseName() + "-%1"; + if ( ! from.suffix().isEmpty() ) { + name += '.' + from.suffix(); } - } - // add new entries / modify existing - const QList &cdates = curr.entries().keys(); - for (const QDate &d : cdates) { - if ( org.entries().contains( d ) && curr.entry( d ) == org.entry( d ) ) { - continue; + int i = 0; + bool ok = false; + while ( ! ok && i < 1000 ) { + ++i; + ok = QFile::rename( file.fileName(), name.arg( i ) ); } - Completion::Entry *e = new Completion::Entry( *( curr.entry( d ) ) ); - cmd->addCommand( new ModifyCompletionEntryCmd( org, d, e ) ); - } - } - if ( package->settings.usedEffort ) { - Completion::UsedEffort *ue = new Completion::UsedEffort(); - Completion::Entry prev; - Completion::EntryList::ConstIterator entriesIt = curr.entries().constBegin(); - const Completion::EntryList::ConstIterator entriesEnd = curr.entries().constEnd(); - for (; entriesIt != entriesEnd; ++entriesIt) { - const QDate &d = entriesIt.key(); - const Completion::Entry &e = *entriesIt.value(); - // set used effort from date entry and remove used effort from date entry - Completion::UsedEffort::ActualEffort effort( e.totalPerformed - prev.totalPerformed ); - ue->setEffort( d, effort ); - prev = e; - } - cmd->addCommand( new AddCompletionUsedEffortCmd( org, resource, ue ) ); - } - bool docsaved = false; - if ( package->settings.documents ) { - //TODO: handle remote files - QMap::const_iterator it = package->documents.constBegin(); - QMap::const_iterator end = package->documents.constEnd(); - for ( ; it != end; ++it ) { - const QUrl src = QUrl::fromLocalFile(it.key()); - KIO::CopyJob *job = KIO::move( src, it.value(), KIO::Overwrite ); - if ( job->exec() ) { - docsaved = true; - //TODO: async - debugPlan<<"Moved file:"<isEmpty() ) { - KMessageBox::information( 0, i18n( "Nothing to save from this package" ) ); - } - // add a copy to our tasks list of transmitted packages - WorkPackage *wp = new WorkPackage( from->workPackage() ); - wp->setParentTask( to ); - if ( ! wp->transmitionTime().isValid() ) { - wp->setTransmitionTime( package->timeTag ); - } - wp->setTransmitionStatus( WorkPackage::TS_Receive ); - cmd->addCommand( new WorkPackageAddCmd( m_project, to, wp ) ); - addCommand( cmd ); } void MainDocument::paintContent( QPainter &, const QRect &) { // Don't embed this app!!! } void MainDocument::slotViewDestroyed() { } void MainDocument::setLoadingTemplate(bool loading) { m_loadingTemplate = loading; } void MainDocument::setLoadingSharedResourcesTemplate(bool loading) { m_loadingSharedResourcesTemplate = loading; } bool MainDocument::completeLoading( KoStore *store ) { // If we get here the new project is loaded and set if (m_loadingSharedProject) { // this file is loaded by another project // to read resource appointments, // so we must not load any extra stuff return true; } if ( m_loadingTemplate ) { //debugPlan<<"Loading template, generate unique ids"; m_project->generateUniqueIds(); m_project->setConstraintStartTime( QDateTime(QDate::currentDate(), QTime(0, 0, 0), Qt::LocalTime) ); m_project->setConstraintEndTime( m_project->constraintStartTime().addYears( 2 ) ); m_project->locale()->setCurrencyLocale(QLocale::AnyLanguage, QLocale::AnyCountry); m_project->locale()->setCurrencySymbol(QString()); } else if ( isImporting() ) { // NOTE: I don't think this is a good idea. // Let the filter generate ids for non-plan files. // If the user wants to create a new project from an old one, // he should use Tools -> Insert Project File //m_project->generateUniqueNodeIds(); } if (m_loadingSharedResourcesTemplate && m_project->calendarCount() > 0) { Calendar *c = m_project->calendarAt(0); c->setTimeZone(QTimeZone::systemTimeZone()); } if (m_project->useSharedResources() && !m_project->sharedResourcesFile().isEmpty() && !m_skipSharedProjects) { QUrl url = QUrl::fromLocalFile(m_project->sharedResourcesFile()); if (url.isValid()) { insertResourcesFile(url, m_project->loadProjectsAtStartup() ? m_project->sharedProjectsUrl() : QUrl()); } } if ( store == 0 ) { // can happen if loading a template debugPlan<<"No store"; return true; // continue anyway } delete m_context; m_context = new Context(); KoXmlDocument doc; if ( loadAndParse( store, "context.xml", doc ) ) { store->close(); m_context->load( doc ); } else warnPlan<<"No context"; return true; } // TODO: // Due to splitting of KoDocument into a document and a part, // we simulate the old behaviour by registering all views in the document. // Find a better solution! void MainDocument::registerView( View* view ) { if ( view && ! m_views.contains( view ) ) { m_views << QPointer( view ); } } bool MainDocument::completeSaving( KoStore *store ) { foreach ( View *view, m_views ) { if ( view ) { if ( store->open( "context.xml" ) ) { if ( m_context == 0 ) m_context = new Context(); QDomDocument doc = m_context->save( view ); KoStoreDevice dev( store ); QByteArray s = doc.toByteArray(); // this is already Utf8! (void)dev.write( s.data(), s.size() ); (void)store->close(); m_viewlistModified = false; emit viewlistModified( false ); } break; } } return true; } bool MainDocument::loadAndParse(KoStore *store, const QString &filename, KoXmlDocument &doc) { //debugPlan << "oldLoadAndParse: Trying to open " << filename; if (!store->open(filename)) { warnPlan << "Entry " << filename << " not found!"; // d->lastErrorMessage = i18n( "Could not find %1",filename ); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = doc.setContent( store->device(), &errorMsg, &errorLine, &errorColumn ); if ( !ok ) { errorPlan << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg; /* d->lastErrorMessage = i18n( "Parsing error in %1 at line %2, column %3\nError message: %4" ,filename ,errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, QCoreApplication::UnicodeUTF8));*/ store->close(); return false; } debugPlan << "File " << filename << " loaded and parsed"; return true; } void MainDocument::insertFile( const QUrl &url, Node *parent, Node *after ) { Part *part = new Part( this ); MainDocument *doc = new MainDocument( part ); part->setDocument( doc ); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave( 0 ); //disable doc->m_insertFileInfo.url = url; doc->m_insertFileInfo.parent = parent; doc->m_insertFileInfo.after = after; connect(doc, &KoDocument::completed, this, &MainDocument::insertFileCompleted); connect(doc, &KoDocument::canceled, this, &MainDocument::insertFileCancelled); doc->openUrl( url ); } void MainDocument::insertFileCompleted() { debugPlan<( sender() ); if ( doc ) { Project &p = doc->getProject(); insertProject( p, doc->m_insertFileInfo.parent, doc->m_insertFileInfo.after ); doc->documentPart()->deleteLater(); // also deletes document } else { KMessageBox::error( 0, i18n("Internal error, failed to insert file.") ); } } void MainDocument::insertResourcesFile(const QUrl &url, const QUrl &projects) { insertSharedProjects(projects); // prepare for insertion after shared resources m_sharedProjectsFiles.removeAll(url); // resource file is not a project Part *part = new Part( this ); MainDocument *doc = new MainDocument( part ); doc->m_skipSharedProjects = true; // should not have shared projects, but... part->setDocument( doc ); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave( 0 ); //disable doc->setCheckAutoSaveFile(false); connect(doc, &KoDocument::completed, this, &MainDocument::insertResourcesFileCompleted); connect(doc, &KoDocument::canceled, this, &MainDocument::insertFileCancelled); doc->openUrl( url ); } void MainDocument::insertResourcesFileCompleted() { debugPlanShared<( sender() ); if (doc) { Project &p = doc->getProject(); mergeResources(p); m_project->setSharedResourcesLoaded(true); doc->documentPart()->deleteLater(); // also deletes document slotInsertSharedProject(); // insert shared bookings } else { KMessageBox::error( 0, i18n("Internal error, failed to insert file.") ); } } void MainDocument::insertFileCancelled( const QString &error ) { debugPlan<( sender() ); if ( doc ) { doc->documentPart()->deleteLater(); // also deletes document } } void MainDocument::clearResourceAssignments() { foreach (Resource *r, m_project->resourceList()) { r->clearExternalAppointments(); } } void MainDocument::loadResourceAssignments(QUrl url) { insertSharedProjects(url); slotInsertSharedProject(); } void MainDocument::insertSharedProjects(const QList &urls) { clearResourceAssignments(); m_sharedProjectsFiles = urls; slotInsertSharedProject(); } void MainDocument::insertSharedProjects(const QUrl &url) { m_sharedProjectsFiles.clear(); QFileInfo fi(url.path()); if (!fi.exists()) { return; } if (fi.isFile()) { m_sharedProjectsFiles = QList() << url; debugPlan<<"Get all projects in file:"<m_skipSharedProjects = true; // never load recursively part->setDocument( doc ); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave( 0 ); //disable doc->setCheckAutoSaveFile(false); doc->m_loadingSharedProject = true; connect(doc, &KoDocument::completed, this, &MainDocument::insertSharedProjectCompleted); connect(doc, &KoDocument::canceled, this, &MainDocument::insertSharedProjectCancelled); doc->openUrl(m_sharedProjectsFiles.takeFirst()); } void MainDocument::insertSharedProjectCompleted() { debugPlanShared<( sender() ); if (doc) { Project &p = doc->getProject(); debugPlanShared<id()<<"Loaded project:"<id() && p.isScheduled(ANYSCHEDULED)) { // FIXME: improve! // find a suitable schedule ScheduleManager *sm = 0; foreach(ScheduleManager *m, p.allScheduleManagers()) { if (m->isBaselined()) { sm = m; break; } if (m->isScheduled()) { sm = m; // take the last one, more likely to be subschedule } } if (sm) { foreach(Resource *r, p.resourceList()) { Resource *res = m_project->resource(r->id()); if (res && res->isShared()) { Appointment *app = new Appointment(); app->setAuxcilliaryInfo(p.name()); foreach(const Appointment *a, r->appointments(sm->scheduleId())) { *app += *a; } if (app->isEmpty()) { delete app; } else { res->addExternalAppointment(p.id(), app); debugPlanShared<name()<<"added:"<auxcilliaryInfo()<documentPart()->deleteLater(); // also deletes document emit insertSharedProject(); // do next file } else { KMessageBox::error( 0, i18n("Internal error, failed to insert file.") ); } } void MainDocument::insertSharedProjectCancelled( const QString &error ) { debugPlanShared<( sender() ); if ( doc ) { doc->documentPart()->deleteLater(); // also deletes document } } bool MainDocument::insertProject( Project &project, Node *parent, Node *after ) { debugPlan<<&project; // make sure node ids in new project is unique also in old project QList existingIds = m_project->nodeDict().keys(); foreach ( Node *n, project.allNodes() ) { QString oldid = n->id(); n->setId( project.uniqueNodeId( existingIds ) ); project.removeId( oldid ); // remove old id project.registerNodeId( n ); // register new id } MacroCommand *m = new InsertProjectCmd( project, parent==0?m_project:parent, after, kundo2_i18n( "Insert project" ) ); if ( m->isEmpty() ) { delete m; } else { addCommand( m ); } return true; } // check if calendar 'c' has children that will not be removed (normally 'Local' calendars) bool canRemoveCalendar(const Calendar *c, const QList &lst) { for (Calendar *cc : c->calendars()) { if (!lst.contains(cc)) { return false; } if (!canRemoveCalendar(cc, lst)) { return false; } } return true; } // sort parent calendars before children QList sortedRemoveCalendars(Project &shared, const QList &lst) { QList result; for (Calendar *c : lst) { if (c->isShared() && !shared.calendar(c->id())) { result << c; } result += sortedRemoveCalendars(shared, c->calendars()); } return result; } bool MainDocument::mergeResources(Project &project) { debugPlanShared<<&project; // Just in case, remove stuff not related to resources foreach(Node *n, project.childNodeIterator()) { debugPlanShared<<"Project not empty, delete node:"<name(); NodeDeleteCmd cmd(n); cmd.execute(); } foreach(ScheduleManager *m, project.scheduleManagers()) { debugPlanShared<<"Project not empty, delete schedule:"<name(); DeleteScheduleManagerCmd cmd(project, m); cmd.execute(); } foreach(Account *a, project.accounts().accountList()) { debugPlanShared<<"Project not empty, delete account:"<name(); RemoveAccountCmd cmd(project, a); cmd.execute(); } // Mark all resources / groups as shared foreach(ResourceGroup *g, project.resourceGroups()) { g->setShared(true); } foreach(Resource *r, project.resourceList()) { r->setShared(true); } // Mark all calendars shared foreach(Calendar *c, project.allCalendars()) { c->setShared(true); } // check if any shared stuff has been removed QList removedGroups; QList removedResources; QList removedCalendars; QStringList removed; foreach(ResourceGroup *g, m_project->resourceGroups()) { if (g->isShared() && !project.findResourceGroup(g->id())) { removedGroups << g; removed << i18n("Group: %1", g->name()); } } foreach(Resource *r, m_project->resourceList()) { if (r->isShared() && !project.findResource(r->id())) { removedResources << r; removed << i18n("Resource: %1", r->name()); } } removedCalendars = sortedRemoveCalendars(project, m_project->calendars()); for (Calendar *c : qAsConst(removedCalendars)) { removed << i18n("Calendar: %1", c->name()); } if (!removed.isEmpty()) { KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancelList( 0, i18n("Shared resources has been removed from the shared resources file." "\nSelect how they shall be treated in this project."), removed, xi18nc("@title:window", "Shared resources"), KStandardGuiItem::remove(), KGuiItem(i18n("Convert")), KGuiItem(i18n("Keep")) ); switch (result) { case KMessageBox::Yes: // Remove for (Resource *r : qAsConst(removedResources)) { RemoveResourceCmd cmd(r->parentGroup(), r); cmd.redo(); } for (ResourceGroup *g : qAsConst(removedGroups)) { if (g->resources().isEmpty()) { RemoveResourceGroupCmd cmd(m_project, g); cmd.redo(); } else { // we may have put local resource(s) in this group // so we need to keep it g->setShared(false); m_project->removeResourceGroupId(g->id()); g->setId(m_project->uniqueResourceGroupId()); m_project->insertResourceGroupId(g->id(), g); } } for (Calendar *c : qAsConst(removedCalendars)) { CalendarRemoveCmd cmd(m_project, c); cmd.redo(); } break; case KMessageBox::No: // Convert for (Resource *r : qAsConst(removedResources)) { r->setShared(false); m_project->removeResourceId(r->id()); r->setId(m_project->uniqueResourceId()); m_project->insertResourceId(r->id(), r); } for (ResourceGroup *g : qAsConst(removedGroups)) { g->setShared(false); m_project->removeResourceGroupId(g->id()); g->setId(m_project->uniqueResourceGroupId()); m_project->insertResourceGroupId(g->id(), g); } for (Calendar *c : qAsConst(removedCalendars)) { c->setShared(false); m_project->removeCalendarId(c->id()); c->setId(m_project->uniqueCalendarId()); m_project->insertCalendarId(c->id(), c); } break; case KMessageBox::Cancel: // Keep break; default: break; } } // update values of already existing objects QStringList l1; foreach(ResourceGroup *g, project.resourceGroups()) { l1 << g->id(); } QStringList l2; foreach(ResourceGroup *g, m_project->resourceGroups()) { l2 << g->id(); } debugPlanShared< removegroups; foreach(ResourceGroup *g, project.resourceGroups()) { ResourceGroup *group = m_project->findResourceGroup(g->id()); if (group) { if (!group->isShared()) { // User has probably created shared resources from this project, // so the resources exists but are local ones. // Convert to shared and do not load the group from shared. removegroups << g; group->setShared(true); debugPlanShared<<"Set group to shared:"<id(); } group->setName(g->name()); group->setType(g->type()); debugPlanShared<<"Updated group:"<id(); } } QList removeresources; foreach(Resource *r, project.resourceList()) { Resource *resource = m_project->findResource(r->id()); if (resource) { if (!resource->isShared()) { // User has probably created shared resources from this project, // so the resources exists but are local ones. // Convert to shared and do not load the resource from shared. removeresources << r; resource->setShared(true); debugPlanShared<<"Set resource to shared:"<id(); } resource->setName(r->name()); resource->setInitials(r->initials()); resource->setEmail(r->email()); resource->setType(r->type()); resource->setAutoAllocate(r->autoAllocate()); resource->setAvailableFrom(r->availableFrom()); resource->setAvailableUntil(r->availableUntil()); resource->setUnits(r->units()); resource->setNormalRate(r->normalRate()); resource->setOvertimeRate(r->overtimeRate()); QString id = r->calendar(true) ? r->calendar(true)->id() : QString(); resource->setCalendar(m_project->findCalendar(id)); id = r->account() ? r->account()->name() : QString(); resource->setAccount(m_project->accounts().findAccount(id)); resource->setRequiredIds(r->requiredIds()); resource->setTeamMemberIds(r->teamMemberIds()); debugPlanShared<<"Updated resource:"<id(); } } QList removecalendars; foreach(Calendar *c, project.allCalendars()) { Calendar *calendar = m_project->findCalendar(c->id()); if (calendar) { if (!calendar->isShared()) { // User has probably created shared resources from this project, // so the calendar exists but are local ones. // Convert to shared and do not load the resource from shared. removecalendars << c; calendar->setShared(true); debugPlanShared<<"Set calendar to shared:"<id(); } *calendar = *c; debugPlanShared<<"Updated calendar:"<id(); } } debugPlanShared<<"Remove:"<childCount() == 0) { removecalendars.removeAt(i); debugPlanShared<<"Delete calendar:"<id(); CalendarRemoveCmd cmd(&project, c); cmd.execute(); } } } for (Resource *r : qAsConst(removeresources)) { debugPlanShared<<"Delete resource:"<id(); RemoveResourceCmd cmd(r->parentGroup(), r); cmd.execute(); } for (ResourceGroup *g : qAsConst(removegroups)) { debugPlanShared<<"Delete group:"<id(); RemoveResourceGroupCmd cmd(&project, g); cmd.execute(); } // insert new objects Q_ASSERT(project.childNodeIterator().isEmpty()); InsertProjectCmd cmd(project, m_project, 0); cmd.execute(); return true; } void MainDocument::insertViewListItem( View */*view*/, const ViewListItem *item, const ViewListItem *parent, int index ) { // FIXME callers should take care that they now get a signal even if originating from themselves emit viewListItemAdded(item, parent, index); setModified( true ); m_viewlistModified = true; } void MainDocument::removeViewListItem( View */*view*/, const ViewListItem *item ) { // FIXME callers should take care that they now get a signal even if originating from themselves emit viewListItemRemoved(item); setModified( true ); m_viewlistModified = true; } void MainDocument::setModified( bool mod ) { debugPlan<name().isEmpty()) { setUrl(QUrl(m_project->name() + ".plan")); } Calendar *week = 0; if (KPlatoSettings::generateWeek()) { bool always = KPlatoSettings::generateWeekChoice() == KPlatoSettings::EnumGenerateWeekChoice::Always; bool ifnone = KPlatoSettings::generateWeekChoice() == KPlatoSettings::EnumGenerateWeekChoice::NoneExists; if (always || (ifnone && m_project->calendarCount() == 0)) { // create a calendar week = new Calendar(i18nc("Base calendar name", "Base")); m_project->addCalendar(week); CalendarDay vd(CalendarDay::NonWorking); for (int i = Qt::Monday; i <= Qt::Sunday; ++i) { if (m_config.isWorkingday(i)) { CalendarDay wd(CalendarDay::Working); TimeInterval ti(m_config.dayStartTime(i), m_config.dayLength(i)); wd.addInterval(ti); week->setWeekday(i, wd); } else { week->setWeekday(i, vd); } } } } #ifdef HAVE_KHOLIDAYS if (KPlatoSettings::generateHolidays()) { bool inweek = week != 0 && KPlatoSettings::generateHolidaysChoice() == KPlatoSettings::EnumGenerateHolidaysChoice::InWeekCalendar; bool subcalendar = week != 0 && KPlatoSettings::generateHolidaysChoice() == KPlatoSettings::EnumGenerateHolidaysChoice::AsSubCalendar; bool separate = week == 0 || KPlatoSettings::generateHolidaysChoice() == KPlatoSettings::EnumGenerateHolidaysChoice::AsSeparateCalendar; Calendar *c = 0; if (inweek) { c = week; qDebug()<addCalendar(c, week); qDebug()<addCalendar(c); qDebug()<setHolidayRegion(KPlatoSettings::region()); } #endif } // creates a "new" project from current project (new ids etc) void MainDocument::createNewProject() { setEmpty(); clearUndoHistory(); setModified( false ); resetURL(); KoDocumentInfo *info = documentInfo(); info->resetMetaData(); info->setProperty( "title", "" ); setTitleModified(); m_project->generateUniqueNodeIds(); Duration dur = m_project->constraintEndTime() - m_project->constraintStartTime(); m_project->setConstraintStartTime( QDateTime(QDate::currentDate(), QTime(0, 0, 0), Qt::LocalTime) ); m_project->setConstraintEndTime( m_project->constraintStartTime() + dur ); while ( m_project->numScheduleManagers() > 0 ) { foreach ( ScheduleManager *sm, m_project->allScheduleManagers() ) { if ( sm->childCount() > 0 ) { continue; } if ( sm->expected() ) { sm->expected()->setDeleted( true ); sm->setExpected( 0 ); } m_project->takeScheduleManager( sm ); delete sm; } } foreach ( Schedule *s, m_project->schedules() ) { m_project->takeSchedule( s ); delete s; } foreach ( Node *n, m_project->allNodes() ) { foreach ( Schedule *s, n->schedules() ) { n->takeSchedule( s ); delete s; } } foreach ( Resource *r, m_project->resourceList() ) { foreach ( Schedule *s, r->schedules() ) { r->takeSchedule( s ); delete s; } } } void MainDocument::setIsTaskModule(bool value) { m_isTaskModule = value; } bool MainDocument::isTaskModule() const { return m_isTaskModule; } } //KPlato namespace diff --git a/src/kptmaindocument.h b/src/kptmaindocument.h index 72cc3ad1..6fd36931 100644 --- a/src/kptmaindocument.h +++ b/src/kptmaindocument.h @@ -1,254 +1,257 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2004 - 2010 Dag Andersen Copyright (C) 2006 Raphael Langerhorst Copyright (C) 2007 Thorsten Zachmann - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTMAINDOCUMENT_H #define KPTMAINDOCUMENT_H #include "plan_export.h" +#include "kptpackage.h" #include "kpttask.h" #include "kptconfig.h" #include "kptwbsdefinition.h" #include "kptxmlloaderobject.h" #include "about/aboutpage.h" #include "KoDocument.h" #include #include #define PLAN_MIME_TYPE "application/x-vnd.kde.plan" /// The main namespace. namespace KPlato { class DocumentChild; class Project; class Context; class SchedulerPlugin; class ViewListItem; class View; class Package; class PLAN_EXPORT MainDocument : public KoDocument { Q_OBJECT public: explicit MainDocument(KoPart *part); ~MainDocument(); /// reimplemented from KoDocument virtual QByteArray nativeFormatMimeType() const { return PLAN_MIME_TYPE; } /// reimplemented from KoDocument virtual QByteArray nativeOasisMimeType() const { return ""; } /// reimplemented from KoDocument virtual QStringList extraNativeMimeTypes() const { return QStringList() << PLAN_MIME_TYPE; } void setReadWrite( bool rw ); void configChanged(); virtual void paintContent( QPainter& painter, const QRect& rect); void setProject( Project *project ); Project &getProject() { return *m_project; } const Project &getProject() const { return * m_project; } /** * Return the set of SupportedSpecialFormats that the kplato wants to * offer in the "Save" file dialog. * Note: SaveEncrypted is not supported. */ virtual int supportedSpecialFormats() const { return SaveAsDirectoryStore; } // The load and save functions. Look in the file kplato.dtd for info virtual bool loadXML( const KoXmlDocument &document, KoStore *store ); virtual QDomDocument saveXML(); /// Save a workpackage file containing @p node with schedule identity @p id, owned by @p resource QDomDocument saveWorkPackageXML( const Node *node, long id, Resource *resource = 0 ); bool saveOdf( SavingContext &/*documentContext */) { return false; } bool loadOdf( KoOdfReadStore & odfStore ); Config &config() { return m_config; } Context *context() const { return m_context; } WBSDefinition &wbsDefinition() { return m_project->wbsDefinition(); } const XMLLoaderObject &xmlLoader() const { return m_xmlLoader; } DocumentChild *createChild( KoDocument *doc, const QRect &geometry = QRect() ); bool saveWorkPackageToStream( QIODevice * dev, const Node *node, long id, Resource *resource = 0 ); bool saveWorkPackageFormat( const QString &file, const Node *node, long id, Resource *resource = 0 ); bool saveWorkPackageUrl( const QUrl & _url, const Node *node, long id, Resource *resource = 0 ); - void mergeWorkPackages(); - void mergeWorkPackage( const Package *package ); - void terminateWorkPackage( const Package *package ); /// Load the workpackage from @p url into @p project. Return true if successful, else false. bool loadWorkPackage( Project &project, const QUrl &url ); Package *loadWorkPackageXML( Project& project, QIODevice*, const KoXmlDocument& document, const QUrl& url ); QMap workPackages() const { return m_workpackages; } + void clearWorkPackages() { + qDeleteAll(m_workpackages); + m_workpackages.clear(); + m_checkingForWorkPackages = false; + } void insertFile( const QUrl &url, Node *parent, Node *after = 0 ); bool insertProject( Project &project, Node *parent, Node *after ); bool mergeResources(Project &project); KPlatoAboutPage &aboutPage() { return m_aboutPage; } bool extractFiles( KoStore *store, Package *package ); bool extractFile( KoStore *store, Package *package, const Document *doc ); void registerView( View *view ); /// Create a new project from this project /// Generates new project id and task ids /// Keeps resource- and calendar ids void createNewProject(); bool isTaskModule() const; using KoDocument::setModified; public Q_SLOTS: void setModified( bool mod ); /// Inserts an item into all other views than @p view void insertViewListItem(KPlato::View *view, const KPlato::ViewListItem *item, const KPlato::ViewListItem *parent, int index); /// Removes the view list item from all other views than @p view void removeViewListItem(KPlato::View *view, const KPlato::ViewListItem *item); /// View selector has been modified void slotViewlistModified(); /// Check for workpackages /// If @p keep is true, packages that has been refused will not be checked for again void checkForWorkPackages(bool keep); + /// Remove @p package + void terminateWorkPackage( const KPlato::Package *package ); void setLoadingTemplate( bool ); void setLoadingSharedResourcesTemplate( bool ); void insertResourcesFile(const QUrl &url, const QUrl &projects = QUrl()); void slotProjectCreated(); /// Prepare for insertion of resource assignments of shared resources from the project(s) in @p urls void insertSharedProjects(const QList &urls); /// Prepare for insertion of resource assignments of shared resources from the project(s) in @p url void insertSharedProjects(const QUrl &url); /// Clear resource assignments of shared resources void clearResourceAssignments(); /// Load resource assignments of shared resources from the project(s) in @p url void loadResourceAssignments(QUrl url); void setIsTaskModule(bool value); void autoCheckForWorkPackages(); Q_SIGNALS: void changed(); void workPackageLoaded(); void viewlistModified( bool ); void viewListItemAdded(const KPlato::ViewListItem *item, const KPlato::ViewListItem *parent, int index); void viewListItemRemoved(const KPlato::ViewListItem *item); void insertSharedProject(); protected: /// Load kplato specific files virtual bool completeLoading( KoStore* store ); /// Save kplato specific files virtual bool completeSaving( KoStore* store ); - void mergeWorkPackage( Task *to, const Task *from, const Package *package ); - // used by insert file struct InsertFileInfo { QUrl url; Node *parent; Node *after; } m_insertFileInfo; protected Q_SLOTS: void slotViewDestroyed(); void addSchedulerPlugin(const QString&, KPlato::SchedulerPlugin *plugin); void checkForWorkPackage(); void insertFileCompleted(); void insertResourcesFileCompleted(); void insertFileCancelled( const QString& ); void slotInsertSharedProject(); void insertSharedProjectCompleted(); void insertSharedProjectCancelled( const QString& ); - void workPackageMergeDialogFinished( int result ); - private: bool loadAndParse(KoStore* store, const QString& filename, KoXmlDocument& doc); void loadSchedulerPlugins(); private: Project *m_project; QWidget* m_parentWidget; Config m_config; Context *m_context; XMLLoaderObject m_xmlLoader; bool m_loadingTemplate; bool m_loadingSharedResourcesTemplate; QMap m_schedulerPlugins; QMap m_workpackages; QFileInfoList m_infoList; + QList m_skipUrls; QMap m_mergedPackages; KPlatoAboutPage m_aboutPage; QDomDocument m_reports; bool m_viewlistModified; bool m_checkingForWorkPackages; QList > m_views; bool m_loadingSharedProject; QList m_sharedProjectsFiles; bool m_skipSharedProjects; bool m_isTaskModule; }; } //KPlato namespace #endif diff --git a/src/kptview.cpp b/src/kptview.cpp index f9b10917..062e88be 100644 --- a/src/kptview.cpp +++ b/src/kptview.cpp @@ -1,3325 +1,3382 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2011 Dag Andersen Copyright (C) 2012 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptview.h" #include #include #include "KoDocumentInfo.h" #include "KoMainWindow.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 #include #include #include #include #include #include #include "kptlocale.h" #include "kptviewbase.h" #include "kptaccountsview.h" #include "kptaccountseditor.h" #include "kptcalendareditor.h" #include "kptfactory.h" #include "kptmilestoneprogressdialog.h" #include "kpttaskdescriptiondialog.h" #include "kptdocumentsdialog.h" #include "kptnode.h" #include "kptmaindocument.h" #include "kptproject.h" #include "kptmainprojectdialog.h" #include "kpttask.h" #include "kptsummarytaskdialog.h" #include "kpttaskdialog.h" #include "kpttaskprogressdialog.h" #include "kptganttview.h" #include "kpttaskeditor.h" #include "kptdependencyeditor.h" #include "kptperteditor.h" #include "kptdatetime.h" #include "kptcommand.h" #include "kptrelation.h" #include "kptrelationdialog.h" #include "kptresourceappointmentsview.h" #include "kptresourceeditor.h" #include "kptscheduleeditor.h" #include "kptresourcedialog.h" #include "kptresource.h" #include "kptstandardworktimedialog.h" #include "kptwbsdefinitiondialog.h" #include "kptresourceassignmentview.h" #include "kpttaskstatusview.h" #include "kptsplitterview.h" #include "kptpertresult.h" #include "ConfigProjectPanel.h" #include "ConfigWorkVacationPanel.h" #include "kpttaskdefaultpanel.h" #include "kptworkpackageconfigpanel.h" #include "kptcolorsconfigpanel.h" #include "kptinsertfiledlg.h" #include "kptloadsharedprojectsdialog.h" #include "kpthtmlview.h" #include "about/aboutpage.h" #include "kptlocaleconfigmoneydialog.h" #include "kptflatproxymodel.h" #include "kpttaskstatusmodel.h" +#include "kptworkpackagemergedialog.h" #include "performance/PerformanceStatusView.h" #include "performance/ProjectStatusView.h" #include "reportsgenerator/ReportsGeneratorView.h" #ifdef PLAN_USE_KREPORT #include "reports/reportview.h" #include "reports/reportdata.h" #endif #include "kptviewlistdialog.h" #include "kptviewlistdocker.h" #include "kptviewlist.h" #include "kptschedulesdocker.h" #include "kptpart.h" #include "kptdebug.h" #include "calligraplansettings.h" #include "kptprintingcontrolprivate.h" // #include "KPtViewAdaptor.h" #include namespace KPlato { //------------------------------- ConfigDialog::ConfigDialog(QWidget *parent, const QString& name, KConfigSkeleton *config ) : KConfigDialog( parent, name, config ), m_config( config ) { KConfigDialogManager::changedMap()->insert("KRichTextWidget", SIGNAL(textChanged()) ); } bool ConfigDialog::hasChanged() { QRegExp kcfg( "kcfg_*" ); foreach ( KRichTextWidget *w, findChildren( kcfg ) ) { KConfigSkeletonItem *item = m_config->findItem( w->objectName().mid(5) ); if ( ! item->isEqual( w->toHtml() ) ) { return true; } } return false; } void ConfigDialog::updateSettings() { bool changed = false; QRegExp kcfg( "kcfg_*" ); foreach ( KRichTextWidget *w, findChildren( kcfg ) ) { KConfigSkeletonItem *item = m_config->findItem( w->objectName().mid(5) ); if ( ! item ) { warnPlan << "The setting '" << w->objectName().mid(5) << "' has disappeared!"; continue; } if ( ! item->isEqual( QVariant( w->toHtml() ) ) ) { item->setProperty( QVariant( w->toHtml() ) ); changed = true; } } if ( changed ) { m_config->save(); } } void ConfigDialog::updateWidgets() { QRegExp kcfg( "kcfg_*" ); foreach ( KRichTextWidget *w, findChildren( kcfg ) ) { KConfigSkeletonItem *item = m_config->findItem( w->objectName().mid(5) ); if ( ! item ) { warnPlan << "The setting '" << w->objectName().mid(5) << "' has disappeared!"; continue; } if ( ! item->isEqual( QVariant( w->toHtml() ) ) ) { w->setHtml( item->property().toString() ); } } } void ConfigDialog::updateWidgetsDefault() { bool usedefault = m_config->useDefaults( true ); updateWidgets(); m_config->useDefaults( usedefault ); } bool ConfigDialog::isDefault() { bool bUseDefaults = m_config->useDefaults(true); bool result = !hasChanged(); m_config->useDefaults(bUseDefaults); return result; } //------------------------------------ View::View(KoPart *part, MainDocument *doc, QWidget *parent) : KoView(part, doc, parent), m_currentEstimateType( Estimate::Use_Expected ), m_scheduleActionGroup( new QActionGroup( this ) ), m_readWrite( false ), m_defaultView(1), m_partpart (part) { //debugPlan; doc->registerView( this ); setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName()); if ( !doc->isReadWrite() ) setXMLFile( "calligraplan_readonly.rc" ); else setXMLFile( "calligraplan.rc" ); // new ViewAdaptor( this ); m_sp = new QSplitter( this ); QVBoxLayout *layout = new QVBoxLayout( this ); layout->setMargin(0); layout->addWidget( m_sp ); ViewListDocker *docker = 0; if ( mainWindow() == 0 ) { // Don't use docker if embedded m_viewlist = new ViewListWidget(doc, m_sp); m_viewlist->setProject( &( getProject() ) ); connect( m_viewlist, &ViewListWidget::selectionChanged, this, &View::slotSelectionChanged); connect( this, &View::currentScheduleManagerChanged, m_viewlist, &ViewListWidget::setSelectedSchedule); connect( m_viewlist, &ViewListWidget::updateViewInfo, this, &View::slotUpdateViewInfo); } else { ViewListDockerFactory vl(this); docker = static_cast(mainWindow()->createDockWidget(&vl)); if (docker->view() != this) { docker->setView(this); } m_viewlist = docker->viewList(); #if 0 //SchedulesDocker SchedulesDockerFactory sdf; SchedulesDocker *sd = dynamic_cast( createDockWidget( &sdf ) ); Q_ASSERT( sd ); sd->setProject( &getProject() ); connect(sd, SIGNAL(selectionChanged(KPlato::ScheduleManager*)), SLOT(slotSelectionChanged(KPlato::ScheduleManager*))); connect(this, &View::currentScheduleManagerChanged, sd, SLOT(setSelectedSchedule(KPlato::ScheduleManager*))); #endif } m_tab = new QStackedWidget( m_sp ); //////////////////////////////////////////////////////////////////////////////////////////////////// // Add sub views createIntroductionView(); // The menu items // ------ File /* actionCreateTemplate = new QAction( i18n( "&Create Template From Document..." ), this ); actionCollection()->addAction("file_createtemplate", actionCreateTemplate ); connect( actionCreateTemplate, SIGNAL(triggered(bool)), SLOT(slotCreateTemplate()) ); */ actionCreateNewProject = new QAction( i18n( "&Create New Project..." ), this ); actionCollection()->addAction("file_createnewproject", actionCreateNewProject ); connect( actionCreateNewProject, &QAction::triggered, this, &View::slotCreateNewProject ); // ------ Edit actionCut = actionCollection()->addAction(KStandardAction::Cut, "edit_cut", this, SLOT(slotEditCut())); actionCopy = actionCollection()->addAction(KStandardAction::Copy, "edit_copy", this, SLOT(slotEditCopy())); actionPaste = actionCollection()->addAction(KStandardAction::Paste, "edit_paste", this, SLOT(slotEditPaste())); // ------ View actionCollection()->addAction( KStandardAction::Redisplay, "view_refresh" , this, SLOT(slotRefreshView()) ); actionViewSelector = new KToggleAction(i18n("Show Selector"), this); actionCollection()->addAction("view_show_selector", actionViewSelector ); connect( actionViewSelector, &QAction::triggered, this, &View::slotViewSelector ); // ------ Insert // ------ Project actionEditMainProject = new QAction(koIcon("view-time-schedule-edit"), i18n("Edit Main Project..."), this); actionCollection()->addAction("project_edit", actionEditMainProject ); connect( actionEditMainProject, &QAction::triggered, this, &View::slotProjectEdit ); actionEditStandardWorktime = new QAction(koIcon("configure"), i18n("Define Estimate Conversions..."), this); actionCollection()->addAction("project_worktime", actionEditStandardWorktime ); connect( actionEditStandardWorktime, &QAction::triggered, this, &View::slotProjectWorktime ); // ------ Tools actionDefineWBS = new QAction(koIcon("configure"), i18n("Define WBS Pattern..."), this); actionCollection()->addAction("tools_define_wbs", actionDefineWBS ); connect( actionDefineWBS, &QAction::triggered, this, &View::slotDefineWBS ); actionInsertFile = new QAction(koIcon("document-import"), i18n("Insert Project File..."), this); actionCollection()->addAction("insert_file", actionInsertFile ); connect( actionInsertFile, &QAction::triggered, this, &View::slotInsertFile ); actionLoadSharedProjects = new QAction(koIcon("document-import"), i18n("Load Shared Projects..."), this); actionCollection()->addAction("load_shared_projects", actionLoadSharedProjects ); connect( actionLoadSharedProjects, &QAction::triggered, this, &View::slotLoadSharedProjects ); // ------ Settings actionConfigure = new QAction(koIcon("configure"), i18n("Configure Plan..."), this); actionCollection()->addAction("configure", actionConfigure ); connect( actionConfigure, &QAction::triggered, this, &View::slotConfigure ); actionCurrencyConfig = new QAction(koIcon("configure"), i18n("Define Currency..."), this); actionCollection()->addAction( "config_currency", actionCurrencyConfig ); connect( actionCurrencyConfig, &QAction::triggered, this, &View::slotCurrencyConfig ); #ifdef PLAN_USE_KREPORT actionOpenReportFile = new QAction(koIcon("document-open"), i18n("Open Report Definition File..."), this); actionCollection()->addAction( "reportdesigner_open_file", actionOpenReportFile ); connect( actionOpenReportFile, QAction::triggered, this, &View::slotOpenReportFile); #endif // ------ Help actionIntroduction = new QAction(koIcon("dialog-information"), i18n("Introduction to Plan"), this); actionCollection()->addAction("plan_introduction", actionIntroduction ); connect( actionIntroduction, &QAction::triggered, this, &View::slotIntroduction ); // ------ Popup actionOpenNode = new QAction(koIcon("document-edit"), i18n("Edit..."), this); actionCollection()->addAction("node_properties", actionOpenNode ); connect( actionOpenNode, &QAction::triggered, this, &View::slotOpenCurrentNode ); actionTaskProgress = new QAction(koIcon("document-edit"), i18n("Progress..."), this); actionCollection()->addAction("task_progress", actionTaskProgress ); connect( actionTaskProgress, &QAction::triggered, this, &View::slotTaskProgress ); actionDeleteTask = new QAction(koIcon("edit-delete"), i18n("Delete Task"), this); actionCollection()->addAction("delete_task", actionDeleteTask ); connect( actionDeleteTask, &QAction::triggered, this, &View::slotDeleteCurrentTask ); actionTaskDescription = new QAction(koIcon("document-edit"), i18n("Description..."), this); actionCollection()->addAction("task_description", actionTaskDescription ); connect( actionTaskDescription, &QAction::triggered, this, &View::slotTaskDescription ); actionDocuments = new QAction(koIcon("document-edit"), i18n("Documents..."), this); actionCollection()->addAction("task_documents", actionDocuments ); connect( actionDocuments, &QAction::triggered, this, &View::slotDocuments ); actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask ); connect( actionIndentTask, &QAction::triggered, this, &View::slotIndentTask ); actionUnindentTask= new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask ); connect( actionUnindentTask, &QAction::triggered, this, &View::slotUnindentTask ); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Task Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp ); connect( actionMoveTaskUp, &QAction::triggered, this, &View::slotMoveTaskUp ); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Task Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown ); connect( actionMoveTaskDown, &QAction::triggered, this, &View::slotMoveTaskDown ); actionEditResource = new QAction(koIcon("document-edit"), i18n("Edit Resource..."), this); actionCollection()->addAction("edit_resource", actionEditResource ); connect( actionEditResource, &QAction::triggered, this, &View::slotEditCurrentResource ); actionEditRelation = new QAction(koIcon("document-edit"), i18n("Edit Dependency..."), this); actionCollection()->addAction("edit_dependency", actionEditRelation ); connect( actionEditRelation, &QAction::triggered, this, &View::slotModifyCurrentRelation ); actionDeleteRelation = new QAction(koIcon("edit-delete"), i18n("Delete Dependency"), this); actionCollection()->addAction("delete_dependency", actionDeleteRelation ); connect( actionDeleteRelation, &QAction::triggered, this, &View::slotDeleteRelation ); // Viewlist popup connect( m_viewlist, &ViewListWidget::createView, this, &View::slotCreateView ); + m_workPackageButton = new QToolButton(this); + m_workPackageButton->hide(); + m_workPackageButton->setIcon(koIcon("application-x-vnd.kde.plan.work")); + m_workPackageButton->setText(i18n("Work Packages...")); + m_workPackageButton->setToolTip(i18nc("@info:tooltip", "Work packages available")); + m_workPackageButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + connect(m_workPackageButton, &QToolButton::clicked, this, &View::openWorkPackageMergeDialog); m_estlabel = new QLabel( "", 0 ); if ( statusBar() ) { addStatusBarItem( m_estlabel, 0, true ); } connect( &getProject(), &Project::scheduleManagerAdded, this, &View::slotScheduleAdded ); connect( &getProject(), &Project::scheduleManagerRemoved, this, &View::slotScheduleRemoved ); connect( &getProject(), &Project::scheduleManagersSwapped, this, &View::slotScheduleSwapped ); connect( &getProject(), &Project::sigCalculationFinished, this, &View::slotScheduleCalculated ); slotPlugScheduleActions(); connect( doc, &MainDocument::changed, this, &View::slotUpdate ); connect( m_scheduleActionGroup, &QActionGroup::triggered, this, &View::slotViewSchedule ); connect( getPart(), &MainDocument::workPackageLoaded, this, &View::slotWorkPackageLoaded ); // views take time for large projects QTimer::singleShot(0, this, &View::initiateViews); const QList pluginFactories = KoPluginLoader::instantiatePluginFactories(QStringLiteral("calligraplan/extensions")); foreach (KPluginFactory* factory, pluginFactories) { QObject *object = factory->create(this, QVariantList()); KXMLGUIClient *clientPlugin = dynamic_cast(object); if (clientPlugin) { insertChildClient(clientPlugin); } else { // not our/valid plugin, so delete the created object object->deleteLater(); } } // do not watch task module changes if we are editing one if (!doc->isTaskModule()) { QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); if (!dir.isEmpty()) { dir += "/taskmodules"; m_dirwatch.addDir(dir, KDirWatch::WatchFiles); QStringList modules = KoResourcePaths::findAllResources( "calligraplan_taskmodules", "*.plan", KoResourcePaths::NoDuplicates|KoResourcePaths::Recursive ); for (const QString &f : modules) { m_dirwatch.addFile(f); } connect(&m_dirwatch, &KDirWatch::created, this, &View::taskModuleFileChanged); connect(&m_dirwatch, &KDirWatch::deleted, this, &View::taskModuleFileChanged); } } //debugPlan<<" end"; } View::~View() { // Disconnect and delete so we do not get called by destroyd() signal const QMap map = m_scheduleActions; // clazy:exclude=qmap-with-pointer-key QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { disconnect(it.key(), &QObject::destroyed, this, &View::slotActionDestroyed); m_scheduleActionGroup->removeAction(it.key()); delete it.key(); } ViewBase *view = currentView(); if (view) { // deactivate view to remove dockers etc slotGuiActivated(view, false); } /* removeStatusBarItem( m_estlabel ); delete m_estlabel;*/ } void View::initiateViews() { QApplication::setOverrideCursor( Qt::WaitCursor ); createViews(); connect( m_viewlist, &ViewListWidget::activated, this, &View::slotViewActivated ); // after createViews() !! connect( m_viewlist, &ViewListWidget::viewListItemRemoved, this, &View::slotViewListItemRemoved ); // after createViews() !! connect( m_viewlist, &ViewListWidget::viewListItemInserted, this, &View::slotViewListItemInserted ); ViewListDocker *docker = qobject_cast( m_viewlist->parent() ); if ( docker ) { // after createViews() !! connect( m_viewlist, &ViewListWidget::modified, docker, &ViewListDocker::slotModified); connect( m_viewlist, &ViewListWidget::modified, getPart(), &MainDocument::slotViewlistModified); connect(getPart(), &MainDocument::viewlistModified, docker, &ViewListDocker::updateWindowTitle); } connect( m_tab, &QStackedWidget::currentChanged, this, &View::slotCurrentChanged ); slotSelectDefaultView(); loadContext(); QApplication::restoreOverrideCursor(); } void View::slotCreateNewProject() { debugPlan; if ( KMessageBox::Continue == KMessageBox::warningContinueCancel( this, xi18nc( "@info", "This action cannot be undone." "Create a new Project from the current project " "with new project- and task identities." "Resource- and calendar identities are not changed." "All scheduling information is removed." "Do you want to continue?" ) ) ) { emit currentScheduleManagerChanged(0); getPart()->createNewProject(); slotOpenNode( &getProject() ); } } void View::createViews() { Context *ctx = getPart()->context(); if ( ctx && ctx->isLoaded() ) { debugPlan<<"isLoaded"; KoXmlNode n = ctx->context().namedItem( "categories" ); if ( n.isNull() ) { warnPlan<<"No categories"; } else { n = n.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() != "category") { continue; } debugPlan<<"category: "<addCategory( ct, cn ); KoXmlNode n1 = e.firstChild(); for ( ; ! n1.isNull(); n1 = n1.nextSibling() ) { if ( ! n1.isElement() ) { continue; } KoXmlElement e1 = n1.toElement(); if (e1.tagName() != "view") { continue; } ViewBase *v = 0; QString type = e1.attribute( "viewtype" ); QString tag = e1.attribute( "tag" ); QString name = e1.attribute( "name" ); QString tip = e1.attribute( "tooltip" ); v = createView( cat, type, tag, name, tip ); //KoXmlNode settings = e1.namedItem( "settings " ); ???? KoXmlNode settings = e1.firstChild(); for ( ; ! settings.isNull(); settings = settings.nextSibling() ) { if ( settings.nodeName() == "settings" ) { break; } } if ( v && settings.isElement() ) { debugPlan<<" settings"; v->loadContext( settings.toElement() ); } } } } } else { debugPlan<<"Default"; ViewBase *v = 0; ViewListItem *cat; QString ct = "Editors"; cat = m_viewlist->addCategory( ct, defaultCategoryInfo( ct ).name ); createCalendarEditor( cat, "CalendarEditor", QString(), TIP_USE_DEFAULT_TEXT ); createAccountsEditor( cat, "AccountsEditor", QString(), TIP_USE_DEFAULT_TEXT ); v = createResourceEditor( cat, "ResourceEditor", QString(), TIP_USE_DEFAULT_TEXT ); v = createTaskEditor( cat, "TaskEditor", QString(), TIP_USE_DEFAULT_TEXT ); m_defaultView = m_tab->count() - 1; v->showColumns(QList() << NodeModel::NodeName << NodeModel::NodeType << NodeModel::NodePriority << NodeModel::NodeAllocation << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeResponsible << NodeModel::NodeDescription ); v = createTaskEditor( cat, "TaskConstraintEditor", i18n("Task Constraints"), i18n("Edit task scheduling constraints") ); v->showColumns(QList() << NodeModel::NodeName << NodeModel::NodeType << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeDescription ); v = createTaskEditor( cat, "TaskCostEditor", i18n("Task Cost"), i18n("Edit task cost") ); v->showColumns(QList() << NodeModel::NodeName << NodeModel::NodeType << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription ); createDependencyEditor( cat, "DependencyEditor", QString(), TIP_USE_DEFAULT_TEXT ); // Do not show by default // createPertEditor( cat, "PertEditor", QString(), TIP_USE_DEFAULT_TEXT ); createScheduleHandler( cat, "ScheduleHandlerView", QString(), TIP_USE_DEFAULT_TEXT ); ct = "Views"; cat = m_viewlist->addCategory( ct, defaultCategoryInfo( ct ).name ); createGanttView( cat, "GanttView", QString(), TIP_USE_DEFAULT_TEXT ); createMilestoneGanttView( cat, "MilestoneGanttView", QString(), TIP_USE_DEFAULT_TEXT ); createResourceAppointmentsView( cat, "ResourceAppointmentsView", QString(), TIP_USE_DEFAULT_TEXT ); createResourceAppointmentsGanttView( cat, "ResourceAppointmentsGanttView", QString(), TIP_USE_DEFAULT_TEXT ); createAccountsView( cat, "AccountsView", QString(), TIP_USE_DEFAULT_TEXT ); ct = "Execution"; cat = m_viewlist->addCategory( ct, defaultCategoryInfo( ct ).name ); createProjectStatusView( cat, "ProjectStatusView", QString(), TIP_USE_DEFAULT_TEXT ); createPerformanceStatusView( cat, "PerformanceStatusView", QString(), TIP_USE_DEFAULT_TEXT ); v = createTaskStatusView( cat, "TaskStatusView", QString(), TIP_USE_DEFAULT_TEXT ); v = createTaskView( cat, "TaskView", QString(), TIP_USE_DEFAULT_TEXT ); v = createTaskWorkPackageView( cat, "TaskWorkPackageView", QString(), TIP_USE_DEFAULT_TEXT ); ct = "Reports"; cat = m_viewlist->addCategory(ct, defaultCategoryInfo(ct).name); createReportsGeneratorView(cat, "ReportsGeneratorView", i18n("Generate reports"), TIP_USE_DEFAULT_TEXT); #ifdef PLAN_USE_KREPORT // Let user add reports explicitly, we prefer reportsgenerator now // A little hack to get the user started... #if 0 ReportView *rv = qobject_cast( createReportView( cat, "ReportView", i18n( "Task Status Report" ), TIP_USE_DEFAULT_TEXT ) ); if ( rv ) { QDomDocument doc; doc.setContent( standardTaskStatusReport() ); rv->loadXML( doc ); } #endif #endif } } ViewBase *View::createView( ViewListItem *cat, const QString &type, const QString &tag, const QString &name, const QString &tip, int index ) { ViewBase *v = 0; //NOTE: type is the same as classname (so if it is changed...) if ( type == "CalendarEditor" ) { v = createCalendarEditor( cat, tag, name, tip, index ); } else if ( type == "AccountsEditor" ) { v = createAccountsEditor( cat, tag, name, tip, index ); } else if ( type == "ResourceEditor" ) { v = createResourceEditor( cat, tag, name, tip, index ); } else if ( type == "TaskEditor" ) { v = createTaskEditor( cat, tag, name, tip, index ); } else if ( type == "DependencyEditor" ) { v = createDependencyEditor( cat, tag, name, tip, index ); } else if ( type == "PertEditor" ) { v = createPertEditor( cat, tag, name, tip, index ); } else if ( type == "ScheduleEditor" ) { v = createScheduleEditor( cat, tag, name, tip, index ); } else if ( type == "ScheduleHandlerView" ) { v = createScheduleHandler( cat, tag, name, tip, index ); } else if ( type == "ProjectStatusView" ) { v = createProjectStatusView( cat, tag, name, tip, index ); } else if ( type == "TaskStatusView" ) { v = createTaskStatusView( cat, tag, name, tip, index ); } else if ( type == "TaskView" ) { v = createTaskView( cat, tag, name, tip, index ); } else if ( type == "TaskWorkPackageView" ) { v = createTaskWorkPackageView( cat, tag, name, tip, index ); } else if ( type == "GanttView" ) { v = createGanttView( cat, tag, name, tip, index ); } else if ( type == "MilestoneGanttView" ) { v = createMilestoneGanttView( cat, tag, name, tip, index ); } else if ( type == "ResourceAppointmentsView" ) { v = createResourceAppointmentsView( cat, tag, name, tip, index ); } else if ( type == "ResourceAppointmentsGanttView" ) { v = createResourceAppointmentsGanttView( cat, tag, name, tip, index ); } else if ( type == "AccountsView" ) { v = createAccountsView( cat, tag, name, tip, index ); } else if ( type == "PerformanceStatusView" ) { v = createPerformanceStatusView( cat, tag, name, tip, index ); } else if ( type == "ReportsGeneratorView" ) { v = createReportsGeneratorView(cat, tag, name, tip, index); } else if ( type == "ReportView" ) { #ifdef PLAN_USE_KREPORT v = createReportView( cat, tag, name, tip, index ); #endif } else { warnPlan<<"Unknown viewtype: "<type() == ViewListItem::ItemType_SubView ) { itm->setViewInfo( defaultViewInfo( itm->viewType() ) ); } else if ( itm->type() == ViewListItem::ItemType_Category ) { ViewInfo vi = defaultCategoryInfo( itm->tag() ); itm->setViewInfo( vi ); } } ViewInfo View::defaultViewInfo( const QString &type ) const { ViewInfo vi; if ( type == "CalendarEditor" ) { vi.name = i18n( "Work & Vacation" ); vi.tip = xi18nc( "@info:tooltip", "Edit working- and vacation days for resources" ); } else if ( type == "AccountsEditor" ) { vi.name = i18n( "Cost Breakdown Structure" ); vi.tip = xi18nc( "@info:tooltip", "Edit cost breakdown structure." ); } else if ( type == "ResourceEditor" ) { vi.name = i18n( "Resources" ); vi.tip = xi18nc( "@info:tooltip", "Edit resource breakdown structure" ); } else if ( type == "TaskEditor" ) { vi.name = i18n( "Tasks" ); vi.tip = xi18nc( "@info:tooltip", "Edit work breakdown structure" ); } else if ( type == "DependencyEditor" ) { vi.name = i18n( "Dependencies (Graphic)" ); vi.tip = xi18nc( "@info:tooltip", "Edit task dependencies" ); } else if ( type == "PertEditor" ) { vi.name = i18n( "Dependencies (List)" ); vi.tip = xi18nc( "@info:tooltip", "Edit task dependencies" ); } else if ( type == "ScheduleEditor" ) { // This view is not used stand-alone atm vi.name = i18n( "Schedules" ); } else if ( type == "ScheduleHandlerView" ) { vi.name = i18n( "Schedules" ); vi.tip = xi18nc( "@info:tooltip", "Calculate and analyze project schedules" ); } else if ( type == "ProjectStatusView" ) { vi.name = i18n( "Project Performance Chart" ); vi.tip = xi18nc( "@info:tooltip", "View project status information" ); } else if ( type == "TaskStatusView" ) { vi.name = i18n( "Task Status" ); vi.tip = xi18nc( "@info:tooltip", "View task progress information" ); } else if ( type == "TaskView" ) { vi.name = i18n( "Task Execution" ); vi.tip = xi18nc( "@info:tooltip", "View task execution information" ); } else if ( type == "TaskWorkPackageView" ) { vi.name = i18n( "Work Package View" ); vi.tip = xi18nc( "@info:tooltip", "View task work package information" ); } else if ( type == "GanttView" ) { vi.name = i18n( "Gantt" ); vi.tip = xi18nc( "@info:tooltip", "View Gantt chart" ); } else if ( type == "MilestoneGanttView" ) { vi.name = i18n( "Milestone Gantt" ); vi.tip = xi18nc( "@info:tooltip", "View milestone Gantt chart" ); } else if ( type == "ResourceAppointmentsView" ) { vi.name = i18n( "Resource Assignments" ); vi.tip = xi18nc( "@info:tooltip", "View resource assignments in a table" ); } else if ( type == "ResourceAppointmentsGanttView" ) { vi.name = i18n( "Resource Assignments (Gantt)" ); vi.tip = xi18nc( "@info:tooltip", "View resource assignments in Gantt chart" ); } else if ( type == "AccountsView" ) { vi.name = i18n( "Cost Breakdown" ); vi.tip = xi18nc( "@info:tooltip", "View planned and actual cost" ); } else if ( type == "PerformanceStatusView" ) { vi.name = i18n( "Tasks Performance Chart" ); vi.tip = xi18nc( "@info:tooltip", "View tasks performance status information" ); } else if ( type == "ReportsGeneratorView" ) { vi.name = i18n( "Reports Generator" ); vi.tip = xi18nc( "@info:tooltip", "Generate reports" ); } else if ( type == "ReportView" ) { vi.name = i18n( "Report" ); vi.tip = xi18nc( "@info:tooltip", "View report" ); } else { warnPlan<<"Unknown viewtype: "<count()-1) : m_visitedViews.at(m_visitedViews.count() - 2); debugPlan<<"Prev:"<setCurrentIndex(view); return; } if ( url.url().startsWith( QLatin1String( "about:plan" ) ) ) { getPart()->aboutPage().generatePage( v->htmlPart(), url ); return; } } if ( url.scheme() == QLatin1String("help") ) { KHelpClient::invokeHelp( "", url.fileName() ); return; } // try to open the url debugPlan<htmlPart().setJScriptEnabled(false); v->htmlPart().setJavaEnabled(false); v->htmlPart().setMetaRefreshEnabled(false); v->htmlPart().setPluginsEnabled(false); slotOpenUrlRequest( v, QUrl( "about:plan/main" ) ); connect( v, &HtmlView::openUrlRequest, this, &View::slotOpenUrlRequest ); m_tab->addWidget( v ); return v; } ViewBase *View::createResourceAppointmentsGanttView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceAppointmentsGanttView *v = new ResourceAppointmentsGanttView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceAppointmentsGanttView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, v, &ResourceAppointmentsGanttView::setScheduleManager); connect( v, &ResourceAppointmentsGanttView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->setProject( &( getProject() ) ); v->setScheduleManager( currentScheduleManager() ); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createResourceAppointmentsView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceAppointmentsView *v = new ResourceAppointmentsView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceAppointmentsView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, v, &ResourceAppointmentsView::setScheduleManager); connect( v, &ResourceAppointmentsView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->setProject( &( getProject() ) ); v->setScheduleManager( currentScheduleManager() ); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createResourceEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceEditor *resourceeditor = new ResourceEditor(getKoPart(), getPart(), m_tab ); resourceeditor->setViewSplitMode(false); m_tab->addWidget( resourceeditor ); resourceeditor->setProject( &(getProject()) ); ViewListItem *i = m_viewlist->addView( cat, tag, name, resourceeditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( resourceeditor, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( resourceeditor, &ResourceEditor::deleteObjectList, this, &View::slotDeleteResourceObjects ); connect( resourceeditor, &ResourceEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); resourceeditor->updateReadWrite( m_readWrite ); return resourceeditor; } ViewBase *View::createTaskEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskEditor *taskeditor = new TaskEditor(getKoPart(), getPart(), m_tab ); taskeditor->setViewSplitMode(false); m_tab->addWidget( taskeditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, taskeditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } taskeditor->setProject( &(getProject()) ); taskeditor->setScheduleManager( currentScheduleManager() ); connect( this, &View::currentScheduleManagerChanged, taskeditor, &TaskEditor::setScheduleManager); connect( taskeditor, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( taskeditor, &TaskEditor::addTask, this, &View::slotAddTask ); connect( taskeditor, &TaskEditor::addMilestone, this, &View::slotAddMilestone ); connect( taskeditor, &TaskEditor::addSubtask, this, &View::slotAddSubTask ); connect( taskeditor, &TaskEditor::addSubMilestone, this, &View::slotAddSubMilestone ); connect(taskeditor, &TaskEditor::deleteTaskList, this, &View::slotDeleteTaskList); connect( taskeditor, &TaskEditor::moveTaskUp, this, &View::slotMoveTaskUp ); connect( taskeditor, &TaskEditor::moveTaskDown, this, &View::slotMoveTaskDown ); connect( taskeditor, &TaskEditor::indentTask, this, &View::slotIndentTask ); connect( taskeditor, &TaskEditor::unindentTask, this, &View::slotUnindentTask ); connect(taskeditor, &TaskEditor::saveTaskModule, this, &View::saveTaskModule); connect(taskeditor, &TaskEditor::removeTaskModule, this, &View::removeTaskModule); connect(taskeditor, &ViewBase::openDocument, static_cast(m_partpart), &Part::openTaskModule); connect(this, &View::taskModulesChanged, taskeditor, &TaskEditor::setTaskModules); connect( taskeditor, &TaskEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); taskeditor->updateReadWrite( m_readWrite ); // last: QStringList modules = KoResourcePaths::findAllResources( "calligraplan_taskmodules", "*.plan", KoResourcePaths::NoDuplicates|KoResourcePaths::Recursive ); debugPlan<setTaskModules( modules ); return taskeditor; } ViewBase *View::createAccountsEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { AccountsEditor *ae = new AccountsEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( ae ); ViewListItem *i = m_viewlist->addView( cat, tag, name, ae, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "AccountsEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } ae->draw( getProject() ); connect( ae, &ViewBase::guiActivated, this, &View::slotGuiActivated ); ae->updateReadWrite( m_readWrite ); return ae; } ViewBase *View::createCalendarEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { CalendarEditor *calendareditor = new CalendarEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( calendareditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, calendareditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "CalendarEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } calendareditor->draw( getProject() ); connect( calendareditor, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( calendareditor, &CalendarEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); calendareditor->updateReadWrite( m_readWrite ); return calendareditor; } ViewBase *View::createScheduleHandler( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ScheduleHandlerView *handler = new ScheduleHandlerView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( handler ); ViewListItem *i = m_viewlist->addView( cat, tag, name, handler, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ScheduleHandlerView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( handler->scheduleEditor(), &ScheduleEditor::addScheduleManager, this, &View::slotAddScheduleManager ); connect( handler->scheduleEditor(), &ScheduleEditor::deleteScheduleManager, this, &View::slotDeleteScheduleManager ); connect( handler->scheduleEditor(), &ScheduleEditor::moveScheduleManager, this, &View::slotMoveScheduleManager); connect( handler->scheduleEditor(), &ScheduleEditor::calculateSchedule, this, &View::slotCalculateSchedule ); connect( handler->scheduleEditor(), &ScheduleEditor::baselineSchedule, this, &View::slotBaselineSchedule ); connect( handler, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, handler, &ScheduleHandlerView::currentScheduleManagerChanged ); connect( handler, &ScheduleHandlerView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect(handler, &ScheduleHandlerView::editNode, this, &View::slotOpenNode); connect(handler, &ScheduleHandlerView::editResource, this, &View::slotEditResource); handler->draw( getProject() ); handler->updateReadWrite( m_readWrite ); return handler; } ScheduleEditor *View::createScheduleEditor( QWidget *parent ) { ScheduleEditor *scheduleeditor = new ScheduleEditor(getKoPart(), getPart(), parent ); connect( scheduleeditor, &ScheduleEditor::addScheduleManager, this, &View::slotAddScheduleManager ); connect( scheduleeditor, &ScheduleEditor::deleteScheduleManager, this, &View::slotDeleteScheduleManager ); connect( scheduleeditor, &ScheduleEditor::calculateSchedule, this, &View::slotCalculateSchedule ); connect( scheduleeditor, &ScheduleEditor::baselineSchedule, this, &View::slotBaselineSchedule ); scheduleeditor->updateReadWrite( m_readWrite ); return scheduleeditor; } ViewBase *View::createScheduleEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ScheduleEditor *scheduleeditor = new ScheduleEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( scheduleeditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, scheduleeditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ScheduleEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } scheduleeditor->setProject( &( getProject() ) ); connect( scheduleeditor, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( scheduleeditor, &ScheduleEditor::addScheduleManager, this, &View::slotAddScheduleManager ); connect( scheduleeditor, &ScheduleEditor::deleteScheduleManager, this, &View::slotDeleteScheduleManager ); connect( scheduleeditor, &ScheduleEditor::calculateSchedule, this, &View::slotCalculateSchedule ); connect( scheduleeditor, &ScheduleEditor::baselineSchedule, this, &View::slotBaselineSchedule ); scheduleeditor->updateReadWrite( m_readWrite ); return scheduleeditor; } ViewBase *View::createDependencyEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { DependencyEditor *editor = new DependencyEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( editor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, editor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "DependencyEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } editor->draw( getProject() ); connect( editor, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( editor, &DependencyEditor::addRelation, this, &View::slotAddRelation); connect( editor, &DependencyEditor::modifyRelation, this, &View::slotModifyRelation); connect( editor, &DependencyEditor::editRelation, this, &View::slotEditRelation); connect( editor, &DependencyEditor::editNode, this, &View::slotOpenNode); connect( editor, &DependencyEditor::addTask, this, &View::slotAddTask ); connect( editor, &DependencyEditor::addMilestone, this, &View::slotAddMilestone ); connect( editor, &DependencyEditor::addSubMilestone, this, &View::slotAddSubMilestone ); connect( editor, &DependencyEditor::addSubtask, this, &View::slotAddSubTask ); connect( editor, &DependencyEditor::deleteTaskList, this, &View::slotDeleteTaskList); connect( this, &View::currentScheduleManagerChanged, editor, &DependencyEditor::setScheduleManager); connect( editor, &DependencyEditor::requestPopupMenu, this, &View::slotPopupMenuRequested); editor->updateReadWrite( m_readWrite ); editor->setScheduleManager( currentScheduleManager() ); return editor; } ViewBase *View::createPertEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { PertEditor *perteditor = new PertEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( perteditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, perteditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "PertEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } perteditor->draw( getProject() ); connect( perteditor, &ViewBase::guiActivated, this, &View::slotGuiActivated ); m_updatePertEditor = true; perteditor->updateReadWrite( m_readWrite ); return perteditor; } ViewBase *View::createProjectStatusView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ProjectStatusView *v = new ProjectStatusView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ProjectStatusView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, v, &ProjectStatusView::setScheduleManager); v->updateReadWrite( m_readWrite ); v->setProject( &getProject() ); v->setScheduleManager( currentScheduleManager() ); return v; } ViewBase *View::createPerformanceStatusView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { PerformanceStatusView *v = new PerformanceStatusView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "PerformanceStatusView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, v, &PerformanceStatusView::setScheduleManager); connect( v, &PerformanceStatusView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->updateReadWrite( m_readWrite ); v->setProject( &getProject() ); v->setScheduleManager( currentScheduleManager() ); return v; } ViewBase *View::createTaskStatusView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskStatusView *taskstatusview = new TaskStatusView(getKoPart(), getPart(), m_tab ); taskstatusview->setViewSplitMode(false); m_tab->addWidget( taskstatusview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, taskstatusview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskStatusView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( taskstatusview, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, taskstatusview, &TaskStatusView::setScheduleManager); connect( taskstatusview, &TaskStatusView::requestPopupMenu, this, &View::slotPopupMenuRequested); taskstatusview->updateReadWrite( m_readWrite ); taskstatusview->draw( getProject() ); taskstatusview->setScheduleManager( currentScheduleManager() ); return taskstatusview; } ViewBase *View::createTaskView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskView *v = new TaskView(getKoPart(), getPart(), m_tab ); v->setViewSplitMode(false); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->draw( getProject() ); v->setScheduleManager( currentScheduleManager() ); connect( this, &View::currentScheduleManagerChanged, v, &TaskView::setScheduleManager); connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( v, &TaskView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createTaskWorkPackageView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskWorkPackageView *v = new TaskWorkPackageView(getKoPart(), getPart(), m_tab ); v->setViewSplitMode(false); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskWorkPackageView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->setProject( &getProject() ); v->setScheduleManager( currentScheduleManager() ); connect( this, &View::currentScheduleManagerChanged, v, &TaskWorkPackageView::setScheduleManager); connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( v, &TaskWorkPackageView::requestPopupMenu, this, &View::slotPopupMenuRequested); connect( v, &TaskWorkPackageView::mailWorkpackage, this, &View::slotMailWorkpackage ); - connect( v, &TaskWorkPackageView::mailWorkpackages, this, &View::slotMailWorkpackages ); - + connect( v, &TaskWorkPackageView::publishWorkpackages, this, &View::slotPublishWorkpackages ); + connect(v, &TaskWorkPackageView::openWorkpackages, this, &View::openWorkPackageMergeDialog); + connect(this, &View::workPackagesAvailable, v, &TaskWorkPackageView::slotWorkpackagesAvailable); connect(v, &TaskWorkPackageView::checkForWorkPackages, getPart(), &MainDocument::checkForWorkPackages); + connect(v, &TaskWorkPackageView::loadWorkPackageUrl, this, &View::loadWorkPackage); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createGanttView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { GanttView *ganttview = new GanttView(getKoPart(), getPart(), m_tab, koDocument()->isReadWrite() ); m_tab->addWidget( ganttview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, ganttview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "GanttView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } ganttview->setProject( &( getProject() ) ); ganttview->setScheduleManager( currentScheduleManager() ); connect( ganttview, &ViewBase::guiActivated, this, &View::slotGuiActivated ); /* TODO: Review these connect( ganttview, SIGNAL(addRelation(KPlato::Node*,KPlato::Node*,int)), SLOT(slotAddRelation(KPlato::Node*,KPlato::Node*,int)) ); connect( ganttview, SIGNAL(modifyRelation(KPlato::Relation*,int)), SLOT(slotModifyRelation(KPlato::Relation*,int)) ); connect( ganttview, SIGNAL(modifyRelation(KPlato::Relation*)), SLOT(slotModifyRelation(KPlato::Relation*)) ); connect( ganttview, SIGNAL(itemDoubleClicked()), SLOT(slotOpenNode()) ); connect( ganttview, SIGNAL(itemRenamed(KPlato::Node*,QString)), this, SLOT(slotRenameNode(KPlato::Node*,QString)) );*/ connect( this, &View::currentScheduleManagerChanged, ganttview, &GanttView::setScheduleManager); connect( ganttview, &GanttView::requestPopupMenu, this, &View::slotPopupMenuRequested); ganttview->updateReadWrite( m_readWrite ); return ganttview; } ViewBase *View::createMilestoneGanttView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { MilestoneGanttView *ganttview = new MilestoneGanttView(getKoPart(), getPart(), m_tab, koDocument()->isReadWrite() ); m_tab->addWidget( ganttview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, ganttview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "MilestoneGanttView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } ganttview->setProject( &( getProject() ) ); ganttview->setScheduleManager( currentScheduleManager() ); connect( ganttview, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( this, &View::currentScheduleManagerChanged, ganttview, &MilestoneGanttView::setScheduleManager); connect( ganttview, &MilestoneGanttView::requestPopupMenu, this, &View::slotPopupMenuRequested); ganttview->updateReadWrite( m_readWrite ); return ganttview; } ViewBase *View::createAccountsView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { AccountsView *accountsview = new AccountsView(getKoPart(), &getProject(), getPart(), m_tab ); m_tab->addWidget( accountsview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, accountsview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "AccountsView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } accountsview->setScheduleManager( currentScheduleManager() ); connect( this, &View::currentScheduleManagerChanged, accountsview, &AccountsView::setScheduleManager); connect( accountsview, &ViewBase::guiActivated, this, &View::slotGuiActivated ); accountsview->updateReadWrite( m_readWrite ); return accountsview; } ViewBase *View::createResourceAssignmentView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceAssignmentView *resourceAssignmentView = new ResourceAssignmentView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( resourceAssignmentView ); m_updateResourceAssignmentView = true; ViewListItem *i = m_viewlist->addView( cat, tag, name, resourceAssignmentView, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceAssignmentView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } resourceAssignmentView->draw( getProject() ); connect( resourceAssignmentView, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( resourceAssignmentView, &ResourceAssignmentView::requestPopupMenu, this, &View::slotPopupMenuRequested); resourceAssignmentView->updateReadWrite( m_readWrite ); return resourceAssignmentView; } ViewBase *View::createReportsGeneratorView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ReportsGeneratorView *v = new ReportsGeneratorView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo( "ReportsGeneratorView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->setProject( &getProject() ); connect( this, &View::currentScheduleManagerChanged, v, &ViewBase::setScheduleManager ); connect( this, &View::currentScheduleManagerChanged, v, &ViewBase::slotRefreshView); v->setScheduleManager( currentScheduleManager() ); connect( v, &ViewBase::guiActivated, this, &View::slotGuiActivated ); connect( v, &ReportsGeneratorView::requestPopupMenu, this, &View::slotPopupMenuRequested); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createReportView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { #ifdef PLAN_USE_KREPORT ReportView *v = new ReportView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ReportView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->setProject( &getProject() ); connect( this, &View::currentScheduleManagerChanged, v, &ReportView::setScheduleManager); connect( this, &View::currentScheduleManagerChanged, v, SLOT(slotRefreshView())); v->setScheduleManager( currentScheduleManager() ); connect( v, &ReportView::guiActivated, this, &View::slotGuiActivated); v->updateReadWrite( m_readWrite ); return v; #else Q_UNUSED(cat) Q_UNUSED(tag) Q_UNUSED(name) Q_UNUSED(tip) Q_UNUSED(index) return 0; #endif } Project& View::getProject() const { return getPart() ->getProject(); } KoPrintJob * View::createPrintJob() { KoView *v = qobject_cast( canvas() ); if ( v == 0 ) { return 0; } return v->createPrintJob(); } ViewBase *View::currentView() const { return qobject_cast( m_tab->currentWidget() ); } void View::slotEditCut() { ViewBase *v = currentView(); if ( v ) { v->slotEditCut(); } } void View::slotEditCopy() { ViewBase *v = currentView(); if ( v ) { v->slotEditCopy(); } } void View::slotEditPaste() { ViewBase *v = currentView(); if ( v ) { v->slotEditPaste(); } } void View::slotRefreshView() { ViewBase *v = currentView(); if ( v ) { debugPlan<slotRefreshView(); } } void View::slotViewSelector( bool show ) { //debugPlan; m_viewlist->setVisible( show ); } void View::slotInsertResourcesFile(const QString &file, const QUrl &projects) { getPart()->insertResourcesFile(QUrl(file), projects); } void View::slotInsertFile() { InsertFileDialog *dlg = new InsertFileDialog( getProject(), currentTask(), this ); connect(dlg, &QDialog::finished, this, &View::slotInsertFileFinished); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotInsertFileFinished( int result ) { InsertFileDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { getPart()->insertFile( dlg->url(), dlg->parentNode(), dlg->afterNode() ); } dlg->deleteLater(); } void View::slotLoadSharedProjects() { LoadSharedProjectsDialog *dlg = new LoadSharedProjectsDialog( getProject(), getPart()->url(), this ); connect(dlg, &QDialog::finished, this, &View::slotLoadSharedProjectsFinished); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotLoadSharedProjectsFinished( int result ) { LoadSharedProjectsDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { getPart()->insertSharedProjects(dlg->urls()); } dlg->deleteLater(); } void View::slotProjectEdit() { slotOpenNode( &getProject() ); } void View::slotProjectWorktime() { StandardWorktimeDialog *dia = new StandardWorktimeDialog( getProject(), this ); connect(dia, &QDialog::finished, this, &View::slotProjectWorktimeFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotProjectWorktimeFinished( int result ) { StandardWorktimeDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { //debugPlan<<"Modifying calendar(s)"; getPart() ->addCommand( cmd ); //also executes } } dia->deleteLater(); } void View::slotSelectionChanged( ScheduleManager *sm ) { debugPlan<setChecked( true ); // this doesn't trigger QActionGroup slotViewSchedule( a ); } QList View::sortedActionList() { QMap lst; const QMap map = m_scheduleActions; // clazy:exclude=qmap-with-pointer-key QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { lst.insert(it.key()->objectName(), it.key()); } return lst.values(); } void View::slotScheduleSwapped(ScheduleManager *from, ScheduleManager *to) { if (currentScheduleManager() == from) { QAction *a = m_scheduleActions.key(to); if (a) { a->setChecked(true); } } } void View::slotScheduleRemoved( const ScheduleManager *sch ) { debugPlan<name(); QAction *a = 0; QAction *checked = m_scheduleActionGroup->checkedAction(); QMapIterator i( m_scheduleActions ); while (i.hasNext()) { i.next(); if ( i.value() == sch ) { a = i.key(); break; } } if ( a ) { unplugActionList( "view_schedule_list" ); delete a; plugActionList( "view_schedule_list", sortedActionList() ); if ( checked && checked != a ) { checked->setChecked( true ); } else if ( ! m_scheduleActions.isEmpty() ) { m_scheduleActions.firstKey()->setChecked( true ); } } slotViewSchedule( m_scheduleActionGroup->checkedAction() ); } void View::slotScheduleAdded( const ScheduleManager *sch ) { ScheduleManager *s = const_cast( sch ); QAction *checked = m_scheduleActionGroup->checkedAction(); unplugActionList( "view_schedule_list" ); QAction *act = addScheduleAction( s ); plugActionList( "view_schedule_list", sortedActionList() ); if (!currentScheduleManager()) { if ( act ) { act->setChecked( true ); } else if ( ! m_scheduleActions.isEmpty() ) { m_scheduleActions.firstKey()->setChecked( true ); } slotViewSchedule( m_scheduleActionGroup->checkedAction() ); } } void View::slotScheduleCalculated(Project *project, ScheduleManager *manager) { Q_UNUSED(project); if (manager == currentScheduleManager()) { slotViewScheduleManager(manager); } } QAction *View::addScheduleAction( ScheduleManager *sch ) { QAction *act = 0; QString n = sch->name(); act = new KToggleAction( n, this); actionCollection()->addAction(n, act ); m_scheduleActions.insert( act, sch ); m_scheduleActionGroup->addAction( act ); //debugPlan<<"Add:"<name(); m_scheduleActions.remove( static_cast( o ) ); } void View::slotPlugScheduleActions() { ScheduleManager *current = currentScheduleManager(); unplugActionList( "view_schedule_list" ); const QMap map = m_scheduleActions; // clazy:exclude=qmap-with-pointer-key QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { m_scheduleActionGroup->removeAction(it.key()); delete it.key(); } m_scheduleActions.clear(); QAction *ca = 0; foreach( ScheduleManager *sm, getProject().allScheduleManagers() ) { QAction *act = addScheduleAction(sm); if (sm == current) { ca = act; } } plugActionList( "view_schedule_list", sortedActionList() ); if ( ca == 0 && m_scheduleActionGroup->actions().count() > 0 ) { ca = m_scheduleActionGroup->actions().constFirst(); } if ( ca ) { ca->setChecked( true ); } slotViewSchedule( ca ); } void View::slotCalculateSchedule( Project *project, ScheduleManager *sm ) { if ( project == 0 || sm == 0 ) { return; } if ( sm->parentManager() && ! sm->parentManager()->isScheduled() ) { // the parent must be scheduled return; } CalculateScheduleCmd *cmd = new CalculateScheduleCmd( *project, sm, kundo2_i18nc("@info:status 1=schedule name", "Calculate %1", sm->name() ) ); getPart() ->addCommand( cmd ); slotUpdate(); } void View::slotRemoveCommands() { while ( ! m_undocommands.isEmpty() ) { m_undocommands.last()->undo(); delete m_undocommands.takeLast(); } } void View::slotBaselineSchedule( Project *project, ScheduleManager *sm ) { if ( project == 0 || sm == 0 ) { return; } if ( ! sm->isBaselined() && project->isBaselined() ) { KMessageBox::sorry( this, i18n( "Cannot baseline. The project is already baselined." ) ); return; } KUndo2Command *cmd; if ( sm->isBaselined() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "This schedule is baselined. Do you want to remove the baseline?" ) ); if ( res == KMessageBox::Cancel ) { return; } cmd = new ResetBaselineScheduleCmd( *sm, kundo2_i18n( "Reset baseline %1", sm->name() ) ); } else { cmd = new BaselineScheduleCmd( *sm, kundo2_i18n( "Baseline %1", sm->name() ) ); } getPart() ->addCommand( cmd ); } void View::slotAddScheduleManager( Project *project ) { if ( project == 0 ) { return; } ScheduleManager *sm = project->createScheduleManager(); AddScheduleManagerCmd *cmd = new AddScheduleManagerCmd( *project, sm, -1, kundo2_i18n( "Add schedule %1", sm->name() ) ); getPart() ->addCommand( cmd ); } void View::slotDeleteScheduleManager( Project *project, ScheduleManager *sm ) { if ( project == 0 || sm == 0) { return; } DeleteScheduleManagerCmd *cmd = new DeleteScheduleManagerCmd( *project, sm, kundo2_i18n( "Delete schedule %1", sm->name() ) ); getPart() ->addCommand( cmd ); } void View::slotMoveScheduleManager( ScheduleManager *sm, ScheduleManager *parent, int index ) { if ( sm == 0 ) { return; } MoveScheduleManagerCmd *cmd = new MoveScheduleManagerCmd( sm, parent, index, kundo2_i18n( "Move schedule %1", sm->name() ) ); getPart() ->addCommand( cmd ); } void View::slotAddSubTask() { Task * node = getProject().createTask( getPart() ->config().taskDefaults() ); SubTaskAddDialog *dia = new SubTaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, &QDialog::finished, this, &View::slotAddSubTaskFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddSubTaskFinished( int result ) { SubTaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command *m = dia->buildCommand(); getPart() ->addCommand( m ); // add task to project } dia->deleteLater(); } void View::slotAddTask() { Task * node = getProject().createTask( getPart() ->config().taskDefaults() ); TaskAddDialog *dia = new TaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, &QDialog::finished, this, &View::slotAddTaskFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddTaskFinished( int result ) { TaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command *m = dia->buildCommand(); getPart() ->addCommand( m ); // add task to project } dia->deleteLater(); } void View::slotAddMilestone() { Task * node = getProject().createTask(); node->estimate() ->clear(); TaskAddDialog *dia = new TaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, &QDialog::finished, this, &View::slotAddMilestoneFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddMilestoneFinished( int result ) { TaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { MacroCommand *c = new MacroCommand( kundo2_i18n( "Add milestone" ) ); c->addCommand( dia->buildCommand() ); getPart() ->addCommand( c ); // add task to project } dia->deleteLater(); } void View::slotAddSubMilestone() { Task * node = getProject().createTask(); node->estimate() ->clear(); SubTaskAddDialog *dia = new SubTaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, &QDialog::finished, this, &View::slotAddSubMilestoneFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddSubMilestoneFinished( int result ) { SubTaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { MacroCommand *c = new MacroCommand( kundo2_i18n( "Add sub-milestone" ) ); c->addCommand( dia->buildCommand() ); getPart() ->addCommand( c ); // add task to project } dia->deleteLater(); } void View::slotDefineWBS() { //debugPlan; Project &p = getProject(); WBSDefinitionDialog *dia = new WBSDefinitionDialog( p, p.wbsDefinition(), this ); connect(dia, &QDialog::finished, this, &View::slotDefineWBSFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotDefineWBSFinished( int result ) { //debugPlan; WBSDefinitionDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted ) { KUndo2Command *cmd = dia->buildCommand(); if ( cmd ) { getPart()->addCommand( cmd ); } } dia->deleteLater(); } void View::slotConfigure() { //debugPlan; if( KConfigDialog::showDialog("Plan Settings") ) { return; } ConfigDialog *dialog = new ConfigDialog( this, "Plan Settings", KPlatoSettings::self() ); dialog->addPage(new ConfigProjectPanel(), i18n("Project Defaults"), koIconName("calligraplan") ); dialog->addPage(new ConfigWorkVacationPanel(), i18n("Work & Vacation"), koIconName("view-calendar") ); dialog->addPage(new TaskDefaultPanel(), i18n("Task Defaults"), koIconName("view-task") ); dialog->addPage(new ColorsConfigPanel(), i18n("Task Colors"), koIconName("fill-color") ); dialog->addPage(new WorkPackageConfigPanel(), i18n("Work Package"), koIconName("calligraplanwork") ); dialog->show(); } void View::slotIntroduction() { m_tab->setCurrentIndex(0); } Calendar *View::currentCalendar() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } return v->currentCalendar(); } Node *View::currentNode() const { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } Node * task = v->currentNode(); if ( 0 != task ) { return task; } return &( getProject() ); } Task *View::currentTask() const { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } Node * task = v->currentNode(); if ( task ) { return dynamic_cast( task ); } return 0; } Resource *View::currentResource() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } return v->currentResource(); } ResourceGroup *View::currentResourceGroup() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } return v->currentResourceGroup(); } void View::slotOpenCurrentNode() { //debugPlan; Node * node = currentNode(); slotOpenNode( node ); } void View::slotOpenNode( Node *node ) { //debugPlan; if ( !node ) return ; switch ( node->type() ) { case Node::Type_Project: { Project * project = static_cast( node ); MainProjectDialog *dia = new MainProjectDialog( *project, this ); connect(dia, &MainProjectDialog::dialogFinished, this, &View::slotProjectEditFinished); connect(dia, &MainProjectDialog::sigLoadSharedResources, this, &View::slotInsertResourcesFile); connect(dia, &MainProjectDialog::loadResourceAssignments, getPart(), &MainDocument::loadResourceAssignments); connect(dia, &MainProjectDialog::clearResourceAssignments, getPart(), &MainDocument::clearResourceAssignments); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: { Task *task = static_cast( node ); TaskDialog *dia = new TaskDialog( getProject(), *task, getProject().accounts(), this ); connect(dia, &QDialog::finished, this, &View::slotTaskEditFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Milestone: { // Use the normal task dialog for now. // Maybe milestone should have it's own dialog, but we need to be able to // enter a duration in case we accidentally set a tasks duration to zero // and hence, create a milestone Task *task = static_cast( node ); TaskDialog *dia = new TaskDialog( getProject(), *task, getProject().accounts(), this ); connect(dia, &QDialog::finished, this, &View::slotTaskEditFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Summarytask: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); SummaryTaskDialog *dia = new SummaryTaskDialog( *task, this ); connect(dia, &QDialog::finished, this, &View::slotSummaryTaskEditFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } default: break; // avoid warnings } } void View::slotProjectEditFinished( int result ) { MainProjectDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } void View::slotTaskEditFinished( int result ) { TaskDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } void View::slotSummaryTaskEditFinished( int result ) { SummaryTaskDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } ScheduleManager *View::currentScheduleManager() const { return m_scheduleActions.value( m_scheduleActionGroup->checkedAction() ); } long View::activeScheduleId() const { ScheduleManager *s = m_scheduleActions.value( m_scheduleActionGroup->checkedAction() ); return s == nullptr || s->expected() == nullptr ? -1 : s->expected()->id(); } void View::setActiveSchedule( long id ) { if ( id != -1 ) { QMap::const_iterator it = m_scheduleActions.constBegin(); for (; it != m_scheduleActions.constEnd(); ++it ) { int mid = it.value()->expected() == nullptr ? -1 : it.value()->expected()->id(); if (mid == id) { it.key()->setChecked( true ); slotViewSchedule( it.key() ); // signal not emitted from group, so trigger it here break; } } } } void View::slotTaskProgress() { //debugPlan; Node * node = currentNode(); if ( !node ) return ; switch ( node->type() ) { case Node::Type_Project: { break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); TaskProgressDialog *dia = new TaskProgressDialog( *task, currentScheduleManager(), getProject().standardWorktime(), this ); connect(dia, &QDialog::finished, this, &View::slotTaskProgressFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Milestone: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); MilestoneProgressDialog *dia = new MilestoneProgressDialog( *task, this ); connect(dia, &QDialog::finished, this, &View::slotMilestoneProgressFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Summarytask: { // TODO break; } default: break; // avoid warnings } } void View::slotTaskProgressFinished( int result ) { TaskProgressDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotMilestoneProgressFinished( int result ) { MilestoneProgressDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotTaskDescription() { //debugPlan; Node * node = currentNode(); if ( !node ) return ; switch ( node->type() ) { case Node::Type_Subproject: //TODO break; case Node::Type_Project: case Node::Type_Task: case Node::Type_Milestone: case Node::Type_Summarytask: { TaskDescriptionDialog *dia = new TaskDescriptionDialog( *node, this ); connect(dia, &QDialog::finished, this, &View::slotTaskDescriptionFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } default: break; // avoid warnings } } void View::slotTaskDescriptionFinished( int result ) { TaskDescriptionDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotDocuments() { //debugPlan; Node * node = currentNode(); if ( !node ) { return ; } switch ( node->type() ) { case Node::Type_Subproject: //TODO break; case Node::Type_Project: case Node::Type_Summarytask: case Node::Type_Task: case Node::Type_Milestone: { DocumentsDialog *dia = new DocumentsDialog(*node, this); connect(dia, &QDialog::finished, this, &View::slotDocumentsFinished); dia->show(); dia->raise(); dia->activateWindow(); break; } default: break; // avoid warnings } } void View::slotDocumentsFinished( int result ) { DocumentsDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart()->addCommand( m ); } } dia->deleteLater(); } void View::slotDeleteTaskList( QList lst ) { //debugPlan; foreach ( Node *n, lst ) { if ( n->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "A task that has been scheduled will be deleted. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } break; } } if ( lst.count() == 1 ) { getPart()->addCommand( new NodeDeleteCmd( lst.takeFirst(), kundo2_i18n( "Delete task" ) ) ); return; } int num = 0; MacroCommand *cmd = new MacroCommand( kundo2_i18np( "Delete task", "Delete tasks", lst.count() ) ); while ( !lst.isEmpty() ) { Node *node = lst.takeFirst(); if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); continue; } bool del = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { del = false; // node is going to be deleted when we delete n break; } } if ( del ) { //debugPlan<name(); cmd->addCommand( new NodeDeleteCmd( node, kundo2_i18n( "Delete task" ) ) ); num++; } } if ( num > 0 ) { getPart()->addCommand( cmd ); } else { delete cmd; } } void View::slotDeleteTask( Node *node ) { //debugPlan; if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); return ; } if ( node->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "This task has been scheduled. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } } NodeDeleteCmd *cmd = new NodeDeleteCmd( node, kundo2_i18n( "Delete task" ) ); getPart() ->addCommand( cmd ); } void View::slotDeleteCurrentTask() { //debugPlan; return slotDeleteTask( currentNode() ); } void View::slotIndentTask() { //debugPlan; Node * node = currentNode(); if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); return ; } if ( getProject().canIndentTask( node ) ) { NodeIndentCmd * cmd = new NodeIndentCmd( *node, kundo2_i18n( "Indent task" ) ); getPart() ->addCommand( cmd ); } } void View::slotUnindentTask() { //debugPlan; Node * node = currentNode(); if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); return ; } if ( getProject().canUnindentTask( node ) ) { NodeUnindentCmd * cmd = new NodeUnindentCmd( *node, kundo2_i18n( "Unindent task" ) ); getPart() ->addCommand( cmd ); } } void View::slotMoveTaskUp() { //debugPlan; Node * task = currentNode(); if ( 0 == task ) { // is always != 0. At least we would get the Project, but you never know who might change that // so better be careful errorPlan << "No current task" << endl; return ; } if ( Node::Type_Project == task->type() ) { debugPlan <<"The root node cannot be moved up"; return ; } if ( getProject().canMoveTaskUp( task ) ) { NodeMoveUpCmd * cmd = new NodeMoveUpCmd( *task, kundo2_i18n( "Move task up" ) ); getPart() ->addCommand( cmd ); } } void View::slotMoveTaskDown() { //debugPlan; Node * task = currentNode(); if ( 0 == task ) { // is always != 0. At least we would get the Project, but you never know who might change that // so better be careful return ; } if ( Node::Type_Project == task->type() ) { debugPlan <<"The root node cannot be moved down"; return ; } if ( getProject().canMoveTaskDown( task ) ) { NodeMoveDownCmd * cmd = new NodeMoveDownCmd( *task, kundo2_i18n( "Move task down" ) ); getPart() ->addCommand( cmd ); } } void View::openRelationDialog( Node *par, Node *child ) { //debugPlan; Relation * rel = new Relation( par, child ); AddRelationDialog *dia = new AddRelationDialog( getProject(), rel, this ); connect(dia, &QDialog::finished, this, &View::slotAddRelationFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddRelationFinished( int result ) { AddRelationDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotAddRelation( Node *par, Node *child, int linkType ) { //debugPlan; if ( linkType == Relation::FinishStart || linkType == Relation::StartStart || linkType == Relation::FinishFinish ) { Relation * rel = new Relation( par, child, static_cast( linkType ) ); getPart() ->addCommand( new AddRelationCmd( getProject(), rel, kundo2_i18n( "Add task dependency" ) ) ); } else { openRelationDialog( par, child ); } } void View::slotEditRelation( Relation *rel ) { //debugPlan; ModifyRelationDialog *dia = new ModifyRelationDialog( getProject(), rel, this ); connect(dia, &QDialog::finished, this, &View::slotModifyRelationFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotModifyRelationFinished( int result ) { ModifyRelationDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return ; } if ( result == QDialog::Accepted) { KUndo2Command *cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } void View::slotModifyRelation( Relation *rel, int linkType ) { //debugPlan; if ( linkType == Relation::FinishStart || linkType == Relation::StartStart || linkType == Relation::FinishFinish ) { getPart() ->addCommand( new ModifyRelationTypeCmd( rel, static_cast( linkType ) ) ); } else { slotEditRelation( rel ); } } void View::slotModifyCurrentRelation() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return; } Relation *rel = v->currentRelation(); if ( rel ) { slotEditRelation( rel ); } } void View::slotDeleteRelation() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return; } Relation *rel = v->currentRelation(); if ( rel ) { getPart()->addCommand( new DeleteRelationCmd( getProject(), rel, kundo2_i18n( "Delete task dependency" ) ) ); } } void View::slotEditCurrentResource() { //debugPlan; slotEditResource( currentResource() ); } void View::slotEditResource( Resource *resource ) { if ( resource == 0 ) { return ; } ResourceDialog *dia = new ResourceDialog( getProject(), resource, this ); connect(dia, &QDialog::finished, this, &View::slotEditResourceFinished); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotEditResourceFinished( int result ) { //debugPlan; ResourceDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return ; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) getPart() ->addCommand( cmd ); } dia->deleteLater(); } void View::slotDeleteResource( Resource *resource ) { getPart()->addCommand( new RemoveResourceCmd( resource->parentGroup(), resource, kundo2_i18n( "Delete resource" ) ) ); } void View::slotDeleteResourceGroup( ResourceGroup *group ) { getPart()->addCommand( new RemoveResourceGroupCmd( group->project(), group, kundo2_i18n( "Delete resourcegroup" ) ) ); } void View::slotDeleteResourceObjects( QObjectList lst ) { //debugPlan; foreach ( QObject *o, lst ) { Resource *r = qobject_cast( o ); if ( r && r->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "A resource that has been scheduled will be deleted. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } break; } ResourceGroup *g = qobject_cast( o ); if ( g && g->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "A resource that has been scheduled will be deleted. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } break; } } if ( lst.count() == 1 ) { Resource *r = qobject_cast( lst.first() ); if ( r ) { slotDeleteResource( r ); } else { ResourceGroup *g = qobject_cast( lst.first() ); if ( g ) { slotDeleteResourceGroup( g ); } } return; } // int num = 0; MacroCommand *cmd = 0, *rc = 0, *gc = 0; foreach ( QObject *o, lst ) { Resource *r = qobject_cast( o ); if ( r ) { if ( rc == 0 ) rc = new MacroCommand( KUndo2MagicString() ); rc->addCommand( new RemoveResourceCmd( r->parentGroup(), r ) ); continue; } ResourceGroup *g = qobject_cast( o ); if ( g ) { if ( gc == 0 ) gc = new MacroCommand( KUndo2MagicString() ); gc->addCommand( new RemoveResourceGroupCmd( g->project(), g ) ); } } if ( rc || gc ) { KUndo2MagicString s; if ( rc && gc ) { s = kundo2_i18n( "Delete resourcegroups and resources" ); } else if ( rc ) { s = kundo2_i18np( "Delete resource", "Delete resources", lst.count() ); } else { s = kundo2_i18np( "Delete resourcegroup", "Delete resourcegroups", lst.count() ); } cmd = new MacroCommand( s ); } if ( rc ) cmd->addCommand( rc ); if ( gc ) cmd->addCommand( gc ); if ( cmd ) getPart()->addCommand( cmd ); } void View::updateReadWrite( bool readwrite ) { m_readWrite = readwrite; m_viewlist->setReadWrite( readwrite ); } MainDocument *View::getPart() const { return ( MainDocument * ) koDocument(); } KoPart *View::getKoPart() const { return m_partpart; } void View::slotConnectNode() { //debugPlan; /* NodeItem *curr = ganttview->currentItem(); if (curr) { debugPlan<<"node="<getNode().name(); }*/ } QMenu * View::popupMenu( const QString& name ) { //debugPlan; if ( factory() ) { return ( ( QMenu* ) factory() ->container( name, this ) ); } debugPlan<<"No factory"; return 0L; } void View::slotUpdate() { //debugPlan<<"calculate="<currentWidget() ); } void View::slotGuiActivated( ViewBase *view, bool activate ) { //FIXME: Avoid unplug if possible, it flashes the gui // always unplug, in case they already are plugged foreach( const QString &name, view->actionListNames() ) { unplugActionList( name ); } if ( activate ) { foreach( const QString &name, view->actionListNames() ) { plugActionList( name, view->actionList( name ) ); } foreach ( DockWidget *ds, view->dockers() ) { m_dockers.append( ds ); ds->activate( mainWindow() ); } if (!m_dockers.isEmpty()) {debugPlan<<"Added dockers:"<deactivate( mainWindow() ); } } } void View::guiActivateEvent( bool activated ) { if ( activated ) { // plug my own actionlists, they may be gone slotPlugScheduleActions(); } // propagate to sub-view ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v ) { v->setGuiActive( activated ); } } void View::slotViewListItemRemoved( ViewListItem *item ) { getPart()->removeViewListItem( this, item ); } void View::removeViewListItem( const ViewListItem *item ) { if ( item == 0 ) { return; } ViewListItem *itm = m_viewlist->findItem( item->tag() ); if ( itm == 0 ) { return; } m_viewlist->removeViewListItem( itm ); return; } void View::slotViewListItemInserted( ViewListItem *item, ViewListItem *parent, int index ) { getPart()->insertViewListItem( this, item, parent, index ); } void View::addViewListItem( const ViewListItem *item, const ViewListItem *parent, int index ) { if ( item == 0 ) { return; } if ( parent == 0 ) { if ( item->type() != ViewListItem::ItemType_Category ) { return; } m_viewlist->blockSignals( true ); ViewListItem *cat = m_viewlist->addCategory( item->tag(), item->text( 0 ) ); cat->setToolTip( 0, item->toolTip( 0 ) ); m_viewlist->blockSignals( false ); return; } ViewListItem *cat = m_viewlist->findCategory( parent->tag() ); if ( cat == 0 ) { return; } m_viewlist->blockSignals( true ); createView( cat, item->viewType(), item->tag(), item->text( 0 ), item->toolTip( 0 ), index ); m_viewlist->blockSignals( false ); } void View::createReportView(const QDomDocument &doc) { #ifdef PLAN_USE_KREPORT QPointer vd = new ViewListReportsDialog( this, *m_viewlist, doc, this ); vd->exec(); // FIXME make non-crash delete vd; #else Q_UNUSED(doc) #endif } void View::slotOpenReportFile() { #ifdef PLAN_USE_KREPORT QFileDialog *dlg = new QFileDialog(this); connect(dlg, &QDialog::finished, &View::slotOpenReportFileFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); #endif } void View::slotOpenReportFileFinished( int result ) { #ifdef PLAN_USE_KREPORT QFileDialog *fdlg = qobject_cast( sender() ); if ( fdlg == 0 || result != QDialog::Accepted ) { return; } QString fn = fdlg->selectedFiles().value(0); if ( fn.isEmpty() ) { return; } QFile file( fn ); if ( ! file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { KMessageBox::sorry( this, xi18nc( "@info", "Cannot open file:
%1", fn ) ); return; } QDomDocument doc; doc.setContent( &file ); createReportView(doc); #else Q_UNUSED(result) #endif } void View::slotReportDesignFinished( int /*result */) { #ifdef PLAN_USE_KREPORT if ( sender() ) { sender()->deleteLater(); } #endif } void View::slotCreateView() { ViewListDialog *dlg = new ViewListDialog( this, *m_viewlist, this ); connect(dlg, &QDialog::finished, this, &View::slotCreateViewFinished); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotCreateViewFinished( int ) { if ( sender() ) { sender()->deleteLater(); } } void View::slotViewActivated( ViewListItem *item, ViewListItem *prev ) { QApplication::setOverrideCursor( Qt::WaitCursor ); if ( prev && prev->type() == ViewListItem::ItemType_Category && m_viewlist->previousViewItem() ) { // A view is shown anyway... ViewBase *v = qobject_cast( m_viewlist->previousViewItem()->view() ); if ( v ) { v->setGuiActive( false ); } } else if ( prev && prev->type() == ViewListItem::ItemType_SubView ) { ViewBase *v = qobject_cast( prev->view() ); if ( v ) { v->setGuiActive( false ); } } if ( item && item->type() == ViewListItem::ItemType_SubView ) { //debugPlan<<"Activate:"<setCurrentWidget( item->view() ); // Add sub-view specific gui ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v ) { v->setGuiActive( true ); } } QApplication::restoreOverrideCursor(); } QWidget *View::canvas() const { return m_tab->currentWidget();//KoView::canvas(); } KoPageLayout View::pageLayout() const { return currentView()->pageLayout(); } void View::setPageLayout(const KoPageLayout &pageLayout) { currentView()->setPageLayout(pageLayout); } QPrintDialog *View::createPrintDialog( KoPrintJob *printJob, QWidget *parent ) { debugPlan<( printJob ); if ( ! job ) { return 0; } QPrintDialog *dia = KoView::createPrintDialog( job, parent ); PrintingDialog *j = dynamic_cast( job ); if ( j ) { new PrintingControlPrivate( j, dia ); } return dia; } void View::slotCurrentChanged( int view ) { m_visitedViews << view; ViewListItem *item = m_viewlist->findItem( qobject_cast( m_tab->currentWidget() ) ); m_viewlist->setCurrentItem( item ); } void View::slotSelectDefaultView() { m_tab->setCurrentIndex(qMin(m_defaultView, m_tab->count()-1)); } void View::updateView( QWidget * ) { QApplication::setOverrideCursor( Qt::WaitCursor ); //setScheduleActionsEnabled(); QWidget *widget2; widget2 = m_viewlist->findView( "ResourceAssignmentView" ); if ( widget2 && m_updateResourceAssignmentView ) static_cast( widget2 ) ->draw( getProject() ); m_updateResourceAssignmentView = false; QApplication::restoreOverrideCursor(); } void View::slotRenameNode( Node *node, const QString& name ) { //debugPlan<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; } NodeModifyNameCmd * cmd = new NodeModifyNameCmd( *node, name, s ); getPart() ->addCommand( cmd ); } } void View::slotPopupMenuRequested( const QString& menuname, const QPoint & pos ) { QMenu * menu = this->popupMenu( menuname ); if ( menu ) { //debugPlan<actions().count(); ViewBase *v = qobject_cast( m_tab->currentWidget() ); //debugPlan< lst; if ( v ) { lst = v->contextActionList(); debugPlan<addSeparator(); foreach ( QAction *a, lst ) { menu->addAction( a ); } } } menu->exec( pos ); foreach ( QAction *a, lst ) { menu->removeAction( a ); } } } void View::slotPopupMenu( const QString& menuname, const QPoint &pos, ViewListItem *item ) { //debugPlan<context(); if ( ctx == 0 || ! ctx->isLoaded() ) { return false; } KoXmlElement n = ctx->context(); QString cv = n.attribute( "current-view" ); if ( ! cv.isEmpty() ) { m_viewlist->setSelected( m_viewlist->findItem( cv ) ); } else debugPlan<<"No current view"; long id = n.attribute( "current-schedule", "-1" ).toLong(); if ( id != -1 ) { setActiveSchedule( id ); } else debugPlan<<"No current schedule"; return true; } void View::saveContext( QDomElement &me ) const { //debugPlan; long id = activeScheduleId(); if ( id != -1 ) { me.setAttribute( "current-schedule", QString::number((qlonglong)id) ); } ViewListItem *item = m_viewlist->findItem( qobject_cast( m_tab->currentWidget() ) ); if ( item ) { me.setAttribute("current-view", item->tag() ); } m_viewlist->save( me ); } -bool View::loadWorkPackage( Project &project, const QUrl &url ) +void View::loadWorkPackage(Project *project, const QList &urls) { - return getPart()->loadWorkPackage( project, url ); + bool loaded = false; + for (const QUrl &url : urls) { + loaded |= getPart()->loadWorkPackage(*project, url); + } + if (loaded) { + emit slotWorkPackageLoaded(); + } } void View::setLabel( ScheduleManager *sm ) { //debugPlan; Schedule *s = sm == 0 ? 0 : sm->expected(); if ( s && !s->isDeleted() && s->isScheduled() ) { m_estlabel->setText( sm->name() ); return; } m_estlabel->setText( xi18nc( "@info:status", "Not scheduled" ) ); } void View::slotWorkPackageLoaded() { debugPlan<workPackages(); + addStatusBarItem(m_workPackageButton, 0, true); + emit workPackagesAvailable(true); +} + +void View::openWorkPackageMergeDialog() +{ + WorkPackageMergeDialog *dlg = new WorkPackageMergeDialog(&getProject(), getPart()->workPackages(), this); + connect(dlg, &QDialog::finished, this, &View::workPackageMergeDialogFinished); + connect(dlg, SIGNAL(terminateWorkPackage(const KPlato::Package*)), getPart(), SLOT(terminateWorkPackage(const KPlato::Package*))); + connect(dlg, &WorkPackageMergeDialog::executeCommand, koDocument(), &KoDocument::addCommand); + dlg->open(); + removeStatusBarItem(m_workPackageButton); + emit workPackagesAvailable(false); +} + +void View::workPackageMergeDialogFinished( int result ) +{ + debugPlanWp<<"result:"<( sender() ); + Q_ASSERT(dlg); + if (!getPart()->workPackages().isEmpty()) { + slotWorkPackageLoaded(); + } + if (dlg) { + dlg->deleteLater(); + } } + void View::slotMailWorkpackage( Node *node, Resource *resource ) { debugPlan; QTemporaryFile tmpfile(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); tmpfile.setAutoRemove( false ); if ( ! tmpfile.open() ) { debugPlan<<"Failed to open file"; KMessageBox::error(0, i18n("Failed to open temporary file" ) ); return; } QUrl url = QUrl::fromLocalFile( tmpfile.fileName() ); if ( ! getPart()->saveWorkPackageUrl( url, node, activeScheduleId(), resource ) ) { debugPlan<<"Failed to save to file"; KMessageBox::error(0, xi18nc( "@info", "Failed to save to temporary file:
%1", url.url() ) ); return; } QStringList attachURLs; attachURLs << url.url(); QString to = resource == 0 ? node->leader() : ( resource->name() + " <" + resource->email() + '>' ); QString cc; QString bcc; QString subject = i18n( "Work Package: %1", node->name() ); QString body = i18nc( "1=project name, 2=task name", "%1\n%2", getProject().name(), node->name() ); QString messageFile; KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); } -void View::slotMailWorkpackages( const QList &nodes, Resource *resource ) +void View::slotPublishWorkpackages( const QList &nodes, Resource *resource, bool mailTo ) { - debugPlan; + debugPlanWp<leader() yet"; return; } - QString to = resource->name() + " <" + resource->email() + '>'; - QString subject = i18n( "Work Package for project: %1", getProject().name() ); + bool mail = mailTo; QString body; QStringList attachURLs; + QString path; + if (getProject().workPackageInfo().publishUrl.isValid()) { + path = getProject().workPackageInfo().publishUrl.path(); + debugPlanWp<<"publish:"<saveWorkPackageUrl( url, n, activeScheduleId(), resource ) ) { debugPlan<<"Failed to save to file"; KMessageBox::error(0, xi18nc( "@info", "Failed to save to temporary file:
%1", url.url() ) ); return; } attachURLs << url.url(); body += n->name() + '\n'; } + if (mail) { + debugPlanWp<name() + " <" + resource->email() + '>'; + QString subject = i18n( "Work Package for project: %1", getProject().name() ); + QString cc; + QString bcc; + QString messageFile; - QString cc; - QString bcc; - QString messageFile; - - KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); + KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); + } } void View::slotCurrencyConfig() { LocaleConfigMoneyDialog *dlg = new LocaleConfigMoneyDialog( getProject().locale(), this ); connect(dlg, &QDialog::finished, this, &View::slotCurrencyConfigFinished); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotCurrencyConfigFinished( int result ) { LocaleConfigMoneyDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { KUndo2Command *c = dlg->buildCommand( getProject() ); if ( c ) { getPart()->addCommand( c ); } } dlg->deleteLater(); } void View::saveTaskModule( const QUrl &url, Project *project ) { // NOTE: workaround: KoResourcePaths::saveLocation( "calligraplan_taskmodules" ); does not work const QString dir = KoResourcePaths::saveLocation( "appdata", "taskmodules/" ); debugPlan<<"dir="<setDocument( doc ); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave( 0 ); //disable doc->insertProject( *project, 0, 0 ); // FIXME: destroys project, find better way doc->getProject().setName( project->name() ); doc->getProject().setLeader( project->leader() ); doc->getProject().setDescription( project->description() ); doc->saveNativeFormat( dir + url.fileName() ); part->deleteLater(); // also deletes document debugPlan<" "" "" "%1" "" "" "predefined" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "") .arg( i18n( "Report" ), i18nc( "Project manager", "Manager:" ), i18n( "Project:" ), i18n( "Task Status Report" ), i18nc( "As in: Page 1 of 2", "of" ), i18n( "Page" ), i18nc( "Task name", "Name" ), i18nc( "Task completion", "Completion (%)" ) ); #endif return s; } } //KPlato namespace diff --git a/src/kptview.h b/src/kptview.h index 5ca2d29a..6e4e2ca8 100644 --- a/src/kptview.h +++ b/src/kptview.h @@ -1,457 +1,462 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2010 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTVIEW_H #define KPTVIEW_H #include "plan_export.h" #include #include "kptcontext.h" #include "kptviewbase.h" #include #include #include #include #include class QMenu; class QPrintDialog; class QStackedWidget; class QSplitter; class QUrl; +class QToolButton; class KUndo2Command; class QAction; class KToggleAction; class QLabel; class KConfigSkeleton; class KConfigSkeletonItem; class KoView; namespace KPlato { class View; class ViewBase; class ViewListItem; class ViewListWidget; struct ViewInfo; class AccountsView; class ResourceCoverageView; class GanttView; class PertEditor; class AccountsEditor; class TaskEditor; class CalendarEditor; class ScheduleEditor; class ScheduleManager; class CalculateScheduleCmd; class ResourceAssignmentView; class TaskStatusView; class Calendar; class MainDocument; class Part; class Node; class Project; class Task; class MainSchedule; class Schedule; class Resource; class ResourceGroup; class Relation; class Context; class ViewAdaptor; class HtmlView; class ReportView; class ReportDesignDialog; class DockWidget; class ConfigDialog : public KConfigDialog { Q_OBJECT public: ConfigDialog( QWidget *parent, const QString &name, KConfigSkeleton *config ); protected Q_SLOTS: /// Return true if any widget has changed virtual bool hasChanged(); /** * Update the settings from the dialog. * Virtual function for custom additions. * * Example use: User clicks Ok or Apply button in a configure dialog. */ virtual void updateSettings(); /** * Update the dialog based on the settings. * Virtual function for custom additions. * * Example use: Initialisation of dialog. * Example use: User clicks Reset button in a configure dialog. */ virtual void updateWidgets(); /** * Update the dialog based on the default settings. * Virtual function for custom additions. * * Example use: User clicks Defaults button in a configure dialog. */ virtual void updateWidgetsDefault(); /** * Returns whether the current state of the dialog is * the same as the default configuration. */ virtual bool isDefault(); private: KConfigSkeleton *m_config; QMap m_signalsmap; QMap m_itemmap; QMap m_propertymap; }; //------------- class PLAN_EXPORT View : public KoView { Q_OBJECT public: explicit View(KoPart *part, MainDocument *doc, QWidget *parent = 0); ~View(); MainDocument *getPart() const; KoPart *getKoPart() const; Project& getProject() const; QMenu *popupMenu( const QString& name ); virtual bool loadContext(); virtual void saveContext( QDomElement &context ) const; - /// Load the workpackage from @p url into @p project. Return true if successful, else false. - bool loadWorkPackage( Project &project, const QUrl &url ); - QWidget *canvas() const; KoPageLayout pageLayout() const; void setPageLayout(const KoPageLayout &pageLayout); ScheduleManager *currentScheduleManager() const; long activeScheduleId() const; void setActiveSchedule( long id ); /// Returns the default view information like standard name and tooltip for view type @p type ViewInfo defaultViewInfo( const QString &type ) const; /// Returns the default category information like standard name and tooltip for category type @p type ViewInfo defaultCategoryInfo( const QString &type ) const; ViewBase *createTaskEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createResourceEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createAccountsEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createCalendarEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createScheduleHandler( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ScheduleEditor *createScheduleEditor( QWidget *parent ); ViewBase *createScheduleEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createDependencyEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createPertEditor( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createProjectStatusView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createPerformanceStatusView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createTaskStatusView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createTaskView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createTaskWorkPackageView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createGanttView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createMilestoneGanttView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createResourceAppointmentsView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createResourceAppointmentsGanttView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createAccountsView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createResourceAssignmentView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createChartView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createReportView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); ViewBase *createReportsGeneratorView( ViewListItem *cat, const QString &tag, const QString &name = QString(), const QString &tip = QString(), int index = -1 ); KoPrintJob * createPrintJob(); QPrintDialog* createPrintDialog(KoPrintJob*, QWidget*); Q_SIGNALS: void currentScheduleManagerChanged(KPlato::ScheduleManager *sm); void taskModulesChanged(const QStringList &modules); + void workPackagesAvailable(bool); public Q_SLOTS: void slotUpdate(); void slotCreateNewProject(); void slotEditCurrentResource(); void slotEditResource(KPlato::Resource *resource); void slotEditCut(); void slotEditCopy(); void slotEditPaste(); void slotRefreshView(); void slotViewSelector( bool show ); void slotAddTask(); void slotAddSubTask(); void slotAddMilestone(); void slotAddSubMilestone(); void slotProjectEdit(); void slotDefineWBS(); void slotCurrencyConfig(); void slotCreateView(); void slotConfigure(); void slotIntroduction(); void openRelationDialog(KPlato::Node *par, KPlato::Node *child); void slotEditRelation(KPlato::Relation *rel); void slotAddRelation(KPlato::Node *par, KPlato::Node *child, int linkType); void slotModifyRelation(KPlato::Relation *rel, int linkType); void slotModifyCurrentRelation(); void slotDeleteRelation(); void slotRenameNode(KPlato::Node *node, const QString& name); void slotPopupMenuRequested( const QString& menuname, const QPoint &pos ); void slotPopupMenu( const QString& menuname, const QPoint &pos, KPlato::ViewListItem *item ); void addViewListItem(const KPlato::ViewListItem *item, const KPlato::ViewListItem *parent, int index); void removeViewListItem(const KPlato::ViewListItem *item); void slotOpenReportFile(); void slotSelectionChanged(KPlato::ScheduleManager *sm); void slotUpdateViewInfo(KPlato::ViewListItem *itm); + /// Load the workpackages from @p urls into @p project. + void loadWorkPackage(KPlato::Project *project, const QList &urls ); + protected Q_SLOTS: void slotGuiActivated(KPlato::ViewBase *view, bool); void slotViewActivated(KPlato::ViewListItem*, KPlato::ViewListItem*); void slotPlugScheduleActions(); void slotViewSchedule( QAction *act ); void slotScheduleAdded(const KPlato::ScheduleManager*); void slotScheduleRemoved( const KPlato::ScheduleManager*); void slotScheduleSwapped(KPlato::ScheduleManager *from, KPlato::ScheduleManager *to); void slotScheduleCalculated(KPlato::Project *project, KPlato::ScheduleManager *manager); void slotAddScheduleManager(KPlato::Project *project); void slotDeleteScheduleManager(KPlato::Project *project, KPlato::ScheduleManager *sm); void slotMoveScheduleManager(KPlato::ScheduleManager *sm, KPlato::ScheduleManager *parent, int index); void slotCalculateSchedule(KPlato::Project*, KPlato::ScheduleManager*); void slotBaselineSchedule(KPlato::Project *project, KPlato::ScheduleManager *sm); void slotProjectWorktime(); void slotOpenCurrentNode(); void slotOpenNode(KPlato::Node *node); void slotTaskProgress(); void slotTaskDescription(); void slotDocuments(); void slotDeleteTaskList(QList lst); void slotDeleteTask(KPlato::Node *node); void slotDeleteCurrentTask(); void slotIndentTask(); void slotUnindentTask(); void slotMoveTaskUp(); void slotMoveTaskDown(); void slotConnectNode(); void slotDeleteResource(KPlato::Resource *resource); void slotDeleteResourceGroup(KPlato::ResourceGroup *group); void slotDeleteResourceObjects( QObjectList ); void slotCurrentChanged( int ); void slotSelectDefaultView(); void slotInsertResourcesFile(const QString&, const QUrl &projects); void slotInsertFile(); void slotLoadSharedProjects(); void slotWorkPackageLoaded(); void slotMailWorkpackage(KPlato::Node *node, KPlato::Resource *resource = 0); - void slotMailWorkpackages(const QList &nodes, KPlato::Resource *resource = 0 ); + void slotPublishWorkpackages(const QList &nodes, KPlato::Resource *resource, bool mailTo); void slotOpenUrlRequest(KPlato::HtmlView *v, const QUrl &url); void createReportView(const QDomDocument &doc); void saveTaskModule(const QUrl &url, KPlato::Project *project); void removeTaskModule( const QUrl &url ); void taskModuleFileChanged(const QString &path); protected: virtual void guiActivateEvent( bool activated ); virtual void updateReadWrite( bool readwrite ); QList sortedActionList(); QAction *addScheduleAction( ScheduleManager *sch ); void setLabel( ScheduleManager *sm = 0 ); Task *currentTask() const; Node *currentNode() const; Resource *currentResource(); ResourceGroup *currentResourceGroup(); Calendar *currentCalendar(); void updateView( QWidget *widget ); ViewBase *currentView() const; ViewBase *createIntroductionView(); private Q_SLOTS: void slotActionDestroyed( QObject *o ); void slotViewListItemRemoved(KPlato::ViewListItem *item); void slotViewListItemInserted(KPlato::ViewListItem *item, KPlato::ViewListItem *parent, int index); void slotProjectEditFinished( int result ); void slotTaskEditFinished( int result ); void slotSummaryTaskEditFinished( int result ); void slotEditResourceFinished( int result ); void slotProjectWorktimeFinished( int result ); void slotDefineWBSFinished( int result ); void slotCurrencyConfigFinished( int result ); void slotInsertFileFinished( int result ); void slotAddSubTaskFinished( int result ); void slotAddTaskFinished( int result ); void slotAddSubMilestoneFinished( int result ); void slotAddMilestoneFinished( int result ); void slotTaskProgressFinished( int result ); void slotMilestoneProgressFinished( int result ); void slotTaskDescriptionFinished( int result ); void slotDocumentsFinished(int result); void slotAddRelationFinished( int result ); void slotModifyRelationFinished( int result ); void slotReportDesignFinished( int result ); void slotOpenReportFileFinished( int result ); void slotCreateViewFinished( int result ); void slotLoadSharedProjectsFinished( int result ); - + void openWorkPackageMergeDialog(); + void workPackageMergeDialogFinished( int result ); void slotRemoveCommands(); void initiateViews(); void slotViewScheduleManager(KPlato::ScheduleManager *sm); private: void createViews(); ViewBase *createView( ViewListItem *cat, const QString &type, const QString &tag, const QString &name, const QString &tip, int index = -1 ); QString standardTaskStatusReport() const; private: QSplitter *m_sp; QStackedWidget *m_tab; ViewListWidget *m_viewlist; ViewListItem *m_viewlistItem; // requested popupmenu item //QDockWidget *m_toolbox; int m_viewGrp; int m_defaultFontSize; int m_currentEstimateType; bool m_updateAccountsview; bool m_updateResourceAssignmentView; bool m_updatePertEditor; QLabel *m_estlabel; + QToolButton *m_workPackageButton; ViewAdaptor* m_dbus; QActionGroup *m_scheduleActionGroup; QMap m_scheduleActions; QMultiMap m_calculationcommands; QList m_undocommands; bool m_readWrite; int m_defaultView; QList m_visitedViews; QList m_dockers; // ------ File QAction *actionCreateTemplate; QAction *actionCreateNewProject; // ------ Edit QAction *actionCut; QAction *actionCopy; QAction *actionPaste; // ------ View KToggleAction *actionViewSelector; // ------ Insert // ------ Project QAction *actionEditMainProject; // ------ Tools QAction *actionEditStandardWorktime; QAction *actionDefineWBS; QAction *actionInsertFile; QAction *actionCurrencyConfig; QAction *actionLoadSharedProjects; QAction *actionOpenReportFile; // ------ Settings QAction *actionConfigure; // ------ Help QAction *actionIntroduction; // ------ Popup QAction *actionOpenNode; QAction *actionTaskProgress; QAction *actionTaskDescription; QAction *actionDocuments; QAction *actionDeleteTask; QAction *actionIndentTask; QAction *actionUnindentTask; QAction *actionMoveTaskUp; QAction *actionMoveTaskDown; QAction *actionEditResource; QAction *actionEditRelation; QAction *actionDeleteRelation; //Test QAction *actNoInformation; QMap m_reportActionMap; KoPart *m_partpart; KDirWatch m_dirwatch; }; } //Kplato namespace #endif diff --git a/src/kptworkpackageconfigpanel.ui b/src/kptworkpackageconfigpanel.ui index 762bcf2a..2febf99e 100644 --- a/src/kptworkpackageconfigpanel.ui +++ b/src/kptworkpackageconfigpanel.ui @@ -1,99 +1,146 @@ KPlato::WorkPackageConfigPanel 0 0 490 - 160 + 405 - - - 0 - - - - - Check for work packages + + + + + Retrieve + + + + + Check for work packages + + + + + + + + + Retrieve: + + + + + + + + + - - - - Retrieve URL: + + + + Archive + + + + + Delete file after it has been retrieved + + + + + + + Archive file after it has been retrieved + + + + + + + + + Archive: + + + + + + + + + - - - - - - - Delete file after it has been retrieved - - - - - - - Save file after it has been retrieved + + + + Publish + + + + + + + Publish: + + + + + + + + + - - - - Save URL: - - - - - - - + Qt::Vertical 20 40 KUrlRequester QFrame
kurlrequester.h
+ 1
kcfg_SaveFile toggled(bool) kcfg_SaveUrl setEnabled(bool) 164 72 196 98
diff --git a/src/libs/kernel/kptcommand.cpp b/src/libs/kernel/kptcommand.cpp index ea45677d..18281cd3 100644 --- a/src/libs/kernel/kptcommand.cpp +++ b/src/libs/kernel/kptcommand.cpp @@ -1,3817 +1,3834 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2011 Dag Andersen Copyright (C) 2016 Dag Andersen + Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptcommand.h" #include "kptaccount.h" #include "kptappointment.h" #include "kptproject.h" #include "kpttask.h" #include "kptcalendar.h" #include "kptrelation.h" #include "kptresource.h" #include "kptdocuments.h" #include "kptlocale.h" #include "kptdebug.h" #include const QLoggingCategory &PLANCMDINSPROJECT_LOG() { static const QLoggingCategory category("calligra.plan.command.insertProject"); return category; } #define debugPlanInsertProject qCDebug(PLANCMDINSPROJECT_LOG) #define warnPlanInsertProject qCWarning(PLANCMDINSPROJECT_LOG) #define errorPlanInsertProject qCCritical(PLANCMDINSPROJECT_LOG) namespace KPlato { void NamedCommand::setSchScheduled() { QHash::ConstIterator it; for ( it = m_schedules.constBegin(); it != m_schedules.constEnd(); ++it ) { //debugPlan << it.key() ->name() <<":" << it.value(); it.key() ->setScheduled( it.value() ); } } void NamedCommand::setSchScheduled( bool state ) { QHash::ConstIterator it; for ( it = m_schedules.constBegin(); it != m_schedules.constEnd(); ++it ) { //debugPlan << it.key() ->name() <<":" << state; it.key() ->setScheduled( state ); } } void NamedCommand::addSchScheduled( Schedule *sch ) { //debugPlan << sch->id() <<":" << sch->isScheduled(); m_schedules.insert( sch, sch->isScheduled() ); foreach ( Appointment * a, sch->appointments() ) { if ( a->node() == sch ) { m_schedules.insert( a->resource(), a->resource() ->isScheduled() ); } else if ( a->resource() == sch ) { m_schedules.insert( a->node(), a->node() ->isScheduled() ); } } } //--------- MacroCommand::~MacroCommand() { while ( ! cmds.isEmpty() ) { delete cmds.takeLast(); } } void MacroCommand::addCommand( KUndo2Command *cmd ) { cmds.append( cmd ); } void MacroCommand::execute() { foreach ( KUndo2Command *c, cmds ) { c->redo(); } } void MacroCommand::unexecute() { for (int i = cmds.count() - 1; i >= 0; --i) { cmds.at( i )->undo(); } } //------------------------------------------------- CalendarAddCmd::CalendarAddCmd( Project *project, Calendar *cal, int pos, Calendar *parent, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_cal( cal ), m_pos( pos ), m_parent( parent ), m_mine( true ) { //debugPlan<name(); Q_ASSERT( project != 0 ); } CalendarAddCmd::~CalendarAddCmd() { if ( m_mine ) delete m_cal; } void CalendarAddCmd::execute() { if ( m_project ) { m_project->addCalendar( m_cal, m_parent, m_pos ); m_mine = false; } //debugPlan<name()<<" added to:"<name(); } void CalendarAddCmd::unexecute() { if ( m_project ) { m_project->takeCalendar( m_cal ); m_mine = true; } //debugPlan<name(); } CalendarRemoveCmd::CalendarRemoveCmd( Project *project, Calendar *cal, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_parent( cal->parentCal() ), m_cal( cal ), m_index( -1 ), m_mine( false ), m_cmd( new MacroCommand(KUndo2MagicString()) ) { Q_ASSERT( project != 0 ); m_index = m_parent ? m_parent->indexOf( cal ) : project->indexOf( cal ); foreach ( Resource *r, project->resourceList() ) { if ( r->calendar( true ) == cal ) { m_cmd->addCommand( new ModifyResourceCalendarCmd( r, 0 ) ); } } if ( project->defaultCalendar() == cal ) { m_cmd->addCommand( new ProjectModifyDefaultCalendarCmd( project, 0 ) ); } foreach ( Calendar *c, cal->calendars() ) { m_cmd->addCommand( new CalendarRemoveCmd( project, c ) ); } } CalendarRemoveCmd::~CalendarRemoveCmd() { delete m_cmd; if ( m_mine ) delete m_cal; } void CalendarRemoveCmd::execute() { m_cmd->execute(); m_project->takeCalendar( m_cal ); m_mine = true; } void CalendarRemoveCmd::unexecute() { m_project->addCalendar( m_cal, m_parent, m_index ); m_cmd->unexecute(); m_mine = false; } CalendarMoveCmd::CalendarMoveCmd( Project *project, Calendar *cal, int position, Calendar *parent, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_cal( cal ), m_newpos( position ), m_newparent( parent ), m_oldparent( cal->parentCal() ) { //debugPlan<name(); Q_ASSERT( project != 0 ); m_oldpos = m_oldparent ? m_oldparent->indexOf( cal ) : project->indexOf( cal ); } void CalendarMoveCmd::execute() { m_project->takeCalendar( m_cal ); m_project->addCalendar( m_cal, m_newparent, m_newpos ); } void CalendarMoveCmd::unexecute() { m_project->takeCalendar( m_cal ); m_project->addCalendar( m_cal, m_oldparent, m_oldpos ); } CalendarModifyNameCmd::CalendarModifyNameCmd( Calendar *cal, const QString& newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ) { m_oldvalue = cal->name(); m_newvalue = newvalue; //debugPlan<name(); } void CalendarModifyNameCmd::execute() { m_cal->setName( m_newvalue ); //debugPlan<name(); } void CalendarModifyNameCmd::unexecute() { m_cal->setName( m_oldvalue ); //debugPlan<name(); } CalendarModifyParentCmd::CalendarModifyParentCmd( Project *project, Calendar *cal, Calendar *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_cal( cal ), m_cmd( new MacroCommand( KUndo2MagicString() ) ), m_oldindex( -1 ), m_newindex( -1 ) { m_oldvalue = cal->parentCal(); m_newvalue = newvalue; m_oldindex = m_oldvalue ? m_oldvalue->indexOf( cal ) : m_project->indexOf( cal ); if ( newvalue ) { m_cmd->addCommand( new CalendarModifyTimeZoneCmd( cal, newvalue->timeZone() ) ); } //debugPlan<name(); } CalendarModifyParentCmd::~CalendarModifyParentCmd() { delete m_cmd; } void CalendarModifyParentCmd::execute() { m_project->takeCalendar( m_cal ); m_project->addCalendar( m_cal, m_newvalue, m_newindex ); m_cmd->execute(); } void CalendarModifyParentCmd::unexecute() { m_cmd->unexecute(); m_project->takeCalendar( m_cal ); m_project->addCalendar( m_cal, m_oldvalue, m_oldindex ); } CalendarModifyTimeZoneCmd::CalendarModifyTimeZoneCmd( Calendar *cal, const QTimeZone &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_newvalue( value ), m_cmd( new MacroCommand( KUndo2MagicString() ) ) { m_oldvalue = cal->timeZone(); foreach ( Calendar *c, cal->calendars() ) { m_cmd->addCommand( new CalendarModifyTimeZoneCmd( c, value ) ); } //debugPlan<name(); } CalendarModifyTimeZoneCmd::~CalendarModifyTimeZoneCmd() { delete m_cmd; } void CalendarModifyTimeZoneCmd::execute() { m_cmd->execute(); m_cal->setTimeZone( m_newvalue ); } void CalendarModifyTimeZoneCmd::unexecute() { m_cal->setTimeZone( m_oldvalue ); m_cmd->unexecute(); } #ifdef HAVE_KHOLIDAYS CalendarModifyHolidayRegionCmd::CalendarModifyHolidayRegionCmd( Calendar *cal, const QString &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_newvalue( value ) { m_oldvalue = cal->holidayRegionCode(); } CalendarModifyHolidayRegionCmd::~CalendarModifyHolidayRegionCmd() { } void CalendarModifyHolidayRegionCmd::execute() { m_cal->setHolidayRegion( m_newvalue ); } void CalendarModifyHolidayRegionCmd::unexecute() { m_cal->setHolidayRegion( m_oldvalue ); } #endif CalendarAddDayCmd::CalendarAddDayCmd( Calendar *cal, CalendarDay *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_mine( true ) { m_newvalue = newvalue; //debugPlan<name(); } CalendarAddDayCmd::~CalendarAddDayCmd() { //debugPlan; if ( m_mine ) delete m_newvalue; } void CalendarAddDayCmd::execute() { //debugPlan<name(); m_cal->addDay( m_newvalue ); m_mine = false; } void CalendarAddDayCmd::unexecute() { //debugPlan<name(); m_cal->takeDay( m_newvalue ); m_mine = true; } CalendarRemoveDayCmd::CalendarRemoveDayCmd( Calendar *cal,CalendarDay *day, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_value( day ), m_mine( false ) { //debugPlan<name(); // TODO check if any resources uses this calendar init(); } CalendarRemoveDayCmd::CalendarRemoveDayCmd( Calendar *cal, const QDate &day, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_mine( false ) { m_value = cal->findDay( day ); //debugPlan<name(); // TODO check if any resources uses this calendar init(); } void CalendarRemoveDayCmd::init() { } void CalendarRemoveDayCmd::execute() { //debugPlan<name(); m_cal->takeDay( m_value ); m_mine = true; } void CalendarRemoveDayCmd::unexecute() { //debugPlan<name(); m_cal->addDay( m_value ); m_mine = false; } CalendarModifyDayCmd::CalendarModifyDayCmd( Calendar *cal, CalendarDay *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_mine( true ) { m_newvalue = value; m_oldvalue = cal->findDay( value->date() ); //debugPlan<name()<<" old:("<takeDay( m_oldvalue ); } m_cal->addDay( m_newvalue ); m_mine = false; } void CalendarModifyDayCmd::unexecute() { //debugPlan; m_cal->takeDay( m_newvalue ); if ( m_oldvalue ) { m_cal->addDay( m_oldvalue ); } m_mine = true; } CalendarModifyStateCmd::CalendarModifyStateCmd( Calendar *calendar, CalendarDay *day, CalendarDay::State value, const KUndo2MagicString& name ) : NamedCommand( name ), m_calendar( calendar ), m_day( day ), m_cmd( new MacroCommand( KUndo2MagicString() ) ) { m_newvalue = value; m_oldvalue = (CalendarDay::State)day->state(); if ( value != CalendarDay::Working ) { foreach ( TimeInterval *ti, day->timeIntervals() ) { m_cmd->addCommand( new CalendarRemoveTimeIntervalCmd( calendar, day, ti ) ); } } } CalendarModifyStateCmd::~CalendarModifyStateCmd() { delete m_cmd; } void CalendarModifyStateCmd::execute() { //debugPlan; m_cmd->execute(); m_calendar->setState( m_day, m_newvalue ); } void CalendarModifyStateCmd::unexecute() { //debugPlan; m_calendar->setState( m_day, m_oldvalue ); m_cmd->unexecute(); } CalendarModifyTimeIntervalCmd::CalendarModifyTimeIntervalCmd( Calendar *calendar, TimeInterval &newvalue, TimeInterval *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_calendar( calendar ) { m_value = value; // keep pointer m_oldvalue = *value; // save value m_newvalue = newvalue; } void CalendarModifyTimeIntervalCmd::execute() { //debugPlan; m_calendar->setWorkInterval( m_value, m_newvalue ); } void CalendarModifyTimeIntervalCmd::unexecute() { //debugPlan; m_calendar->setWorkInterval( m_value, m_oldvalue ); } CalendarAddTimeIntervalCmd::CalendarAddTimeIntervalCmd( Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_calendar( calendar ), m_day( day ), m_value( value ), m_mine( true ) { } CalendarAddTimeIntervalCmd::~CalendarAddTimeIntervalCmd() { if ( m_mine ) delete m_value; } void CalendarAddTimeIntervalCmd::execute() { //debugPlan; m_calendar->addWorkInterval( m_day, m_value ); m_mine = false; } void CalendarAddTimeIntervalCmd::unexecute() { //debugPlan; m_calendar->takeWorkInterval( m_day, m_value ); m_mine = true; } CalendarRemoveTimeIntervalCmd::CalendarRemoveTimeIntervalCmd( Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name ) : CalendarAddTimeIntervalCmd( calendar, day, value, name ) { m_mine = false ; } void CalendarRemoveTimeIntervalCmd::execute() { CalendarAddTimeIntervalCmd::unexecute(); } void CalendarRemoveTimeIntervalCmd::unexecute() { CalendarAddTimeIntervalCmd::execute(); } CalendarModifyWeekdayCmd::CalendarModifyWeekdayCmd( Calendar *cal, int weekday, CalendarDay *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_weekday( weekday ), m_cal( cal ), m_value( value ), m_orig( *( cal->weekday( weekday ) ) ) { //debugPlan << cal->name() <<" (" << value <<")"; } CalendarModifyWeekdayCmd::~CalendarModifyWeekdayCmd() { //debugPlan << m_weekday <<":" << m_value; delete m_value; } void CalendarModifyWeekdayCmd::execute() { m_cal->setWeekday( m_weekday, *m_value ); } void CalendarModifyWeekdayCmd::unexecute() { m_cal->setWeekday( m_weekday, m_orig ); } CalendarModifyDateCmd::CalendarModifyDateCmd( Calendar *cal, CalendarDay *day, const QDate &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_cal( cal ), m_day( day ), m_newvalue( value ), m_oldvalue( day->date() ) { //debugPlan << cal->name() <<" (" << value <<")"; } void CalendarModifyDateCmd::execute() { m_cal->setDate( m_day, m_newvalue ); } void CalendarModifyDateCmd::unexecute() { m_cal->setDate( m_day, m_oldvalue ); } ProjectModifyDefaultCalendarCmd::ProjectModifyDefaultCalendarCmd( Project *project, Calendar *cal, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_newvalue( cal ), m_oldvalue( project->defaultCalendar() ) { //debugPlan << cal->name() <<" (" << value <<")"; } void ProjectModifyDefaultCalendarCmd::execute() { m_project->setDefaultCalendar( m_newvalue ); } void ProjectModifyDefaultCalendarCmd::unexecute() { m_project->setDefaultCalendar( m_oldvalue ); } NodeDeleteCmd::NodeDeleteCmd( Node *node, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_index( -1 ), m_relCmd(0) { m_parent = node->parentNode(); m_mine = false; m_project = static_cast( node->projectNode() ); if ( m_project ) { foreach ( Schedule * s, m_project->schedules() ) { if ( s && s->isScheduled() ) { // Only invalidate schedules this node is part of Schedule *ns = node->findSchedule( s->id() ); if ( ns && ! ns->isDeleted() ) { addSchScheduled( s ); } } } } m_cmd = new MacroCommand( KUndo2MagicString() ); QList lst = node->childNodeIterator(); for ( int i = lst.count(); i > 0; --i ) { m_cmd->addCommand( new NodeDeleteCmd( lst[ i - 1 ] ) ); } if ( node->runningAccount() ) { m_cmd->addCommand( new NodeModifyRunningAccountCmd( *node, node->runningAccount(), 0 ) ); } if ( node->startupAccount() ) { m_cmd->addCommand( new NodeModifyRunningAccountCmd( *node, node->startupAccount(), 0 ) ); } if ( node->shutdownAccount() ) { m_cmd->addCommand( new NodeModifyRunningAccountCmd( *node, node->shutdownAccount(), 0 ) ); } } NodeDeleteCmd::~NodeDeleteCmd() { delete m_relCmd; // before node if ( m_mine ) { delete m_node; } delete m_cmd; while ( !m_appointments.isEmpty() ) delete m_appointments.takeFirst(); } void NodeDeleteCmd::execute() { if ( m_parent && m_project ) { m_index = m_parent->findChildNode( m_node ); //debugPlan<name()<<""<dependChildNodes() ) { m_relCmd->addCommand( new DeleteRelationCmd( *m_project, r ) ); } foreach ( Relation * r, m_node->dependParentNodes() ) { m_relCmd->addCommand( new DeleteRelationCmd( *m_project, r ) ); } } m_relCmd->execute(); if ( m_cmd ) { m_cmd->execute(); } m_project->takeTask( m_node ); m_mine = true; setSchScheduled( false ); } } void NodeDeleteCmd::unexecute() { if ( m_parent && m_project ) { //debugPlan<name()<<""<addSubTask( m_node, m_index, m_parent ); if ( m_cmd ) { m_cmd->unexecute(); } m_relCmd->unexecute(); m_mine = false; setSchScheduled(); } } TaskAddCmd::TaskAddCmd( Project *project, Node *node, Node *after, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_node( node ), m_after( after ), m_added( false ) { // set some reasonable defaults for normally calculated values if ( after && after->parentNode() && after->parentNode() != project ) { node->setStartTime( after->parentNode() ->startTime() ); node->setEndTime( node->startTime() + node->duration() ); } else { if ( project->constraint() == Node::MustFinishOn ) { node->setEndTime( project->endTime() ); node->setStartTime( node->endTime() - node->duration() ); } else { node->setStartTime( project->startTime() ); node->setEndTime( node->startTime() + node->duration() ); } } node->setEarlyStart( node->startTime() ); node->setLateFinish( node->endTime() ); node->setWorkStartTime( node->startTime() ); node->setWorkEndTime( node->endTime() ); } TaskAddCmd::~TaskAddCmd() { if ( !m_added ) delete m_node; } void TaskAddCmd::execute() { //debugPlan<name(); m_project->addTask( m_node, m_after ); m_added = true; } void TaskAddCmd::unexecute() { m_project->takeTask( m_node ); m_added = false; } SubtaskAddCmd::SubtaskAddCmd( Project *project, Node *node, Node *parent, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_node( node ), m_parent( parent ), m_added( false ), m_cmd( 0 ) { // set some reasonable defaults for normally calculated values node->setStartTime( parent->startTime() ); node->setEndTime( node->startTime() + node->duration() ); node->setEarlyStart( node->startTime() ); node->setLateFinish( node->endTime() ); node->setWorkStartTime( node->startTime() ); node->setWorkEndTime( node->endTime() ); // Summarytasks can't have resources, so remove resource requests from the new parent foreach ( ResourceGroupRequest *r, parent->requests().requests() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new RemoveResourceGroupRequestCmd( r ) ); } // Also remove accounts if ( parent->runningAccount() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new NodeModifyRunningAccountCmd( *parent, parent->runningAccount(), 0 ) ); } if ( parent->startupAccount() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new NodeModifyStartupAccountCmd( *parent, parent->startupAccount(), 0 ) ); } if ( parent->shutdownAccount() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new NodeModifyShutdownAccountCmd( *parent, parent->shutdownAccount(), 0 ) ); } } SubtaskAddCmd::~SubtaskAddCmd() { delete m_cmd; if ( !m_added ) delete m_node; } void SubtaskAddCmd::execute() { m_project->addSubTask( m_node, m_parent ); if ( m_cmd ) { m_cmd->execute(); } m_added = true; } void SubtaskAddCmd::unexecute() { m_project->takeTask( m_node ); if ( m_cmd ) { m_cmd->unexecute(); } m_added = false; } NodeModifyNameCmd::NodeModifyNameCmd( Node &node, const QString& nodename, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newName( nodename ), oldName( node.name() ) { } void NodeModifyNameCmd::execute() { m_node.setName( newName ); } void NodeModifyNameCmd::unexecute() { m_node.setName( oldName ); } NodeModifyPriorityCmd::NodeModifyPriorityCmd(Node &node, int oldValue, int newValue, const KUndo2MagicString& name ) : NamedCommand( name ) , m_node( node ) , m_oldValue(oldValue) , m_newValue(newValue) { } void NodeModifyPriorityCmd::execute() { m_node.setPriority(m_newValue); } void NodeModifyPriorityCmd::unexecute() { m_node.setPriority(m_oldValue); } NodeModifyLeaderCmd::NodeModifyLeaderCmd( Node &node, const QString& leader, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newLeader( leader ), oldLeader( node.leader() ) { } void NodeModifyLeaderCmd::execute() { m_node.setLeader( newLeader ); } void NodeModifyLeaderCmd::unexecute() { m_node.setLeader( oldLeader ); } NodeModifyDescriptionCmd::NodeModifyDescriptionCmd( Node &node, const QString& description, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newDescription( description ), oldDescription( node.description() ) { } void NodeModifyDescriptionCmd::execute() { m_node.setDescription( newDescription ); } void NodeModifyDescriptionCmd::unexecute() { m_node.setDescription( oldDescription ); } NodeModifyConstraintCmd::NodeModifyConstraintCmd( Node &node, Node::ConstraintType c, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newConstraint( c ), oldConstraint( static_cast( node.constraint() ) ) { } void NodeModifyConstraintCmd::execute() { m_node.setConstraint( newConstraint ); } void NodeModifyConstraintCmd::unexecute() { m_node.setConstraint( oldConstraint ); } NodeModifyConstraintStartTimeCmd::NodeModifyConstraintStartTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newTime( dt ), oldTime( node.constraintStartTime() ) { if ( node.projectNode() ) { m_timeZone = static_cast( node.projectNode() )->timeZone(); } } void NodeModifyConstraintStartTimeCmd::execute() { m_node.setConstraintStartTime( DateTime( newTime, m_timeZone ) ); } void NodeModifyConstraintStartTimeCmd::unexecute() { m_node.setConstraintStartTime( oldTime ); } NodeModifyConstraintEndTimeCmd::NodeModifyConstraintEndTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newTime( dt ), oldTime( node.constraintEndTime() ) { if ( node.projectNode() ) { m_timeZone = static_cast( node.projectNode() )->timeZone(); } } void NodeModifyConstraintEndTimeCmd::execute() { m_node.setConstraintEndTime( DateTime( newTime, m_timeZone ) ); } void NodeModifyConstraintEndTimeCmd::unexecute() { m_node.setConstraintEndTime( oldTime ); } NodeModifyStartTimeCmd::NodeModifyStartTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newTime( dt ), oldTime( node.startTime() ) { m_timeZone = static_cast( node.projectNode() )->timeZone(); } void NodeModifyStartTimeCmd::execute() { m_node.setStartTime( DateTime( newTime, m_timeZone ) ); } void NodeModifyStartTimeCmd::unexecute() { m_node.setStartTime( oldTime ); } NodeModifyEndTimeCmd::NodeModifyEndTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newTime( dt ), oldTime( node.endTime() ) { m_timeZone = static_cast( node.projectNode() )->timeZone(); } void NodeModifyEndTimeCmd::execute() { m_node.setEndTime( DateTime( newTime, m_timeZone ) ); } void NodeModifyEndTimeCmd::unexecute() { m_node.setEndTime( oldTime ); } NodeModifyIdCmd::NodeModifyIdCmd( Node &node, const QString& id, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newId( id ), oldId( node.id() ) { } void NodeModifyIdCmd::execute() { m_node.setId( newId ); } void NodeModifyIdCmd::unexecute() { m_node.setId( oldId ); } NodeIndentCmd::NodeIndentCmd( Node &node, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_newparent( 0 ), m_newindex( -1 ), m_cmd( 0 ) { } NodeIndentCmd::~NodeIndentCmd() { delete m_cmd; } void NodeIndentCmd::execute() { m_oldparent = m_node.parentNode(); m_oldindex = m_oldparent->findChildNode( &m_node ); Project *p = dynamic_cast( m_node.projectNode() ); if ( p && p->indentTask( &m_node, m_newindex ) ) { m_newparent = m_node.parentNode(); m_newindex = m_newparent->findChildNode( &m_node ); // Summarytasks can't have resources, so remove resource requests from the new parent if ( m_cmd == 0 ) { foreach ( ResourceGroupRequest *r, m_newparent->requests().requests() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new RemoveResourceGroupRequestCmd( r ) ); } // Also remove accounts if ( m_newparent->runningAccount() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new NodeModifyRunningAccountCmd( *m_newparent, m_newparent->runningAccount(), 0 ) ); } if ( m_newparent->startupAccount() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new NodeModifyStartupAccountCmd( *m_newparent, m_newparent->startupAccount(), 0 ) ); } if ( m_newparent->shutdownAccount() ) { if ( m_cmd == 0 ) m_cmd = new MacroCommand( KUndo2MagicString() ); m_cmd->addCommand( new NodeModifyShutdownAccountCmd( *m_newparent, m_newparent->shutdownAccount(), 0 ) ); } } if ( m_cmd ) { m_cmd->execute(); } } } void NodeIndentCmd::unexecute() { Project * p = dynamic_cast( m_node.projectNode() ); if ( m_newindex != -1 && p && p->unindentTask( &m_node ) ) { m_newindex = -1; if ( m_cmd ) { m_cmd->unexecute(); } } } NodeUnindentCmd::NodeUnindentCmd( Node &node, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_newparent( 0 ), m_newindex( -1 ) {} void NodeUnindentCmd::execute() { m_oldparent = m_node.parentNode(); m_oldindex = m_oldparent->findChildNode( &m_node ); Project *p = dynamic_cast( m_node.projectNode() ); if ( p && p->unindentTask( &m_node ) ) { m_newparent = m_node.parentNode(); m_newindex = m_newparent->findChildNode( &m_node ); } } void NodeUnindentCmd::unexecute() { Project * p = dynamic_cast( m_node.projectNode() ); if ( m_newindex != -1 && p && p->indentTask( &m_node, m_oldindex ) ) { m_newindex = -1; } } NodeMoveUpCmd::NodeMoveUpCmd( Node &node, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_moved( false ) { m_project = static_cast( m_node.projectNode() ); } void NodeMoveUpCmd::execute() { if ( m_project ) { m_moved = m_project->moveTaskUp( &m_node ); } } void NodeMoveUpCmd::unexecute() { if ( m_project && m_moved ) { m_project->moveTaskDown( &m_node ); } m_moved = false; } NodeMoveDownCmd::NodeMoveDownCmd( Node &node, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_moved( false ) { m_project = static_cast( m_node.projectNode() ); } void NodeMoveDownCmd::execute() { if ( m_project ) { m_moved = m_project->moveTaskDown( &m_node ); } } void NodeMoveDownCmd::unexecute() { if ( m_project && m_moved ) { m_project->moveTaskUp( &m_node ); } m_moved = false; } NodeMoveCmd::NodeMoveCmd( Project *project, Node *node, Node *newParent, int newPos, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_node( node ), m_newparent( newParent ), m_newpos( newPos ), m_moved( false ) { m_oldparent = node->parentNode(); Q_ASSERT( m_oldparent ); } void NodeMoveCmd::execute() { if ( m_project ) { m_oldpos = m_oldparent->indexOf( m_node ); m_moved = m_project->moveTask( m_node, m_newparent, m_newpos ); if ( m_moved ) { if ( m_cmd.isEmpty() ) { // Summarytasks can't have resources, so remove resource requests from the new parent foreach ( ResourceGroupRequest *r, m_newparent->requests().requests() ) { m_cmd.addCommand( new RemoveResourceGroupRequestCmd( r ) ); } // TODO appointments ?? } m_cmd.execute(); } } } void NodeMoveCmd::unexecute() { if ( m_project && m_moved ) { m_moved = m_project->moveTask( m_node, m_oldparent, m_oldpos ); m_cmd.unexecute(); } m_moved = false; } AddRelationCmd::AddRelationCmd( Project &project, Relation *rel, const KUndo2MagicString& name ) : NamedCommand( name ), m_rel( rel ), m_project( project ) { m_taken = true; } AddRelationCmd::~AddRelationCmd() { if ( m_taken ) delete m_rel; } void AddRelationCmd::execute() { //debugPlan<parent()<<" to"<child(); m_taken = false; m_project.addRelation( m_rel, false ); } void AddRelationCmd::unexecute() { m_taken = true; m_project.takeRelation( m_rel ); } DeleteRelationCmd::DeleteRelationCmd( Project &project, Relation *rel, const KUndo2MagicString& name ) : NamedCommand( name ), m_rel( rel ), m_project( project ) { m_taken = false; } DeleteRelationCmd::~DeleteRelationCmd() { if ( m_taken ) { // do not access nodes, the may already be deleted m_rel->setParent(0); m_rel->setChild(0); delete m_rel; } } void DeleteRelationCmd::execute() { //debugPlan<parent()<<" to"<child(); m_taken = true; m_project.takeRelation( m_rel ); } void DeleteRelationCmd::unexecute() { m_taken = false; m_project.addRelation( m_rel, false ); } ModifyRelationTypeCmd::ModifyRelationTypeCmd( Relation *rel, Relation::Type type, const KUndo2MagicString& name ) : NamedCommand( name ), m_rel( rel ), m_newtype( type ) { m_oldtype = rel->type(); m_project = dynamic_cast( rel->parent() ->projectNode() ); } void ModifyRelationTypeCmd::execute() { if ( m_project ) { m_project->setRelationType( m_rel, m_newtype ); } } void ModifyRelationTypeCmd::unexecute() { if ( m_project ) { m_project->setRelationType( m_rel, m_oldtype ); } } ModifyRelationLagCmd::ModifyRelationLagCmd( Relation *rel, Duration lag, const KUndo2MagicString& name ) : NamedCommand( name ), m_rel( rel ), m_newlag( lag ) { m_oldlag = rel->lag(); m_project = dynamic_cast( rel->parent() ->projectNode() ); } void ModifyRelationLagCmd::execute() { if ( m_project ) { m_project->setRelationLag( m_rel, m_newlag ); } } void ModifyRelationLagCmd::unexecute() { if ( m_project ) { m_project->setRelationLag( m_rel, m_oldlag ); } } AddResourceRequestCmd::AddResourceRequestCmd( ResourceGroupRequest *group, ResourceRequest *request, const KUndo2MagicString& name ) : NamedCommand( name ), m_group( group ), m_request( request ) { m_mine = true; } AddResourceRequestCmd::~AddResourceRequestCmd() { if ( m_mine ) delete m_request; } void AddResourceRequestCmd::execute() { //debugPlan<<"group="<project() ); if ( m_group->project() ) { m_group->project()->addResource( m_group, m_resource, m_index ); m_mine = false; //debugPlan<<"added:"<project() ); if ( m_group->project() ) { m_group->project()->takeResource( m_group, m_resource ); //debugPlan<<"removed:"<project() ); } RemoveResourceCmd::RemoveResourceCmd( ResourceGroup *group, Resource *resource, const KUndo2MagicString& name ) : AddResourceCmd( group, resource, name ) { //debugPlan<requests(); if ( group->project() ) { foreach ( Schedule * s, group->project()->schedules() ) { Schedule *rs = resource->findSchedule( s->id() ); if ( rs && ! rs->isDeleted() ) { debugPlan<name(); addSchScheduled( s ); } } } if ( resource->account() ) { m_cmd.addCommand( new ResourceModifyAccountCmd( *resource, resource->account(), 0 ) ); } } RemoveResourceCmd::~RemoveResourceCmd() { while ( !m_appointments.isEmpty() ) delete m_appointments.takeFirst(); } void RemoveResourceCmd::execute() { foreach ( ResourceRequest * r, m_requests ) { r->parent() ->takeResourceRequest( r ); //debugPlan<<"Remove request for"<resource()->name(); } AddResourceCmd::unexecute(); m_cmd.execute(); setSchScheduled( false ); } void RemoveResourceCmd::unexecute() { foreach ( ResourceRequest * r, m_requests ) { r->parent() ->addResourceRequest( r ); //debugPlan<<"Add request for"<resource()->name(); } m_cmd.unexecute(); AddResourceCmd::execute(); setSchScheduled(); } MoveResourceCmd::MoveResourceCmd( ResourceGroup *group, Resource *resource, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( *(group->project()) ), m_resource( resource ), m_oldvalue( resource->parentGroup() ), m_newvalue( group ) { foreach ( ResourceRequest * r, resource->requests() ) { cmd.addCommand( new RemoveResourceRequestCmd( r->parent(), r ) ); } } void MoveResourceCmd::execute() { cmd.execute(); m_project.moveResource( m_newvalue, m_resource ); } void MoveResourceCmd::unexecute() { m_project.moveResource( m_oldvalue, m_resource ); cmd.unexecute(); } ModifyResourceNameCmd::ModifyResourceNameCmd( Resource *resource, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->name(); } void ModifyResourceNameCmd::execute() { m_resource->setName( m_newvalue ); } void ModifyResourceNameCmd::unexecute() { m_resource->setName( m_oldvalue ); } ModifyResourceInitialsCmd::ModifyResourceInitialsCmd( Resource *resource, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->initials(); } void ModifyResourceInitialsCmd::execute() { m_resource->setInitials( m_newvalue ); } void ModifyResourceInitialsCmd::unexecute() { m_resource->setInitials( m_oldvalue ); } ModifyResourceEmailCmd::ModifyResourceEmailCmd( Resource *resource, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->email(); } void ModifyResourceEmailCmd::execute() { m_resource->setEmail( m_newvalue ); } void ModifyResourceEmailCmd::unexecute() { m_resource->setEmail( m_oldvalue ); } ModifyResourceAutoAllocateCmd::ModifyResourceAutoAllocateCmd( Resource *resource,bool value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->autoAllocate(); } void ModifyResourceAutoAllocateCmd::execute() { m_resource->setAutoAllocate( m_newvalue ); } void ModifyResourceAutoAllocateCmd::unexecute() { m_resource->setAutoAllocate( m_oldvalue ); } ModifyResourceTypeCmd::ModifyResourceTypeCmd( Resource *resource, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->type(); } void ModifyResourceTypeCmd::execute() { m_resource->setType( ( Resource::Type ) m_newvalue ); } void ModifyResourceTypeCmd::unexecute() { m_resource->setType( ( Resource::Type ) m_oldvalue ); } ModifyResourceUnitsCmd::ModifyResourceUnitsCmd( Resource *resource, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->units(); } void ModifyResourceUnitsCmd::execute() { m_resource->setUnits( m_newvalue ); } void ModifyResourceUnitsCmd::unexecute() { m_resource->setUnits( m_oldvalue ); } ModifyResourceAvailableFromCmd::ModifyResourceAvailableFromCmd( Resource *resource, const QDateTime& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->availableFrom(); m_timeZone = resource->timeZone(); } void ModifyResourceAvailableFromCmd::execute() { m_resource->setAvailableFrom( DateTime( m_newvalue, m_timeZone ) ); } void ModifyResourceAvailableFromCmd::unexecute() { m_resource->setAvailableFrom( m_oldvalue ); } ModifyResourceAvailableUntilCmd::ModifyResourceAvailableUntilCmd( Resource *resource, const QDateTime& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->availableUntil(); m_timeZone = resource->timeZone(); } void ModifyResourceAvailableUntilCmd::execute() { m_resource->setAvailableUntil( DateTime( m_newvalue, m_timeZone ) ); } void ModifyResourceAvailableUntilCmd::unexecute() { m_resource->setAvailableUntil( m_oldvalue ); } ModifyResourceNormalRateCmd::ModifyResourceNormalRateCmd( Resource *resource, double value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->normalRate(); } void ModifyResourceNormalRateCmd::execute() { m_resource->setNormalRate( m_newvalue ); } void ModifyResourceNormalRateCmd::unexecute() { m_resource->setNormalRate( m_oldvalue ); } ModifyResourceOvertimeRateCmd::ModifyResourceOvertimeRateCmd( Resource *resource, double value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->overtimeRate(); } void ModifyResourceOvertimeRateCmd::execute() { m_resource->setOvertimeRate( m_newvalue ); } void ModifyResourceOvertimeRateCmd::unexecute() { m_resource->setOvertimeRate( m_oldvalue ); } ModifyResourceCalendarCmd::ModifyResourceCalendarCmd( Resource *resource, Calendar *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->calendar( true ); } void ModifyResourceCalendarCmd::execute() { m_resource->setCalendar( m_newvalue ); } void ModifyResourceCalendarCmd::unexecute() { m_resource->setCalendar( m_oldvalue ); } ModifyRequiredResourcesCmd::ModifyRequiredResourcesCmd( Resource *resource, const QStringList &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_newvalue( value ) { m_oldvalue = resource->requiredIds(); } void ModifyRequiredResourcesCmd::execute() { m_resource->setRequiredIds( m_newvalue ); } void ModifyRequiredResourcesCmd::unexecute() { m_resource->setRequiredIds( m_oldvalue ); } AddResourceTeamCmd::AddResourceTeamCmd( Resource *team, const QString &member, const KUndo2MagicString& name ) : NamedCommand( name ), m_team( team ), m_member( member ) { } void AddResourceTeamCmd::execute() { m_team->addTeamMemberId( m_member ); } void AddResourceTeamCmd::unexecute() { m_team->removeTeamMemberId( m_member ); } RemoveResourceTeamCmd::RemoveResourceTeamCmd( Resource *team, const QString &member, const KUndo2MagicString& name ) : NamedCommand( name ), m_team( team ), m_member( member ) { } void RemoveResourceTeamCmd::execute() { m_team->removeTeamMemberId( m_member ); } void RemoveResourceTeamCmd::unexecute() { m_team->addTeamMemberId( m_member ); } RemoveResourceGroupCmd::RemoveResourceGroupCmd( Project *project, ResourceGroup *group, const KUndo2MagicString& name ) : NamedCommand( name ), m_group( group ), m_project( project ), m_cmd( 0 ) { m_index = project->indexOf( group ); m_mine = false; if ( !m_group->requests().isEmpty() ) { m_cmd = new MacroCommand(KUndo2MagicString()); foreach( ResourceGroupRequest * r, m_group->requests() ) { m_cmd->addCommand( new RemoveResourceGroupRequestCmd( r ) ); } } } RemoveResourceGroupCmd::~RemoveResourceGroupCmd() { delete m_cmd; if ( m_mine ) delete m_group; } void RemoveResourceGroupCmd::execute() { // remove all requests to this group if ( m_cmd ) { m_cmd->execute(); } if ( m_project ) m_project->takeResourceGroup( m_group ); m_mine = true; } void RemoveResourceGroupCmd::unexecute() { if ( m_project ) m_project->addResourceGroup( m_group, m_index ); m_mine = false; // add all requests if ( m_cmd ) { m_cmd->unexecute(); } } AddResourceGroupCmd::AddResourceGroupCmd( Project *project, ResourceGroup *group, const KUndo2MagicString& name ) : RemoveResourceGroupCmd( project, group, name ) { m_mine = true; } void AddResourceGroupCmd::execute() { RemoveResourceGroupCmd::unexecute(); } void AddResourceGroupCmd::unexecute() { RemoveResourceGroupCmd::execute(); } ModifyResourceGroupNameCmd::ModifyResourceGroupNameCmd( ResourceGroup *group, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_group( group ), m_newvalue( value ) { m_oldvalue = group->name(); } void ModifyResourceGroupNameCmd::execute() { m_group->setName( m_newvalue ); } void ModifyResourceGroupNameCmd::unexecute() { m_group->setName( m_oldvalue ); } ModifyResourceGroupTypeCmd::ModifyResourceGroupTypeCmd( ResourceGroup *group, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_group( group ), m_newvalue( value ) { m_oldvalue = group->type(); } void ModifyResourceGroupTypeCmd::execute() { m_group->setType( static_cast( m_newvalue) ); } void ModifyResourceGroupTypeCmd::unexecute() { m_group->setType( static_cast( m_oldvalue ) ); } ModifyCompletionEntrymodeCmd::ModifyCompletionEntrymodeCmd( Completion &completion, Completion::Entrymode value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), oldvalue( m_completion.entrymode() ), newvalue( value ) { } void ModifyCompletionEntrymodeCmd::execute() { m_completion.setEntrymode( newvalue ); } void ModifyCompletionEntrymodeCmd::unexecute() { m_completion.setEntrymode( oldvalue ); } ModifyCompletionPercentFinishedCmd::ModifyCompletionPercentFinishedCmd( Completion &completion, const QDate &date, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), m_date( date ), m_newvalue( value ), m_oldvalue( completion.percentFinished( date ) ) { if ( ! completion.entries().contains( date ) ) { Completion::Entry *e = new Completion::Entry(); Completion::Entry *latest = completion.entry( completion.entryDate() ); if ( latest ) { *e = *latest; } cmd.addCommand( new AddCompletionEntryCmd( completion, date, e ) ); } } void ModifyCompletionPercentFinishedCmd::execute() { cmd.execute(); m_completion.setPercentFinished( m_date, m_newvalue ); } void ModifyCompletionPercentFinishedCmd::unexecute() { m_completion.setPercentFinished( m_date, m_oldvalue ); cmd.unexecute(); } ModifyCompletionRemainingEffortCmd::ModifyCompletionRemainingEffortCmd( Completion &completion, const QDate &date, const Duration &value, const KUndo2MagicString &name ) : NamedCommand( name ), m_completion( completion ), m_date( date ), m_newvalue( value ), m_oldvalue( completion.remainingEffort( date ) ) { if ( ! completion.entries().contains( date ) ) { Completion::Entry *e = new Completion::Entry(); Completion::Entry *latest = completion.entry( completion.entryDate() ); if ( latest ) { *e = *latest; } cmd.addCommand( new AddCompletionEntryCmd( completion, date, e ) ); } } void ModifyCompletionRemainingEffortCmd::execute() { cmd.execute(); m_completion.setRemainingEffort( m_date, m_newvalue ); } void ModifyCompletionRemainingEffortCmd::unexecute() { m_completion.setRemainingEffort( m_date, m_oldvalue ); cmd.unexecute(); } ModifyCompletionActualEffortCmd::ModifyCompletionActualEffortCmd( Completion &completion, const QDate &date, const Duration &value, const KUndo2MagicString &name ) : NamedCommand( name ), m_completion( completion ), m_date( date ), m_newvalue( value ), m_oldvalue( completion.actualEffort( date ) ) { if ( ! completion.entries().contains( date ) ) { Completion::Entry *e = new Completion::Entry(); Completion::Entry *latest = completion.entry( completion.entryDate() ); if ( latest ) { *e = *latest; } cmd.addCommand( new AddCompletionEntryCmd( completion, date, e ) ); } } void ModifyCompletionActualEffortCmd::execute() { cmd.execute(); m_completion.setActualEffort( m_date, m_newvalue ); } void ModifyCompletionActualEffortCmd::unexecute() { m_completion.setActualEffort( m_date, m_oldvalue ); cmd.unexecute(); } ModifyCompletionStartedCmd::ModifyCompletionStartedCmd( Completion &completion, bool value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), oldvalue( m_completion.isStarted() ), newvalue( value ) { } void ModifyCompletionStartedCmd::execute() { m_completion.setStarted( newvalue ); } void ModifyCompletionStartedCmd::unexecute() { m_completion.setStarted( oldvalue ); } ModifyCompletionFinishedCmd::ModifyCompletionFinishedCmd( Completion &completion, bool value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), oldvalue( m_completion.isFinished() ), newvalue( value ) { } void ModifyCompletionFinishedCmd::execute() { m_completion.setFinished( newvalue ); } void ModifyCompletionFinishedCmd::unexecute() { m_completion.setFinished( oldvalue ); } ModifyCompletionStartTimeCmd::ModifyCompletionStartTimeCmd( Completion &completion, const QDateTime &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), oldvalue( m_completion.startTime() ), newvalue( value ) { m_timeZone = static_cast( completion.node()->projectNode() )->timeZone(); } void ModifyCompletionStartTimeCmd::execute() { m_completion.setStartTime( DateTime( newvalue, m_timeZone ) ); } void ModifyCompletionStartTimeCmd::unexecute() { m_completion.setStartTime( oldvalue ); } ModifyCompletionFinishTimeCmd::ModifyCompletionFinishTimeCmd( Completion &completion, const QDateTime &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), oldvalue( m_completion.finishTime() ), newvalue( value ) { m_timeZone = static_cast( completion.node()->projectNode() )->timeZone(); } void ModifyCompletionFinishTimeCmd::execute() { m_completion.setFinishTime( DateTime( newvalue, m_timeZone ) ); } void ModifyCompletionFinishTimeCmd::unexecute() { m_completion.setFinishTime( oldvalue ); } AddCompletionEntryCmd::AddCompletionEntryCmd( Completion &completion, const QDate &date, Completion::Entry *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), m_date( date ), newvalue( value ), m_newmine( true ) { } AddCompletionEntryCmd::~AddCompletionEntryCmd() { if ( m_newmine ) delete newvalue; } void AddCompletionEntryCmd::execute() { Q_ASSERT( ! m_completion.entries().contains( m_date ) ); m_completion.addEntry( m_date, newvalue ); m_newmine = false; } void AddCompletionEntryCmd::unexecute() { m_completion.takeEntry( m_date ); m_newmine = true; } RemoveCompletionEntryCmd::RemoveCompletionEntryCmd( Completion &completion, const QDate &date, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), m_date( date ), m_mine( false ) { value = m_completion.entry( date ); } RemoveCompletionEntryCmd::~RemoveCompletionEntryCmd() { debugPlan<addCommand( new RemoveCompletionEntryCmd( completion, date ) ); cmd->addCommand( new AddCompletionEntryCmd( completion, date, value ) ); } ModifyCompletionEntryCmd::~ModifyCompletionEntryCmd() { delete cmd; } void ModifyCompletionEntryCmd::execute() { cmd->execute(); } void ModifyCompletionEntryCmd::unexecute() { cmd->unexecute(); } AddCompletionUsedEffortCmd::AddCompletionUsedEffortCmd( Completion &completion, const Resource *resource, Completion::UsedEffort *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_completion( completion ), m_resource( resource ), newvalue( value ), m_newmine( true ), m_oldmine( false) { oldvalue = m_completion.usedEffort( resource ); } AddCompletionUsedEffortCmd::~AddCompletionUsedEffortCmd() { if ( m_oldmine ) delete oldvalue; if ( m_newmine ) delete newvalue; } void AddCompletionUsedEffortCmd::execute() { if ( oldvalue ) { m_completion.takeUsedEffort( m_resource ); m_oldmine = true; } m_completion.addUsedEffort( m_resource, newvalue ); m_newmine = false; } void AddCompletionUsedEffortCmd::unexecute() { m_completion.takeUsedEffort( m_resource ); if ( oldvalue ) { m_completion.addUsedEffort( m_resource, oldvalue ); } m_newmine = true; m_oldmine = false; } AddCompletionActualEffortCmd::AddCompletionActualEffortCmd( Completion::UsedEffort &ue, const QDate &date, const Completion::UsedEffort::ActualEffort &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_usedEffort( ue ), m_date( date ), newvalue( value ) { oldvalue = ue.effort( date ); } AddCompletionActualEffortCmd::~AddCompletionActualEffortCmd() { } void AddCompletionActualEffortCmd::execute() { m_usedEffort.takeEffort( m_date ); if ( newvalue.effort() > 0 ) { m_usedEffort.setEffort( m_date, newvalue ); } } void AddCompletionActualEffortCmd::unexecute() { m_usedEffort.takeEffort( m_date ); if ( oldvalue.effort() > 0 ) { m_usedEffort.setEffort( m_date, oldvalue ); } } AddAccountCmd::AddAccountCmd( Project &project, Account *account, const QString& parent, int index, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_account( account ), m_parent( 0 ), m_index( index ), m_parentName( parent ) { m_mine = true; } AddAccountCmd::AddAccountCmd( Project &project, Account *account, Account *parent, int index, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_account( account ), m_parent( parent ), m_index( index ) { m_mine = true; } AddAccountCmd::~AddAccountCmd() { if ( m_mine ) delete m_account; } void AddAccountCmd::execute() { if ( m_parent == 0 && !m_parentName.isEmpty() ) { m_parent = m_project.accounts().findAccount( m_parentName ); } m_project.accounts().insert( m_account, m_parent, m_index ); m_mine = false; } void AddAccountCmd::unexecute() { m_project.accounts().take( m_account ); m_mine = true; } RemoveAccountCmd::RemoveAccountCmd( Project &project, Account *account, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_account( account ), m_parent( account->parent() ) { if ( m_parent ) { m_index = m_parent->accountList().indexOf( account ); } else { m_index = project.accounts().accountList().indexOf( account ); } m_mine = false; m_isDefault = account == project.accounts().defaultAccount(); for (Account::CostPlace *cp : m_account->costPlaces()) { if (cp->node()) { if (cp->running()) { m_cmd.addCommand(new NodeModifyRunningAccountCmd(*cp->node(), cp->node()->runningAccount(), 0)); } if (cp->startup()) { m_cmd.addCommand(new NodeModifyStartupAccountCmd(*cp->node(), cp->node()->startupAccount(), 0)); } if (cp->shutdown()) { m_cmd.addCommand(new NodeModifyShutdownAccountCmd(*cp->node(), cp->node()->shutdownAccount(), 0)); } } else if (cp->resource()) { m_cmd.addCommand(new ResourceModifyAccountCmd(*cp->resource(), cp->resource()->account(), 0)); } } for (int i = account->accountList().count()-1; i >= 0; --i) { m_cmd.addCommand(new RemoveAccountCmd(project, account->accountList().at(i))); } } RemoveAccountCmd::~RemoveAccountCmd() { if ( m_mine ) delete m_account; } void RemoveAccountCmd::execute() { if ( m_isDefault ) { m_project.accounts().setDefaultAccount( 0 ); } m_cmd.execute(); // remove costplaces and children m_project.accounts().take( m_account ); m_mine = true; } void RemoveAccountCmd::unexecute() { m_project.accounts().insert( m_account, m_parent, m_index ); m_cmd.unexecute(); // add costplaces && children if ( m_isDefault ) { m_project.accounts().setDefaultAccount( m_account ); } m_mine = false; } RenameAccountCmd::RenameAccountCmd( Account *account, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_account( account ) { m_oldvalue = account->name(); m_newvalue = value; } void RenameAccountCmd::execute() { m_account->setName( m_newvalue ); } void RenameAccountCmd::unexecute() { m_account->setName( m_oldvalue ); } ModifyAccountDescriptionCmd::ModifyAccountDescriptionCmd( Account *account, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_account( account ) { m_oldvalue = account->description(); m_newvalue = value; } void ModifyAccountDescriptionCmd::execute() { m_account->setDescription( m_newvalue ); } void ModifyAccountDescriptionCmd::unexecute() { m_account->setDescription( m_oldvalue ); } NodeModifyStartupCostCmd::NodeModifyStartupCostCmd( Node &node, double value, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ) { m_oldvalue = node.startupCost(); m_newvalue = value; } void NodeModifyStartupCostCmd::execute() { m_node.setStartupCost( m_newvalue ); } void NodeModifyStartupCostCmd::unexecute() { m_node.setStartupCost( m_oldvalue ); } NodeModifyShutdownCostCmd::NodeModifyShutdownCostCmd( Node &node, double value, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ) { m_oldvalue = node.shutdownCost(); m_newvalue = value; } void NodeModifyShutdownCostCmd::execute() { m_node.setShutdownCost( m_newvalue ); } void NodeModifyShutdownCostCmd::unexecute() { m_node.setShutdownCost( m_oldvalue ); } NodeModifyRunningAccountCmd::NodeModifyRunningAccountCmd( Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void NodeModifyRunningAccountCmd::execute() { //debugPlan; if ( m_oldvalue ) { m_oldvalue->removeRunning( m_node ); } if ( m_newvalue ) { m_newvalue->addRunning( m_node ); } } void NodeModifyRunningAccountCmd::unexecute() { //debugPlan; if ( m_newvalue ) { m_newvalue->removeRunning( m_node ); } if ( m_oldvalue ) { m_oldvalue->addRunning( m_node ); } } NodeModifyStartupAccountCmd::NodeModifyStartupAccountCmd( Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void NodeModifyStartupAccountCmd::execute() { //debugPlan; if ( m_oldvalue ) { m_oldvalue->removeStartup( m_node ); } if ( m_newvalue ) { m_newvalue->addStartup( m_node ); } } void NodeModifyStartupAccountCmd::unexecute() { //debugPlan; if ( m_newvalue ) { m_newvalue->removeStartup( m_node ); } if ( m_oldvalue ) { m_oldvalue->addStartup( m_node ); } } NodeModifyShutdownAccountCmd::NodeModifyShutdownAccountCmd( Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void NodeModifyShutdownAccountCmd::execute() { //debugPlan; if ( m_oldvalue ) { m_oldvalue->removeShutdown( m_node ); } if ( m_newvalue ) { m_newvalue->addShutdown( m_node ); } } void NodeModifyShutdownAccountCmd::unexecute() { //debugPlan; if ( m_newvalue ) { m_newvalue->removeShutdown( m_node ); } if ( m_oldvalue ) { m_oldvalue->addShutdown( m_node ); } } ModifyDefaultAccountCmd::ModifyDefaultAccountCmd( Accounts &acc, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_accounts( acc ) { m_oldvalue = oldvalue; m_newvalue = newvalue; //debugPlan; } void ModifyDefaultAccountCmd::execute() { //debugPlan; m_accounts.setDefaultAccount( m_newvalue ); } void ModifyDefaultAccountCmd::unexecute() { //debugPlan; m_accounts.setDefaultAccount( m_oldvalue ); } ResourceModifyAccountCmd::ResourceModifyAccountCmd( Resource &resource, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ) { m_oldvalue = oldvalue; m_newvalue = newvalue; } void ResourceModifyAccountCmd::execute() { //debugPlan; if ( m_oldvalue ) { m_oldvalue->removeRunning( m_resource ); } if ( m_newvalue ) { m_newvalue->addRunning( m_resource ); } } void ResourceModifyAccountCmd::unexecute() { //debugPlan; if ( m_newvalue ) { m_newvalue->removeRunning( m_resource ); } if ( m_oldvalue ) { m_oldvalue->addRunning( m_resource ); } } ProjectModifyConstraintCmd::ProjectModifyConstraintCmd( Project &node, Node::ConstraintType c, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newConstraint( c ), oldConstraint( static_cast( node.constraint() ) ) { } void ProjectModifyConstraintCmd::execute() { m_node.setConstraint( newConstraint ); } void ProjectModifyConstraintCmd::unexecute() { m_node.setConstraint( oldConstraint ); } ProjectModifyStartTimeCmd::ProjectModifyStartTimeCmd( Project &node, const QDateTime& dt, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newTime( dt ), oldTime( node.startTime() ) { m_timeZone = node.timeZone(); } void ProjectModifyStartTimeCmd::execute() { m_node.setConstraintStartTime( DateTime( newTime, m_timeZone ) ); } void ProjectModifyStartTimeCmd::unexecute() { m_node.setConstraintStartTime( oldTime ); } ProjectModifyEndTimeCmd::ProjectModifyEndTimeCmd( Project &node, const QDateTime& dt, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), newTime( dt ), oldTime( node.endTime() ) { m_timeZone = node.timeZone(); } void ProjectModifyEndTimeCmd::execute() { m_node.setEndTime( DateTime( newTime, m_timeZone ) ); m_node.setConstraintEndTime( DateTime( newTime, m_timeZone ) ); } void ProjectModifyEndTimeCmd::unexecute() { m_node.setConstraintEndTime( oldTime ); } +ProjectModifyWorkPackageInfoCmd::ProjectModifyWorkPackageInfoCmd(Project &project, const Project::WorkPackageInfo &wpi, const KUndo2MagicString& name) + : NamedCommand(name) + , m_node(project) + , m_newWpi(wpi) + , m_oldWpi(project.workPackageInfo()) +{ +} +void ProjectModifyWorkPackageInfoCmd::execute() +{ + m_node.setWorkPackageInfo(m_newWpi); +} +void ProjectModifyWorkPackageInfoCmd::unexecute() +{ + m_node.setWorkPackageInfo(m_oldWpi); +} + //---------------------------- SwapScheduleManagerCmd::SwapScheduleManagerCmd(Project &project, ScheduleManager *from, ScheduleManager *to, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( project ), m_from( from ), m_to( to ) { } SwapScheduleManagerCmd::~SwapScheduleManagerCmd() { } void SwapScheduleManagerCmd::execute() { m_node.swapScheduleManagers(m_from, m_to); } void SwapScheduleManagerCmd::unexecute() { m_node.swapScheduleManagers(m_to, m_from); } AddScheduleManagerCmd::AddScheduleManagerCmd( Project &node, ScheduleManager *sm, int index, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_parent( sm->parentManager() ), m_sm( sm ), m_index( index ), m_exp( sm->expected() ), m_mine( true) { } AddScheduleManagerCmd::AddScheduleManagerCmd( ScheduleManager *parent, ScheduleManager *sm, int index, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( parent->project() ), m_parent( parent ), m_sm( sm ), m_index( index ), m_exp( sm->expected() ), m_mine( true) { } AddScheduleManagerCmd::~AddScheduleManagerCmd() { if ( m_mine ) { m_sm->setParentManager( 0 ); delete m_sm; } } void AddScheduleManagerCmd::execute() { m_node.addScheduleManager( m_sm, m_parent, m_index ); m_sm->setExpected( m_exp ); m_mine = false; } void AddScheduleManagerCmd::unexecute() { m_node.takeScheduleManager( m_sm ); m_sm->setExpected( 0 ); m_mine = true; } DeleteScheduleManagerCmd::DeleteScheduleManagerCmd( Project &node, ScheduleManager *sm, const KUndo2MagicString& name ) : AddScheduleManagerCmd( node, sm, -1, name ) { m_mine = false; m_index = m_parent ? m_parent->indexOf( sm ) : node.indexOf( sm ); foreach ( ScheduleManager *s, sm->children() ) { cmd.addCommand( new DeleteScheduleManagerCmd( node, s ) ); } } void DeleteScheduleManagerCmd::execute() { cmd.execute(); AddScheduleManagerCmd::unexecute(); } void DeleteScheduleManagerCmd::unexecute() { AddScheduleManagerCmd::execute(); cmd.unexecute(); } MoveScheduleManagerCmd::MoveScheduleManagerCmd( ScheduleManager *sm, ScheduleManager *newparent, int newindex, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), m_oldparent( sm->parentManager() ), m_newparent( newparent ), m_newindex( newindex ) { m_oldindex = sm->parentManager() ? sm->parentManager()->indexOf( sm ) : sm->project().indexOf( sm ); } void MoveScheduleManagerCmd::execute() { m_sm->project().moveScheduleManager( m_sm, m_newparent, m_newindex ); } void MoveScheduleManagerCmd::unexecute() { m_sm->project().moveScheduleManager( m_sm, m_oldparent, m_oldindex ); } ModifyScheduleManagerNameCmd::ModifyScheduleManagerNameCmd( ScheduleManager &sm, const QString& value, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), oldvalue( sm.name() ), newvalue( value ) { } void ModifyScheduleManagerNameCmd::execute() { m_sm.setName( newvalue ); } void ModifyScheduleManagerNameCmd::unexecute() { m_sm.setName( oldvalue ); } ModifyScheduleManagerAllowOverbookingCmd::ModifyScheduleManagerAllowOverbookingCmd( ScheduleManager &sm, bool value, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), oldvalue( sm.allowOverbooking() ), newvalue( value ) { } void ModifyScheduleManagerAllowOverbookingCmd::execute() { m_sm.setAllowOverbooking( newvalue ); } void ModifyScheduleManagerAllowOverbookingCmd::unexecute() { m_sm.setAllowOverbooking( oldvalue ); } ModifyScheduleManagerDistributionCmd::ModifyScheduleManagerDistributionCmd( ScheduleManager &sm, bool value, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), oldvalue( sm.usePert() ), newvalue( value ) { } void ModifyScheduleManagerDistributionCmd::execute() { m_sm.setUsePert( newvalue ); } void ModifyScheduleManagerDistributionCmd::unexecute() { m_sm.setUsePert( oldvalue ); } ModifyScheduleManagerSchedulingDirectionCmd::ModifyScheduleManagerSchedulingDirectionCmd( ScheduleManager &sm, bool value, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), oldvalue( sm.schedulingDirection() ), newvalue( value ) { } void ModifyScheduleManagerSchedulingDirectionCmd::execute() { m_sm.setSchedulingDirection( newvalue ); } void ModifyScheduleManagerSchedulingDirectionCmd::unexecute() { m_sm.setSchedulingDirection( oldvalue ); } ModifyScheduleManagerSchedulerCmd::ModifyScheduleManagerSchedulerCmd( ScheduleManager &sm, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), oldvalue( sm.schedulerPluginIndex() ), newvalue( value ) { } void ModifyScheduleManagerSchedulerCmd::execute() { m_sm.setSchedulerPlugin( newvalue ); } void ModifyScheduleManagerSchedulerCmd::unexecute() { m_sm.setSchedulerPlugin( oldvalue ); } ModifyScheduleManagerSchedulingGranularityCmd::ModifyScheduleManagerSchedulingGranularityCmd( ScheduleManager &sm, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ), oldvalue( sm.granularity() ), newvalue( value ) { } void ModifyScheduleManagerSchedulingGranularityCmd::execute() { m_sm.setGranularity( newvalue ); } void ModifyScheduleManagerSchedulingGranularityCmd::unexecute() { m_sm.setGranularity( oldvalue ); } CalculateScheduleCmd::CalculateScheduleCmd( Project &node, ScheduleManager *sm, const KUndo2MagicString& name ) : NamedCommand( name ), m_node( node ), m_sm( sm ), m_first( true ), m_newexpected( 0 ) { if (sm->recalculate() && sm->isScheduled()) { m_sm = new ScheduleManager(node); m_sm->setRecalculate(true); m_sm->setGranularity(sm->granularity()); m_sm->setUsePert(sm->usePert()); m_sm->setSchedulerPluginId(sm->schedulerPluginId()); m_sm->setAllowOverbooking(sm->allowOverbooking()); m_sm->setName(sm->name()); preCmd.addCommand(new AddScheduleManagerCmd(sm, m_sm)); postCmd.addCommand(new SwapScheduleManagerCmd(node, sm, m_sm)); postCmd.addCommand(new MoveScheduleManagerCmd(m_sm, sm->parentManager(), sm->parentManager() ? sm->parentManager()->indexOf(sm)+1 : node.indexOf(sm)+1)); postCmd.addCommand(new DeleteScheduleManagerCmd(node, sm)); } m_oldexpected = m_sm->expected(); } void CalculateScheduleCmd::execute() { Q_ASSERT( m_sm ); preCmd.redo(); if ( m_first ) { m_sm->calculateSchedule(); if ( m_sm->calculationResult() != ScheduleManager::CalculationCanceled ) { m_first = false; } m_newexpected = m_sm->expected(); } else { m_sm->setExpected( m_newexpected ); } postCmd.redo(); } void CalculateScheduleCmd::unexecute() { if ( m_sm->scheduling() ) { // terminate scheduling QApplication::setOverrideCursor( Qt::WaitCursor ); m_sm->haltCalculation(); m_first = true; QApplication::restoreOverrideCursor(); } postCmd.undo(); m_sm->setExpected( m_oldexpected ); preCmd.undo(); } //------------------------ BaselineScheduleCmd::BaselineScheduleCmd( ScheduleManager &sm, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ) { } void BaselineScheduleCmd::execute() { m_sm.setBaselined( true ); } void BaselineScheduleCmd::unexecute() { m_sm.setBaselined( false ); } ResetBaselineScheduleCmd::ResetBaselineScheduleCmd( ScheduleManager &sm, const KUndo2MagicString& name ) : NamedCommand( name ), m_sm( sm ) { } void ResetBaselineScheduleCmd::execute() { m_sm.setBaselined( false ); } void ResetBaselineScheduleCmd::unexecute() { m_sm.setBaselined( true ); } //------------------------ ModifyStandardWorktimeYearCmd::ModifyStandardWorktimeYearCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), swt( wt ), m_oldvalue( oldvalue ), m_newvalue( newvalue ) { } void ModifyStandardWorktimeYearCmd::execute() { swt->setYear( m_newvalue ); } void ModifyStandardWorktimeYearCmd::unexecute() { swt->setYear( m_oldvalue ); } ModifyStandardWorktimeMonthCmd::ModifyStandardWorktimeMonthCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), swt( wt ), m_oldvalue( oldvalue ), m_newvalue( newvalue ) { } void ModifyStandardWorktimeMonthCmd::execute() { swt->setMonth( m_newvalue ); } void ModifyStandardWorktimeMonthCmd::unexecute() { swt->setMonth( m_oldvalue ); } ModifyStandardWorktimeWeekCmd::ModifyStandardWorktimeWeekCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), swt( wt ), m_oldvalue( oldvalue ), m_newvalue( newvalue ) { } void ModifyStandardWorktimeWeekCmd::execute() { swt->setWeek( m_newvalue ); } void ModifyStandardWorktimeWeekCmd::unexecute() { swt->setWeek( m_oldvalue ); } ModifyStandardWorktimeDayCmd::ModifyStandardWorktimeDayCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name ) : NamedCommand( name ), swt( wt ), m_oldvalue( oldvalue ), m_newvalue( newvalue ) { } void ModifyStandardWorktimeDayCmd::execute() { swt->setDay( m_newvalue ); } void ModifyStandardWorktimeDayCmd::unexecute() { swt->setDay( m_oldvalue ); } //---------------- DocumentAddCmd::DocumentAddCmd( Documents &docs, Document *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_docs( docs ), m_mine( true ) { Q_ASSERT( value ); m_value = value; } DocumentAddCmd::~DocumentAddCmd() { //debugPlan; if ( m_mine ) delete m_value; } void DocumentAddCmd::execute() { m_docs.addDocument( m_value ); m_mine = false; } void DocumentAddCmd::unexecute() { m_docs.takeDocument( m_value ); m_mine = true; } //---------------- DocumentRemoveCmd::DocumentRemoveCmd( Documents &docs, Document *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_docs( docs ), m_mine( false ) { Q_ASSERT( value ); m_value = value; } DocumentRemoveCmd::~DocumentRemoveCmd() { //debugPlan; if ( m_mine ) delete m_value; } void DocumentRemoveCmd::execute() { m_docs.takeDocument( m_value ); m_mine = true; } void DocumentRemoveCmd::unexecute() { m_docs.addDocument( m_value ); m_mine = false; } //---------------- DocumentModifyUrlCmd::DocumentModifyUrlCmd( Document *doc, const QUrl &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_doc( doc ) { Q_ASSERT( doc ); m_value = value; m_oldvalue = doc->url(); } void DocumentModifyUrlCmd::execute() { m_doc->setUrl( m_value ); } void DocumentModifyUrlCmd::unexecute() { m_doc->setUrl( m_oldvalue ); } //---------------- DocumentModifyNameCmd::DocumentModifyNameCmd( Document *doc, const QString &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_doc( doc ) { Q_ASSERT( doc ); m_value = value; m_oldvalue = doc->name(); } void DocumentModifyNameCmd::execute() { m_doc->setName( m_value ); } void DocumentModifyNameCmd::unexecute() { m_doc->setName( m_oldvalue ); } //---------------- DocumentModifyTypeCmd::DocumentModifyTypeCmd( Document *doc, Document::Type value, const KUndo2MagicString& name ) : NamedCommand( name ), m_doc( doc ) { Q_ASSERT( doc ); m_value = value; m_oldvalue = doc->type(); } void DocumentModifyTypeCmd::execute() { m_doc->setType( m_value ); } void DocumentModifyTypeCmd::unexecute() { m_doc->setType( m_oldvalue ); } //---------------- DocumentModifyStatusCmd::DocumentModifyStatusCmd( Document *doc, const QString &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_doc( doc ) { Q_ASSERT( doc ); m_value = value; m_oldvalue = doc->type(); } void DocumentModifyStatusCmd::execute() { m_doc->setStatus( m_value ); } void DocumentModifyStatusCmd::unexecute() { m_doc->setStatus( m_oldvalue ); } //---------------- DocumentModifySendAsCmd::DocumentModifySendAsCmd( Document *doc, const Document::SendAs value, const KUndo2MagicString& name ) : NamedCommand( name ), m_doc( doc ) { Q_ASSERT( doc ); m_value = value; m_oldvalue = doc->sendAs(); } void DocumentModifySendAsCmd::execute() { m_doc->setSendAs( m_value ); } void DocumentModifySendAsCmd::unexecute() { m_doc->setSendAs( m_oldvalue ); } //---------------- WBSDefinitionModifyCmd::WBSDefinitionModifyCmd( Project &project, const WBSDefinition value, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ) { m_newvalue = value; m_oldvalue = m_project.wbsDefinition(); } void WBSDefinitionModifyCmd::execute() { m_project.setWbsDefinition( m_newvalue ); } void WBSDefinitionModifyCmd::unexecute() { m_project.setWbsDefinition( m_oldvalue ); } //---------------- InsertProjectCmd::InsertProjectCmd( Project &project, Node *parent, Node *after, const KUndo2MagicString& name ) : MacroCommand( name ), m_project( static_cast( parent->projectNode() ) ), m_parent( parent ) { Q_ASSERT( &project != m_project ); if ( m_project->defaultCalendar() ) { project.setDefaultCalendar( 0 ); // or else m_project default calendar may be overwitten } QString defaultAccount; if ( ! m_project->accounts().defaultAccount() && project.accounts().defaultAccount() ) { defaultAccount = project.accounts().defaultAccount()->name(); } QHash startupaccountmap; QHash shutdownaccountmap; QHash runningaccountmap; QHash nodecalendarmap; // remove unhandled info in tasks and get accounts and calendars foreach ( Node *n, project.allNodes() ) { if ( n->type() == Node::Type_Task ) { Task *t = static_cast( n ); t->workPackage().clear(); while ( ! t->workPackageLog().isEmpty() ) { WorkPackage *wp = t->workPackageLog().at( 0 ); t->removeWorkPackage( wp ); delete wp; } } if ( n->startupAccount() ) { startupaccountmap.insert( n, n->startupAccount()->name() ); n->setStartupAccount( 0 ); } if ( n->shutdownAccount() ) { shutdownaccountmap.insert( n, n->shutdownAccount()->name() ); n->setShutdownAccount( 0 ); } if ( n->runningAccount() ) { runningaccountmap.insert( n, n->runningAccount()->name() ); n->setRunningAccount( 0 ); } if ( n->estimate()->calendar() ) { nodecalendarmap.insert( n, n->estimate()->calendar()->id() ); n->estimate()->setCalendar( 0 ); } } // get resources pointing to calendars and accounts QHash resaccountmap; QHash rescalendarmap; foreach ( Resource *r, project.resourceList() ) { if ( r->account() ) { resaccountmap.insert( r, r->account()->name() ); r->setAccount( 0 ); } if ( r->calendar() ) { rescalendarmap.insert( r, r->calendar()->id() ); r->setCalendar( 0 ); } } // create add account commands and keep track of used and unused accounts QList unusedAccounts; QMap accountsmap; foreach ( Account *a, m_project->accounts().allAccounts() ) { accountsmap.insert( a->name(), a ); } foreach ( Account *a, project.accounts().accountList() ) { addAccounts( a, 0, unusedAccounts, accountsmap ); } // create add calendar commands and keep track of used and unused calendars QList unusedCalendars; QMap calendarsmap; foreach ( Calendar *c, m_project->allCalendars() ) { calendarsmap.insert( c->id(), c ); } foreach ( Calendar *c, project.calendars() ) { addCalendars( c, 0, unusedCalendars, calendarsmap ); } // get all requests before resources are merged QHash > greqs; QHash > rreqs; foreach ( Node *n, project.allNodes() ) { QList resReq; if ( n->type() != (int)Node::Type_Task || n->requests().isEmpty() ) { continue; } while ( ResourceGroupRequest *gr = n->requests().requests().value( 0 ) ) { while ( ResourceRequest *rr = gr->resourceRequests( false ).value( 0 ) ) { debugPlanInsertProject<<"Get resource request:"<( rr, rr->resource() ) ); // all resource requests shall be reinserted rr->unregisterRequest(); gr->takeResourceRequest( rr ); } // all group requests shall be reinserted greqs[ gr ] = QPair( n, gr->group() ); gr->group()->unregisterRequest( gr ); int i = n->requests().takeRequest( gr ); Q_ASSERT( i >= 0 ); #ifdef NDEBUG Q_UNUSED(i); #endif } } QList allGroups; QList allResources; QList newResources; QHash existingGroups; QHash existingResources; foreach ( ResourceGroup *g, project.resourceGroups() ) { ResourceGroup *gr = m_project->findResourceGroup( g->id() ); if ( gr == 0 ) { addCommand( new AddResourceGroupCmd( m_project, g, kundo2_noi18n("ResourceGroup") ) ); gr = g; debugPlanInsertProject<<"AddResourceGroupCmd:"<name(); } else { existingGroups[ gr ] = g; } allGroups << gr; foreach ( Resource *r, g->resources() ) { while ( Schedule *s = r->schedules().values().value( 0 ) ) { r->deleteSchedule( s ); // schedules not handled } Resource *res = m_project->findResource( r->id() ); if ( res == 0 ) { addCommand( new AddResourceCmd( gr, r, kundo2_noi18n("Resource") ) ); allResources << r; newResources << r; debugPlanInsertProject<<"AddResourceCmd:"<name()<name(); } else { existingResources[ res ] = r; allResources << res; } } } // Update resource account {QHash::const_iterator it = resaccountmap.constBegin(); QHash::const_iterator end = resaccountmap.constEnd(); for ( ; it != end; ++it ) { Resource *r = it.key(); if ( newResources.contains( r ) ) { Q_ASSERT( allResources.contains( r ) ); addCommand( new ResourceModifyAccountCmd( *r, 0, accountsmap.value( it.value() ) ) ); } }} // Update resource calendar {QHash::const_iterator it = rescalendarmap.constBegin(); QHash::const_iterator end = rescalendarmap.constEnd(); for ( ; it != end; ++it ) { Resource *r = it.key(); if ( newResources.contains( r ) ) { Q_ASSERT( allResources.contains( r ) ); addCommand( new ModifyResourceCalendarCmd( r, calendarsmap.value( it.value() ) ) ); } }} // Requests: clean up requests to resources already in m_project int gi = 0; int ri = 0; QHash >::const_iterator gregsIt; for (gregsIt = greqs.constBegin(); gregsIt != greqs.constEnd(); ++gregsIt) { ResourceGroupRequest *gr = gregsIt.key(); QPair pair = gregsIt.value(); Node *n = pair.first; ResourceGroup *newGroup = pair.second; if (ResourceGroup *ng = existingGroups.key(newGroup)) { newGroup = ng; } Q_ASSERT( allGroups.contains( newGroup ) ); gr->setGroup( newGroup ); addCommand( new AddResourceGroupRequestCmd( static_cast( *n ), gr, kundo2_noi18n("Group %1", ++gi) ) ); debugPlanInsertProject<<"Add resource group request:"<name()<<":"<name(); QHash >::const_iterator i = rreqs.constFind( gr ); for ( ; i != rreqs.constEnd() && i.key() == gr; ++i ) { ResourceRequest *rr = i.value().first; Resource *newRes = i.value().second; if (Resource *nr = existingResources.key(newRes)) { newRes = nr; } debugPlanInsertProject<<"Add resource request:"<name()<<":"<name()<<":"<name(); if ( ! rr->requiredResources().isEmpty() ) { // the resource request may have required resources that needs mapping QList required; foreach ( Resource *r, rr->requiredResources() ) { if ( newResources.contains( r ) ) { required << r; debugPlanInsertProject<<"Request: required (new)"<name(); continue; } Resource *r2 = existingResources.key( r ); Q_ASSERT( allResources.contains( r2 ) ); if ( r2 ) { debugPlanInsertProject<<"Request: required (existing)"<name(); required << r2; } } rr->setRequiredResources( required ); } Q_ASSERT( allResources.contains( newRes ) ); // all resource requests shall be reinserted rr->setResource( newRes ); addCommand( new AddResourceRequestCmd( gr, rr, kundo2_noi18n("Resource %1", ++ri) ) ); } } // Add nodes ( ids are unique, no need to check ) Node *node_after = after; for ( int i = 0; i < project.numChildren(); ++i ) { Node *n = project.childNode( i ); Q_ASSERT( n ); while ( Schedule *s = n->schedules().values().value( 0 ) ) { n->takeSchedule( s ); // schedules not handled delete s; } n->setParentNode( 0 ); if ( node_after ) { addCommand( new TaskAddCmd( m_project, n, node_after, kundo2_noi18n("Task") ) ); node_after = n; } else { addCommand( new SubtaskAddCmd( m_project, n, parent, kundo2_noi18n("Subtask") ) ); } addChildNodes( n ); } // Dependencies: foreach ( Node *n, project.allNodes() ) { while ( n->numDependChildNodes() > 0 ) { Relation *r = n->dependChildNodes().at( 0 ); n->takeDependChildNode( r ); r->child()->takeDependParentNode( r ); addCommand( new AddRelationCmd( *m_project, r ) ); } } // node calendar {QHash::const_iterator it = nodecalendarmap.constBegin(); QHash::const_iterator end = nodecalendarmap.constEnd(); for ( ; it != end; ++it ) { addCommand( new ModifyEstimateCalendarCmd( *(it.key()), 0, calendarsmap.value( it.value() ) ) ); }} // node startup account {QHash::const_iterator it = startupaccountmap.constBegin(); QHash::const_iterator end = startupaccountmap.constEnd(); for ( ; it != end; ++it ) { addCommand( new NodeModifyStartupAccountCmd( *(it.key()), 0, accountsmap.value( it.value() ) ) ); }} // node shutdown account {QHash::const_iterator it = shutdownaccountmap.constBegin(); QHash::const_iterator end = shutdownaccountmap.constEnd(); for ( ; it != end; ++it ) { addCommand( new NodeModifyShutdownAccountCmd( *(it.key()), 0, accountsmap.value( it.value() ) ) ); }} // node running account {QHash::const_iterator it = runningaccountmap.constBegin(); QHash::const_iterator end = runningaccountmap.constEnd(); for ( ; it != end; ++it ) { addCommand( new NodeModifyRunningAccountCmd( *(it.key()), 0, accountsmap.value( it.value() ) ) ); }} if ( ! defaultAccount.isEmpty() ) { Account *a = accountsmap.value( defaultAccount ); if ( a && a->list() ) { addCommand( new ModifyDefaultAccountCmd( m_project->accounts(), 0, a ) ); } } // Cleanup // Remove nodes from project so they are not deleted while ( Node *ch = project.childNode( 0 ) ) { project.takeChildNode( ch ); } foreach ( Node *n, project.allNodes() ) { project.removeId( n->id() ); } // Remove calendars from project while ( project.calendarCount() > 0 ) { project.takeCalendar( project.calendarAt( 0 ) ); } qDeleteAll( unusedCalendars ); // Remove accounts from project while ( project.accounts().accountCount() > 0 ) { project.accounts().take( project.accounts().accountAt( 0 ) ); } qDeleteAll( unusedAccounts ); while ( project.numResourceGroups() > 0 ) { ResourceGroup *g = project.resourceGroupAt( 0 ); while ( g->numResources() > 0 ) { g->takeResource( g->resourceAt( 0 ) ); } project.takeResourceGroup( g ); } qDeleteAll( existingResources ); // deletes unused resources qDeleteAll( existingGroups ); // deletes unused resource groups } void InsertProjectCmd::addCalendars( Calendar *calendar, Calendar *parent, QList &unused, QMap &calendarsmap ) { Calendar *par = 0; if ( parent ) { par = calendarsmap.value( parent->id() ); } if ( par == 0 ) { par = parent; } Calendar *cal = calendarsmap.value( calendar->id() ); if ( cal == 0 ) { calendarsmap.insert( calendar->id(), calendar ); addCommand( new CalendarAddCmd( m_project, calendar, -1, par ) ); } else { unused << calendar; } foreach ( Calendar *c, calendar->calendars() ) { addCalendars( c, calendar, unused, calendarsmap ); } } void InsertProjectCmd::addAccounts( Account *account, Account *parent, QList &unused, QMap &accountsmap ) { Account *par = 0; if ( parent ) { par = accountsmap.value( parent->name() ); } if ( par == 0 ) { par = parent; } Account *acc = accountsmap.value( account->name() ); if ( acc == 0 ) { debugPlanInsertProject<<"Move to new project:"<name(); accountsmap.insert( account->name(), account ); addCommand( new AddAccountCmd( *m_project, account, par, -1, kundo2_noi18n( "Add account %1", account->name() ) ) ); } else { debugPlanInsertProject<<"Already exists:"<name(); unused << account; } while ( ! account->accountList().isEmpty() ) { Account *a = account->accountList().first(); account->list()->take( a ); addAccounts( a, account, unused, accountsmap ); } } void InsertProjectCmd::addChildNodes( Node *node ) { // schedules not handled while ( Schedule *s = node->schedules().values().value( 0 ) ) { node->takeSchedule( s ); // schedules not handled delete s; } foreach ( Node *n, node->childNodeIterator() ) { n->setParentNode( 0 ); addCommand( new SubtaskAddCmd( m_project, n, node, kundo2_noi18n("Subtask") ) ); addChildNodes( n ); } // Remove child nodes so they are not added twice while ( Node *ch = node->childNode( 0 ) ) { node->takeChildNode( ch ); } } void InsertProjectCmd::execute() { QApplication::setOverrideCursor( Qt::WaitCursor ); MacroCommand::execute(); QApplication::restoreOverrideCursor(); } void InsertProjectCmd::unexecute() { QApplication::setOverrideCursor( Qt::WaitCursor ); MacroCommand::unexecute(); QApplication::restoreOverrideCursor(); } WorkPackageAddCmd::WorkPackageAddCmd( Project *project, Node *node, WorkPackage *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_project( project ), m_node( node ), m_wp( value ), m_mine( true ) { } WorkPackageAddCmd::~WorkPackageAddCmd() { if ( m_mine ) { delete m_wp; } } void WorkPackageAddCmd::execute() { // FIXME use project //m_project->addWorkPackage( m_node, m_wp ); static_cast( m_node )->addWorkPackage( m_wp ); } void WorkPackageAddCmd::unexecute() { // FIXME use project //m_project->removeWorkPackage( m_node, m_wp ); static_cast( m_node )->removeWorkPackage( m_wp ); } ModifyProjectLocaleCmd::ModifyProjectLocaleCmd( Project &project, const KUndo2MagicString& name ) : MacroCommand( name ), m_project( project ) { }; void ModifyProjectLocaleCmd::execute() { MacroCommand::execute(); m_project.emitLocaleChanged(); } void ModifyProjectLocaleCmd::unexecute() { MacroCommand::unexecute(); m_project.emitLocaleChanged(); } ModifyCurrencySymolCmd::ModifyCurrencySymolCmd( Locale *locale, const QString &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_locale( locale ), m_newvalue( value ), m_oldvalue( locale->currencySymbol() ) { }; void ModifyCurrencySymolCmd::execute() { m_locale->setCurrencySymbol( m_newvalue ); } void ModifyCurrencySymolCmd::unexecute() { m_locale->setCurrencySymbol( m_oldvalue ); } ModifyCurrencyFractionalDigitsCmd::ModifyCurrencyFractionalDigitsCmd( Locale *locale, int value, const KUndo2MagicString& name ) : NamedCommand( name ), m_locale( locale ), m_newvalue( value ), m_oldvalue(locale->monetaryDecimalPlaces()) { }; void ModifyCurrencyFractionalDigitsCmd::execute() { m_locale->setMonetaryDecimalPlaces(m_newvalue); } void ModifyCurrencyFractionalDigitsCmd::unexecute() { m_locale->setMonetaryDecimalPlaces(m_oldvalue); } AddExternalAppointmentCmd::AddExternalAppointmentCmd( Resource *resource, const QString &pid, const QString &pname, const QDateTime &start, const QDateTime &end, double load, const KUndo2MagicString& name ) : NamedCommand( name ), m_resource( resource ), m_pid( pid ), m_pname( pname ), m_start( start ), m_end( end ), m_load( load ) { } void AddExternalAppointmentCmd::execute() { m_resource->addExternalAppointment( m_pid, m_pname, m_start, m_end, m_load ); } void AddExternalAppointmentCmd::unexecute() { m_resource->subtractExternalAppointment( m_pid, m_start, m_end, m_load ); // FIXME do this smarter if ( ! m_resource->externalAppointments( m_pid ).isEmpty() ) { m_resource->takeExternalAppointment( m_pid ); } } ClearExternalAppointmentCmd::ClearExternalAppointmentCmd( Resource *resource, const QString &pid, const KUndo2MagicString &name ) : NamedCommand( name ), m_resource( resource ), m_pid( pid ), m_appointments( 0 ) { } ClearExternalAppointmentCmd::~ClearExternalAppointmentCmd() { delete m_appointments; } void ClearExternalAppointmentCmd::execute() { // debugPlan<name()<takeExternalAppointment( m_pid ); } void ClearExternalAppointmentCmd::unexecute() { // debugPlan<name()<addExternalAppointment( m_pid, m_appointments ); } m_appointments = 0; } ClearAllExternalAppointmentsCmd::ClearAllExternalAppointmentsCmd( Project *project, const KUndo2MagicString &name ) : NamedCommand( name ), m_project( project ) { foreach ( Resource *r, project->resourceList() ) { const QMap map = r->externalProjects(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { m_cmd.addCommand(new ClearExternalAppointmentCmd(r, it.key())); } } } void ClearAllExternalAppointmentsCmd::execute() { m_cmd.redo(); } void ClearAllExternalAppointmentsCmd::unexecute() { m_cmd.undo(); } SharedResourcesFileCmd::SharedResourcesFileCmd(Project *project, const QString &newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->sharedResourcesFile()) , m_newValue(newValue) { } void SharedResourcesFileCmd::execute() { m_project->setSharedResourcesFile(m_newValue); } void SharedResourcesFileCmd::unexecute() { m_project->setSharedResourcesFile(m_oldValue); } UseSharedResourcesCmd::UseSharedResourcesCmd(Project *project, bool newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->useSharedResources()) , m_newValue(newValue) { } void UseSharedResourcesCmd::execute() { m_project->setUseSharedResources(m_newValue); } void UseSharedResourcesCmd::unexecute() { m_project->setUseSharedResources(m_oldValue); } SharedProjectsUrlCmd::SharedProjectsUrlCmd(Project *project, const QUrl &newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->sharedProjectsUrl()) , m_newValue(newValue) { } void SharedProjectsUrlCmd::execute() { m_project->setSharedProjectsUrl(m_newValue); } void SharedProjectsUrlCmd::unexecute() { m_project->setSharedProjectsUrl(m_oldValue); } LoadProjectsAtStartupCmd::LoadProjectsAtStartupCmd(Project *project, bool newValue, const KUndo2MagicString& name) : NamedCommand(name) , m_project(project) , m_oldValue(project->loadProjectsAtStartup()) , m_newValue(newValue) { } void LoadProjectsAtStartupCmd::execute() { m_project->setLoadProjectsAtStartup(m_newValue); } void LoadProjectsAtStartupCmd::unexecute() { m_project->setLoadProjectsAtStartup(m_oldValue); } } //KPlato namespace diff --git a/src/libs/kernel/kptcommand.h b/src/libs/kernel/kptcommand.h index f8d944db..af1e4318 100644 --- a/src/libs/kernel/kptcommand.h +++ b/src/libs/kernel/kptcommand.h @@ -1,2017 +1,2030 @@ /* This file is part of the KDE project Copyright (C) 2004-2007 Dag Andersen Copyright (C) 2011 Dag Andersen Copyright (C) 2016 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTCOMMAND_H #define KPTCOMMAND_H #include "plankernel_export.h" #include #include #include #include "kptappointment.h" #include "kptnode.h" +#include "kptproject.h" #include "kptduration.h" #include "kpttask.h" #include "kptwbsdefinition.h" class QString; /** * @file * This file includes undo/redo commands for kernel data structures */ /// The main namespace namespace KPlato { class Locale; class Account; class Accounts; -class Project; -class Task; class Calendar; class CalendarDay; class Relation; class ResourceGroupRequest; class ResourceRequest; class ResourceGroup; class Resource; class Schedule; class StandardWorktime; class PLANKERNEL_EXPORT NamedCommand : public KUndo2Command { public: explicit NamedCommand( const KUndo2MagicString& name ) : KUndo2Command( name ) {} virtual void redo() { execute(); } virtual void undo() { unexecute(); } virtual void execute() = 0; virtual void unexecute() = 0; protected: /// Set all scheduled in the m_schedules map to their original scheduled state void setSchScheduled(); /// Set all schedules in the m_schedules map to scheduled state @p state void setSchScheduled( bool state ); /// Add a schedule to the m_schedules map along with its current scheduled state void addSchScheduled( Schedule *sch ); QHash m_schedules; }; class PLANKERNEL_EXPORT MacroCommand : public KUndo2Command { public: explicit MacroCommand( const KUndo2MagicString& name = KUndo2MagicString() ) : KUndo2Command( name ) {} ~MacroCommand(); void addCommand( KUndo2Command *cmd ); virtual void redo() { execute(); } virtual void undo() { unexecute(); } virtual void execute(); virtual void unexecute(); bool isEmpty() const { return cmds.isEmpty(); } protected: QList cmds; }; class PLANKERNEL_EXPORT CalendarAddCmd : public NamedCommand { public: CalendarAddCmd( Project *project, Calendar *cal, int pos, Calendar *parent, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarAddCmd(); void execute(); void unexecute(); private: Project *m_project; Calendar *m_cal; int m_pos; Calendar *m_parent; bool m_mine; }; class PLANKERNEL_EXPORT CalendarRemoveCmd : public NamedCommand { public: CalendarRemoveCmd( Project *project, Calendar *cal, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarRemoveCmd(); void execute(); void unexecute(); private: Project *m_project; Calendar *m_parent; Calendar *m_cal; int m_index; bool m_mine; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT CalendarMoveCmd : public NamedCommand { public: CalendarMoveCmd( Project *project, Calendar *cal, int position, Calendar *parent, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project *m_project; Calendar *m_cal; int m_newpos; int m_oldpos; Calendar *m_newparent; Calendar *m_oldparent; }; class PLANKERNEL_EXPORT CalendarModifyNameCmd : public NamedCommand { public: CalendarModifyNameCmd( Calendar *cal, const QString& newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Calendar *m_cal; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT CalendarModifyParentCmd : public NamedCommand { public: CalendarModifyParentCmd( Project *project, Calendar *cal, Calendar *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarModifyParentCmd(); void execute(); void unexecute(); private: Project *m_project; Calendar *m_cal; Calendar *m_newvalue; Calendar *m_oldvalue; MacroCommand *m_cmd; int m_oldindex; int m_newindex; }; class PLANKERNEL_EXPORT CalendarModifyTimeZoneCmd : public NamedCommand { public: CalendarModifyTimeZoneCmd( Calendar *cal, const QTimeZone &value, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarModifyTimeZoneCmd(); void execute(); void unexecute(); private: Calendar *m_cal; QTimeZone m_newvalue; QTimeZone m_oldvalue; MacroCommand *m_cmd; }; #ifdef HAVE_KHOLIDAYS class PLANKERNEL_EXPORT CalendarModifyHolidayRegionCmd : public NamedCommand { public: CalendarModifyHolidayRegionCmd( Calendar *cal, const QString &value, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarModifyHolidayRegionCmd(); void execute(); void unexecute(); private: Calendar *m_cal; QString m_newvalue; QString m_oldvalue; }; #endif class PLANKERNEL_EXPORT CalendarAddDayCmd : public NamedCommand { public: CalendarAddDayCmd( Calendar *cal, CalendarDay *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarAddDayCmd(); void execute(); void unexecute(); protected: Calendar *m_cal; CalendarDay *m_newvalue; bool m_mine; }; class PLANKERNEL_EXPORT CalendarRemoveDayCmd : public NamedCommand { public: CalendarRemoveDayCmd( Calendar *cal, CalendarDay *day, const KUndo2MagicString& name = KUndo2MagicString() ); CalendarRemoveDayCmd( Calendar *cal, const QDate &day, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); protected: Calendar *m_cal; CalendarDay *m_value; bool m_mine; private: void init(); }; class PLANKERNEL_EXPORT CalendarModifyDayCmd : public NamedCommand { public: CalendarModifyDayCmd( Calendar *cal, CalendarDay *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarModifyDayCmd(); void execute(); void unexecute(); private: Calendar *m_cal; CalendarDay *m_newvalue; CalendarDay *m_oldvalue; bool m_mine; }; class PLANKERNEL_EXPORT CalendarModifyStateCmd : public NamedCommand { public: CalendarModifyStateCmd( Calendar *calendar, CalendarDay *day, CalendarDay::State value, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarModifyStateCmd(); void execute(); void unexecute(); private: Calendar *m_calendar; CalendarDay *m_day; CalendarDay::State m_newvalue; CalendarDay::State m_oldvalue; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT CalendarModifyTimeIntervalCmd : public NamedCommand { public: CalendarModifyTimeIntervalCmd( Calendar *calendar, TimeInterval &newvalue, TimeInterval *value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Calendar *m_calendar; TimeInterval *m_value; TimeInterval m_newvalue; TimeInterval m_oldvalue; }; class PLANKERNEL_EXPORT CalendarAddTimeIntervalCmd : public NamedCommand { public: CalendarAddTimeIntervalCmd( Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarAddTimeIntervalCmd(); void execute(); void unexecute(); protected: Calendar *m_calendar; CalendarDay *m_day; TimeInterval *m_value; bool m_mine; }; class PLANKERNEL_EXPORT CalendarRemoveTimeIntervalCmd : public CalendarAddTimeIntervalCmd { public: CalendarRemoveTimeIntervalCmd( Calendar *calendar, CalendarDay *day, TimeInterval *value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); }; class PLANKERNEL_EXPORT CalendarModifyWeekdayCmd : public NamedCommand { public: CalendarModifyWeekdayCmd( Calendar *cal, int weekday, CalendarDay *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~CalendarModifyWeekdayCmd(); void execute(); void unexecute(); private: int m_weekday; Calendar *m_cal; CalendarDay *m_value; CalendarDay m_orig; }; class PLANKERNEL_EXPORT CalendarModifyDateCmd : public NamedCommand { public: CalendarModifyDateCmd( Calendar *cal, CalendarDay *day, const QDate &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Calendar *m_cal; CalendarDay *m_day; QDate m_newvalue, m_oldvalue; }; class PLANKERNEL_EXPORT ProjectModifyDefaultCalendarCmd : public NamedCommand { public: ProjectModifyDefaultCalendarCmd( Project *project, Calendar *cal, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project *m_project; Calendar *m_newvalue, *m_oldvalue; }; class PLANKERNEL_EXPORT NodeDeleteCmd : public NamedCommand { public: explicit NodeDeleteCmd( Node *node, const KUndo2MagicString& name = KUndo2MagicString() ); ~NodeDeleteCmd(); void execute(); void unexecute(); private: Node *m_node; Node *m_parent; Project *m_project; int m_index; bool m_mine; QList m_appointments; MacroCommand *m_cmd; MacroCommand *m_relCmd; }; class PLANKERNEL_EXPORT TaskAddCmd : public NamedCommand { public: TaskAddCmd( Project *project, Node *node, Node *after, const KUndo2MagicString& name = KUndo2MagicString() ); ~TaskAddCmd(); void execute(); void unexecute(); private: Project *m_project; Node *m_node; Node *m_after; bool m_added; }; class PLANKERNEL_EXPORT SubtaskAddCmd : public NamedCommand { public: SubtaskAddCmd( Project *project, Node *node, Node *parent, const KUndo2MagicString& name = KUndo2MagicString() ); ~SubtaskAddCmd(); void execute(); void unexecute(); private: Project *m_project; Node *m_node; Node *m_parent; bool m_added; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT NodeModifyNameCmd : public NamedCommand { public: NodeModifyNameCmd( Node &node, const QString& nodename, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QString newName; QString oldName; }; class PLANKERNEL_EXPORT NodeModifyPriorityCmd : public NamedCommand { public: NodeModifyPriorityCmd(Node &node, int oldValue, int newValue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; int m_oldValue; int m_newValue; }; class PLANKERNEL_EXPORT NodeModifyLeaderCmd : public NamedCommand { public: NodeModifyLeaderCmd( Node &node, const QString& leader, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QString newLeader; QString oldLeader; }; class PLANKERNEL_EXPORT NodeModifyDescriptionCmd : public NamedCommand { public: NodeModifyDescriptionCmd( Node &node, const QString& description, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QString newDescription; QString oldDescription; }; class PLANKERNEL_EXPORT NodeModifyConstraintCmd : public NamedCommand { public: NodeModifyConstraintCmd( Node &node, Node::ConstraintType c, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Node::ConstraintType newConstraint; Node::ConstraintType oldConstraint; }; class PLANKERNEL_EXPORT NodeModifyConstraintStartTimeCmd : public NamedCommand { public: NodeModifyConstraintStartTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyConstraintEndTimeCmd : public NamedCommand { public: NodeModifyConstraintEndTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyStartTimeCmd : public NamedCommand { public: NodeModifyStartTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyEndTimeCmd : public NamedCommand { public: NodeModifyEndTimeCmd( Node &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT NodeModifyIdCmd : public NamedCommand { public: NodeModifyIdCmd( Node &node, const QString& id, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; QString newId; QString oldId; }; class PLANKERNEL_EXPORT NodeIndentCmd : public NamedCommand { public: explicit NodeIndentCmd( Node &node, const KUndo2MagicString& name = KUndo2MagicString() ); ~NodeIndentCmd(); void execute(); void unexecute(); private: Node &m_node; Node *m_oldparent, *m_newparent; int m_oldindex, m_newindex; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT NodeUnindentCmd : public NamedCommand { public: explicit NodeUnindentCmd( Node &node, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Node *m_oldparent, *m_newparent; int m_oldindex, m_newindex; }; class PLANKERNEL_EXPORT NodeMoveUpCmd : public NamedCommand { public: explicit NodeMoveUpCmd( Node &node, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Project *m_project; bool m_moved; }; class PLANKERNEL_EXPORT NodeMoveDownCmd : public NamedCommand { public: explicit NodeMoveDownCmd( Node &node, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Project *m_project; bool m_moved; }; class PLANKERNEL_EXPORT NodeMoveCmd : public NamedCommand { public: NodeMoveCmd( Project *project, Node *node, Node *newParent, int newPos, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project *m_project; Node *m_node; Node *m_newparent; Node *m_oldparent; int m_newpos; int m_oldpos; bool m_moved; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT AddRelationCmd : public NamedCommand { public: AddRelationCmd( Project &project, Relation *rel, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddRelationCmd(); void execute(); void unexecute(); private: Relation *m_rel; Project &m_project; bool m_taken; }; class PLANKERNEL_EXPORT DeleteRelationCmd : public NamedCommand { public: DeleteRelationCmd( Project &project, Relation *rel, const KUndo2MagicString& name = KUndo2MagicString() ); ~DeleteRelationCmd(); void execute(); void unexecute(); private: Relation *m_rel; Project &m_project; bool m_taken; }; class PLANKERNEL_EXPORT ModifyRelationTypeCmd : public NamedCommand { public: ModifyRelationTypeCmd( Relation *rel, Relation::Type type, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project *m_project; Relation *m_rel; Relation::Type m_newtype; Relation::Type m_oldtype; }; class PLANKERNEL_EXPORT ModifyRelationLagCmd : public NamedCommand { public: ModifyRelationLagCmd( Relation *rel, Duration lag, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project *m_project; Relation *m_rel; Duration m_newlag; Duration m_oldlag; }; class PLANKERNEL_EXPORT AddResourceRequestCmd : public NamedCommand { public: AddResourceRequestCmd( ResourceGroupRequest *group, ResourceRequest *request, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddResourceRequestCmd(); void execute(); void unexecute(); private: ResourceGroupRequest *m_group; ResourceRequest *m_request; bool m_mine; }; class PLANKERNEL_EXPORT RemoveResourceRequestCmd : public NamedCommand { public: RemoveResourceRequestCmd( ResourceGroupRequest *group, ResourceRequest *request, const KUndo2MagicString& name = KUndo2MagicString() ); ~RemoveResourceRequestCmd(); void execute(); void unexecute(); private: ResourceGroupRequest *m_group; ResourceRequest *m_request; bool m_mine; }; class PLANKERNEL_EXPORT ModifyResourceRequestUnitsCmd : public NamedCommand { public: ModifyResourceRequestUnitsCmd( ResourceRequest *request, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ResourceRequest *m_request; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyResourceRequestRequiredCmd : public NamedCommand { public: ModifyResourceRequestRequiredCmd( ResourceRequest *request, const QList &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ResourceRequest *m_request; QList m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyResourceGroupRequestUnitsCmd : public NamedCommand { public: ModifyResourceGroupRequestUnitsCmd( ResourceGroupRequest *request, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ResourceGroupRequest *m_request; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateCmd : public NamedCommand { public: ModifyEstimateCmd( Node &node, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); ~ModifyEstimateCmd(); void execute(); void unexecute(); private: Estimate *m_estimate; double m_oldvalue, m_newvalue; int m_optimistic, m_pessimistic; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT EstimateModifyOptimisticRatioCmd : public NamedCommand { public: EstimateModifyOptimisticRatioCmd( Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT EstimateModifyPessimisticRatioCmd : public NamedCommand { public: EstimateModifyPessimisticRatioCmd( Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateTypeCmd : public NamedCommand { public: ModifyEstimateTypeCmd( Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateUnitCmd : public NamedCommand { public: ModifyEstimateUnitCmd( Node &node, Duration::Unit oldvalue, Duration::Unit newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Estimate *m_estimate; Duration::Unit m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT EstimateModifyRiskCmd : public NamedCommand { public: EstimateModifyRiskCmd( Node &node, int oldvalue, int newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Estimate *m_estimate; int m_oldvalue, m_newvalue; }; class PLANKERNEL_EXPORT ModifyEstimateCalendarCmd : public NamedCommand { public: ModifyEstimateCalendarCmd( Node &node, Calendar *oldvalue, Calendar *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Estimate *m_estimate; Calendar *m_oldvalue, *m_newvalue; }; class PLANKERNEL_EXPORT AddResourceGroupRequestCmd : public NamedCommand { public: AddResourceGroupRequestCmd( Task &task, ResourceGroupRequest *request, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Task &m_task; ResourceGroupRequest *m_request; bool m_mine; }; class PLANKERNEL_EXPORT RemoveResourceGroupRequestCmd : public NamedCommand { public: explicit RemoveResourceGroupRequestCmd( ResourceGroupRequest *request, const KUndo2MagicString& name = KUndo2MagicString() ); RemoveResourceGroupRequestCmd( Task &task, ResourceGroupRequest *request, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Task &m_task; ResourceGroupRequest *m_request; bool m_mine; }; class PLANKERNEL_EXPORT AddResourceCmd : public NamedCommand { public: AddResourceCmd( ResourceGroup *group, Resource *resource, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddResourceCmd(); void execute(); void unexecute(); protected: ResourceGroup *m_group; Resource *m_resource; int m_index; bool m_mine; }; class PLANKERNEL_EXPORT RemoveResourceCmd : public AddResourceCmd { public: RemoveResourceCmd( ResourceGroup *group, Resource *resource, const KUndo2MagicString& name = KUndo2MagicString() ); ~RemoveResourceCmd(); void execute(); void unexecute(); private: QList m_requests; QList m_appointments; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT MoveResourceCmd : public NamedCommand { public: MoveResourceCmd( ResourceGroup *group, Resource *resource, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project &m_project; Resource *m_resource; ResourceGroup *m_oldvalue, *m_newvalue; MacroCommand cmd; }; class PLANKERNEL_EXPORT ModifyResourceNameCmd : public NamedCommand { public: ModifyResourceNameCmd( Resource *resource, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceInitialsCmd : public NamedCommand { public: ModifyResourceInitialsCmd( Resource *resource, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceEmailCmd : public NamedCommand { public: ModifyResourceEmailCmd( Resource *resource, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceAutoAllocateCmd : public NamedCommand { public: ModifyResourceAutoAllocateCmd( Resource *resource, bool value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; bool m_newvalue; bool m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceTypeCmd : public NamedCommand { public: ModifyResourceTypeCmd( Resource *resource, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceUnitsCmd : public NamedCommand { public: ModifyResourceUnitsCmd( Resource *resource, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceAvailableFromCmd : public NamedCommand { public: ModifyResourceAvailableFromCmd( Resource *resource, const QDateTime& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QDateTime m_newvalue; DateTime m_oldvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ModifyResourceAvailableUntilCmd : public NamedCommand { public: ModifyResourceAvailableUntilCmd( Resource *resource, const QDateTime& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QDateTime m_newvalue; DateTime m_oldvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ModifyResourceNormalRateCmd : public NamedCommand { public: ModifyResourceNormalRateCmd( Resource *resource, double value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; double m_newvalue; double m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceOvertimeRateCmd : public NamedCommand { public: ModifyResourceOvertimeRateCmd( Resource *resource, double value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; double m_newvalue; double m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceCalendarCmd : public NamedCommand { public: ModifyResourceCalendarCmd( Resource *resource, Calendar *value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; Calendar *m_newvalue; Calendar *m_oldvalue; }; class PLANKERNEL_EXPORT ModifyRequiredResourcesCmd : public NamedCommand { public: ModifyRequiredResourcesCmd( Resource *resource, const QStringList &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QStringList m_newvalue; QStringList m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceAccountCmd : public NamedCommand { public: ModifyResourceAccountCmd( Resource *resource, Account *account, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; Account *m_newvalue; Account *m_oldvalue; }; class PLANKERNEL_EXPORT AddResourceTeamCmd : public NamedCommand { public: AddResourceTeamCmd( Resource *team, const QString &member, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_team; QString m_member; }; class PLANKERNEL_EXPORT RemoveResourceTeamCmd : public NamedCommand { public: RemoveResourceTeamCmd( Resource *team, const QString &member, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_team; QString m_member; }; class PLANKERNEL_EXPORT RemoveResourceGroupCmd : public NamedCommand { public: RemoveResourceGroupCmd( Project *project, ResourceGroup *group, const KUndo2MagicString& name = KUndo2MagicString() ); ~RemoveResourceGroupCmd(); void execute(); void unexecute(); protected: ResourceGroup *m_group; Project *m_project; int m_index; bool m_mine; MacroCommand *m_cmd; }; class PLANKERNEL_EXPORT AddResourceGroupCmd : public RemoveResourceGroupCmd { public: AddResourceGroupCmd( Project *project, ResourceGroup *group, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); }; class PLANKERNEL_EXPORT ModifyResourceGroupNameCmd : public NamedCommand { public: ModifyResourceGroupNameCmd( ResourceGroup *group, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ResourceGroup *m_group; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyResourceGroupTypeCmd : public NamedCommand { public: ModifyResourceGroupTypeCmd( ResourceGroup *group, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ResourceGroup *m_group; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT ModifyCompletionEntrymodeCmd : public NamedCommand { public: ModifyCompletionEntrymodeCmd( Completion &completion, Completion::Entrymode value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; Completion::Entrymode oldvalue; Completion::Entrymode newvalue; }; class PLANKERNEL_EXPORT ModifyCompletionStartedCmd : public NamedCommand { public: ModifyCompletionStartedCmd( Completion &completion, bool value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; bool oldvalue; bool newvalue; }; class PLANKERNEL_EXPORT ModifyCompletionFinishedCmd : public NamedCommand { public: ModifyCompletionFinishedCmd( Completion &completion, bool value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; bool oldvalue; bool newvalue; }; class PLANKERNEL_EXPORT ModifyCompletionStartTimeCmd : public NamedCommand { public: ModifyCompletionStartTimeCmd( Completion &completion, const QDateTime &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; DateTime oldvalue; QDateTime newvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ModifyCompletionFinishTimeCmd : public NamedCommand { public: ModifyCompletionFinishTimeCmd( Completion &completion, const QDateTime &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; DateTime oldvalue; QDateTime newvalue; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT AddCompletionEntryCmd : public NamedCommand { public: AddCompletionEntryCmd( Completion &completion, const QDate &date, Completion::Entry *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddCompletionEntryCmd(); void execute(); void unexecute(); private: Completion &m_completion; QDate m_date; Completion::Entry *newvalue; bool m_newmine; }; class PLANKERNEL_EXPORT RemoveCompletionEntryCmd : public NamedCommand { public: RemoveCompletionEntryCmd( Completion &completion, const QDate& date, const KUndo2MagicString& name = KUndo2MagicString() ); ~RemoveCompletionEntryCmd(); void execute(); void unexecute(); private: Completion &m_completion; QDate m_date; Completion::Entry *value; bool m_mine; }; class PLANKERNEL_EXPORT ModifyCompletionEntryCmd : public NamedCommand { public: ModifyCompletionEntryCmd( Completion &completion, const QDate &date, Completion::Entry *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~ModifyCompletionEntryCmd(); void execute(); void unexecute(); private: MacroCommand *cmd; }; class PLANKERNEL_EXPORT ModifyCompletionPercentFinishedCmd : public NamedCommand { public: ModifyCompletionPercentFinishedCmd( Completion &completion, const QDate &date, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; QDate m_date; int m_newvalue, m_oldvalue; MacroCommand cmd; }; class PLANKERNEL_EXPORT ModifyCompletionRemainingEffortCmd : public NamedCommand { public: ModifyCompletionRemainingEffortCmd( Completion &completion, const QDate &date, const Duration &value, const KUndo2MagicString &name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; QDate m_date; Duration m_newvalue, m_oldvalue; MacroCommand cmd; }; class PLANKERNEL_EXPORT ModifyCompletionActualEffortCmd : public NamedCommand { public: ModifyCompletionActualEffortCmd( Completion &completion, const QDate &date, const Duration &value, const KUndo2MagicString &name = KUndo2MagicString() ); void execute(); void unexecute(); private: Completion &m_completion; QDate m_date; Duration m_newvalue, m_oldvalue; MacroCommand cmd; }; /** * Add used effort for @p resource. * Note that the used effort definition in @p value must contain entries for *all* dates. * If used effort is already defined it will be replaced. */ class PLANKERNEL_EXPORT AddCompletionUsedEffortCmd : public NamedCommand { public: AddCompletionUsedEffortCmd( Completion &completion, const Resource *resource, Completion::UsedEffort *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddCompletionUsedEffortCmd(); void execute(); void unexecute(); private: Completion &m_completion; const Resource *m_resource; Completion::UsedEffort *oldvalue; Completion::UsedEffort *newvalue; bool m_newmine, m_oldmine; }; class PLANKERNEL_EXPORT AddCompletionActualEffortCmd : public NamedCommand { public: AddCompletionActualEffortCmd( Completion::UsedEffort &ue, const QDate &date, const Completion::UsedEffort::ActualEffort &value, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddCompletionActualEffortCmd(); void execute(); void unexecute(); private: Completion::UsedEffort &m_usedEffort; QDate m_date; Completion::UsedEffort::ActualEffort oldvalue; Completion::UsedEffort::ActualEffort newvalue; }; class PLANKERNEL_EXPORT AddAccountCmd : public NamedCommand { public: AddAccountCmd( Project &project, Account *account, Account *parent = 0, int index = -1, const KUndo2MagicString& name = KUndo2MagicString() ); AddAccountCmd( Project &project, Account *account, const QString& parent, int index = -1, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddAccountCmd(); void execute(); void unexecute(); protected: bool m_mine; private: Project &m_project; Account *m_account; Account *m_parent; int m_index; QString m_parentName; }; class PLANKERNEL_EXPORT RemoveAccountCmd : public NamedCommand { public: RemoveAccountCmd( Project &project, Account *account, const KUndo2MagicString& name = KUndo2MagicString() ); ~RemoveAccountCmd(); void execute(); void unexecute(); private: Project &m_project; Account *m_account; Account *m_parent; int m_index; bool m_isDefault; bool m_mine; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT RenameAccountCmd : public NamedCommand { public: RenameAccountCmd( Account *account, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Account *m_account; QString m_oldvalue; QString m_newvalue; }; class PLANKERNEL_EXPORT ModifyAccountDescriptionCmd : public NamedCommand { public: ModifyAccountDescriptionCmd( Account *account, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Account *m_account; QString m_oldvalue; QString m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyStartupCostCmd : public NamedCommand { public: NodeModifyStartupCostCmd( Node &node, double value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyShutdownCostCmd : public NamedCommand { public: NodeModifyShutdownCostCmd( Node &node, double value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyRunningAccountCmd : public NamedCommand { public: NodeModifyRunningAccountCmd( Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyStartupAccountCmd : public NamedCommand { public: NodeModifyStartupAccountCmd( Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT NodeModifyShutdownAccountCmd : public NamedCommand { public: NodeModifyShutdownAccountCmd( Node &node, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Node &m_node; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT ModifyDefaultAccountCmd : public NamedCommand { public: ModifyDefaultAccountCmd( Accounts &acc, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Accounts &m_accounts; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT ResourceModifyAccountCmd : public NamedCommand { public: ResourceModifyAccountCmd( Resource &resource, Account *oldvalue, Account *newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource &m_resource; Account *m_oldvalue; Account *m_newvalue; }; class PLANKERNEL_EXPORT ProjectModifyConstraintCmd : public NamedCommand { public: ProjectModifyConstraintCmd( Project &node, Node::ConstraintType c, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project &m_node; Node::ConstraintType newConstraint; Node::ConstraintType oldConstraint; }; class PLANKERNEL_EXPORT ProjectModifyStartTimeCmd : public NamedCommand { public: ProjectModifyStartTimeCmd( Project &node, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; class PLANKERNEL_EXPORT ProjectModifyEndTimeCmd : public NamedCommand { public: ProjectModifyEndTimeCmd( Project &project, const QDateTime& dt, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project &m_node; QDateTime newTime; DateTime oldTime; QTimeZone m_timeZone; }; +class PLANKERNEL_EXPORT ProjectModifyWorkPackageInfoCmd : public NamedCommand +{ +public: + ProjectModifyWorkPackageInfoCmd( Project &project, const Project::WorkPackageInfo &wpi, const KUndo2MagicString& name = KUndo2MagicString() ); + void execute(); + void unexecute(); + +private: + Project &m_node; + Project::WorkPackageInfo m_newWpi; + Project::WorkPackageInfo m_oldWpi; +}; + class PLANKERNEL_EXPORT SwapScheduleManagerCmd : public NamedCommand { public: SwapScheduleManagerCmd( Project &project, ScheduleManager *from, ScheduleManager *to, const KUndo2MagicString& name = KUndo2MagicString() ); ~SwapScheduleManagerCmd(); void execute(); void unexecute(); protected: Project &m_node; ScheduleManager *m_from; ScheduleManager *m_to; }; class PLANKERNEL_EXPORT AddScheduleManagerCmd : public NamedCommand { public: AddScheduleManagerCmd( Project &project, ScheduleManager *sm, int index = -1, const KUndo2MagicString& name = KUndo2MagicString() ); AddScheduleManagerCmd( ScheduleManager *parent, ScheduleManager *sm, int index = -1, const KUndo2MagicString& name = KUndo2MagicString() ); ~AddScheduleManagerCmd(); void execute(); void unexecute(); protected: Project &m_node; ScheduleManager *m_parent; ScheduleManager *m_sm; int m_index; MainSchedule *m_exp; bool m_mine; }; class PLANKERNEL_EXPORT DeleteScheduleManagerCmd : public AddScheduleManagerCmd { public: DeleteScheduleManagerCmd( Project &project, ScheduleManager *sm, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: MacroCommand cmd; }; class PLANKERNEL_EXPORT MoveScheduleManagerCmd : public NamedCommand { public: MoveScheduleManagerCmd( ScheduleManager *sm, ScheduleManager *newparent, int newindex, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager *m_sm; ScheduleManager *m_oldparent; int m_oldindex; ScheduleManager *m_newparent; int m_newindex; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT ModifyScheduleManagerNameCmd : public NamedCommand { public: ModifyScheduleManagerNameCmd( ScheduleManager &sm, const QString& value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; QString oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerAllowOverbookingCmd : public NamedCommand { public: ModifyScheduleManagerAllowOverbookingCmd( ScheduleManager &sm, bool value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; bool oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerDistributionCmd : public NamedCommand { public: ModifyScheduleManagerDistributionCmd( ScheduleManager &sm, bool value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; bool oldvalue, newvalue; }; class PLANKERNEL_EXPORT CalculateScheduleCmd : public NamedCommand { public: CalculateScheduleCmd( Project &project, ScheduleManager *sm, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project &m_node; QPointer m_sm; bool m_first; MainSchedule *m_oldexpected; MainSchedule *m_newexpected; MacroCommand preCmd; MacroCommand postCmd; }; class PLANKERNEL_EXPORT BaselineScheduleCmd : public NamedCommand { public: explicit BaselineScheduleCmd( ScheduleManager &sm, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; }; class PLANKERNEL_EXPORT ResetBaselineScheduleCmd : public NamedCommand { public: explicit ResetBaselineScheduleCmd( ScheduleManager &sm, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulingDirectionCmd : public NamedCommand { public: ModifyScheduleManagerSchedulingDirectionCmd( ScheduleManager &sm, bool value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; bool oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulerCmd : public NamedCommand { public: ModifyScheduleManagerSchedulerCmd( ScheduleManager &sm, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; int oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyScheduleManagerSchedulingGranularityCmd : public NamedCommand { public: ModifyScheduleManagerSchedulingGranularityCmd( ScheduleManager &sm, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: ScheduleManager &m_sm; int oldvalue, newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeYearCmd : public NamedCommand { public: ModifyStandardWorktimeYearCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeMonthCmd : public NamedCommand { public: ModifyStandardWorktimeMonthCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeWeekCmd : public NamedCommand { public: ModifyStandardWorktimeWeekCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT ModifyStandardWorktimeDayCmd : public NamedCommand { public: ModifyStandardWorktimeDayCmd( StandardWorktime *wt, double oldvalue, double newvalue, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: StandardWorktime *swt; double m_oldvalue; double m_newvalue; }; class PLANKERNEL_EXPORT DocumentAddCmd : public NamedCommand { public: DocumentAddCmd( Documents& docs, Document *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~DocumentAddCmd(); void execute(); void unexecute(); private: Documents& m_docs; Document *m_value; bool m_mine; }; class PLANKERNEL_EXPORT DocumentRemoveCmd : public NamedCommand { public: DocumentRemoveCmd( Documents& docs, Document *value, const KUndo2MagicString& name = KUndo2MagicString() ); ~DocumentRemoveCmd(); void execute(); void unexecute(); private: Documents& m_docs; Document *m_value; bool m_mine; }; class PLANKERNEL_EXPORT DocumentModifyUrlCmd : public NamedCommand { public: DocumentModifyUrlCmd( Document *doc, const QUrl &url, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Document *m_doc; QUrl m_value; QUrl m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifyNameCmd : public NamedCommand { public: DocumentModifyNameCmd( Document *doc, const QString &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Document *m_doc; QString m_value; QString m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifyTypeCmd : public NamedCommand { public: DocumentModifyTypeCmd( Document *doc, Document::Type value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Document *m_doc; Document::Type m_value; Document::Type m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifyStatusCmd : public NamedCommand { public: DocumentModifyStatusCmd( Document *doc, const QString &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Document *m_doc; QString m_value; QString m_oldvalue; }; class PLANKERNEL_EXPORT DocumentModifySendAsCmd : public NamedCommand { public: DocumentModifySendAsCmd( Document *doc, const Document::SendAs value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Document *m_doc; Document::SendAs m_value; Document::SendAs m_oldvalue; }; class PLANKERNEL_EXPORT WBSDefinitionModifyCmd : public NamedCommand { public: WBSDefinitionModifyCmd( Project &project, const WBSDefinition value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project &m_project; WBSDefinition m_newvalue, m_oldvalue; }; class PLANKERNEL_EXPORT InsertProjectCmd : public MacroCommand { public: InsertProjectCmd( Project &project, Node *parent, Node *after, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); protected: void addAccounts( Account *account, Account *parent, QList &unused, QMap &all ); void addCalendars( Calendar *calendar, Calendar *parent, QList &unused, QMap &all ); void addChildNodes( Node *node ); private: Project *m_project; Node *m_parent; Node *m_after; }; class PLANKERNEL_EXPORT WorkPackageAddCmd : public NamedCommand { public: WorkPackageAddCmd( Project *project, Node *node, WorkPackage *wp, const KUndo2MagicString& name = KUndo2MagicString() ); ~WorkPackageAddCmd(); void execute(); void unexecute(); private: Project *m_project; Node *m_node; WorkPackage *m_wp; bool m_mine; }; class PLANKERNEL_EXPORT ModifyProjectLocaleCmd : public MacroCommand { public: ModifyProjectLocaleCmd( Project &project, const KUndo2MagicString &name ); void execute(); void unexecute(); private: Project &m_project; }; class PLANKERNEL_EXPORT ModifyCurrencySymolCmd : public NamedCommand { public: ModifyCurrencySymolCmd( Locale *locale, const QString &value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Locale *m_locale; QString m_newvalue; QString m_oldvalue; }; class PLANKERNEL_EXPORT ModifyCurrencyFractionalDigitsCmd : public NamedCommand { public: ModifyCurrencyFractionalDigitsCmd( Locale *locale, int value, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Locale *m_locale; int m_newvalue; int m_oldvalue; }; class PLANKERNEL_EXPORT AddExternalAppointmentCmd : public NamedCommand { public: AddExternalAppointmentCmd( Resource *resource, const QString &pid, const QString &pname, const QDateTime &start, const QDateTime &end, double load, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Resource *m_resource; QString m_pid; QString m_pname; QDateTime m_start; QDateTime m_end; double m_load; }; class PLANKERNEL_EXPORT ClearExternalAppointmentCmd : public NamedCommand { public: ClearExternalAppointmentCmd( Resource *resource, const QString &pid, const KUndo2MagicString& name = KUndo2MagicString() ); ~ClearExternalAppointmentCmd(); void execute(); void unexecute(); private: Resource *m_resource; QString m_pid; Appointment *m_appointments; }; class PLANKERNEL_EXPORT ClearAllExternalAppointmentsCmd : public NamedCommand { public: explicit ClearAllExternalAppointmentsCmd( Project *project, const KUndo2MagicString& name = KUndo2MagicString() ); void execute(); void unexecute(); private: Project *m_project; MacroCommand m_cmd; }; class PLANKERNEL_EXPORT SharedResourcesFileCmd : public NamedCommand { public: explicit SharedResourcesFileCmd(Project *project, const QString &newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute(); void unexecute(); private: Project *m_project; QString m_oldValue; QString m_newValue; }; class PLANKERNEL_EXPORT UseSharedResourcesCmd : public NamedCommand { public: explicit UseSharedResourcesCmd(Project *project, bool newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute(); void unexecute(); private: Project *m_project; bool m_oldValue; bool m_newValue; }; class PLANKERNEL_EXPORT SharedProjectsUrlCmd : public NamedCommand { public: explicit SharedProjectsUrlCmd(Project *project, const QUrl &newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute(); void unexecute(); private: Project *m_project; QUrl m_oldValue; QUrl m_newValue; }; class PLANKERNEL_EXPORT LoadProjectsAtStartupCmd : public NamedCommand { public: explicit LoadProjectsAtStartupCmd(Project *project, bool newValue, const KUndo2MagicString& name = KUndo2MagicString()); void execute(); void unexecute(); private: Project *m_project; bool m_oldValue; bool m_newValue; }; } //KPlato namespace #endif //COMMAND_H diff --git a/src/libs/kernel/kptdebug.cpp b/src/libs/kernel/kptdebug.cpp index b9a69e26..50f139b1 100644 --- a/src/libs/kernel/kptdebug.cpp +++ b/src/libs/kernel/kptdebug.cpp @@ -1,45 +1,51 @@ /* This file is part of the KDE project Copyright (C) 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptdebug.h" const QLoggingCategory &PLANSHARED_LOG() { static const QLoggingCategory category("calligra.plan.shared"); return category; } const QLoggingCategory &PLANDEPEDITOR_LOG() { static const QLoggingCategory category("calligra.plan.dependencyeditor"); return category; } const QLoggingCategory &PLANXML_LOG() { static const QLoggingCategory category("calligra.plan.xml"); return category; } const QLoggingCategory &PLAN_LOG() { static const QLoggingCategory category("calligra.plan"); return category; } + +const QLoggingCategory &PLANWP_LOG() +{ + static const QLoggingCategory category("calligra.plan.wp"); + return category; +} diff --git a/src/libs/kernel/kptdebug.h b/src/libs/kernel/kptdebug.h index 81ebaba5..ba4662da 100644 --- a/src/libs/kernel/kptdebug.h +++ b/src/libs/kernel/kptdebug.h @@ -1,54 +1,60 @@ /* This file is part of the KDE project Copyright (C) 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. */ #ifndef KPTDEBUG_H #define KPTDEBUG_H #include "plankernel_export.h" #include #include #include extern const PLANKERNEL_EXPORT QLoggingCategory &PLAN_LOG(); #define debugPlan qCDebug(PLAN_LOG)< This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // clazy:excludeall=qstring-arg #include "kptpackage.h" using namespace KPlato; Package::Package() - : task( 0 ), - toTask( 0 ) + : task( 0 ) + , toTask( 0 ) { } diff --git a/src/libs/kernel/kptproject.cpp b/src/libs/kernel/kptproject.cpp index 5b4ebe19..fed9a73b 100644 --- a/src/libs/kernel/kptproject.cpp +++ b/src/libs/kernel/kptproject.cpp @@ -1,2969 +1,3002 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2010, 2012 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptproject.h" #include "kptlocale.h" #include "kptappointment.h" #include "kpttask.h" #include "kptdatetime.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptwbsdefinition.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "kptschedulerplugin.h" #include "kptdebug.h" #include #include #include #include #include #include namespace KPlato { Project::Project( Node *parent ) : Node( parent ), m_accounts( *this ), m_defaultCalendar( 0 ), m_config( &emptyConfig ), m_schedulerPlugins(), m_useSharedResources(false), m_sharedResourcesLoaded(false), m_loadProjectsAtStartup(false) { //debugPlan<<"("<setDefaultValues(*this); } Project::Project( ConfigBase &config, bool useDefaultValues, Node *parent ) : Node( parent ), m_accounts( *this ), m_defaultCalendar( 0 ), m_config( &config ), m_schedulerPlugins(), m_useSharedResources(false), m_sharedResourcesLoaded(false), m_loadProjectsAtStartup(false) { debugPlan<<"("<setDefaultValues(*this); } } void Project::init() { m_refCount = 1; // always used by creator m_constraint = Node::MustStartOn; m_standardWorktime = new StandardWorktime(); m_timeZone = QTimeZone::systemTimeZone(); // local timezone as default //debugPlan<= 0 ); if ( m_refCount <= 0 ) { emit aboutToBeDeleted(); deleteLater(); } } Project::~Project() { debugPlan<<"("<blockChanged(); } for (Resource *r : qAsConst(resourceIdDict)) { r->blockChanged(); } for (ResourceGroup *g : qAsConst(resourceGroupIdDict)) { g->blockChanged(); } delete m_standardWorktime; while ( !m_resourceGroups.isEmpty() ) delete m_resourceGroups.takeFirst(); while ( !m_calendars.isEmpty() ) delete m_calendars.takeFirst(); while ( !m_managers.isEmpty() ) delete m_managers.takeFirst(); m_config = 0; //not mine, don't delete } int Project::type() const { return Node::Type_Project; } void Project::generateUniqueNodeIds() { foreach ( Node *n, nodeIdDict ) { debugPlan<name()<<"old"<id(); QString uid = uniqueNodeId(); nodeIdDict.remove( n->id() ); n->setId( uid ); nodeIdDict[ uid ] = n; debugPlan<name()<<"new"<id(); } } void Project::generateUniqueIds() { generateUniqueNodeIds(); foreach ( ResourceGroup *g, resourceGroupIdDict ) { if (g->isShared()) { continue; } resourceGroupIdDict.remove( g->id() ); g->setId( uniqueResourceGroupId() ); resourceGroupIdDict[ g->id() ] = g; } foreach ( Resource *r, resourceIdDict ) { if (r->isShared()) { continue; } resourceIdDict.remove( r->id() ); r->setId( uniqueResourceId() ); resourceIdDict[ r->id() ] = r; } foreach ( Calendar *c, calendarIdDict ) { if (c->isShared()) { continue; } calendarIdDict.remove( c->id() ); c->setId( uniqueCalendarId() ); calendarIdDict[ c->id() ] = c; } } void Project::calculate( Schedule *schedule, const DateTime &dt ) { if ( schedule == 0 ) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate( dt ); } void Project::calculate( const DateTime &dt ) { if ( m_currentSchedule == 0 ) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; QLocale locale; DateTime time = dt.isValid() ? dt : DateTime( QDateTime::currentDateTime() ); MainSchedule *cs = static_cast( m_currentSchedule ); Estimate::Use estType = ( Estimate::Use ) cs->type(); if ( type() == Type_Project ) { cs->setPhaseName( 0, i18n( "Init" ) ); cs->logInfo( i18n( "Schedule project from: %1", locale.toString(dt, QLocale::ShortFormat) ), 0 ); initiateCalculation( *cs ); initiateCalculationLists( *cs ); // must be after initiateCalculation() !! propagateEarliestStart( time ); // Calculate lateFinish from time. If a task has started, remainingEffort is used. cs->setPhaseName( 1, i18nc( "Schedule project forward", "Forward" ) ); cs->logInfo( i18n( "Calculate finish" ), 1 ); cs->lateFinish = calculateForward( estType ); cs->lateFinish = checkEndConstraints( cs->lateFinish ); propagateLatestFinish( cs->lateFinish ); // Calculate earlyFinish. If a task has started, remainingEffort is used. cs->setPhaseName( 2, i18nc( "Schedule project backward","Backward" ) ); cs->logInfo( i18n( "Calculate start" ), 2 ); calculateBackward( estType ); // Schedule. If a task has started, remainingEffort is used and appointments are copied from parent cs->setPhaseName( 3, i18n( "Schedule" ) ); cs->logInfo( i18n( "Schedule tasks forward" ), 3 ); cs->endTime = scheduleForward( cs->startTime, estType ); cs->logInfo( i18n( "Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat) ), 3 ); if ( cs->endTime > m_constraintEndTime ) { cs->logError( i18n( "Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else if ( cs->endTime == m_constraintEndTime ) { cs->logWarning( i18n( "Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else { cs->logInfo( i18n( "Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } calcCriticalPath( false ); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged( cs ); emit projectChanged(); } else if ( type() == Type_Subproject ) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::calculate( ScheduleManager &sm ) { emit sigCalculationStarted( this, &sm ); sm.setScheduling( true ); m_progress = 0; int nodes = 0; foreach ( Node *n, nodeIdDict ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { nodes++; } } int maxprogress = nodes * 3; if ( sm.recalculate() ) { emit maxProgress( maxprogress ); sm.setMaxProgress( maxprogress ); incProgress(); if ( sm.parentManager() ) { sm.expected()->startTime = sm.parentManager()->expected()->startTime; sm.expected()->earlyStart = sm.parentManager()->expected()->earlyStart; } incProgress(); calculate( sm.expected(), sm.recalculateFrom() ); } else { emit maxProgress( maxprogress ); sm.setMaxProgress( maxprogress ); calculate( sm.expected() ); emit scheduleChanged( sm.expected() ); setCurrentSchedule( sm.expected()->id() ); } emit sigProgress( maxprogress ); emit sigCalculationFinished( this, &sm ); emit scheduleManagerChanged( &sm ); emit projectCalculated( &sm ); emit projectChanged(); sm.setScheduling( false ); } void Project::calculate( Schedule *schedule ) { if ( schedule == 0 ) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate(); } void Project::calculate() { if ( m_currentSchedule == 0 ) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; MainSchedule *cs = static_cast( m_currentSchedule ); bool backwards = false; if ( cs->manager() ) { backwards = cs->manager()->schedulingDirection(); } QLocale locale; Estimate::Use estType = ( Estimate::Use ) cs->type(); if ( type() == Type_Project ) { QTime timer; timer.start(); initiateCalculation( *cs ); initiateCalculationLists( *cs ); // must be after initiateCalculation() !! if ( ! backwards ) { cs->setPhaseName( 0, i18n( "Init" ) ); cs->logInfo( i18n( "Schedule project forward from: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 0 ); cs->startTime = m_constraintStartTime; cs->earlyStart = m_constraintStartTime; // Calculate from start time propagateEarliestStart( cs->earlyStart ); cs->setPhaseName( 1, i18nc( "Schedule project forward", "Forward" ) ); cs->logInfo( i18n( "Calculate late finish" ), 1 ); cs->lateFinish = calculateForward( estType ); // cs->lateFinish = checkEndConstraints( cs->lateFinish ); cs->logInfo( i18n( "Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat) ), 1 ); propagateLatestFinish( cs->lateFinish ); cs->setPhaseName( 2, i18nc( "Schedule project backward", "Backward" ) ); cs->logInfo( i18n( "Calculate early start" ), 2 ); calculateBackward( estType ); cs->setPhaseName( 3, i18n( "Schedule" ) ); cs->logInfo( i18n( "Schedule tasks forward" ), 3 ); cs->endTime = scheduleForward( cs->startTime, estType ); cs->duration = cs->endTime - cs->startTime; cs->logInfo( i18n( "Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat) ), 3 ); if ( cs->endTime > m_constraintEndTime ) { cs->constraintError = true; cs->logError( i18n( "Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else if ( cs->endTime == m_constraintEndTime ) { cs->logWarning( i18n( "Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else { cs->logInfo( i18n( "Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } calcCriticalPath( false ); } else { cs->setPhaseName( 0, i18n( "Init" ) ); cs->logInfo( i18n( "Schedule project backward from: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 0 ); // Calculate from end time propagateLatestFinish( m_constraintEndTime ); cs->setPhaseName( 1, i18nc( "Schedule project backward", "Backward" ) ); cs->logInfo( i18n( "Calculate early start" ), 1 ); cs->earlyStart = calculateBackward( estType ); // cs->earlyStart = checkStartConstraints( cs->earlyStart ); cs->logInfo( i18n( "Early start calculated: %1", locale.toString(cs->earlyStart, QLocale::ShortFormat) ), 1 ); propagateEarliestStart( cs->earlyStart ); cs->setPhaseName( 2, i18nc( "Schedule project forward", "Forward" ) ); cs->logInfo( i18n( "Calculate late finish" ), 2 ); cs->lateFinish = qMax( m_constraintEndTime, calculateForward( estType ) ); cs->logInfo( i18n( "Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat) ), 2 ); cs->setPhaseName( 3, i18n( "Schedule" ) ); cs->logInfo( i18n( "Schedule tasks backward" ), 3 ); cs->startTime = scheduleBackward( cs->lateFinish, estType ); cs->endTime = cs->startTime; foreach ( Node *n, allNodes() ) { if ( n->type() == Type_Task || n->type() == Type_Milestone ) { DateTime e = n->endTime( cs->id() ); if ( cs->endTime < e ) { cs->endTime = e; } } } if ( cs->endTime > m_constraintEndTime ) { cs->constraintError = true; cs->logError( i18n( "Failed to finish project within target time" ), 3 ); } cs->duration = cs->endTime - cs->startTime; cs->logInfo( i18n( "Scheduled start: %1, target time: %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); if ( cs->startTime < m_constraintStartTime ) { cs->constraintError = true; cs->logError( i18n( "Must start project early in order to finish in time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); } else if ( cs->startTime == m_constraintStartTime ) { cs->logWarning( i18n( "Start project exactly on time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); } else { cs->logInfo( i18n( "Can start project later than time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); } calcCriticalPath( true ); } cs->logInfo( i18n( "Calculation took: %1", KFormat().formatDuration( timer.elapsed() ) ) ); // TODO: fix this uncertainty, manager should *always* be available if (cs->manager()) { finishCalculation(*(cs->manager())); } } else if ( type() == Type_Subproject ) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::finishCalculation( ScheduleManager &sm ) { MainSchedule *cs = sm.expected(); if (nodeIdDict.count() > 1) { // calculate project duration cs->startTime = m_constraintEndTime; cs->endTime = m_constraintStartTime; for (const Node *n : qAsConst(nodeIdDict)) { cs->startTime = qMin(cs->startTime, n->startTime(cs->id())); cs->endTime = qMax(cs->endTime, n->endTime(cs->id())); } cs->duration = cs->endTime - cs->startTime; } calcCriticalPath( false ); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged( cs ); emit projectChanged(); debugPlan<startTime<endTime<<"-------------------------"; } void Project::setProgress( int progress, ScheduleManager *sm ) { m_progress = progress; if ( sm ) { sm->setProgress( progress ); } emit sigProgress( progress ); } void Project::setMaxProgress( int max, ScheduleManager *sm ) { if ( sm ) { sm->setMaxProgress( max ); } emitMaxProgress( max ); } void Project::incProgress() { m_progress += 1; emit sigProgress( m_progress ); } void Project::emitMaxProgress( int value ) { emit maxProgress( value ); } bool Project::calcCriticalPath( bool fromEnd ) { //debugPlan; MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 ) { return false; } if ( fromEnd ) { QListIterator startnodes = cs->startNodes(); while ( startnodes.hasNext() ) { startnodes.next() ->calcCriticalPath( fromEnd ); } } else { QListIterator endnodes = cs->endNodes(); while ( endnodes.hasNext() ) { endnodes.next() ->calcCriticalPath( fromEnd ); } } calcCriticalPathList( cs ); return false; } void Project::calcCriticalPathList( MainSchedule *cs ) { //debugPlan<name(); cs->clearCriticalPathList(); foreach ( Node *n, allNodes() ) { if ( n->numDependParentNodes() == 0 && n->inCriticalPath( cs->id() ) ) { cs->addCriticalPath(); cs->addCriticalPathNode( n ); calcCriticalPathList( cs, n ); } } cs->criticalPathListCached = true; //debugPlan<<*(criticalPathList( cs->id() )); } void Project::calcCriticalPathList( MainSchedule *cs, Node *node ) { //debugPlan<name()<<", "<id(); bool newPath = false; QList lst = *( cs->currentCriticalPath() ); foreach ( Relation *r, node->dependChildNodes() ) { if ( r->child()->inCriticalPath( cs->id() ) ) { if ( newPath ) { cs->addCriticalPath( &lst ); //debugPlan<name()<<" new path"; } cs->addCriticalPathNode( r->child() ); calcCriticalPathList( cs, r->child() ); newPath = true; } } } const QList< QList > *Project::criticalPathList( long id ) { Schedule *s = schedule( id ); if ( s == 0 ) { //debugPlan<<"No schedule with id="<( s ); if ( ! ms->criticalPathListCached ) { initiateCalculationLists( *ms ); calcCriticalPathList( ms ); } return ms->criticalPathList(); } QList Project::criticalPath( long id, int index ) { Schedule *s = schedule( id ); if ( s == 0 ) { //debugPlan<<"No schedule with id="<(); } MainSchedule *ms = static_cast( s ); if ( ! ms->criticalPathListCached ) { initiateCalculationLists( *ms ); calcCriticalPathList( ms ); } return ms->criticalPath( index ); } DateTime Project::startTime( long id ) const { Schedule *s = schedule( id ); return s ? s->startTime : m_constraintStartTime; } DateTime Project::endTime( long id ) const { Schedule *s = schedule( id ); return s ? s->endTime : m_constraintEndTime; } Duration Project::duration( long id ) const { Schedule *s = schedule( id ); return s ? s->duration : Duration::zeroDuration; } Duration *Project::getRandomDuration() { return 0L; } DateTime Project::checkStartConstraints( const DateTime &dt ) const { DateTime t = dt; foreach ( Node *n, nodeIdDict ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { switch ( n->constraint() ) { case Node::FixedInterval: case Node::StartNotEarlier: case Node::MustStartOn: t = qMin( t, qMax( n->constraintStartTime(), m_constraintStartTime ) ); break; default: break; } } } return t; } DateTime Project::checkEndConstraints( const DateTime &dt ) const { DateTime t = dt; foreach ( Node *n, nodeIdDict ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { switch ( n->constraint() ) { case Node::FixedInterval: case Node::FinishNotLater: case Node::MustFinishOn: t = qMax( t, qMin( n->constraintEndTime(), m_constraintEndTime ) ); break; default: break; } } } return t; } #ifndef PLAN_NLOGDEBUG bool Project::checkParent( Node *n, const QList &list, QList &checked ) { if ( n->isStartNode() ) { debugPlan< lst = list; lst << n; foreach ( Relation *r, n->dependParentNodes() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if ( ! checkParent( r->parent(), lst, checked ) ) { return false; } } Task *t = static_cast( n ); foreach ( Relation *r, t->parentProxyRelations() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; debugPlan<<"Proxy:"<parent()<<":"<parent(), lst, checked ) ) { return false; } } return true; } bool Project::checkChildren( Node *n, const QList &list, QList &checked ) { if ( n->isEndNode() ) { debugPlan< lst = list; lst << n; foreach ( Relation *r, n->dependChildNodes() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if ( ! checkChildren( r->child(), lst, checked ) ) { return false; } } Task *t = static_cast( n ); foreach ( Relation *r, t->childProxyRelations() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } debugPlan<<"Proxy:"<parent()<<":"<child(), lst, checked ) ) { return false; } } return true; } #endif void Project::tasksForward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); foreach ( Task *t, allTasks() ) { switch ( t->constraint() ) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.append( t ); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.append( t ); break; default: if ( t->isEndNode() ) { m_terminalNodes.append( t ); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"End nodes:"< lst; QList rel; Q_ASSERT( checkParent( n, lst, rel ) ); Q_UNUSED( n ); } #endif } void Project::tasksBackward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); foreach ( Task *t, allTasks() ) { switch ( t->constraint() ) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.append( t ); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.append( t ); break; default: if ( t->isStartNode() ) { m_terminalNodes.append( t ); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"Start nodes:"< lst; QList rel; Q_ASSERT( checkChildren( n, lst, rel ) ); Q_UNUSED( n ); } #endif } DateTime Project::calculateForward( int use ) { //debugPlan<( m_currentSchedule ); if ( cs == 0 ) { return finish; } if ( type() == Node::Type_Project ) { QTime timer; timer.start(); cs->logInfo( i18n( "Start calculating forward" ) ); m_visitedForward = true; if ( ! m_visitedBackward ) { // setup tasks tasksForward(); // Do all hard constrained first foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateEarlyFinish( use ); // do not do predeccessors if ( time > finish ) { finish = time; } } // do the predeccessors foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } // now try to schedule soft constrained *with* predeccessors foreach ( Node *n, m_softConstraints ) { cs->logDebug( "Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } // and then the rest using the end nodes to calculate everything (remaining) foreach ( Task *n, m_terminalNodes ) { cs->logDebug( "Calculate using end task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } } else { // tasks have been calculated backwards in this order foreach ( Node *n, cs->backwardNodes() ) { DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } } cs->logInfo( i18n( "Finished calculating forward: %1 ms", timer.elapsed() ) ); } else { //TODO: subproject } return finish; } DateTime Project::calculateBackward( int use ) { //debugPlan<( m_currentSchedule ); if ( cs == 0 ) { return start; } if ( type() == Node::Type_Project ) { QTime timer; timer.start(); cs->logInfo( i18n( "Start calculating backward" ) ); m_visitedBackward = true; if ( ! m_visitedForward ) { // setup tasks tasksBackward(); // Do all hard constrained first foreach ( Task *n, m_hardConstraints ) { cs->logDebug( "Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateLateStart( use ); // do not do predeccessors if ( ! start.isValid() || time < start ) { start = time; } } // then do the predeccessors foreach ( Task *n, m_hardConstraints ) { cs->logDebug( "Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } // now try to schedule soft constrained *with* predeccessors foreach ( Task *n, m_softConstraints ) { cs->logDebug( "Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } // and then the rest using the start nodes to calculate everything (remaining) foreach ( Task *n, m_terminalNodes ) { cs->logDebug( "Calculate using start task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } } else { // tasks have been calculated forwards in this order foreach ( Node *n, cs->forwardNodes() ) { DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } } cs->logInfo( i18n( "Finished calculating backward: %1 ms", timer.elapsed() ) ); } else { //TODO: subproject } return start; } DateTime Project::scheduleForward( const DateTime &earliest, int use ) { DateTime end; MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 || stopcalculation ) { return DateTime(); } QTime timer; timer.start(); cs->logInfo( i18n( "Start scheduling forward" ) ); resetVisited(); // Schedule in the same order as calculated forward // Do all hard constrained first foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleFromStartTime( use ); // do not do predeccessors if ( time > end ) { end = time; } } foreach ( Node *n, cs->forwardNodes() ) { cs->logDebug( "Schedule task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleForward( earliest, use ); if ( time > end ) { end = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo( i18n( "Finished scheduling forward: %1 ms", timer.elapsed() ) ); foreach ( Node *n, allNodes() ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { Q_ASSERT( n->isScheduled() ); } } return end; } DateTime Project::scheduleBackward( const DateTime &latest, int use ) { DateTime start; MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 || stopcalculation ) { return start; } QTime timer; timer.start(); cs->logInfo( i18n( "Start scheduling backward" ) ); resetVisited(); // Schedule in the same order as calculated backward // Do all hard constrained first foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleFromEndTime( use ); // do not do predeccessors if ( ! start.isValid() || time < start ) { start = time; } } foreach ( Node *n, cs->backwardNodes() ) { cs->logDebug( "Schedule task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleBackward( latest, use ); if ( ! start.isValid() || time < start ) { start = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo( i18n( "Finished scheduling backward: %1 ms", timer.elapsed() ) ); foreach ( Node *n, allNodes() ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { Q_ASSERT( n->isScheduled() ); } } return start; } void Project::adjustSummarytask() { MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 || stopcalculation ) { return; } QListIterator it( cs->summaryTasks() ); while ( it.hasNext() ) { it.next() ->adjustSummarytask(); } } void Project::initiateCalculation( MainSchedule &sch ) { //debugPlan< git( m_resourceGroups ); while ( git.hasNext() ) { git.next() ->initiateCalculation( sch ); } Node::initiateCalculation( sch ); } void Project::initiateCalculationLists( MainSchedule &sch ) { //debugPlan< it = childNodeIterator(); while ( it.hasNext() ) { it.next() ->initiateCalculationLists( sch ); } } else { //TODO: subproject } } bool Project::load( KoXmlElement &element, XMLLoaderObject &status ) { //debugPlan<<"--->"; m_useSharedResources = false; // default should off in case old project // load locale first KoXmlNode n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "locale" ) { Locale *l = locale(); l->setCurrencySymbol(e.attribute( "currency-symbol", "")); if ( e.hasAttribute( "currency-digits" ) ) { l->setMonetaryDecimalPlaces(e.attribute("currency-digits").toInt()); } QLocale::Language language = QLocale::AnyLanguage; QLocale::Country country = QLocale::AnyCountry; if (e.hasAttribute("language")) { language = static_cast(e.attribute("language").toInt()); } if (e.hasAttribute("country")) { country = static_cast(e.attribute("country").toInt()); } l->setCurrencyLocale(language, country); } else if (e.tagName() == "shared-resources") { m_useSharedResources = e.attribute("use", "0").toInt(); m_sharedResourcesFile = e.attribute("file"); m_sharedProjectsUrl = QUrl(e.attribute("projects-url")); m_loadProjectsAtStartup = (bool)e.attribute("projects-loadatstartup", "0").toInt(); } else if (e.tagName() == QLatin1String("documents")) { m_documents.load(e, status); + } else if (e.tagName() == QLatin1String("workpackageinfo")) { + if (e.hasAttribute("check-for-workpackages")) { + m_workPackageInfo.checkForWorkPackages = e.attribute("check-for-workpackages").toInt(); + } + if (e.hasAttribute("retrieve-url")) { + m_workPackageInfo.retrieveUrl = QUrl(e.attribute("retrieve-url")); + } + if (e.hasAttribute("delete-after-retrieval")) { + m_workPackageInfo.deleteAfterRetrieval = e.attribute("delete-after-retrieval").toInt(); + } + if (e.hasAttribute("archive-after-retrieval")) { + m_workPackageInfo.archiveAfterRetrieval = e.attribute("archive-after-retrieval").toInt(); + } + if (e.hasAttribute("archive-url")) { + m_workPackageInfo.archiveUrl = QUrl(e.attribute("archive-url")); + } + if (e.hasAttribute("publish-url")) { + m_workPackageInfo.publishUrl = QUrl(e.attribute("publish-url")); + } } } QList cals; QString s; bool ok = false; setName( element.attribute( "name" ) ); removeId( m_id ); m_id = element.attribute( "id" ); registerNodeId( this ); m_priority = element.attribute(QStringLiteral("priority"), "0").toInt(); m_leader = element.attribute( "leader" ); m_description = element.attribute( "description" ); QTimeZone tz( element.attribute( "timezone" ).toLatin1() ); if ( tz.isValid() ) { m_timeZone = tz; } else warnPlan<<"No timezone specified, using default (local)"; status.setProjectTimeZone( m_timeZone ); // Allow for both numeric and text s = element.attribute( "scheduling", "0" ); m_constraint = ( Node::ConstraintType ) s.toInt( &ok ); if ( !ok ) setConstraint( s ); if ( m_constraint != Node::MustStartOn && m_constraint != Node::MustFinishOn ) { errorPlan << "Illegal constraint: " << constraintToString(); setConstraint( Node::MustStartOn ); } s = element.attribute( "start-time" ); if ( !s.isEmpty() ) m_constraintStartTime = DateTime::fromString( s, m_timeZone ); s = element.attribute( "end-time" ); if ( !s.isEmpty() ) m_constraintEndTime = DateTime::fromString( s, m_timeZone ); status.setProgress( 10 ); // Load the project children // Do calendars first, they only reference other calendars //debugPlan<<"Calendars--->"; n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "calendar" ) { // Load the calendar. // Referenced by resources Calendar * child = new Calendar(); child->setProject( this ); if ( child->load( e, status ) ) { cals.append( child ); // temporary, reorder later } else { // TODO: Complain about this errorPlan << "Failed to load calendar"; delete child; } } else if ( e.tagName() == "standard-worktime" ) { // Load standard worktime StandardWorktime * child = new StandardWorktime(); if ( child->load( e, status ) ) { setStandardWorktime( child ); } else { errorPlan << "Failed to load standard worktime"; delete child; } } } // calendars references calendars in arbritary saved order bool added = false; do { added = false; QList lst; while ( !cals.isEmpty() ) { Calendar *c = cals.takeFirst(); c->m_blockversion = true; if ( c->parentId().isEmpty() ) { addCalendar( c, status.baseCalendar() ); // handle pre 0.6 version added = true; //debugPlan<<"added to project:"<name(); } else { Calendar *par = calendar( c->parentId() ); if ( par ) { par->m_blockversion = true; addCalendar( c, par ); added = true; //debugPlan<<"added:"<name()<<" to parent:"<name(); par->m_blockversion = false; } else { lst.append( c ); // treat later //debugPlan<<"treat later:"<name(); } } c->m_blockversion = false; } cals = lst; } while ( added ); if ( ! cals.isEmpty() ) { errorPlan<<"All calendars not saved!"; } //debugPlan<<"Calendars<---"; status.setProgress( 15 ); // Resource groups and resources, can reference calendars n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "resource-group" ) { // Load the resources // References calendars ResourceGroup * child = new ResourceGroup(); if ( child->load( e, status ) ) { addResourceGroup( child ); } else { // TODO: Complain about this delete child; } } } status.setProgress( 20 ); // The main stuff n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "project" ) { //debugPlan<<"Sub project--->"; /* // Load the subproject Project * child = new Project( this ); if ( child->load( e ) ) { if ( !addTask( child, this ) ) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if ( e.tagName() == "task" ) { //debugPlan<<"Task--->"; // Load the task (and resourcerequests). // Depends on resources already loaded Task * child = new Task( this ); if ( child->load( e, status ) ) { if ( !addTask( child, this ) ) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } status.setProgress( 70 ); // These go last n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { debugPlan<"; // Load accounts // References tasks if ( !m_accounts.load( e, *this ) ) { errorPlan << "Failed to load accounts"; } } else if ( e.tagName() == "relation" ) { //debugPlan<<"Relation--->"; // Load the relation // References tasks Relation * child = new Relation(); if ( !child->load( e, *this ) ) { // TODO: Complain about this errorPlan << "Failed to load relation"; delete child; } //debugPlan<<"Relation<---"; } else if ( e.tagName() == "schedules" ) { //debugPlan<<"Project schedules & task appointments--->"; // References tasks and resources KoXmlNode sn = e.firstChild(); for ( ; ! sn.isNull(); sn = sn.nextSibling() ) { if ( ! sn.isElement() ) { continue; } KoXmlElement el = sn.toElement(); //debugPlan<loadXML( el, status ) ) { if ( add ) addScheduleManager( sm ); } else { errorPlan << "Failed to load schedule manager"; delete sm; } } else { debugPlan<<"No schedule manager ?!"; } } //debugPlan<<"Node schedules<---"; } else if ( e.tagName() == "resource-teams" ) { //debugPlan<<"Resource teams--->"; // References other resources KoXmlNode tn = e.firstChild(); for ( ; ! tn.isNull(); tn = tn.nextSibling() ) { if ( ! tn.isElement() ) { continue; } KoXmlElement el = tn.toElement(); if ( el.tagName() == "team" ) { Resource *r = findResource( el.attribute( "team-id" ) ); Resource *tm = findResource( el.attribute( "member-id" ) ); if ( r == 0 || tm == 0 ) { errorPlan<<"resource-teams: cannot find resources"; continue; } if ( r == tm ) { errorPlan<<"resource-teams: a team cannot be a member of itself"; continue; } r->addTeamMemberId( tm->id() ); } else { errorPlan<<"resource-teams: unhandled tag"<currencySymbolExplicit().isEmpty()) { loc.setAttribute("currency-symbol", l->currencySymbolExplicit()); } loc.setAttribute("currency-digits", l->monetaryDecimalPlaces()); loc.setAttribute("language", l->currencyLanguage()); loc.setAttribute("country", l->currencyCountry()); QDomElement share = me.ownerDocument().createElement( "shared-resources" ); me.appendChild(share); share.setAttribute("use", m_useSharedResources); share.setAttribute("file", m_sharedResourcesFile); share.setAttribute("projects-url", QString(m_sharedProjectsUrl.toEncoded())); share.setAttribute("projects-loadatstartup", m_loadProjectsAtStartup); + QDomElement wpi = me.ownerDocument().createElement( "workpackageinfo" ); + me.appendChild(wpi); + wpi.setAttribute("check-for-workpackages", m_workPackageInfo.checkForWorkPackages); + wpi.setAttribute("retrieve-url", m_workPackageInfo.retrieveUrl.toString(QUrl::None)); + wpi.setAttribute("delete-after-retrieval", m_workPackageInfo.deleteAfterRetrieval); + wpi.setAttribute("archive-after-retrieval", m_workPackageInfo.archiveAfterRetrieval); + wpi.setAttribute("archive-url", m_workPackageInfo.archiveUrl.toString(QUrl::None)); + wpi.setAttribute("publish-url", m_workPackageInfo.publishUrl.toString(QUrl::None)); + m_documents.save(me); if (context.saveAll(this)) { m_accounts.save( me ); // save calendars foreach ( Calendar *c, calendarIdDict ) { c->save( me ); } // save standard worktime if ( m_standardWorktime ) m_standardWorktime->save( me ); // save project resources, must be after calendars QListIterator git( m_resourceGroups ); while ( git.hasNext() ) { git.next() ->save( me ); } } // Only save parent relations QListIterator it( m_dependParentNodes ); while ( it.hasNext() ) { Relation *r = it.next(); if (context.saveNode(r->parent()) && context.saveNode(r->child())) { r->save(me, context); } } if (context.saveAll(this)) { for ( int i = 0; i < numChildren(); i++ ) // Save all children childNode(i)->save(me, context); } // Now we can save relations assuming no tasks have relations outside the project QListIterator nodes(m_nodes); while (nodes.hasNext()) { nodes.next()->saveRelations(me, context); } if (context.saveAll(this)) { if ( !m_managers.isEmpty() ) { QDomElement el = me.ownerDocument().createElement( "schedules" ); me.appendChild( el ); foreach ( ScheduleManager *sm, m_managers ) { sm->saveXML( el ); } } // save resource teams QDomElement el = me.ownerDocument().createElement( "resource-teams" ); me.appendChild( el ); foreach ( Resource *r, resourceIdDict ) { if ( r->type() != Resource::Type_Team ) { continue; } foreach ( const QString &id, r->teamMemberIds() ) { QDomElement e = el.ownerDocument().createElement( "team" ); el.appendChild( e ); e.setAttribute( "team-id", r->id() ); e.setAttribute( "member-id", id ); } } } } void Project::saveWorkPackageXML( QDomElement &element, const Node *node, long id ) const { QDomElement me = element.ownerDocument().createElement( "project" ); element.appendChild( me ); me.setAttribute( "name", m_name ); me.setAttribute( "leader", m_leader ); me.setAttribute( "id", m_id ); me.setAttribute( "description", m_description ); me.setAttribute( "timezone", m_timeZone.isValid() ? QString::fromLatin1(m_timeZone.id()) : QString() ); me.setAttribute( "scheduling", constraintToString() ); me.setAttribute( "start-time", m_constraintStartTime.toString( Qt::ISODate ) ); me.setAttribute( "end-time", m_constraintEndTime.toString( Qt::ISODate ) ); QListIterator git( m_resourceGroups ); while ( git.hasNext() ) { git.next() ->saveWorkPackageXML( me, node->assignedResources( id ) ); } if ( node == 0 ) { return; } node->saveWorkPackageXML( me, id ); foreach ( ScheduleManager *sm, m_managerIdMap ) { if ( sm->scheduleId() == id ) { QDomElement el = me.ownerDocument().createElement( "schedules" ); me.appendChild( el ); sm->saveWorkPackageXML( el, *node ); break; } } } void Project::setParentSchedule( Schedule *sch ) { QListIterator it = m_nodes; while ( it.hasNext() ) { it.next() ->setParentSchedule( sch ); } } void Project::addResourceGroup( ResourceGroup *group, int index ) { int i = index == -1 ? m_resourceGroups.count() : index; emit resourceGroupToBeAdded( group, i ); m_resourceGroups.insert( i, group ); setResourceGroupId( group ); group->setProject( this ); foreach ( Resource *r, group->resources() ) { setResourceId( r ); r->setProject( this ); } emit resourceGroupAdded( group ); emit projectChanged(); } ResourceGroup *Project::takeResourceGroup( ResourceGroup *group ) { int i = m_resourceGroups.indexOf( group ); Q_ASSERT( i != -1 ); if ( i == -1 ) { return 0; } emit resourceGroupToBeRemoved( group ); ResourceGroup *g = m_resourceGroups.takeAt( i ); Q_ASSERT( group == g ); g->setProject( 0 ); removeResourceGroupId( g->id() ); foreach ( Resource *r, g->resources() ) { r->setProject( 0 ); removeResourceId( r->id() ); } emit resourceGroupRemoved( g ); emit projectChanged(); return g; } QList &Project::resourceGroups() { return m_resourceGroups; } void Project::addResource( ResourceGroup *group, Resource *resource, int index ) { int i = index == -1 ? group->numResources() : index; emit resourceToBeAdded( group, i ); group->addResource( i, resource, 0 ); setResourceId( resource ); emit resourceAdded( resource ); emit projectChanged(); } Resource *Project::takeResource( ResourceGroup *group, Resource *resource ) { emit resourceToBeRemoved( resource ); bool result = removeResourceId( resource->id() ); Q_ASSERT( result == true ); if (!result) { warnPlan << "Could not remove resource with id" << resource->id(); } resource->removeRequests(); // not valid anymore Resource *r = group->takeResource( resource ); Q_ASSERT( resource == r ); if (resource != r) { warnPlan << "Could not take resource from group"; } emit resourceRemoved( resource ); emit projectChanged(); return r; } void Project::moveResource( ResourceGroup *group, Resource *resource ) { if ( group == resource->parentGroup() ) { return; } takeResource( resource->parentGroup(), resource ); addResource( group, resource ); return; } QMap< QString, QString > Project::externalProjects() const { QMap< QString, QString > map; foreach ( Resource *r, resourceList() ) { for( QMapIterator it( r->externalProjects() ); it.hasNext(); ) { it.next(); if ( ! map.contains( it.key() ) ) { map[ it.key() ] = it.value(); } } } return map; } bool Project::addTask( Node* task, Node* position ) { // we want to add a task at the given position. => the new node will // become next sibling right after position. if ( 0 == position ) { return addSubTask( task, this ); } //debugPlan<<"Add"<name()<<" after"<name(); // in case we want to add to the main project, we make it child element // of the root element. if ( Node::Type_Project == position->type() ) { return addSubTask( task, position ); } // find the position // we have to tell the parent that we want to delete one of its children Node* parentNode = position->parentNode(); if ( !parentNode ) { debugPlan <<"parent node not found???"; return false; } int index = parentNode->findChildNode( position ); if ( -1 == index ) { // ok, it does not exist debugPlan <<"Task not found???"; return false; } return addSubTask( task, index + 1, parentNode ); } bool Project::addSubTask( Node* task, Node* parent ) { // append task to parent return addSubTask( task, -1, parent ); } bool Project::addSubTask( Node* task, int index, Node* parent, bool emitSignal ) { // we want to add a subtask to the node "parent" at the given index. // If parent is 0, add to this Node *p = parent; if ( 0 == p ) { p = this; } if ( !registerNodeId( task ) ) { errorPlan << "Failed to register node id, can not add subtask: " << task->name(); return false; } int i = index == -1 ? p->numChildren() : index; if ( emitSignal ) emit nodeToBeAdded( p, i ); p->insertChildNode( i, task ); connect( this, &Project::standardWorktimeChanged, task, &Node::slotStandardWorktimeChanged ); if ( emitSignal ) { emit nodeAdded( task ); emit projectChanged(); if ( p != this && p->numChildren() == 1 ) { emit nodeChanged( p ); } } return true; } void Project::takeTask( Node *node, bool emitSignal ) { //debugPlan<name(); Node * parent = node->parentNode(); if ( parent == 0 ) { debugPlan <<"Node must have a parent!"; return; } removeId( node->id() ); if ( emitSignal ) emit nodeToBeRemoved( node ); disconnect( this, &Project::standardWorktimeChanged, node, &Node::slotStandardWorktimeChanged ); parent->takeChildNode( node ); if ( emitSignal ) { emit nodeRemoved( node ); emit projectChanged(); if ( parent != this && parent->type() != Node::Type_Summarytask ) { emit nodeChanged( parent ); } } } bool Project::canMoveTask( Node* node, Node *newParent ) { //debugPlan<name()<<" to"<name(); if ( node == this ) { return false; } Node *p = newParent; while ( p && p != this ) { if ( ! node->canMoveTo( p ) ) { return false; } p = p->parentNode(); } return true; } bool Project::moveTask( Node* node, Node *newParent, int newPos ) { //debugPlan<name()<<" to"<name()<<","<parentNode(); int oldPos = oldParent->indexOf( node ); int i = newPos < 0 ? newParent->numChildren() : newPos; if ( oldParent == newParent && i == oldPos ) { // no need to move to where it already is return false; } int newRow = i; if ( oldParent == newParent && newPos > oldPos ) { ++newRow; // itemmodels wants new row *before* node is removed from old position } debugPlan<name()<<"at"<indexOf( node )<<"to"<name()<numChildren() == 0 ) { emit nodeChanged( oldParent ); } if ( newParent != this && newParent->numChildren() == 1 ) { emit nodeChanged( newParent ); } return true; } bool Project::canIndentTask( Node* node ) { if ( 0 == node ) { // should always be != 0. At least we would get the Project, // but you never know who might change that, so better be careful return false; } if ( node->type() == Node::Type_Project ) { //debugPlan<<"The root node cannot be indented"; return false; } // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if ( !parentNode ) { return false; } if ( parentNode->findChildNode( node ) == -1 ) { errorPlan << "Tasknot found???"; return false; } Node *sib = node->siblingBefore(); if ( !sib ) { //debugPlan<<"new parent node not found"; return false; } if ( node->findParentRelation( sib ) || node->findChildRelation( sib ) ) { //debugPlan<<"Cannot have relations to parent"; return false; } return true; } bool Project::indentTask( Node* node, int index ) { if ( canIndentTask( node ) ) { Node * newParent = node->siblingBefore(); int i = index == -1 ? newParent->numChildren() : index; moveTask( node, newParent, i ); //debugPlan; return true; } return false; } bool Project::canUnindentTask( Node* node ) { if ( 0 == node ) { // is always != 0. At least we would get the Project, but you // never know who might change that, so better be careful return false; } if ( Node::Type_Project == node->type() ) { //debugPlan<<"The root node cannot be unindented"; return false; } // we have to find the parent of task to manipulate its list of children // and we need the parent's parent too Node* parentNode = node->parentNode(); if ( !parentNode ) { return false; } Node* grandParentNode = parentNode->parentNode(); if ( !grandParentNode ) { //debugPlan<<"This node already is at the top level"; return false; } int index = parentNode->findChildNode( node ); if ( -1 == index ) { errorPlan << "Tasknot found???"; return false; } return true; } bool Project::unindentTask( Node* node ) { if ( canUnindentTask( node ) ) { Node * parentNode = node->parentNode(); Node *grandParentNode = parentNode->parentNode(); int i = grandParentNode->indexOf( parentNode ) + 1; if ( i == 0 ) { i = grandParentNode->numChildren(); } moveTask( node, grandParentNode, i ); //debugPlan; return true; } return false; } bool Project::canMoveTaskUp( Node* node ) { if ( node == 0 ) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if ( !parentNode ) { //debugPlan<<"No parent found"; return false; } if ( parentNode->findChildNode( node ) == -1 ) { errorPlan << "Tasknot found???"; return false; } if ( node->siblingBefore() ) { return true; } return false; } bool Project::moveTaskUp( Node* node ) { if ( canMoveTaskUp( node ) ) { moveTask( node, node->parentNode(), node->parentNode()->indexOf( node ) - 1 ); return true; } return false; } bool Project::canMoveTaskDown( Node* node ) { if ( node == 0 ) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if ( !parentNode ) { return false; } if ( parentNode->findChildNode( node ) == -1 ) { errorPlan << "Tasknot found???"; return false; } if ( node->siblingAfter() ) { return true; } return false; } bool Project::moveTaskDown( Node* node ) { if ( canMoveTaskDown( node ) ) { moveTask( node, node->parentNode(), node->parentNode()->indexOf( node ) + 1 ); return true; } return false; } Task *Project::createTask() { Task * node = new Task(); node->setId( uniqueNodeId() ); reserveId( node->id(), node ); return node; } Task *Project::createTask( const Task &def ) { Task * node = new Task( def ); node->setId( uniqueNodeId() ); reserveId( node->id(), node ); return node; } Node *Project::findNode( const QString &id ) const { if ( m_parent == 0 ) { if ( nodeIdDict.contains( id ) ) { return nodeIdDict[ id ]; } return 0; } return m_parent->findNode( id ); } bool Project::nodeIdentExists( const QString &id ) const { return nodeIdDict.contains( id ) || nodeIdReserved.contains( id ); } QString Project::uniqueNodeId( int seed ) const { Q_UNUSED(seed); QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString ident = s + KRandom::randomString( 10 ); // int i = seed; while ( nodeIdentExists( ident ) ) { ident = s + KRandom::randomString( 10 ); } return ident; } QString Project::uniqueNodeId( const QList &existingIds, int seed ) { QString id = uniqueNodeId( seed ); while ( existingIds.contains( id ) ) { id = uniqueNodeId( seed ); } return id; } bool Project::removeId( const QString &id ) { //debugPlan <<"id=" << id; if ( m_parent ) { return m_parent->removeId( id ); } //debugPlan << "id=" << id<< nodeIdDict.contains(id); return nodeIdDict.remove( id ); } void Project::reserveId( const QString &id, Node *node ) { //debugPlan <<"id=" << id << node->name(); nodeIdReserved.insert( id, node ); } bool Project::registerNodeId( Node *node ) { nodeIdReserved.remove( node->id() ); if ( node->id().isEmpty() ) { warnPlan << "Node id is empty, cannot register it"; return false; } Node *rn = findNode( node->id() ); if ( rn == 0 ) { //debugPlan <<"id=" << node->id() << node->name(); nodeIdDict.insert( node->id(), node ); return true; } if ( rn != node ) { errorPlan << "Id already exists for different task: " << node->id(); return false; } //debugPlan<<"Already exists" <<"id=" << node->id() << node->name(); return true; } QList Project::allNodes() const { QList lst = nodeIdDict.values(); int me = lst.indexOf( const_cast( this ) ); if ( me != -1 ) { lst.removeAt( me ); } return lst; } QList Project::allTasks( const Node *parent ) const { QList lst; const Node *p = parent ? parent : this; foreach ( Node *n, p->childNodeIterator() ) { if ( n->type() == Node::Type_Task || n->type() == Type_Milestone ) { lst << static_cast( n ); } lst += allTasks( n ); } return lst; } bool Project::isStarted() const { const QList tasks = allTasks(); for (const Task *t : tasks) { if (t->isStarted()) { return true; } } return false; } bool Project::setResourceGroupId( ResourceGroup *group ) { if ( group == 0 ) { return false; } if ( ! group->id().isEmpty() ) { ResourceGroup *g = findResourceGroup( group->id() ); if ( group == g ) { return true; } else if ( g == 0 ) { insertResourceGroupId( group->id(), group ); return true; } } QString id = uniqueResourceGroupId(); group->setId( id ); if ( id.isEmpty() ) { return false; } insertResourceGroupId( id, group ); return true; } QString Project::uniqueResourceGroupId() const { QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString id = s + KRandom::randomString( 10 ); while ( resourceGroupIdDict.contains( id ) ) { id = s + KRandom::randomString( 10 ); } return id; } ResourceGroup *Project::group( const QString& id ) { return findResourceGroup( id ); } ResourceGroup *Project::groupByName( const QString& name ) const { foreach ( ResourceGroup *g, resourceGroupIdDict ) { if ( g->name() == name ) { return g; } } return 0; } QList Project::autoAllocateResources() const { QList lst; foreach ( Resource *r, resourceIdDict ) { if ( r->autoAllocate() ) { lst << r; } } return lst; } void Project::insertResourceId( const QString &id, Resource *resource ) { resourceIdDict.insert( id, resource ); } bool Project::removeResourceId( const QString &id ) { return resourceIdDict.remove( id ); } bool Project::setResourceId( Resource *resource ) { if ( resource == 0 ) { return false; } if ( ! resource->id().isEmpty() ) { Resource *r = findResource( resource->id() ); if ( resource == r ) { return true; } else if ( r == 0 ) { insertResourceId( resource->id(), resource ); return true; } } QString id = uniqueResourceId(); resource->setId( id ); if ( id.isEmpty() ) { return false; } insertResourceId( id, resource ); return true; } QString Project::uniqueResourceId() const { QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString id = s + KRandom::randomString( 10 ); while ( resourceIdDict.contains( id ) ) { id = s + KRandom::randomString( 10 ); } return id; } Resource *Project::resource( const QString& id ) { return findResource( id ); } Resource *Project::resourceByName( const QString& name ) const { QHash::const_iterator it; for (it = resourceIdDict.constBegin(); it != resourceIdDict.constEnd(); ++it) { Resource *r = it.value(); if ( r->name() == name ) { Q_ASSERT( it.key() == r->id() ); return r; } } return 0; } QStringList Project::resourceNameList() const { QStringList lst; foreach ( Resource *r, resourceIdDict ) { lst << r->name(); } return lst; } EffortCostMap Project::plannedEffortCostPrDay( QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( start, end, id, typ ); } return ec; } EffortCostMap Project::plannedEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( resource, start, end, id, typ ); } return ec; } EffortCostMap Project::actualEffortCostPrDay( QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( start, end, id, typ ); } return ec; } EffortCostMap Project::actualEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( resource, start, end, id, typ ); } return ec; } // Returns the total planned effort for this project (or subproject) Duration Project::plannedEffort( long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->plannedEffort( id, typ ); } return eff; } // Returns the total planned effort for this project (or subproject) on date Duration Project::plannedEffort( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->plannedEffort( date, id, typ ); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date Duration Project::plannedEffortTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->plannedEffortTo( date, id, typ ); } return eff; } // Returns the total actual effort for this project (or subproject) upto and including date Duration Project::actualEffortTo( QDate date ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->actualEffortTo( date ); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date double Project::plannedCostTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; double c = 0; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { c += it.next() ->plannedCostTo( date, id, typ ); } return c; } // Returns the total actual cost for this project (or subproject) upto and including date EffortCost Project::actualCostTo( long int id, QDate date ) const { //debugPlan; EffortCost c; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { c += it.next() ->actualCostTo( id, date ); } return c; } Duration Project::budgetedWorkPerformed( QDate date, long id ) const { //debugPlan; Duration e; foreach (Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed( date, id ); } return e; } double Project::budgetedCostPerformed( QDate date, long id ) const { //debugPlan; double c = 0.0; foreach (Node *n, childNodeIterator()) { c += n->budgetedCostPerformed( date, id ); } return c; } double Project::effortPerformanceIndex( QDate date, long id ) const { //debugPlan; debugPlan< 0.0 ) { r = p / s; } debugPlan< date ? end : date), id ); double budgetAtCompletion; double plannedCompleted; double budgetedCompleted; bool useEffort = false; //FIXME if ( useEffort ) { budgetAtCompletion = plan.totalEffort().toDouble( Duration::Unit_h ); plannedCompleted = plan.effortTo( date ).toDouble( Duration::Unit_h ); //actualCompleted = actual.effortTo( date ).toDouble( Duration::Unit_h ); budgetedCompleted = budgetedWorkPerformed( date, id ).toDouble( Duration::Unit_h ); } else { budgetAtCompletion = plan.totalCost(); plannedCompleted = plan.costTo( date ); budgetedCompleted = budgetedCostPerformed( date, id ); } double c = 0.0; if ( budgetAtCompletion > 0.0 ) { double percentageCompletion = budgetedCompleted / budgetAtCompletion; c = budgetAtCompletion * percentageCompletion; //?? debugPlan<name()<<","<<(parent?parent->name():"No parent"); int row = parent == 0 ? m_calendars.count() : parent->calendars().count(); if ( index >= 0 && index < row ) { row = index; } emit calendarToBeAdded( parent, row ); calendar->setProject( this ); if ( parent == 0 ) { calendar->setParentCal( 0 ); // in case m_calendars.insert( row, calendar ); } else { calendar->setParentCal( parent, row ); } if ( calendar->isDefault() ) { setDefaultCalendar( calendar ); } setCalendarId( calendar ); emit calendarAdded( calendar ); emit projectChanged(); } void Project::takeCalendar( Calendar *calendar ) { emit calendarToBeRemoved( calendar ); removeCalendarId( calendar->id() ); if ( calendar == m_defaultCalendar ) { m_defaultCalendar = 0; } if ( calendar->parentCal() == 0 ) { int i = indexOf( calendar ); if ( i != -1 ) { m_calendars.removeAt( i ); } } else { calendar->setParentCal( 0 ); } emit calendarRemoved( calendar ); calendar->setProject( 0 ); emit projectChanged(); } int Project::indexOf( const Calendar *calendar ) const { return m_calendars.indexOf( const_cast(calendar) ); } Calendar *Project::calendar( const QString& id ) const { return findCalendar( id ); } Calendar *Project::calendarByName( const QString& name ) const { foreach( Calendar *c, calendarIdDict ) { if ( c->name() == name ) { return c; } } return 0; } const QList &Project::calendars() const { return m_calendars; } QList Project::allCalendars() const { return calendarIdDict.values(); } QStringList Project::calendarNames() const { QStringList lst; foreach( Calendar *c, calendarIdDict ) { lst << c->name(); } return lst; } bool Project::setCalendarId( Calendar *calendar ) { if ( calendar == 0 ) { return false; } if ( ! calendar->id().isEmpty() ) { Calendar *c = findCalendar( calendar->id() ); if ( calendar == c ) { return true; } else if ( c == 0 ) { insertCalendarId( calendar->id(), calendar ); return true; } } QString id = uniqueCalendarId(); calendar->setId( id ); if ( id.isEmpty() ) { return false; } insertCalendarId( id, calendar ); return true; } QString Project::uniqueCalendarId() const { QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString id = s + KRandom::randomString( 10 ); while ( calendarIdDict.contains( id ) ) { id = s + KRandom::randomString( 10 ); } return id; } void Project::setDefaultCalendar( Calendar *cal ) { if ( m_defaultCalendar ) { m_defaultCalendar->setDefault( false ); } m_defaultCalendar = cal; if ( cal ) { cal->setDefault( true ); } emit defaultCalendarChanged( cal ); emit projectChanged(); } void Project::setStandardWorktime( StandardWorktime * worktime ) { if ( m_standardWorktime != worktime ) { delete m_standardWorktime; m_standardWorktime = worktime; m_standardWorktime->setProject( this ); emit standardWorktimeChanged( worktime ); } } void Project::emitDocumentAdded( Node *node , Document *doc , int index ) { emit documentAdded( node, doc, index ); } void Project::emitDocumentRemoved( Node *node , Document *doc , int index ) { emit documentRemoved( node, doc, index ); } void Project::emitDocumentChanged( Node *node , Document *doc , int index ) { emit documentChanged( node, doc, index ); } bool Project::linkExists( const Node *par, const Node *child ) const { if ( par == 0 || child == 0 || par == child || par->isDependChildOf( child ) ) { return false; } foreach ( Relation *r, par->dependChildNodes() ) { if ( r->child() == child ) { return true; } } return false; } bool Project::legalToLink( const Node *par, const Node *child ) const { //debugPlan<isDependChildOf( child ) ) { return false; } if ( linkExists( par, child ) ) { return false; } bool legal = true; // see if par/child is related if ( legal && ( par->isParentOf( child ) || child->isParentOf( par ) ) ) { legal = false; } if ( legal ) legal = legalChildren( par, child ); if ( legal ) legal = legalParents( par, child ); if ( legal ) { foreach ( Node *p, par->childNodeIterator() ) { if ( ! legalToLink( p, child ) ) { return false; } } } return legal; } bool Project::legalParents( const Node *par, const Node *child ) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for ( int i = 0; i < par->numDependParentNodes() && legal; ++i ) { Node *pNode = par->getDependParentNode( i ) ->parent(); if ( child->isParentOf( pNode ) || pNode->isParentOf( child ) ) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren( pNode, child ); } if ( legal ) legal = legalParents( pNode, child ); } return legal; } bool Project::legalChildren( const Node *par, const Node *child ) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for ( int j = 0; j < child->numDependChildNodes() && legal; ++j ) { Node *cNode = child->getDependChildNode( j ) ->child(); if ( par->isParentOf( cNode ) || cNode->isParentOf( par ) ) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren( par, cNode ); } } return legal; } WBSDefinition &Project::wbsDefinition() { return m_wbsDefinition; } void Project::setWbsDefinition( const WBSDefinition &def ) { //debugPlan; m_wbsDefinition = def; emit wbsDefinitionChanged(); emit projectChanged(); } QString Project::generateWBSCode( QList &indexes, bool sortable ) const { QString code = m_wbsDefinition.projectCode(); if (sortable) { int fw = (nodeIdDict.count() / 10) + 1; QLatin1Char fc('0'); foreach ( int index, indexes ) { code += ".%1"; code = code.arg(QString::number(index), fw, fc); } //debugPlan< hash = resourceIdDict; foreach ( Resource * r, hash ) { r->setCurrentSchedule( id ); } emit currentScheduleChanged(); emit projectChanged(); } ScheduleManager *Project::scheduleManager( long id ) const { foreach ( ScheduleManager *sm, m_managers ) { if ( sm->scheduleId() == id ) { return sm; } } return 0; } ScheduleManager *Project::scheduleManager( const QString &id ) const { return m_managerIdMap.value( id ); } ScheduleManager *Project::findScheduleManagerByName( const QString &name ) const { //debugPlan; ScheduleManager *m = 0; foreach( ScheduleManager *sm, m_managers ) { m = sm->findManager( name ); if ( m ) { break; } } return m; } QList Project::allScheduleManagers() const { QList lst; foreach ( ScheduleManager *sm, m_managers ) { lst << sm; lst << sm->allChildren(); } return lst; } QString Project::uniqueScheduleName() const { //debugPlan; QString n = i18n( "Plan" ); bool unique = findScheduleManagerByName( n ) == 0; if ( unique ) { return n; } n += " %1"; int i = 1; for ( ; true; ++i ) { unique = findScheduleManagerByName( n.arg( i ) ) == 0; if ( unique ) { break; } } return n.arg( i ); } void Project::addScheduleManager( ScheduleManager *sm, ScheduleManager *parent, int index ) { int row = parent == 0 ? m_managers.count() : parent->childCount(); if ( index >= 0 && index < row ) { row = index; } if ( parent == 0 ) { emit scheduleManagerToBeAdded( parent, row ); m_managers.insert( row, sm ); } else { emit scheduleManagerToBeAdded( parent, row ); sm->setParentManager( parent, row ); } if ( sm->managerId().isEmpty() ) { sm->setManagerId( uniqueScheduleManagerId() ); } Q_ASSERT( ! m_managerIdMap.contains( sm->managerId() ) ); m_managerIdMap.insert( sm->managerId(), sm ); emit scheduleManagerAdded( sm ); emit projectChanged(); //debugPlan<<"Added:"<name()<<", now"<children() ) { takeScheduleManager( s ); } if ( sm->scheduling() ) { sm->stopCalculation(); } int index = -1; if ( sm->parentManager() ) { int index = sm->parentManager()->indexOf( sm ); if ( index >= 0 ) { emit scheduleManagerToBeRemoved( sm ); sm->setParentManager( 0 ); m_managerIdMap.remove( sm->managerId() ); emit scheduleManagerRemoved( sm ); emit projectChanged(); } } else { index = indexOf( sm ); if ( index >= 0 ) { emit scheduleManagerToBeRemoved( sm ); m_managers.removeAt( indexOf( sm ) ); m_managerIdMap.remove( sm->managerId() ); emit scheduleManagerRemoved( sm ); emit projectChanged(); } } return index; } void Project::swapScheduleManagers(ScheduleManager *from, ScheduleManager *to) { emit scheduleManagersSwapped(from, to); } void Project::moveScheduleManager( ScheduleManager *sm, ScheduleManager *newparent, int newindex ) { //debugPlan<name()<parentManager() ) { m_managers.removeAt( indexOf( sm ) ); } sm->setParentManager( newparent, newindex ); if ( ! newparent ) { m_managers.insert( newindex, sm ); } emit scheduleManagerMoved( sm, newindex ); } bool Project::isScheduleManager( void *ptr ) const { const ScheduleManager *sm = static_cast( ptr ); if ( indexOf( sm ) >= 0 ) { return true; } foreach ( ScheduleManager *p, m_managers ) { if ( p->isParentOf( sm ) ) { return true; } } return false; } ScheduleManager *Project::createScheduleManager( const QString &name ) { //debugPlan<isBaselined() ) { return true; } } return false; } Schedule *s = schedule( id ); return s == 0 ? false : s->isBaselined(); } MainSchedule *Project::createSchedule( const QString& name, Schedule::Type type ) { //debugPlan<<"No of schedules:"<setName( name ); sch->setType( type ); addMainSchedule( sch ); return sch; } void Project::addMainSchedule( MainSchedule *sch ) { if ( sch == 0 ) { return; } //debugPlan<<"No of schedules:"<setId( i ); sch->setNode( this ); addSchedule( sch ); } bool Project::removeCalendarId( const QString &id ) { //debugPlan <<"id=" << id; return calendarIdDict.remove( id ); } void Project::insertCalendarId( const QString &id, Calendar *calendar ) { //debugPlan <<"id=" << id <<":" << calendar->name(); calendarIdDict.insert( id, calendar ); } void Project::changed( Node *node, int property ) { if ( m_parent == 0 ) { Node::changed( node, property ); // reset cache if ( property != Node::Type ) { // add/remove node is handled elsewhere emit nodeChanged( node ); emit projectChanged(); } return; } Node::changed( node, property ); } void Project::changed( ResourceGroup *group ) { //debugPlan; emit resourceGroupChanged( group ); emit projectChanged(); } void Project::changed( ScheduleManager *sm ) { emit scheduleManagerChanged( sm ); emit projectChanged(); } void Project::changed( MainSchedule *sch ) { //debugPlan<id(); emit scheduleChanged( sch ); emit projectChanged(); } void Project::sendScheduleToBeAdded( const ScheduleManager *sm, int row ) { emit scheduleToBeAdded( sm, row ); } void Project::sendScheduleAdded( const MainSchedule *sch ) { //debugPlan<id(); emit scheduleAdded( sch ); emit projectChanged(); } void Project::sendScheduleToBeRemoved( const MainSchedule *sch ) { //debugPlan<id(); emit scheduleToBeRemoved( sch ); } void Project::sendScheduleRemoved( const MainSchedule *sch ) { //debugPlan<id(); emit scheduleRemoved( sch ); emit projectChanged(); } void Project::changed( Resource *resource ) { emit resourceChanged( resource ); emit projectChanged(); } void Project::changed( Calendar *cal ) { emit calendarChanged( cal ); emit projectChanged(); } void Project::changed( StandardWorktime *w ) { emit standardWorktimeChanged( w ); emit projectChanged(); } bool Project::addRelation( Relation *rel, bool check ) { if ( rel->parent() == 0 || rel->child() == 0 ) { return false; } if ( check && !legalToLink( rel->parent(), rel->child() ) ) { return false; } emit relationToBeAdded( rel, rel->parent()->numDependChildNodes(), rel->child()->numDependParentNodes() ); rel->parent()->addDependChildNode( rel ); rel->child()->addDependParentNode( rel ); emit relationAdded( rel ); emit projectChanged(); return true; } void Project::takeRelation( Relation *rel ) { emit relationToBeRemoved( rel ); rel->parent() ->takeDependChildNode( rel ); rel->child() ->takeDependParentNode( rel ); emit relationRemoved( rel ); emit projectChanged(); } void Project::setRelationType( Relation *rel, Relation::Type type ) { emit relationToBeModified( rel ); rel->setType( type ); emit relationModified( rel ); emit projectChanged(); } void Project::setRelationLag( Relation *rel, const Duration &lag ) { emit relationToBeModified( rel ); rel->setLag( lag ); emit relationModified( rel ); emit projectChanged(); } QList Project::flatNodeList( Node *parent ) { QList lst; Node *p = parent == 0 ? this : parent; //debugPlan<name()<childNodeIterator() ) { lst.append( n ); if ( n->numChildren() > 0 ) { lst += flatNodeList( n ); } } return lst; } void Project::setSchedulerPlugins( const QMap &plugins ) { m_schedulerPlugins = plugins; debugPlan< Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTPROJECT_H #define KPTPROJECT_H #include "plankernel_export.h" #include "kptnode.h" #include "kptglobal.h" #include "kptaccount.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptduration.h" #include "kptresource.h" #include "kptwbsdefinition.h" #include "kptconfigbase.h" #include #include #include #include #include /// The main namespace. namespace KPlato { class Locale; class Schedule; class StandardWorktime; class ScheduleManager; class XMLLoaderObject; class Task; class SchedulerPlugin; class KPlatoXmlLoaderBase; class XmlSaveContext; /** * Project is the main node in a project, it contains child nodes and * possibly sub-projects. A sub-project is just another instantiation of this * node however. * * A note on timezones: * To be able to handle resources working in different timezones and * to facilitate data exchange with other applications like PIMs or * and groupware servers, the project has a timezone that is used for * all datetimes in nodes and schedules. * By default the local timezone is used. * * A resources timezone is defined by the associated calendar. * * Note that a projects datetimes are always displayed/modified in the timezone * it was originally created, not necessarily in your current local timezone. */ class PLANKERNEL_EXPORT Project : public Node { Q_OBJECT public: explicit Project( Node *parent = 0 ); explicit Project( ConfigBase &config, Node *parent = 0 ); explicit Project( ConfigBase &config, bool useDefaultValues, Node *parent = 0 ); ~Project(); /// Reference this project. void ref() { ++m_refCount; } /// De-reference this project. Deletes project of ref count <= 0 void deref(); /// Returns the node type. Can be Type_Project or Type_Subproject. virtual int type() const; /** * Calculate the schedules managed by the schedule manager * * @param sm Schedule manager */ void calculate( ScheduleManager &sm ); /** * Re-calculate the schedules managed by the schedule manager * * @param sm Schedule manager * @param dt The datetime from when the schedule shall be re-calculated */ void calculate( ScheduleManager &sm, const DateTime &dt ); virtual DateTime startTime( long id = -1 ) const; virtual DateTime endTime( long id = -1 ) const; /// Returns the calculated duration for schedule @p id Duration duration( long id = -1 ) const; using Node::duration; /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ Duration *getRandomDuration(); virtual bool load( KoXmlElement &element, XMLLoaderObject &status ); virtual void save(QDomElement &element, const XmlSaveContext &context) const; using Node::saveWorkPackageXML; /// Save a workpackage document containing @p node with schedule identity @p id void saveWorkPackageXML( QDomElement &element, const Node *node, long id ) const; /** * Add the node @p task to the project, after node @p position * If @p position is zero or the project node, it will be added to this project. */ bool addTask( Node* task, Node* position ); /** * Add the node @p task to the @p parent */ bool addSubTask( Node* task, Node* parent ); /** * Add the node @p task to @p parent, in position @p index * If @p parent is zero, it will be added to this project. */ bool addSubTask( Node* task, int index, Node* parent, bool emitSignal = true ); /** * Remove the @p node. * The node is not deleted. */ void takeTask( Node *node, bool emitSignal = true ); bool canMoveTask( Node* node, Node *newParent ); bool moveTask( Node* node, Node *newParent, int newPos ); bool canIndentTask( Node* node ); bool indentTask( Node* node, int index = -1 ); bool canUnindentTask( Node* node ); bool unindentTask( Node* node ); bool canMoveTaskUp( Node* node ); bool moveTaskUp( Node* node ); bool canMoveTaskDown( Node* node ); bool moveTaskDown( Node* node ); /** * Create a task with a unique id. * The task is not added to the project. Do this with addSubTask(). */ Task *createTask(); /** * Create a copy of @p def with a unique id. * The task is not added to the project. Do this with addSubTask(). */ Task *createTask( const Task &def ); /// @return true if any of the tasks has been started bool isStarted() const; int resourceGroupCount() const { return m_resourceGroups.count(); } QList &resourceGroups(); /// Adds the resource group to the project. virtual void addResourceGroup( ResourceGroup *resource, int index = -1 ); /** * Removes the resource group @p resource from the project. * The resource group is not deleted. */ ResourceGroup *takeResourceGroup( ResourceGroup *resource ); int indexOf( ResourceGroup *resource ) const { return m_resourceGroups.indexOf( resource ); } ResourceGroup *resourceGroupAt( int pos ) const { return m_resourceGroups.value( pos ); } int numResourceGroups() const { return m_resourceGroups.count(); } /// Returns the resourcegroup with identity id. ResourceGroup *group( const QString& id ); /// Returns the resource group with the matching name, 0 if no match is found. ResourceGroup *groupByName( const QString& name ) const; /** * Adds the resource to the project and resource group. * Always use this to add resources. */ void addResource( ResourceGroup *group, Resource *resource, int index = -1 ); /** * Removes the resource from the project and resource group. * The resource is not deleted. * Always use this to remove resources. */ Resource *takeResource( ResourceGroup *group, Resource *resource ); /// Move @p resource to the new @p group. Requests are removed. void moveResource( ResourceGroup *group, Resource *resource ); /// Returns the resource with identity id. Resource *resource( const QString& id ); /// Returns the resource with matching name, 0 if no match is found. Resource *resourceByName( const QString& name ) const; QStringList resourceNameList() const; /// Returns a list of all resources QList resourceList() const { return resourceIdDict.values(); } virtual EffortCostMap plannedEffortCostPrDay( QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; using Node::plannedEffort; /// Returns the total planned effort for this project (or subproject) virtual Duration plannedEffort( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this project (or subproject) on date virtual Duration plannedEffort( QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; using Node::plannedEffortTo; /// Returns the planned effort up to and including date virtual Duration plannedEffortTo( QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the actual effort up to and including @p date virtual Duration actualEffortTo( QDate date ) const; /** * Planned cost up to and including date * @param date The cost is calculated from the start of the project upto including date. * @param id Identity of the schedule to be used. * @param typ the type of calculation. * @sa EffortCostCalculationType */ virtual double plannedCostTo( QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /** * Actual cost up to and including @p date * @param id Identity of the schedule to be used. * @param date The cost is calculated from the start of the project upto including date. */ virtual EffortCost actualCostTo( long int id, QDate date ) const; virtual EffortCostMap actualEffortCostPrDay( QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; virtual EffortCostMap actualEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; double effortPerformanceIndex( QDate date, long id ) const; double schedulePerformanceIndex( QDate date, long id ) const; /// Returns the effort planned to be used to reach the actual percent finished virtual Duration budgetedWorkPerformed( QDate date, long id = CURRENTSCHEDULE ) const; /// Returns the cost planned to be used to reach the actual percent finished virtual double budgetedCostPerformed( QDate date, long id = CURRENTSCHEDULE ) const; /// Budgeted Cost of Work Scheduled ( up to @p date ) virtual double bcws( QDate date, long id = BASELINESCHEDULE ) const; /// Budgeted Cost of Work Performed virtual double bcwp( long id = BASELINESCHEDULE ) const; /// Budgeted Cost of Work Performed ( up to @p date ) virtual double bcwp( QDate date, long id = BASELINESCHEDULE ) const; Calendar *defaultCalendar() const { return m_defaultCalendar; } void setDefaultCalendar( Calendar *cal ); const QList &calendars() const; void addCalendar( Calendar *calendar, Calendar *parent = 0, int index = -1 ); void takeCalendar( Calendar *calendar ); int indexOf( const Calendar *calendar ) const; /// Returns the calendar with identity id. Calendar *calendar( const QString& id ) const; /// Returns a list of all calendars QStringList calendarNames() const; /// Find calendar by name Calendar *calendarByName( const QString &name ) const; void changed( Calendar *cal ); QList allCalendars() const; /// Return number of calendars int calendarCount() const { return m_calendars.count(); } /// Return the calendar at @p index, 0 if index out of bounds Calendar *calendarAt( int index ) const { return m_calendars.value( index ); } /** * Defines the length of days, weeks, months and years * and the standard working week. * Used for estimation and calculation of effort, * and presentation in gantt chart. */ StandardWorktime *standardWorktime() { return m_standardWorktime; } void setStandardWorktime( StandardWorktime * worktime ); void changed( StandardWorktime* ); /// Check if a link exists between node @p par and @p child. bool linkExists( const Node *par, const Node *child ) const; /// Check if node @p par can be linked to node @p child. bool legalToLink( const Node *par, const Node *child ) const; using Node::legalToLink; virtual const QHash &nodeDict() { return nodeIdDict; } /// Return a list of all nodes in the project (excluding myself) QList allNodes() const; /// Return the number of all nodes in the project (excluding myself) int nodeCount() const { return nodeIdDict.count() - 1; } /// Return a list of all tasks and milestones int the wbs order QList allTasks( const Node *parent = 0 ) const; using Node::findNode; /// Find the node with identity id virtual Node *findNode( const QString &id ) const; using Node::removeId; /// Remove the node with identity id from the registers virtual bool removeId( const QString &id ); /// Reserve @p id for the @p node virtual void reserveId( const QString &id, Node *node ); /// Register @p node. The nodes id must be unique and non-empty. bool registerNodeId( Node *node ); /// Create a unique id. QString uniqueNodeId( int seed = 1 ) const; /// Check if node @p id is used bool nodeIdentExists( const QString &id ) const; /// Create a unique id. QString uniqueNodeId( const QList &existingIds, int seed = 1 ); ResourceGroup *findResourceGroup( const QString &id ) const { if ( resourceGroupIdDict.contains( id ) ) return resourceGroupIdDict[ id ]; return 0; } /// Remove the resourcegroup with identity id from the register bool removeResourceGroupId( const QString &id ) { if ( resourceGroupIdDict.contains( id ) ) return resourceGroupIdDict.remove( id ); return false; } /// Insert the resourcegroup with identity id void insertResourceGroupId( const QString &id, ResourceGroup* group ) { resourceGroupIdDict.insert( id, group ); } /// Generate, set and insert unique id bool setResourceGroupId( ResourceGroup *group); /// returns a unique resourcegroup id QString uniqueResourceGroupId() const; /// Return a list of resources that will be allocated to new tasks QList autoAllocateResources() const; Resource *findResource( const QString &id ) const { if ( resourceIdDict.contains( id ) ) return resourceIdDict[ id ]; return 0; } /// Remove the resource with identity id from the register bool removeResourceId( const QString &id ); /// Insert the resource with identity id void insertResourceId( const QString &id, Resource *resource ); /// Generate, set and insert unique id bool setResourceId( Resource *resource ); /// returns a unique resource id QString uniqueResourceId() const; /// Find the calendar with identity id virtual Calendar *findCalendar( const QString &id ) const { if ( id.isEmpty() || !calendarIdDict.contains( id ) ) return 0; return calendarIdDict[ id ]; } /// Remove the calendar with identity id from the register virtual bool removeCalendarId( const QString &id ); /// Insert the calendar with identity id virtual void insertCalendarId( const QString &id, Calendar *calendar ); /// Set and insert a unique id for calendar bool setCalendarId( Calendar *calendar ); /// returns a unique calendar id QString uniqueCalendarId() const; /// Return reference to WBS Definition WBSDefinition &wbsDefinition(); /// Set WBS Definition to @p def void setWbsDefinition( const WBSDefinition &def ); /// Generate WBS Code virtual QString generateWBSCode( QList &indexes, bool sortable = false ) const; Accounts &accounts() { return m_accounts; } const Accounts &accounts() const { return m_accounts; } /** * Set current schedule to the schedule with identity @p id, for me and my children * Note that this is used (and may be changed) when calculating schedules */ virtual void setCurrentSchedule( long id ); /// Create new schedule with unique name and id of type Expected. MainSchedule *createSchedule(); /// Create new schedule with unique id. MainSchedule *createSchedule( const QString& name, Schedule::Type type ); /// Add the schedule to the project. A fresh id will be generated for the schedule. void addMainSchedule( MainSchedule *schedule ); /// Set parent schedule for my children virtual void setParentSchedule( Schedule *sch ); /// Find the schedule manager that manages the Schedule with @p id ScheduleManager *scheduleManager( long id ) const; /// Find the schedule manager with @p id ScheduleManager *scheduleManager( const QString &id ) const; /// Create a unique schedule name (This may later be changed by the user) QString uniqueScheduleName() const; /// Create a unique schedule manager identity QString uniqueScheduleManagerId() const; ScheduleManager *createScheduleManager(); ScheduleManager *createScheduleManager( const QString &name ); /// Returns a list of all top level schedule managers QList scheduleManagers() const { return m_managers; } int numScheduleManagers() const { return m_managers.count(); } int indexOf( const ScheduleManager *sm ) const { return m_managers.indexOf( const_cast(sm) ); } bool isScheduleManager( void* ptr ) const; void addScheduleManager( ScheduleManager *sm, ScheduleManager *parent = 0, int index = -1 ); int takeScheduleManager( ScheduleManager *sm ); void moveScheduleManager( ScheduleManager *sm, ScheduleManager *newparent = 0, int newindex = -1 ); ScheduleManager *findScheduleManagerByName( const QString &name ) const; /// Returns a list of all schedule managers QList allScheduleManagers() const; /// Return true if schedule with identity @p id is baselined bool isBaselined( long id = ANYSCHEDULED ) const; void changed( ResourceGroup *group ); void changed( Resource *resource ); void changed( ScheduleManager *sm ); void changed( MainSchedule *sch ); void sendScheduleAdded( const MainSchedule *sch ); void sendScheduleToBeAdded( const ScheduleManager *manager, int row ); void sendScheduleRemoved( const MainSchedule *sch ); void sendScheduleToBeRemoved( const MainSchedule *sch ); /// Return the time zone used in this project QTimeZone timeZone() const { return m_timeZone; } /// Set the time zone to be used in this project void setTimeZone( const QTimeZone &tz ) { m_timeZone = tz; } /** * Add a relation between the nodes specified in the relation rel. * Emits signals relationToBeAdded() before the relation is added, * and relationAdded() after it has been added. * @param rel The relation to be added. * @param check If true, the relation is checked for validity * @return true if successful. */ bool addRelation( Relation *rel, bool check=true ); /** * Removes the relation @p rel without deleting it. * Emits signals relationToBeRemoved() before the relation is removed, * and relationRemoved() after it has been removed. */ void takeRelation( Relation *rel ); /** * Modify the @p type of the @p relation. */ void setRelationType( Relation *relation, Relation::Type type ); /** * Modify the @p lag of the @p relation. */ void setRelationLag( Relation *relation, const Duration &lag ); void calcCriticalPathList( MainSchedule *cs ); void calcCriticalPathList( MainSchedule *cs, Node *node ); /** * Returns the list of critical paths for schedule @p id */ const QList< QList > *criticalPathList( long id = CURRENTSCHEDULE ); QList criticalPath( long id = CURRENTSCHEDULE, int index = 0 ); /// Returns a flat list af all nodes QList flatNodeList( Node *parent = 0 ); void generateUniqueNodeIds(); void generateUniqueIds(); const ConfigBase &config() const { return m_config ? *m_config : emptyConfig; } /// Set configuration data void setConfig( ConfigBase *config ) { m_config = config; } const Task &taskDefaults() const { return config().taskDefaults(); } /// Return locale. (Used for currency, everything else is from KGlobal::locale) Locale *locale() { return const_cast(config()).locale(); } /// Return locale. (Used for currency, everything else is from KGlobal::locale) const Locale *locale() const { return config().locale(); } /// Signal that locale data has changed void emitLocaleChanged(); void setSchedulerPlugins( const QMap &plugins ); const QMap &schedulerPlugins() const { return m_schedulerPlugins; } void initiateCalculation( MainSchedule &sch ); void initiateCalculationLists( MainSchedule &sch ); void finishCalculation( ScheduleManager &sm ); void adjustSummarytask(); /// Increments progress and emits signal sigProgress() void incProgress(); /// Emits signal maxProgress() void emitMaxProgress( int value ); bool stopcalculation; /// return a map of all external projects QMap externalProjects() const; void emitDocumentAdded( Node*, Document*, int index ); void emitDocumentRemoved( Node*, Document*, int index ); void emitDocumentChanged( Node*, Document*, int index ); bool useSharedResources() const; void setUseSharedResources(bool on); bool isSharedResourcesLoaded() const; void setSharedResourcesLoaded(bool on); void setSharedResourcesFile(const QString &file); QString sharedResourcesFile() const; void setSharedProjectsUrl(const QUrl &url); QUrl sharedProjectsUrl() const; void setLoadProjectsAtStartup(bool value); bool loadProjectsAtStartup() const; public Q_SLOTS: /// Sets m_progress to @p progress and emits signal sigProgress() /// If @p sm is not 0, progress is also set for the schedule manager void setProgress(int progress, KPlato::ScheduleManager *sm = 0); /// Sets m_maxprogress to @p max and emits signal maxProgress() /// If @p sm is not 0, max progress is also set for the schedule manager void setMaxProgress(int max, KPlato::ScheduleManager *sm = 0 ); void swapScheduleManagers(KPlato::ScheduleManager *from, KPlato::ScheduleManager *to); Q_SIGNALS: void scheduleManagersSwapped(KPlato::ScheduleManager *from, KPlato::ScheduleManager *to); /// Emitted when the project is about to be deleted (The destroyed signal is disabled) void aboutToBeDeleted(); /// Emitted when anything in the project is changed (use with care) void projectChanged(); /// Emitted when the WBS code definition has changed. This may change all nodes. void wbsDefinitionChanged(); /// Emitted when a schedule has been calculated void projectCalculated(KPlato::ScheduleManager *sm); /// Emitted when the pointer to the current schedule has been changed void currentScheduleChanged(); /// Use to show progress during calculation void sigProgress( int ); /// Use to set the maximum progress (minimum always 0) void maxProgress( int ); /// Emitted when calculation starts void sigCalculationStarted(KPlato::Project *project, KPlato::ScheduleManager *sm); /// Emitted when calculation is finished void sigCalculationFinished(KPlato::Project *project, KPlato::ScheduleManager *sm); /// This signal is emitted when one of the nodes members is changed. void nodeChanged(KPlato::Node*); /// This signal is emitted when the node is to be added to the project. void nodeToBeAdded(KPlato::Node*, int); /// This signal is emitted when the node has been added to the project. void nodeAdded(KPlato::Node*); /// This signal is emitted when the node is to be removed from the project. void nodeToBeRemoved(KPlato::Node*); /// This signal is emitted when the node has been removed from the project. void nodeRemoved(KPlato::Node*); /// This signal is emitted when the node is to be moved up, moved down, indented or unindented. void nodeToBeMoved(KPlato::Node* node, int pos, KPlato::Node* newParent, int newPos); /// This signal is emitted when the node has been moved up, moved down, indented or unindented. void nodeMoved(KPlato::Node*); /// This signal is emitted when a document is added void documentAdded(KPlato::Node*, KPlato::Document*, int index); /// This signal is emitted when a document is removed void documentRemoved(KPlato::Node*, KPlato::Document*, int index); /// This signal is emitted when a document is changed void documentChanged(KPlato::Node*, KPlato::Document*, int index); void resourceGroupChanged(KPlato::ResourceGroup *group); void resourceGroupAdded(const KPlato::ResourceGroup *group); void resourceGroupToBeAdded(const KPlato::ResourceGroup *group, int row); void resourceGroupRemoved(const KPlato::ResourceGroup *group); void resourceGroupToBeRemoved(const KPlato::ResourceGroup *group); void resourceChanged(KPlato::Resource *resource); void resourceAdded(const KPlato::Resource *resource); void resourceToBeAdded(const KPlato::ResourceGroup *group, int row); void resourceRemoved(const KPlato::Resource *resource); void resourceToBeRemoved(const KPlato::Resource *resource); void scheduleManagerChanged(KPlato::ScheduleManager *sch); void scheduleManagerAdded(const KPlato::ScheduleManager *sch); void scheduleManagerToBeAdded(const KPlato::ScheduleManager *sch, int row); void scheduleManagerRemoved(const KPlato::ScheduleManager *sch); void scheduleManagerToBeRemoved(const KPlato::ScheduleManager *sch); void scheduleManagerMoved(const KPlato::ScheduleManager *sch, int row); void scheduleManagerToBeMoved(const KPlato::ScheduleManager *sch); void scheduleChanged(KPlato::MainSchedule *sch); void scheduleToBeAdded(const KPlato::ScheduleManager *manager, int row); void scheduleAdded(const KPlato::MainSchedule *sch); void scheduleToBeRemoved(const KPlato::MainSchedule *sch); void scheduleRemoved(const KPlato::MainSchedule *sch); // void currentViewScheduleIdChanged( long id ); void calendarChanged(KPlato::Calendar *cal); void calendarToBeAdded(const KPlato::Calendar *cal, int row); void calendarAdded(const KPlato::Calendar *cal); void calendarToBeRemoved(const KPlato::Calendar *cal); void calendarRemoved(const KPlato::Calendar *cal); /** * Emitted when the default calendar pointer has changed * @param cal The new default calendar. May be 0. */ void defaultCalendarChanged(KPlato::Calendar *cal); /** * Emitted when the standard worktime has been changed. */ void standardWorktimeChanged(KPlato::StandardWorktime*); /// Emitted when the relation @p rel is about to be added. void relationToBeAdded(KPlato::Relation *rel, int parentIndex, int childIndex); /// Emitted when the relation @p rel has been added. void relationAdded(KPlato::Relation *rel); /// Emitted when the relation @p rel is about to be removed. void relationToBeRemoved(KPlato::Relation *rel); /// Emitted when the relation @p rel has been removed. void relationRemoved(KPlato::Relation *rel); /// Emitted when the relation @p rel shall be modified. void relationToBeModified(KPlato::Relation *rel); /// Emitted when the relation @p rel has been modified. void relationModified(KPlato::Relation *rel); /// Emitted when locale data has changed void localeChanged(); protected: /// Calculate the schedule. void calculate( Schedule *scedule ); /// Calculate current schedule void calculate(); /// Re-calculate the schedule from @p dt void calculate( Schedule *scedule, const DateTime &dt ); /// Calculate current schedule from @p dt (Always calculates forward) void calculate( const DateTime &dt ); /// Calculate critical path virtual bool calcCriticalPath( bool fromEnd ); /// Prepare task lists for scheduling void tasksForward(); /// Prepare task lists for scheduling void tasksBackward(); protected: friend class KPlatoXmlLoaderBase; using Node::changed; virtual void changed(Node *node, int property = -1); Accounts m_accounts; QList m_resourceGroups; QList m_calendars; Calendar * m_defaultCalendar; StandardWorktime *m_standardWorktime; DateTime calculateForward( int use ); DateTime calculateBackward( int use ); DateTime scheduleForward( const DateTime &earliest, int use ); DateTime scheduleBackward( const DateTime &latest, int use ); DateTime checkStartConstraints( const DateTime &dt ) const; DateTime checkEndConstraints( const DateTime &dt ) const; bool legalParents( const Node *par, const Node *child ) const; bool legalChildren( const Node *par, const Node *child ) const; #ifndef PLAN_NLOGDEBUG private: static bool checkParent( Node *n, const QList &list, QList &checked ); static bool checkChildren( Node *n, const QList &list, QList &checked ); #endif private: void init(); QHash resourceGroupIdDict; QHash resourceIdDict; QHash nodeIdDict; QMap nodeIdReserved; QMap calendarIdDict; QMap m_managerIdMap; QList m_managers; QTimeZone m_timeZone; WBSDefinition m_wbsDefinition; ConfigBase emptyConfig; QPointer m_config; // this one is not owned by me, don't delete int m_progress; QMap m_schedulerPlugins; int m_refCount; // make it possible to use the project by different threads QList m_hardConstraints; QList m_softConstraints; QList m_terminalNodes; bool m_useSharedResources; bool m_sharedResourcesLoaded; QString m_sharedResourcesFile; QUrl m_sharedProjectsUrl; bool m_loadProjectsAtStartup; + +public: + class WorkPackageInfo { + public: + WorkPackageInfo() : checkForWorkPackages(false), deleteAfterRetrieval(true), archiveAfterRetrieval(false) {} + bool checkForWorkPackages; + QUrl retrieveUrl; + bool deleteAfterRetrieval; + bool archiveAfterRetrieval; + QUrl archiveUrl; + QUrl publishUrl; + + bool operator!=(const WorkPackageInfo &o) { return !operator==(o); } + bool operator==(const WorkPackageInfo &o) { + return checkForWorkPackages == o.checkForWorkPackages + && deleteAfterRetrieval == o.deleteAfterRetrieval + && archiveAfterRetrieval == o.archiveAfterRetrieval + && retrieveUrl == o.retrieveUrl + && archiveUrl == o.archiveUrl + && publishUrl == o.publishUrl; + } + }; + void setWorkPackageInfo(const WorkPackageInfo &wpInfo) { m_workPackageInfo = wpInfo; } + WorkPackageInfo workPackageInfo() const { return m_workPackageInfo; } +private: + WorkPackageInfo m_workPackageInfo; }; } //KPlato namespace #endif diff --git a/src/libs/kernel/kpttask.cpp b/src/libs/kernel/kpttask.cpp index ac9765e4..a12529b6 100644 --- a/src/libs/kernel/kpttask.cpp +++ b/src/libs/kernel/kpttask.cpp @@ -1,3858 +1,3865 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard 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 "kpttask.h" #include "kptappointment.h" #include "kptproject.h" #include "kptduration.h" #include "kptrelation.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include #include #include namespace KPlato { Task::Task(Node *parent) : Node(parent), m_resource(), m_workPackage( this ) { //debugPlan<<"("<setOptimisticRatio(-10); m_estimate->setPessimisticRatio(20); m_estimate->setParentNode( this ); if (m_parent) m_leader = m_parent->leader(); } Task::Task(const Task &task, Node *parent) : Node(task, parent), m_resource(), m_workPackage( this ) { //debugPlan<<"("<setParentNode( this ); } Task::~Task() { while (!m_resource.isEmpty()) { delete m_resource.takeFirst(); } while (!m_parentProxyRelations.isEmpty()) { delete m_parentProxyRelations.takeFirst(); } while (!m_childProxyRelations.isEmpty()) { delete m_childProxyRelations.takeFirst(); } } int Task::type() const { if ( numChildren() > 0) { return Node::Type_Summarytask; } else if ( m_constraint == Node::FixedInterval ) { if ( m_constraintEndTime == m_constraintStartTime ) { return Node::Type_Milestone; } } else if ( m_estimate->expectedEstimate() == 0.0 ) { return Node::Type_Milestone; } return Node::Type_Task; } Duration *Task::getRandomDuration() { return 0L; } ResourceGroupRequest *Task::resourceGroupRequest(const ResourceGroup *group) const { return m_requests.find(group); } void Task::clearResourceRequests() { m_requests.clear(); changed( this ); } void Task::addRequest(ResourceGroup *group, int numResources) { addRequest(new ResourceGroupRequest(group, numResources)); } void Task::addRequest(ResourceGroupRequest *request) { //debugPlan<group()<group()->id()<group()->name(); m_requests.addRequest(request); changed( this ); } void Task::takeRequest(ResourceGroupRequest *request) { //debugPlan< Task::requestedResources() const { return m_requests.requestedResources(); } bool Task::containsRequest( const QString &identity ) const { return m_requests.contains( identity ); } ResourceRequest *Task::resourceRequest( const QString &name ) const { return m_requests.resourceRequest( name ); } QStringList Task::assignedNameList( long id) const { Schedule *s = schedule( id ); if ( s == 0 ) { return QStringList(); } return s->resourceNameList(); } void Task::makeAppointments() { if (m_currentSchedule == 0) return; if (type() == Node::Type_Task) { //debugPlan<startTime<<","<endTime<<";"<duration.toString(); m_requests.makeAppointments(m_currentSchedule); //debugPlan<startTime<<","<endTime<<";"<duration.toString(); } else if (type() == Node::Type_Summarytask) { foreach (Node *n, m_nodes) { n->makeAppointments(); } } else if (type() == Node::Type_Milestone) { //debugPlan<<"Milestone not implemented"; // Well, shouldn't have resources anyway... } } void Task::copySchedule() { if ( m_currentSchedule == 0 || type() != Node::Type_Task ) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast( findSchedule( id ) ); if ( ns == 0 ) { return; } if ( type() == Node::Type_Task ) { copyAppointments( ns->startTime, ns->endTime ); } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; m_currentSchedule->endTime = ns->endTime; m_currentSchedule->lateFinish = ns->lateFinish; m_currentSchedule->duration = ns->duration; // TODO: status flags, etc //debugPlan; } void Task::copyAppointments() { copyAppointments( DateTime(), m_currentSchedule->startTime ); } void Task::copyAppointments( const DateTime &start, const DateTime &end ) { if ( m_currentSchedule == 0 || type() != Node::Type_Task ) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast( findSchedule( id ) ); if ( ns == 0 ) { return; } DateTime st = start.isValid() ? start : ns->startTime; DateTime et = end.isValid() ? end : ns->endTime; //debugPlan<calculationMode(); foreach ( const Appointment *a, ns->appointments() ) { Resource *r = a->resource() == 0 ? 0 : a->resource()->resource(); if ( r == 0 ) { errorPlan<<"No resource"; continue; } AppointmentIntervalList lst = a->intervals( st, et ); if ( lst.isEmpty() ) { //debugPlan<<"No intervals to copy from"<appointments() ) { if ( c->resource()->resource() == r ) { //debugPlan<<"Found current appointment to"<resource()->resource()->name()<add( curr ); curr->setNode( m_currentSchedule ); //debugPlan<<"Created new appointment"<( r->findSchedule( m_currentSchedule->id() ) ); if ( rs == 0 ) { rs = r->createSchedule( m_currentSchedule->parent() ); rs->setId( m_currentSchedule->id() ); rs->setName( m_currentSchedule->name() ); rs->setType( m_currentSchedule->type() ); //debugPlan<<"Resource schedule not found, id="<id(); } rs->setCalculationMode( m_currentSchedule->calculationMode() ); if ( ! rs->appointments().contains( curr ) ) { //debugPlan<<"add to resource"<add( curr ); curr->setResource( rs ); } Appointment app; app.setIntervals( lst ); //foreach ( AppointmentInterval *i, curr->intervals() ) { debugPlan<startTime().toString()<endTime().toString(); } curr->merge( app ); //debugPlan<<"Appointments added"; } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; } void Task::calcResourceOverbooked() { if (m_currentSchedule) m_currentSchedule->calcResourceOverbooked(); } bool Task::load(KoXmlElement &element, XMLLoaderObject &status ) { QString s; bool ok = false; m_id = element.attribute(QStringLiteral("id")); m_priority = element.attribute(QStringLiteral("priority"), "0").toInt(); setName( element.attribute(QStringLiteral("name")) ); m_leader = element.attribute(QStringLiteral("leader")); m_description = element.attribute(QStringLiteral("description")); //debugPlan<load(e)) { if (!project.addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if (e.tagName() == QLatin1String("task")) { if (status.loadTaskChildren()) { // Load the task Task *child = new Task(this); if (child->load(e, status)) { if (!status.project().addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } else if (e.tagName() == QLatin1String("resource")) { // TODO: Load the resource (projects don't have resources yet) } else if (e.tagName() == QLatin1String("estimate") || ( /*status.version() < "0.6" &&*/ e.tagName() == QLatin1String("effort") ) ) { // Load the estimate m_estimate->load(e, status); } else if (e.tagName() == QLatin1String("resourcegroup-request")) { // Load the resource request // Handle multiple requests to same group gracefully (Not really allowed) ResourceGroupRequest *r = m_requests.findGroupRequestById( e.attribute(QStringLiteral("group-id")) ); if ( r ) { warnPlan<<"Multiple requests to same group, loading into existing group"; if ( ! r->load( e, status ) ) { errorPlan<<"Failed to load resource request"; } } else { r = new ResourceGroupRequest(); if (r->load(e, status)) { addRequest(r); } else { errorPlan<<"Failed to load resource request"; delete r; } } } else if (e.tagName() == QLatin1String("workpackage")) { m_workPackage.loadXML( e, status ); } else if (e.tagName() == QLatin1String("progress")) { completion().loadXML( e, status ); } else if (e.tagName() == QLatin1String("schedules")) { KoXmlNode n = e.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == QLatin1String("schedule")) { NodeSchedule *sch = new NodeSchedule(); if (sch->loadXML(el, status)) { sch->setNode(this); addSchedule(sch); } else { errorPlan<<"Failed to load schedule"; delete sch; } } } } else if (e.tagName() == QLatin1String("documents")) { m_documents.load( e, status ); } else if (e.tagName() == QLatin1String("workpackage-log")) { KoXmlNode n = e.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement el = n.toElement(); if (el.tagName() == QLatin1String("workpackage")) { WorkPackage *wp = new WorkPackage( this ); if ( wp->loadLoggedXML( el, status ) ) { m_packageLog << wp; } else { errorPlan<<"Failed to load logged workpackage"; delete wp; } } } } } //debugPlan<save(me); m_documents.save( me ); if ( ! m_requests.isEmpty() ) { m_requests.save(me); } if (context.saveAll(this)) { if (!m_schedules.isEmpty()) { QDomElement schs = me.ownerDocument().createElement(QStringLiteral("schedules")); me.appendChild(schs); foreach (const Schedule *s, m_schedules) { if (!s->isDeleted()) { s->saveXML(schs); } } } completion().saveXML( me ); m_workPackage.saveXML(me); // The workpackage log if (!m_packageLog.isEmpty()) { QDomElement log = me.ownerDocument().createElement(QStringLiteral("workpackage-log")); me.appendChild(log); foreach (const WorkPackage *wp, m_packageLog) { wp->saveLoggedXML( log ); } } } if (context.saveChildren(this)) { for (int i=0; isave(me, context); } } } void Task::saveAppointments(QDomElement &element, long id) const { //debugPlan<save(me); completion().saveXML( me ); if ( m_schedules.contains( id ) && ! m_schedules[ id ]->isDeleted() ) { QDomElement schs = me.ownerDocument().createElement(QStringLiteral("schedules")); me.appendChild(schs); m_schedules[ id ]->saveXML( schs ); } m_documents.save( me ); // TODO: copying documents } bool Task::isStarted() const { return completion().isStarted(); } EffortCostMap Task::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( start, end, id, typ ); } return ec; } Schedule *s = schedule( id ); if ( s ) { return s->plannedEffortCostPrDay( start, end, typ ); } return EffortCostMap(); } EffortCostMap Task::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( resource, start, end, id, typ ); } return ec; } Schedule *s = schedule( id ); if ( s ) { return s->plannedEffortCostPrDay( resource, start, end, typ ); } return EffortCostMap(); } EffortCostMap Task::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( start, end, id, typ ); } return ec; } switch ( completion().entrymode() ) { case Completion::FollowPlan: return plannedEffortCostPrDay( start, end, id, typ ); default: return completion().effortCostPrDay( start, end, id ); } return EffortCostMap(); } EffortCostMap Task::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( resource, start, end, id, typ ); } return ec; } switch ( completion().entrymode() ) { case Completion::FollowPlan: return plannedEffortCostPrDay( resource, start, end, id, typ ); default: return completion().effortCostPrDay( resource, start, end ); } return EffortCostMap(); } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort( const Resource *resource, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( resource, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( resource, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort( long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date Duration Task::plannedEffort( const Resource *resource, QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( resource, date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( resource, date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date Duration Task::plannedEffort(QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date Duration Task::plannedEffortTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo( date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffortTo( date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date Duration Task::plannedEffortTo( const Resource *resource, QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo( resource, date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffortTo( resource, date, typ ); } return eff; } // Returns the total actual effort for this task (or subtasks) Duration Task::actualEffort() const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort(); } } return completion().actualEffort(); } // Returns the total actual effort for this task (or subtasks) on date Duration Task::actualEffort( QDate date ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort( date ); } return eff; } return completion().actualEffort( date ); } // Returns the total actual effort for this task (or subtasks) to date Duration Task::actualEffortTo( QDate date ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffortTo( date ); } return eff; } return completion().actualEffortTo( date ); } EffortCost Task::plannedCost( long id, EffortCostCalculationType typ ) const { //debugPlan; if (type() == Node::Type_Summarytask) { return Node::plannedCost( id, typ ); } EffortCost c; Schedule *s = schedule( id ); if ( s ) { c = s->plannedCost( typ ); } return c; } double Task::plannedCostTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; double c = 0; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { c += n->plannedCostTo( date, id, typ ); } return c; } Schedule *s = schedule( id ); if ( s == 0 ) { return c; } c = s->plannedCostTo( date, typ ); if ( date >= s->startTime.date() ) { c += m_startupCost; } if ( date >= s->endTime.date() ) { c += m_shutdownCost; } return c; } EffortCost Task::actualCostTo( long int id, QDate date ) const { //debugPlan; EffortCostMap ecm = acwp( id ); return EffortCost( ecm.effortTo( date ), ecm.costTo( date ) ); } double Task::bcws( QDate date, long id ) const { //debugPlan; double c = plannedCostTo( date, id ); //debugPlan<bcwsPrDayCache( typ ); if ( ! cache.cached ) { EffortCostMap ec = s->bcwsPrDay( typ ); if ( typ != ECCT_Work ) { if ( m_startupCost > 0.0 ) { ec.add( s->startTime.date(), Duration::zeroDuration, m_startupCost ); } if ( m_shutdownCost > 0.0 ) { ec.add( s->endTime.date(), Duration::zeroDuration, m_shutdownCost ); } cache.effortcostmap = ec; cache.cached = true; } } return cache.effortcostmap; } EffortCostMap Task::bcwpPrDay( long int id, EffortCostCalculationType typ ) { //debugPlan; if ( type() == Node::Type_Summarytask ) { return Node::bcwpPrDay( id, typ ); } Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache cache = s->bcwpPrDayCache( typ ); if ( ! cache.cached ) { // do not use bcws cache, it includes startup/shutdown cost EffortCostMap e = s->plannedEffortCostPrDay( s->appointmentStartTime().date(), s->appointmentEndTime().date(), typ ); if ( completion().isStarted() && ! e.isEmpty() ) { // calculate bcwp on bases of bcws *without* startup/shutdown cost double totEff = e.totalEffort().toDouble( Duration::Unit_h ); double totCost = e.totalCost(); QDate sd = completion().entries().keys().value( 0 ); if ( ! sd.isValid() || e.startDate() < sd ) { sd = e.startDate(); } QDate ed = qMax( e.endDate(), completion().entryDate() ); for ( QDate d = sd; d <= ed; d = d.addDays( 1 ) ) { double p = (double)(completion().percentFinished( d )) / 100.0; EffortCost ec = e.days()[ d ]; ec.setBcwpEffort( totEff * p ); ec.setBcwpCost( totCost * p ); e.insert( d, ec ); } } if ( typ != ECCT_Work ) { // add bcws startup/shutdown cost if ( m_startupCost > 0.0 ) { e.add( s->startTime.date(), Duration::zeroDuration, m_startupCost ); } if ( m_shutdownCost > 0.0 ) { e.add( s->endTime.date(), Duration::zeroDuration, m_shutdownCost ); } // add bcwp startup/shutdown cost if ( m_shutdownCost > 0.0 && completion().finishIsValid() ) { QDate finish = completion().finishTime().date(); e.addBcwpCost( finish, m_shutdownCost ); debugPlan<<"addBcwpCost:"< finish ) { e.addBcwpCost( date, m_shutdownCost ); debugPlan<<"addBcwpCost:"< 0.0 && completion().startIsValid() ) { QDate start = completion().startTime().date(); e.addBcwpCost( start, m_startupCost ); // bcwp is cumulative so add to all entries after start for ( EffortCostDayMap::const_iterator it = e.days().constBegin(); it != e.days().constEnd(); ++it ) { const QDate date = it.key(); if ( date > start ) { e.addBcwpCost( date, m_startupCost ); } } } } cache.effortcostmap = e; cache.cached = true; } return cache.effortcostmap; } Duration Task::budgetedWorkPerformed( QDate date, long id ) const { //debugPlan; Duration e; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed( date, id ); } return e; } e = plannedEffort( id ) * (double)completion().percentFinished( date ) / 100.0; //debugPlan<budgetedCostPerformed( date, id ); } return c; } c = plannedCost( id ).cost() * (double)completion().percentFinished( date ) / 100.0; if ( completion().isStarted() && date >= completion().startTime().date() ) { c += m_startupCost; } if ( completion().isFinished() && date >= completion().finishTime().date() ) { c += m_shutdownCost; } //debugPlan<acwpCache( typ ); if ( ! ec.cached ) { //debugPlan<= completion().startTime().date() ) { c.add( Duration::zeroDuration, m_startupCost ); } if ( completion().isFinished() && date >= completion().finishTime().date() ) { c.add( Duration::zeroDuration, m_shutdownCost ); } return c; } double Task::schedulePerformanceIndex( QDate date, long id ) const { //debugPlan; double r = 1.0; double s = bcws( date, id ); double p = bcwp( date, id ); if ( s > 0.0 ) { r = p / s; } return r; } double Task::effortPerformanceIndex( QDate date, long id ) const { //debugPlan; double r = 1.0; Duration a, b; if ( m_estimate->type() == Estimate::Type_Effort ) { Duration b = budgetedWorkPerformed( date, id ); if ( b == Duration::zeroDuration ) { return r; } Duration a = actualEffortTo( date ); if ( b == Duration::zeroDuration ) { return 1.0; } r = b.toDouble() / a.toDouble(); } else if ( m_estimate->type() == Estimate::Type_Duration ) { //TODO } return r; } //FIXME Handle summarytasks double Task::costPerformanceIndex( long int id, QDate date, bool *error ) const { double res = 0.0; double ac = actualCostTo( id, date ).cost(); bool e = ( ac == 0.0 || completion().percentFinished() == 0 ); if (error) { *error = e; } if (!e) { res = ( plannedCostTo( date, id ) * completion().percentFinished() ) / ( 100 * ac ); } return res; } void Task::initiateCalculation(MainSchedule &sch) { //debugPlan< &list, int use) { DateTime time; // do them forward foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->parent()->calculateForward(use); // early finish switch (r->type()) { case Relation::StartStart: // I can't start earlier than my predesseccor t = r->parent()->earlyStart() + r->lag(); break; case Relation::FinishFinish: { // I can't finish earlier than my predeccessor, so // I can't start earlier than it's (earlyfinish+lag)- my duration t += r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState( Schedule::OBS_Allow ); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("FinishFinish: get duration to calculate early finish") ); #endif t -= duration(t, use, true); m_currentSchedule->setAllowOverbookingState( obs ); break; } default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<earlyFinish; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::CalculateForward ); //cs->logDebug( "calculateForward: earlyStart=" + cs->earlyStart.toString() ); // calculate all predecessors if (!dependParentNodes().isEmpty()) { DateTime time = calculatePredeccessors(dependParentNodes(), use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug( QString( "calculate forward: early start moved to: %1" ).arg( cs->earlyStart.toString() ) ); } } if (!m_parentProxyRelations.isEmpty()) { DateTime time = calculatePredeccessors(m_parentProxyRelations, use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug( QString( "calculate forward: early start moved to: %1" ).arg( cs->earlyStart.toString() ) ); } } m_calculateForwardRun = true; //cs->logDebug( "calculateForward: earlyStart=" + cs->earlyStart.toString() ); return calculateEarlyFinish( use ); } DateTime Task::calculateEarlyFinish(int use) { //debugPlan<usePert(); cs->setCalculationMode( Schedule::CalculateForward ); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug( QStringLiteral( "Start calculate forward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Calculate early finish " ) ); //debugPlan<<"------>"<earlyStart; if (type() == Node::Type_Task) { m_durationForward = m_estimate->value(use, pert); switch (constraint()) { case Node::ASAP: case Node::ALAP: { //debugPlan<earlyStart; cs->earlyStart = workTimeAfter( cs->earlyStart ); m_durationForward = duration(cs->earlyStart, use, false); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + m_earlyFinish.toString() ); #endif if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationForward = duration(cs->earlyStart, use, false); cs->setAllowOverbookingState( obs ); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString() ); #endif } break; } case Node::MustFinishOn: { cs->earlyStart = workTimeAfter( cs->earlyStart ); m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"MustFinishOn:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->earlyFinish = qMax( cs->earlyFinish, m_constraintEndTime ); if ( !cs->allowOverbooking() ) { cs->endTime = cs->earlyFinish; cs->startTime = cs->earlyFinish - duration( cs->earlyFinish, use, true ); makeAppointments(); } m_earlyFinish = cs->earlyFinish; m_durationForward = m_earlyFinish - cs->earlyStart; break; } case Node::FinishNotLater: { m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"FinishNotLater:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = cs->earlyFinish; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MSO/SNE:"<earlyStart; cs->logDebug( constraintToString() + ": " + m_constraintStartTime.toString() + ' ' + cs->earlyStart.toString() ); cs->earlyStart = workTimeAfter( qMax( cs->earlyStart, m_constraintStartTime ) ); if ( cs->earlyStart < m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_durationForward = duration( cs->earlyStart, use, false ); m_earlyFinish = cs->earlyStart + m_durationForward; if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationForward = duration(cs->startTime, use, false); cs->setAllowOverbookingState( obs ); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("MSO/SNE earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString() ); #endif } break; } case Node::FixedInterval: { if ( cs->earlyStart > m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } //cs->earlyStart = m_constraintStartTime; m_durationForward = m_constraintEndTime - m_constraintStartTime; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if ( !cs->allowOverbooking() ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } } } else if (type() == Node::Type_Milestone) { m_durationForward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<earlyStart; //cs->logDebug( QString( "%1: %2, early start: %3" ).arg( constraintToString() ).arg( m_constraintEndTime.toString() ).arg( cs->earlyStart.toString() ) ); if ( cs->earlyStart < m_constraintEndTime ) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if ( cs->earlyStart > m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<earlyStart; if ( cs->earlyStart > m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<earlyStart; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintStartTime - cs->earlyStart; } if ( cs->earlyStart > m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::StartNotEarlier: //debugPlan<<"StartNotEarlier:"<earlyStart; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintStartTime - cs->earlyStart; } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FixedInterval: m_earlyFinish = cs->earlyStart + m_durationForward; break; default: m_earlyFinish = cs->earlyStart + m_durationForward; break; } //debugPlan<insertForwardNode( this ); cs->earlyFinish = cs->earlyStart + m_durationForward; foreach ( const Appointment *a, cs->appointments( Schedule::CalculateForward ) ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo( i18n( "Early finish calculated: %1", locale.toString(cs->earlyFinish, QLocale::ShortFormat) ) ); cs->incProgress(); #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "Finished calculate forward: %1 ms" ).arg( timer.elapsed() ) ); #endif return m_earlyFinish; } DateTime Task::calculateSuccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->child()->calculateBackward(use); switch (r->type()) { case Relation::StartStart: { // I must start before my successor, so // I can't finish later than it's (starttime-lag) + my duration t -= r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState( Schedule::OBS_Allow ); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("StartStart: get duration to calculate late start") ); #endif t += duration(t, use, false); m_currentSchedule->setAllowOverbookingState( obs ); break; } case Relation::FinishFinish: // My successor cannot finish before me, so // I can't finish later than it's latest finish - lag t = r->child()->lateFinish() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } //debugPlan<lateStart; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::CalculateBackward ); //cs->lateFinish = projectNode()->constraintEndTime(); // calculate all successors if (!dependChildNodes().isEmpty()) { DateTime time = calculateSuccessors(dependChildNodes(), use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } if (!m_childProxyRelations.isEmpty()) { DateTime time = calculateSuccessors(m_childProxyRelations, use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } m_calculateBackwardRun = true; return calculateLateStart( use ); } DateTime Task::calculateLateStart(int use) { //debugPlan<lateStart; } bool pert = cs->usePert(); cs->setCalculationMode( Schedule::CalculateBackward ); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug( QStringLiteral( "Start calculate backward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Calculate late start" ) ); cs->logDebug( QStringLiteral( "%1: late finish= %2" ).arg( constraintToString() ).arg( cs->lateFinish.toString() ) ); //debugPlan<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + cs->lateStart.toString() ); #endif if ( !cs->allowOverbooking() ) { cs->startTime = cs->lateStart; cs->endTime = cs->lateFinish; makeAppointments(); // calculate wo checking bookings = latest start possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationBackward = duration(cs->lateFinish, use, true); cs->setAllowOverbookingState( obs ); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP latest start possible: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + (cs->lateFinish-m_durationBackward).toString() ); #endif } break; case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MustStartOn:"<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; if ( cs->lateStart < m_constraintStartTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } else { cs->lateStart = qMax( cs->earlyStart, m_constraintStartTime ); } if ( !cs->allowOverbooking() ) { if ( constraint() == MustStartOn ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintStartTime + duration( m_constraintStartTime, use, false ); } else { cs->startTime = qMax( cs->lateStart, m_constraintStartTime ); cs->endTime = qMax( cs->lateFinish, cs->startTime ); // safety } makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } case Node::MustFinishOn: case Node::FinishNotLater: //debugPlan<<"MustFinishOn:"<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); cs->endTime = cs->lateFinish; if ( cs->lateFinish < m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } else { cs->endTime = qMax( cs->earlyFinish, m_constraintEndTime ); } m_durationBackward = duration(cs->endTime, use, true); cs->startTime = cs->endTime - m_durationBackward; if ( !cs->allowOverbooking() ) { makeAppointments(); } m_durationBackward = cs->lateFinish - cs->startTime; cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FixedInterval: { //cs->lateFinish = m_constraintEndTime; if ( cs->lateFinish < m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_durationBackward = m_constraintEndTime - m_constraintStartTime; if ( cs->lateFinish > m_constraintEndTime ) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } if ( !cs->allowOverbooking() ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } } } else if (type() == Node::Type_Milestone) { m_durationBackward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<lateFinish; if ( m_constraintEndTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if ( m_constraintEndTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<lateFinish; if ( m_constraintEndTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if ( m_constraintEndTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<lateFinish; if ( m_constraintStartTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } else if ( m_constraintStartTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; //cs->logDebug( QString( "%1: constraint:%2, start=%3, finish=%4" ).arg( constraintToString() ).arg( m_constraintStartTime.toString() ).arg( cs->lateStart.toString() ).arg( cs->lateFinish.toString() ) ); break; case Node::StartNotEarlier: //debugPlan<<"MustStartOn:"<lateFinish; if ( m_constraintStartTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish; break; case Node::FixedInterval: cs->lateStart = cs->lateFinish - m_durationBackward; break; default: cs->lateStart = cs->lateFinish - m_durationBackward; break; } //debugPlan<lateFinish; } else if (type() == Node::Type_Summarytask) { warnPlan<<"Summarytasks should not be calculated here: "<insertBackwardNode( this ); cs->lateStart = cs->lateFinish - m_durationBackward; foreach ( const Appointment *a, cs->appointments( Schedule::CalculateBackward ) ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo( i18n( "Late start calculated: %1", locale.toString(cs->lateStart, QLocale::ShortFormat) ) ); cs->incProgress(); #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "Finished calculate backward: %1 ms" ).arg( timer.elapsed() ) ); #endif return cs->lateStart; } DateTime Task::schedulePredeccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } // schedule the predecessors DateTime earliest = r->parent()->earlyStart(); DateTime t = r->parent()->scheduleForward(earliest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my predesseccor t = r->parent()->startTime() + r->lag(); break; case Relation::FinishFinish: // I can't end before my predecessor, so // I can't start before it's endtime - my duration #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("FinishFinish: get duration to calculate earliest start") ); #endif t -= duration(t + r->lag(), use, true); break; default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<endTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; //cs->logDebug( QString( "Schedule forward (early start: %1)" ).arg( cs->earlyStart.toString() ) ); cs->setCalculationMode( Schedule::Scheduling ); DateTime startTime = earliest > cs->earlyStart ? earliest : cs->earlyStart; // First, calculate all my own predecessors DateTime time = schedulePredeccessors(dependParentNodes(), use); if ( time > startTime ) { startTime = time; //debugPlan<earlyStart.toString() ) ); cs->startTime = cs->earlyStart; } QTime timer; timer.start(); cs->logInfo( i18n( "Start schedule forward: %1 ", constraintToString( true ) ) ); QLocale locale; cs->logInfo( i18n( "Schedule from start %1", locale.toString(cs->startTime, QLocale::ShortFormat) ) ); //debugPlan<startTime<<"earliest:"<earlyStart; if ( false/*useCalculateForwardAppointments*/ && m_estimate->type() == Estimate::Type_Effort && ! cs->allowOverbooking() && cs->hasAppointments( Schedule::CalculateForward ) ) { #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString() ); #endif cs->copyAppointments( Schedule::CalculateForward, Schedule::Scheduling ); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); } cs->startTime = cs->appointmentStartTime(); cs->endTime = cs->appointmentEndTime(); Q_ASSERT( cs->startTime.isValid() ); Q_ASSERT( cs->endTime.isValid() ); cs->duration = cs->endTime - cs->startTime; if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); return cs->endTime; } cs->startTime = workTimeAfter( cs->startTime, cs ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString() ); #endif cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } break; case Node::ALAP: // cs->startTime calculated above //debugPlan<startTime<endTime<<" latest="<lateFinish; cs->endTime = workTimeBefore( cs->lateFinish, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<endTime = workTimeBefore( cs->earlyFinish, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; makeAppointments(); } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if ( cs->recalculate() && completion().isStarted() ) { cs->earlyStart = cs->startTime = completion().startTime(); } break; case Node::StartNotEarlier: // cs->startTime calculated above //debugPlan<<"StartNotEarlier:"<startTime<lateStart; cs->startTime = workTimeAfter( qMax( cs->startTime, m_constraintStartTime ), cs ); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::FinishNotLater: // cs->startTime calculated above //debugPlan<<"FinishNotLater:"<startTime; cs->startTime = workTimeAfter( cs->startTime, cs ); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->endTime > m_constraintEndTime) { //warnPlan<<"cs->endTime > m_constraintEndTime"; cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::MustStartOn: // Always try to put it on time cs->startTime = workTimeAfter( m_constraintStartTime, cs ); //debugPlan<<"MustStartOn="<startTime; cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "%1: Schedule from %2 to %3" ).arg( constraintToString() ).arg( cs->startTime.toString() ).arg( cs->endTime.toString() ) ); #endif makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (m_constraintStartTime < cs->startTime ) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::MustFinishOn: // Just try to schedule on time cs->endTime = workTimeBefore( m_constraintEndTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<<"MustFinishOn:"<lateFinish<<":"<startTime<endTime; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if ( cs->endTime != m_constraintEndTime ) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::FixedInterval: { // cs->startTime calculated above //debugPlan<<"FixedInterval="<startTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if ( m_constraintStartTime >= cs->earlyStart ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; } else { cs->startTime = cs->earlyStart; cs->endTime = cs->startTime + cs->duration; cs->constraintError = true; } if ( m_constraintStartTime < cs->startTime ) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->workStartTime = m_constraintStartTime; cs->workEndTime = m_constraintEndTime; //debugPlan<<"FixedInterval="<startTime<<","<endTime; makeAppointments(); break; } default: break; } if ( m_estimate->type() == Estimate::Type_Effort ) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = ( m_estimate->value( use, cs->usePert() ) - cs->plannedEffort() ) > ( 5 * 60000 ); if ( cs->effortNotMet ) { cs->logError( i18n( "Effort not met. Estimate: %1, planned: %2", estimate()->value( use, cs->usePert() ).toHours(), cs->plannedEffort().toHours() ) ); } } } else if (type() == Node::Type_Milestone) { if ( cs->recalculate() && completion().isFinished() ) { cs->startTime = completion().startTime(); cs->endTime = completion().finishTime(); m_visitedForward = true; return cs->endTime; } switch (m_constraint) { case Node::ASAP: { cs->endTime = cs->startTime; // TODO check, do we need to check succeccors earliestStart? cs->positiveFloat = cs->lateFinish - cs->endTime; break; } case Node::ALAP: { cs->startTime = qMax( cs->lateFinish, cs->earlyFinish ); cs->endTime = cs->startTime; cs->positiveFloat = Duration::zeroDuration; break; } case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { //debugPlan<<"MustStartOn:"<startTime; DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "%1: constraint time=%2, start time=%3" ).arg( constraintToString() ).arg( contime.toString() ).arg( cs->startTime.toString() ) ); #endif if ( cs->startTime < contime ) { if ( contime <= cs->lateFinish || contime <= cs->earlyFinish ) { cs->startTime = contime; } } cs->negativeFloat = cs->startTime > contime ? cs->startTime - contime : contime - cs->startTime; if ( cs->negativeFloat != 0 ) { cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: if ( cs->startTime < m_constraintStartTime ) { if ( m_constraintStartTime <= cs->lateFinish || m_constraintStartTime <= cs->earlyFinish ) { cs->startTime = m_constraintStartTime; } } if ( cs->startTime < m_constraintStartTime ) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: //debugPlan<startTime; if (cs->startTime > m_constraintEndTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; //debugPlan<startTime<<","<endTime; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->endTime = cs->startTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime<<" :"<endTime<<""<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime() ) { cs->logError( i18n( "Failed to schedule within project target time" ) ); } foreach ( const Appointment *a, cs->appointments() ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } if ( cs->startTime < cs->earlyStart ) { cs->logWarning( i18n( "Starting earlier than early start" ) ); } if ( cs->endTime > cs->lateFinish ) { cs->logWarning( i18n( "Finishing later than late finish" ) ); } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); m_visitedForward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); cs->logInfo( i18n( "Finished schedule forward: %1 ms", timer.elapsed() ) ); return cs->endTime; } DateTime Task::scheduleSuccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<child()->name(); continue; } // get the successors starttime DateTime latest = r->child()->lateFinish(); DateTime t = r->child()->scheduleBackward(latest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my successor, so // I can't finish later than it's starttime + my duration #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("StartStart: get duration to calculate late finish") ); #endif t += duration(t - r->lag(), use, false); break; case Relation::FinishFinish: t = r->child()->endTime() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } return time; } DateTime Task::scheduleBackward(const DateTime &latest, int use) { if ( m_scheduleBackwardRun ) { return m_currentSchedule->startTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::Scheduling ); DateTime endTime = latest < cs->lateFinish ? latest : cs->lateFinish; // First, calculate all my own successors DateTime time = scheduleSuccessors(dependChildNodes(), use); if (time.isValid() && time < endTime) { endTime = time; } // Then my parents time = scheduleSuccessors(m_childProxyRelations, use); if (time.isValid() && time < endTime) { endTime = time; } if ( ! m_visitedBackward ) { cs->endTime = endTime; } m_scheduleBackwardRun = true; return scheduleFromEndTime( use ); } DateTime Task::scheduleFromEndTime(int use) { //debugPlan<setCalculationMode( Schedule::Scheduling ); bool pert = cs->usePert(); if (m_visitedBackward) { return cs->startTime; } cs->notScheduled = false; if ( !cs->endTime.isValid() ) { cs->endTime = cs->lateFinish; } #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); cs->logDebug( QStringLiteral( "Start schedule backward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Schedule from end time: %1", cs->endTime.toString() ) ); if (type() == Node::Type_Task) { cs->duration = m_estimate->value(use, pert); switch (m_constraint) { case Node::ASAP: { // cs->endTime calculated above //debugPlan<duration = duration( cs->endTime, use, true ); e = cs->endTime; cs->startTime = e - cs->duration; } if ( e > cs->lateFinish ) { cs->schedulingError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to schedule within late finish.", constraintToString() ) ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: late finish=" + cs->lateFinish.toString() + " end time=" + e.toString() ); #endif } else if ( e > cs->endTime ) { cs->schedulingError = true; cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to schedule within successors start time", constraintToString() ) ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: succ. start=" + cs->endTime.toString() + " end time=" + e.toString() ); #endif } if ( cs->lateFinish > e ) { DateTime w = workTimeBefore( cs->lateFinish ); if ( w > e ) { cs->positiveFloat = w - e; } #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: positiveFloat=" + cs->positiveFloat.toString() ); #endif } cs->endTime = e; makeAppointments(); break; } case Node::ALAP: { // cs->endTime calculated above //debugPlan<logDebug( "ALAP: earlyStart=" + cs->earlyStart.toString() + " cs->startTime=" + cs->startTime.toString() ); #endif } else if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; #ifndef PLAN_NLOGDEBUG cs->logDebug( "ALAP: positiveFloat=" + cs->positiveFloat.toString() ); #endif } //debugPlan<endTime; cs->endTime = workTimeBefore( cs->endTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if ( cs->startTime < m_constraintStartTime ) { //warnPlan<<"m_constraintStartTime > cs->lateStart"; cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::FinishNotLater: // cs->endTime calculated above //debugPlan<<"FinishNotLater:"<endTime; if (cs->endTime > m_constraintEndTime) { cs->endTime = qMax( qMin( m_constraintEndTime, cs->lateFinish ), cs->earlyFinish ); } cs->endTime = workTimeBefore( cs->endTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if ( cs->endTime > m_constraintEndTime ) { cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::MustStartOn: // Just try to schedule on time //debugPlan<<"MustStartOn="<startTime.toString(); cs->startTime = workTimeAfter( m_constraintStartTime, cs ); cs->duration = duration(cs->startTime, use, false); if ( cs->endTime >= cs->startTime + cs->duration ) { cs->endTime = cs->startTime + cs->duration; } else { cs->endTime = workTimeBefore( cs->endTime ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; } if (m_constraintStartTime != cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::MustFinishOn: // Just try to schedule on time //debugPlan<endTime<earlyFinish; cs->endTime = workTimeBefore( m_constraintEndTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime ) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); //warnPlan<<"m_constraintEndTime > cs->endTime"; } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::FixedInterval: { // cs->endTime calculated above //debugPlan<endTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if ( cs->endTime > m_constraintEndTime ) { cs->endTime = qMax( m_constraintEndTime, cs->earlyFinish ); } cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->workStartTime = workTimeAfter( cs->startTime ); cs->workEndTime = workTimeBefore( cs->endTime ); makeAppointments(); if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; } default: break; } m_requests.reserve(cs->startTime, cs->duration); if ( m_estimate->type() == Estimate::Type_Effort ) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = ( m_estimate->value( use, cs->usePert() ) - cs->plannedEffort() ) > ( 5 * 60000 ); if ( cs->effortNotMet ) { cs->logError( i18n( "Effort not met. Estimate: %1, planned: %2", estimate()->value( use, cs->usePert() ).toHours(), cs->plannedEffort().toHours() ) ); } } } else if (type() == Node::Type_Milestone) { switch (m_constraint) { case Node::ASAP: if ( cs->endTime < cs->earlyStart ) { cs->schedulingError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to schedule after early start.", constraintToString() ) ); cs->endTime = cs->earlyStart; } else { cs->positiveFloat = cs->lateFinish - cs->endTime; } //cs->endTime = cs->earlyStart; FIXME need to follow predeccessors. Defer scheduling? cs->startTime = cs->endTime; break; case Node::ALAP: cs->startTime = cs->endTime; cs->positiveFloat = cs->lateFinish - cs->endTime; break; case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; if ( contime < cs->earlyStart ) { if ( cs->earlyStart < cs->endTime ) { cs->endTime = cs->earlyStart; } } else if ( contime < cs->endTime ) { cs->endTime = contime; } cs->negativeFloat = cs->endTime > contime ? cs->endTime - contime : contime - cs->endTime; if ( cs->negativeFloat != 0 ) { cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->startTime = cs->endTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: cs->startTime = cs->endTime; if ( m_constraintStartTime > cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: if ( m_constraintEndTime < cs->earlyStart ) { if ( cs->earlyStart < cs->endTime ) { cs->endTime = cs->earlyStart; } } else if ( m_constraintEndTime < cs->endTime ) { cs->endTime = m_constraintEndTime; } if ( m_constraintEndTime > cs->endTime ) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->startTime = cs->endTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->startTime = cs->endTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime() ) { cs->logError( i18n( "Failed to schedule within project target time" ) ); } foreach ( const Appointment *a, cs->appointments() ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat ), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } if ( cs->startTime < cs->earlyStart ) { cs->logWarning( i18n( "Starting earlier than early start" ) ); } if ( cs->endTime > cs->lateFinish ) { cs->logWarning( i18n( "Finishing later than late finish" ) ); } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); m_visitedBackward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); #ifndef PLAN_NLOGDEBUG cs->logDebug( QStringLiteral( "Finished schedule backward: %1 ms" ).arg( timer.elapsed() ) ); #endif return cs->startTime; } void Task::adjustSummarytask() { if (m_currentSchedule == 0) return; if (type() == Type_Summarytask) { DateTime start = m_currentSchedule->lateFinish; DateTime end = m_currentSchedule->earlyStart; foreach (Node *n, m_nodes) { n->adjustSummarytask(); if (n->startTime() < start) start = n->startTime(); if (n->endTime() > end) end = n->endTime(); } m_currentSchedule->startTime = start; m_currentSchedule->endTime = end; m_currentSchedule->duration = end - start; m_currentSchedule->notScheduled = false; //debugPlan<name<<":"<startTime.toString()<<" :"<endTime.toString(); } } Duration Task::duration(const DateTime &time, int use, bool backward) { //debugPlan; // TODO: handle risc if (m_currentSchedule == 0) { errorPlan<<"No current schedule"; return Duration::zeroDuration; } if (!time.isValid()) { #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QStringLiteral("Calculate duration: Start time is not valid") ); #endif return Duration::zeroDuration; } //debugPlan< calcDuration"<<(backward?"(B)":"(F)")<resourceNotAvailable = true; dur = effort; //??? } return dur; } if (m_estimate->type() == Estimate::Type_Duration) { return length( time, dur, backward ); } errorPlan<<"Unsupported estimate type: "<type(); return dur; } Duration Task::length(const DateTime &time, KPlato::Duration duration, bool backward) { return length( time, duration, m_currentSchedule, backward ); } Duration Task::length(const DateTime &time, KPlato::Duration duration, Schedule *sch, bool backward) { //debugPlan<<"--->"<<(backward?"(B)":"(F)")<logDebug( QStringLiteral("Calculate length: estimate == 0") ); #else Q_UNUSED(sch) #endif return l; } Calendar *cal = m_estimate->calendar(); if ( cal == 0) { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculate length: No calendar, return estimate " + duration.toString() ); #endif return duration; } #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculate length from: " + time.toString() ); #endif DateTime logtime = time; bool sts=true; bool match = false; DateTime start = time; int inc = backward ? -1 : 1; DateTime end = start; Duration l1; int nDays = backward ? projectNode()->constraintStartTime().daysTo( time ) : time.daysTo( projectNode()->constraintEndTime() ); for (int i=0; !match && i <= nDays; ++i) { // days end = end.addDays(inc); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); //debugPlan<<"["<logDebug( "Days: duration " + logtime.toString() + " - " + end.toString() + " = " + l.toString() + " (" + (duration - l).toString() + ')' ); #endif logtime = start; for (int i=0; !match && i < 24; ++i) { // hours end = end.addSecs(inc*60*60); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { end = start; break; } //debugPlan<<"duration(h)["<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(s)["<logDebug( "Seconds: duration " + logtime.toString() + " - " + end.toString() + " l " + l.toString() + " (" + (duration - l).toString() + ')' ); #endif for (int i=0; !match && i < 1000; ++i) { //milliseconds end.setTime(end.time().addMSecs(inc)); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Got more than asked for, should not happen! Want: " + duration.toString(Duration::Format_Hour) + " got: " + l.toString(Duration::Format_Hour) ); #endif break; } //debugPlan<<"duration(ms)["<logError( i18n( "Could not match work duration. Want: %1 got: %2", l.toString( Duration::Format_i18nHour ), duration.toString( Duration::Format_i18nHour ) ) ); } DateTime t = end; if (l != Duration::zeroDuration) { if ( backward ) { if ( end < projectNode()->constraintEndTime() ) { t = cal->firstAvailableAfter(end, projectNode()->constraintEndTime()); } } else { if ( end > projectNode()->constraintStartTime() ) { t = cal->firstAvailableBefore(end, projectNode()->constraintStartTime()); } } #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Moved end to work: " + end.toString() + " -> " + t.toString() ); #endif } end = t.isValid() ? t : time; //debugPlan<<"<---"<<(backward?"(B)":"(F)")<time ? end-time : time-end; if ( match ) { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculated length: " + time.toString() + " - " + end.toString() + " = " + l.toString() ); #endif } return l; } void Task::clearProxyRelations() { m_parentProxyRelations.clear(); m_childProxyRelations.clear(); } void Task::addParentProxyRelations( const QList &list ) { //debugPlan<addParentProxyRelations(list); n->addParentProxyRelations(dependParentNodes()); } } else { // add 'this' as child relation to the relations parent //debugPlan<parent()->addChildProxyRelation(this, r); // add a parent relation to myself addParentProxyRelation(r->parent(), r); } } } void Task::addChildProxyRelations( const QList &list) { //debugPlan<addChildProxyRelations(list); n->addChildProxyRelations(dependChildNodes()); } } else { // add 'this' as parent relation to the relations child //debugPlan<child()->addParentProxyRelation(this, r); // add a child relation to myself addChildProxyRelation(r->child(), r); } } } void Task::addParentProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add parent proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addParentProxyRelation(node, rel); } } else { //debugPlan<<"Add parent proxy from"<name()<<" to (me)"<type(), rel->lag())); } } } void Task::addChildProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add child proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addChildProxyRelation(node, rel); } } else { //debugPlan<<"Add child proxy from (me)"<name(); m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag())); } } } bool Task::isEndNode() const { return m_dependChildNodes.isEmpty() && m_childProxyRelations.isEmpty(); } bool Task::isStartNode() const { return m_dependParentNodes.isEmpty() && m_parentProxyRelations.isEmpty(); } DateTime Task::workTimeAfter(const DateTime &dt, Schedule *sch) const { DateTime t; if ( m_estimate->type() == Estimate::Type_Duration ) { if ( m_estimate->calendar() ) { t = m_estimate->calendar()->firstAvailableAfter( dt, projectNode()->constraintEndTime() ); } } else { t = m_requests.workTimeAfter(dt, sch); #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( QStringLiteral( "workTimeAfter: %1 = %2" ).arg( dt.toString() ).arg( t.toString() ) ); #endif } return t.isValid() ? t : dt; } DateTime Task::workTimeBefore(const DateTime &dt, Schedule *sch) const { DateTime t; if ( m_estimate->type() == Estimate::Type_Duration ) { if ( m_estimate->calendar() ) { t = m_estimate->calendar()->firstAvailableBefore( dt, projectNode()->constraintStartTime() ); } } else { t = m_requests.workTimeBefore(dt, sch); } return t.isValid() ? t : dt; } Duration Task::positiveFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->positiveFloat; } void Task::setPositiveFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->positiveFloat = fl; } Duration Task::negativeFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->negativeFloat; } void Task::setNegativeFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->negativeFloat = fl; } Duration Task::freeFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->freeFloat; } void Task::setFreeFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->freeFloat = fl; } Duration Task::startFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 || s->earlyStart > s->lateStart ? Duration::zeroDuration : ( s->earlyStart - s->lateStart ); } Duration Task::finishFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 || s->lateFinish < s->earlyFinish ? Duration::zeroDuration : ( s->lateFinish - s->earlyFinish ); } bool Task::isCritical( long id ) const { Schedule *s = schedule( id ); return s == 0 ? false : s->isCritical(); } bool Task::calcCriticalPath(bool fromEnd) { if (m_currentSchedule == 0) return false; //debugPlan<child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependChildNodes) { if (r->child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } else { if (isStartNode() && startFloat() == 0 && finishFloat() == 0) { m_currentSchedule->inCriticalPath = true; //debugPlan<parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependParentNodes) { if (r->parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } //debugPlan<freeFloat.toString(); } } void Task::setCurrentSchedule(long id) { setCurrentSchedulePtr(findSchedule(id)); Node::setCurrentSchedule(id); } bool Task::effortMetError( long id ) const { Schedule *s = schedule( id ); if (s == 0 || s->notScheduled || m_estimate->type() != Estimate::Type_Effort) { return false; } return s->effortNotMet; } uint Task::state( long id ) const { int st = Node::State_None; if ( ! isScheduled( id ) ) { st |= State_NotScheduled; } if ( completion().isFinished() ) { st |= Node::State_Finished; if ( completion().finishTime() > endTime( id ) ) { st |= State_FinishedLate; } if ( completion().finishTime() < endTime( id ) ) { st |= State_FinishedEarly; } } else if ( completion().isStarted() ) { st |= Node::State_Started; if ( completion().startTime() > startTime( id ) ) { st |= State_StartedLate; } if ( completion().startTime() < startTime( id ) ) { st |= State_StartedEarly; } if ( completion().percentFinished() > 0 ) { st |= State_Running; } if ( endTime( id ) < QDateTime::currentDateTime() ) { st |= State_Late; } } else if ( isScheduled( id ) ) { if ( startTime( id ) < QDateTime::currentDateTime() ) { st |= State_Late; } } st |= State_ReadyToStart; //TODO: check proxy relations foreach ( const Relation *r, m_dependParentNodes ) { if ( ! static_cast( r->parent() )->completion().isFinished() ) { st &= ~Node::State_ReadyToStart; st |= Node::State_NotReadyToStart; break; } } return st; } void Task::addWorkPackage( WorkPackage *wp ) { emit workPackageToBeAdded( this, m_packageLog.count() ); m_packageLog.append( wp ); emit workPackageAdded( this ); } void Task::removeWorkPackage( WorkPackage *wp ) { int index = m_packageLog.indexOf( wp ); if ( index < 0 ) { return; } emit workPackageToBeRemoved( this, index ); m_packageLog.removeAt( index ); emit workPackageRemoved( this ); } WorkPackage *Task::workPackageAt( int index ) const { Q_ASSERT ( index >= 0 && index < m_packageLog.count() ); return m_packageLog.at( index ); } QString Task::wpOwnerName() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.ownerName(); } return m_packageLog.last()->ownerName(); } WorkPackage::WPTransmitionStatus Task::wpTransmitionStatus() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.transmitionStatus(); } return m_packageLog.last()->transmitionStatus(); } DateTime Task::wpTransmitionTime() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.transmitionTime(); } return m_packageLog.last()->transmitionTime(); } //------------------------------------------ Completion::Completion( Node *node ) : m_node( node ), m_started( false ), m_finished( false ), m_entrymode( EnterEffortPerResource ) {} Completion::Completion( const Completion &c ) { copy( c ); } Completion::~Completion() { qDeleteAll( m_entries ); qDeleteAll( m_usedEffort ); } void Completion::copy( const Completion &p ) { m_node = 0; //NOTE m_started = p.isStarted(); m_finished = p.isFinished(); m_startTime = p.startTime(); m_finishTime = p.finishTime(); m_entrymode = p.entrymode(); qDeleteAll( m_entries ); m_entries.clear(); Completion::EntryList::ConstIterator entriesIt = p.entries().constBegin(); const Completion::EntryList::ConstIterator entriesEnd = p.entries().constEnd(); for (; entriesIt != entriesEnd; ++entriesIt) { addEntry( entriesIt.key(), new Entry( *entriesIt.value() ) ); } qDeleteAll( m_usedEffort ); m_usedEffort.clear(); Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapIt = p.usedEffortMap().constBegin(); const Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapEnd = p.usedEffortMap().constEnd(); for (; usedEffortMapIt != usedEffortMapEnd; ++usedEffortMapIt) { addUsedEffort( usedEffortMapIt.key(), new UsedEffort( *usedEffortMapIt.value() ) ); } } bool Completion::operator==( const Completion &p ) { return m_started == p.isStarted() && m_finished == p.isFinished() && m_startTime == p.startTime() && m_finishTime == p.finishTime() && m_entries == p.entries() && m_usedEffort == p.usedEffortMap(); } Completion &Completion::operator=( const Completion &p ) { copy( p ); return *this; } void Completion::changed( int property) { if ( m_node ) { m_node->changed(property); } } void Completion::setStarted( bool on ) { m_started = on; changed(Node::CompletionStarted); } void Completion::setFinished( bool on ) { m_finished = on; changed(Node::CompletionFinished); } void Completion::setStartTime( const DateTime &dt ) { m_startTime = dt; changed(Node::CompletionStartTime); } void Completion::setFinishTime( const DateTime &dt ) { m_finishTime = dt; changed(Node::CompletionFinishTime); } void Completion::setPercentFinished( QDate date, int value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->percentFinished = value; changed(Node::CompletionPercentage); } void Completion::setRemainingEffort( QDate date, KPlato::Duration value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->remainingEffort = value; changed(Node::CompletionRemainingEffort); } void Completion::setActualEffort( QDate date, KPlato::Duration value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->totalPerformed = value; changed(Node::CompletionActualEffort); } void Completion::addEntry( QDate date, Entry *entry ) { m_entries.insert( date, entry ); //debugPlan<percentFinished; } int Completion::percentFinished( QDate date ) const { int x = 0; EntryList::const_iterator it; for (it = m_entries.constBegin(); it != m_entries.constEnd() && it.key() <= date; ++it) { x = it.value()->percentFinished; } return x; } Duration Completion::remainingEffort() const { return m_entries.isEmpty() ? Duration::zeroDuration : m_entries.last()->remainingEffort; } Duration Completion::remainingEffort( QDate date ) const { Duration x; EntryList::const_iterator it; for (it = m_entries.constBegin(); it != m_entries.constEnd() && it.key() <= date; ++it) { x = it.value()->remainingEffort; } return x; } Duration Completion::actualEffort() const { Duration eff; if ( m_entrymode == EnterEffortPerResource ) { foreach( const UsedEffort *ue, m_usedEffort ) { const QMap map = ue->actualEffortMap(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { eff += it.value().effort(); } } } else if ( ! m_entries.isEmpty() ) { eff = m_entries.last()->totalPerformed; } return eff; } Duration Completion::actualEffort( const Resource *resource, QDate date ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return Duration::zeroDuration; } UsedEffort::ActualEffort ae = ue->effort( date ); return ae.effort(); } Duration Completion::actualEffort( QDate date ) const { Duration eff; if ( m_entrymode == EnterEffortPerResource ) { foreach( const UsedEffort *ue, m_usedEffort ) { if ( ue && ue->actualEffortMap().contains( date ) ) { eff += ue->actualEffortMap().value( date ).effort(); } } } else { // Hmmm: How to really know a specific date? if ( m_entries.contains( date ) ) { eff = m_entries[ date ]->totalPerformed; } } return eff; } Duration Completion::actualEffortTo( QDate date ) const { //debugPlan<effortTo( date ); } } else { QListIterator it( m_entries.uniqueKeys() ); it.toBack(); while ( it.hasPrevious() ) { QDate d = it.previous(); if ( d <= date ) { eff = m_entries[ d ]->totalPerformed; break; } } } return eff; } double Completion::averageCostPrHour( QDate date, long id ) const { Schedule *s = m_node->schedule( id ); if ( s == 0 ) { return 0.0; } double cost = 0.0; double eff = 0.0; QList cl; foreach ( const Appointment *a, s->appointments() ) { cl << a->resource()->resource()->normalRate(); double e = a->plannedEffort( date ).toDouble( Duration::Unit_h ); if ( e > 0.0 ) { eff += e; cost += e * cl.last(); } } if ( eff > 0.0 ) { cost /= eff; } else { foreach ( double c, cl ) { cost += c; } cost /= cl.count(); } return cost; } EffortCostMap Completion::effortCostPrDay(QDate start, QDate end, long id ) const { //debugPlan<name()< et ) { break; } Duration e = m_entries[ d ]->totalPerformed; if ( e != Duration::zeroDuration && e != last ) { Duration eff = e - last; ec.insert( d, eff, eff.toDouble( Duration::Unit_h ) * averageCostPrHour( d, id ) ); last = e; } } break; } case EnterEffortPerResource: { std::pair dates = actualStartEndDates(); if ( ! dates.first.isValid() ) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for ( QDate d = st; d <= et; d = d.addDays( 1 ) ) { ec.add( d, actualEffort( d ), actualCost( d ) ); } break; } } return ec; } EffortCostMap Completion::effortCostPrDay(const Resource *resource, QDate start, QDate end, long id ) const { Q_UNUSED(id); //debugPlan<name()< dates = actualStartEndDates(); if ( ! dates.first.isValid() ) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for ( QDate d = st; d <= et; d = d.addDays( 1 ) ) { ec.add( d, actualEffort( resource, d ), actualCost( resource, d ) ); } break; } } return ec; } void Completion::addUsedEffort( const Resource *resource, Completion::UsedEffort *value ) { UsedEffort *v = value == 0 ? new UsedEffort() : value; if ( m_usedEffort.contains( resource ) ) { m_usedEffort[ resource ]->mergeEffort( *v ); delete v; } else { m_usedEffort.insert( resource, v ); } changed(Node::CompletionUsedEffort); } QString Completion::note() const { return m_entries.isEmpty() ? QString() : m_entries.last()->note; } void Completion::setNote( const QString &str ) { if ( ! m_entries.isEmpty() ) { m_entries.last()->note = str; changed(); } } std::pair Completion::actualStartEndDates() const { std::pair p; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { if (!it.value()->actualEffortMap().isEmpty()) { QDate d = it.value()->firstDate(); if (!p.first.isValid() || d < p.first) { p.first = d; } d = it.value()->lastDate(); if (!p.second.isValid() || d > p.second ) { p.second = d; } } } return p; } double Completion::actualCost( QDate date ) const { //debugPlan<normalRate(); double oc = it.key()->overtimeRate(); if (it.value()->actualEffortMap().contains(date)) { UsedEffort::ActualEffort a = it.value()->effort(date); c += a.normalEffort().toDouble( Duration::Unit_h ) * nc; c += a.overtimeEffort().toDouble( Duration::Unit_h ) * oc; } } return c; } double Completion::actualCost( const Resource *resource ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return 0.0; } double c = 0.0; double nc = resource->normalRate(); double oc = resource->overtimeRate(); foreach ( const UsedEffort::ActualEffort &a, ue->actualEffortMap() ) { c += a.normalEffort().toDouble( Duration::Unit_h ) * nc; c += a.overtimeEffort().toDouble( Duration::Unit_h ) * oc; } return c; } double Completion::actualCost() const { double c = 0.0; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { c += actualCost(it.key()); } return c; } double Completion::actualCost( const Resource *resource, QDate date ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return 0.0; } UsedEffort::ActualEffort a = ue->actualEffortMap().value( date ); double c = a.normalEffort().toDouble( Duration::Unit_h ) * resource->normalRate(); c += a.overtimeEffort().toDouble( Duration::Unit_h ) * resource->overtimeRate(); return c; } EffortCostMap Completion::actualEffortCost( long int id, KPlato::EffortCostCalculationType type ) const { //debugPlan; EffortCostMap map; if ( ! isStarted() ) { return map; } QList< QMap > lst; QList< double > rate; QDate start, end; ResourceUsedEffortMap::const_iterator it; for (it = m_usedEffort.constBegin(); it != m_usedEffort.constEnd(); ++it) { const Resource *r = it.key(); //debugPlan<name()<name(); lst << usedEffort(r)->actualEffortMap(); if ( lst.last().isEmpty() ) { lst.takeLast(); continue; } if ( r->type() == Resource::Type_Material ) { if ( type == ECCT_All ) { rate.append( r->normalRate() ); } else if ( type == ECCT_EffortWork ) { rate.append( 0.0 ); } else { lst.takeLast(); continue; } } else { rate.append( r->normalRate() ); } if ( ! start.isValid() || start > lst.last().firstKey() ) { start = lst.last().firstKey(); } if ( ! end.isValid() || end < lst.last().lastKey() ) { end = lst.last().lastKey(); } } if ( ! lst.isEmpty() && start.isValid() && end.isValid() ) { for ( QDate d = start; d <= end; d = d.addDays( 1 ) ) { EffortCost c; for ( int i = 0; i < lst.count(); ++i ) { UsedEffort::ActualEffort a = lst.at( i ).value( d ); double nc = rate.value( i ); Duration eff = a.normalEffort(); double cost = eff.toDouble( Duration::Unit_h ) * nc; c.add( eff, cost ); } if ( c.effort() != Duration::zeroDuration || c.cost() != 0.0 ) { map.add( d, c ); } } } else if ( ! m_entries.isEmpty() ) { QDate st = start.isValid() ? start : m_startTime.date(); QDate et = end.isValid() ? end : m_finishTime.date(); Duration last; foreach ( const QDate &d, m_entries.uniqueKeys() ) { if ( d < st ) { continue; } Duration e = m_entries[ d ]->totalPerformed; if ( e != Duration::zeroDuration && e != last ) { //debugPlan<name()< et ) { break; } } } return map; } EffortCost Completion::actualCostTo( long int id, QDate date ) const { //debugPlan<( m ); } QString Completion::entryModeToString() const { return entrymodeList().value( m_entrymode ); } bool Completion::loadXML( KoXmlElement &element, XMLLoaderObject &status ) { //debugPlan; QString s; m_started = (bool)element.attribute(QStringLiteral("started"), QStringLiteral("0")).toInt(); m_finished = (bool)element.attribute(QStringLiteral("finished"), QStringLiteral("0")).toInt(); s = element.attribute(QStringLiteral("startTime")); if (!s.isEmpty()) { m_startTime = DateTime::fromString(s, status.projectTimeZone()); } s = element.attribute(QStringLiteral("finishTime")); if (!s.isEmpty()) { m_finishTime = DateTime::fromString(s, status.projectTimeZone()); } setEntrymode( element.attribute( QStringLiteral("entrymode") ) ); if (status.version() < QLatin1String("0.6")) { if ( m_started ) { Entry *entry = new Entry( element.attribute(QStringLiteral("percent-finished"), QStringLiteral("0")).toInt(), Duration::fromString(element.attribute(QStringLiteral("remaining-effort"))), Duration::fromString(element.attribute(QStringLiteral("performed-effort"))) ); entry->note = element.attribute(QStringLiteral("note")); QDate date = m_startTime.date(); if ( m_finished ) { date = m_finishTime.date(); } // almost the best we can do ;) addEntry( date, entry ); } } else { KoXmlElement e; forEachElement(e, element) { if (e.tagName() == QLatin1String("completion-entry")) { QDate date; s = e.attribute(QStringLiteral("date")); if ( !s.isEmpty() ) { date = QDate::fromString( s, Qt::ISODate ); } if ( !date.isValid() ) { warnPlan<<"Invalid date: "<percentFinished ); elm.setAttribute( QStringLiteral("remaining-effort"), e->remainingEffort.toString() ); elm.setAttribute( QStringLiteral("performed-effort"), e->totalPerformed.toString() ); elm.setAttribute( QStringLiteral("note"), e->note ); } if ( ! m_usedEffort.isEmpty() ) { QDomElement elm = el.ownerDocument().createElement(QStringLiteral("used-effort")); el.appendChild(elm); ResourceUsedEffortMap::ConstIterator i = m_usedEffort.constBegin(); for ( ; i != m_usedEffort.constEnd(); ++i ) { if ( i.value() == 0 ) { continue; } QDomElement e = elm.ownerDocument().createElement(QStringLiteral("resource")); elm.appendChild(e); e.setAttribute( QStringLiteral("id"), i.key()->id() ); i.value()->saveXML( e ); } } } //-------------- Completion::UsedEffort::UsedEffort() { } Completion::UsedEffort::UsedEffort( const UsedEffort &e ) { mergeEffort( e ); } Completion::UsedEffort::~UsedEffort() { } void Completion::UsedEffort::mergeEffort( const Completion::UsedEffort &value ) { const QMap map = value.actualEffortMap(); QMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { setEffort(it.key(), it.value()); } } void Completion::UsedEffort::setEffort( QDate date, const ActualEffort &value ) { m_actual.insert( date, value ); } Duration Completion::UsedEffort::effortTo( QDate date ) const { Duration eff; QMap::const_iterator it; for (it = m_actual.constBegin(); it != m_actual.constEnd() && it.key() <= date; ++it) { eff += it.value().effort(); } return eff; } Duration Completion::UsedEffort::effort() const { Duration eff; foreach ( const ActualEffort &e, m_actual ) { eff += e.effort(); } return eff; } bool Completion::UsedEffort::operator==( const Completion::UsedEffort &e ) const { return m_actual == e.actualEffortMap(); } bool Completion::UsedEffort::loadXML(KoXmlElement &element, XMLLoaderObject & ) { //debugPlan; KoXmlElement e; forEachElement(e, element) { if (e.tagName() == QLatin1String("actual-effort")) { QDate date = QDate::fromString( e.attribute(QStringLiteral("date")), Qt::ISODate ); if ( date.isValid() ) { ActualEffort a; a.setNormalEffort( Duration::fromString( e.attribute( QStringLiteral("normal-effort") ) ) ); a.setOvertimeEffort( Duration::fromString( e.attribute( QStringLiteral("overtime-effort") ) ) ); setEffort( date, a ); } } } return true; } void Completion::UsedEffort::saveXML(QDomElement &element ) const { if ( m_actual.isEmpty() ) { return; } DateUsedEffortMap::ConstIterator i = m_actual.constBegin(); for ( ; i != m_actual.constEnd(); ++i ) { QDomElement el = element.ownerDocument().createElement(QStringLiteral("actual-effort")); element.appendChild( el ); el.setAttribute( QStringLiteral("overtime-effort"), i.value().overtimeEffort().toString() ); el.setAttribute( QStringLiteral("normal-effort"), i.value().normalEffort().toString() ); el.setAttribute( QStringLiteral("date"), i.key().toString( Qt::ISODate ) ); } } //---------------------------------- WorkPackage::WorkPackage( Task *task ) : m_task( task ), m_manager( 0 ), m_transmitionStatus( TS_None ) { m_completion.setNode( task ); } WorkPackage::WorkPackage( const WorkPackage &wp ) : m_task( 0 ), m_manager( 0 ), m_completion( wp.m_completion ), m_ownerName( wp.m_ownerName ), m_ownerId( wp.m_ownerId ), m_transmitionStatus( wp.m_transmitionStatus ), m_transmitionTime( wp.m_transmitionTime ) { } WorkPackage::~WorkPackage() { } bool WorkPackage::loadXML(KoXmlElement &element, XMLLoaderObject &status ) { Q_UNUSED(status); m_ownerName = element.attribute( QStringLiteral("owner") ); m_ownerId = element.attribute( QStringLiteral("owner-id") ); return true; } void WorkPackage::saveXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); el.setAttribute( QStringLiteral("owner"), m_ownerName ); el.setAttribute( QStringLiteral("owner-id"), m_ownerId ); } bool WorkPackage::loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status ) { m_ownerName = element.attribute( QStringLiteral("owner") ); m_ownerId = element.attribute( QStringLiteral("owner-id") ); m_transmitionStatus = transmitionStatusFromString( element.attribute( QStringLiteral("status") ) ); m_transmitionTime = DateTime( QDateTime::fromString( element.attribute( QStringLiteral("time") ), Qt::ISODate ) ); return m_completion.loadXML( element, status ); } void WorkPackage::saveLoggedXML(QDomElement &element) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); el.setAttribute( QStringLiteral("owner"), m_ownerName ); el.setAttribute( QStringLiteral("owner-id"), m_ownerId ); el.setAttribute( QStringLiteral("status"), transmitionStatusToString( m_transmitionStatus ) ); el.setAttribute( QStringLiteral("time"), m_transmitionTime.toString( Qt::ISODate ) ); m_completion.saveXML( el ); } QList WorkPackage::fetchResources() { return fetchResources( id() ); } QList WorkPackage::fetchResources( long id ) { //debugPlan< lst; if ( id == NOTSCHEDULED ) { if ( m_task ) { lst << m_task->requestedResources(); } } else { if ( m_task ) lst = m_task->assignedResources( id ); foreach ( const Resource *r, m_completion.resources() ) { if ( ! lst.contains( const_cast( r ) ) ) { lst << const_cast( r ); } } } return lst; } Completion &WorkPackage::completion() { return m_completion; } const Completion &WorkPackage::completion() const { return m_completion; } void WorkPackage::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } QString WorkPackage::transmitionStatusToString( WorkPackage::WPTransmitionStatus sts, bool trans ) { QString s = trans ? i18n( "None" ) : QStringLiteral("None"); switch ( sts ) { case TS_Send: s = trans ? i18n( "Send" ) : QStringLiteral("Send"); break; case TS_Receive: s = trans ? i18n( "Receive" ) : QStringLiteral("Receive"); break; + case TS_Rejected: + s = trans ? i18n( "Rejected" ) : QStringLiteral("Rejected"); + break; default: break; } return s; } WorkPackage::WPTransmitionStatus WorkPackage::transmitionStatusFromString( const QString &sts ) { QStringList lst; lst << QStringLiteral("None") << QStringLiteral("Send") << QStringLiteral("Receive"); int s = lst.indexOf( sts ); return s < 0 ? TS_None : static_cast( s ); } void WorkPackage::clear() { //m_task = 0; m_manager = 0; m_ownerName.clear(); m_ownerId.clear(); m_transmitionStatus = TS_None; m_transmitionTime = DateTime(); m_log.clear(); m_completion = Completion(); m_completion.setNode( m_task ); } //-------------------------------- WorkPackageSettings::WorkPackageSettings() : usedEffort( true ), progress( true ), - documents( true ) + documents( true ), + remainingEffort(true) { } void WorkPackageSettings::saveXML( QDomElement &element ) const { QDomElement el = element.ownerDocument().createElement(QStringLiteral("settings")); element.appendChild( el ); el.setAttribute( QStringLiteral("used-effort"), QString::number(usedEffort) ); el.setAttribute( QStringLiteral("progress"), QString::number(progress) ); el.setAttribute( QStringLiteral("documents"), QString::number(documents) ); + el.setAttribute( QStringLiteral("remaining-effort"), QString::number(remainingEffort) ); } bool WorkPackageSettings::loadXML( const KoXmlElement &element ) { usedEffort = (bool)element.attribute( QStringLiteral("used-effort") ).toInt(); progress = (bool)element.attribute( QStringLiteral("progress") ).toInt(); documents = (bool)element.attribute( QStringLiteral("documents") ).toInt(); + remainingEffort = (bool)element.attribute( QStringLiteral("remaining-effort") ).toInt(); return true; } bool WorkPackageSettings::operator==( KPlato::WorkPackageSettings s ) const { return usedEffort == s.usedEffort && progress == s.progress && - documents == s.documents; + documents == s.documents && + remainingEffort == s.remainingEffort; } bool WorkPackageSettings::operator!=( KPlato::WorkPackageSettings s ) const { return ! operator==( s ); } } //KPlato namespace #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae ) { dbg << QStringLiteral( "%1" ).arg( ae.normalEffort().toDouble( KPlato::Duration::Unit_h ), 1 ); return dbg; } #endif diff --git a/src/libs/kernel/kpttask.h b/src/libs/kernel/kpttask.h index 85e01db7..235aa100 100644 --- a/src/libs/kernel/kpttask.h +++ b/src/libs/kernel/kpttask.h @@ -1,766 +1,768 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas Zander zander@kde.org Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASK_H #define KPTTASK_H #include "plankernel_export.h" #include "kptnode.h" #include "kptglobal.h" #include "kptdatetime.h" #include "kptduration.h" #include "kptresource.h" #include #include #include /// The main namespace. namespace KPlato { class Completion; class XmlSaveContext; /** * The Completion class holds information about the tasks progress. */ class PLANKERNEL_EXPORT Completion { public: class PLANKERNEL_EXPORT UsedEffort { public: class PLANKERNEL_EXPORT ActualEffort : public std::pair { public: explicit ActualEffort( KPlato::Duration ne = Duration::zeroDuration, KPlato::Duration oe = Duration::zeroDuration ) : std::pair( ne, oe ) {} ActualEffort( const ActualEffort &e ) : std::pair( e.first, e.second ) {} ~ActualEffort() {} Duration normalEffort() const { return first; } void setNormalEffort( KPlato::Duration e ) { first = e; } Duration overtimeEffort() const { return second; } void setOvertimeEffort( KPlato::Duration e ) { second = e; } /// Returns the sum of normalEffort + overtimeEffort Duration effort() const { return first + second; } void setEffort( KPlato::Duration ne, KPlato::Duration oe = Duration::zeroDuration ) { first = ne; second = oe; } }; UsedEffort(); UsedEffort( const UsedEffort &e ); ~UsedEffort(); bool operator==( const UsedEffort &e ) const; bool operator!=( const UsedEffort &e ) const { return !operator==( e ); } void mergeEffort( const UsedEffort &value ); void setEffort( QDate date, const ActualEffort &value ); /// Returns the total effort up to @p date Duration effortTo( QDate date ) const; /// Returns the total effort on @p date ActualEffort effort( QDate date ) const { return m_actual.value( date ); } ActualEffort takeEffort( QDate date ) { return m_actual.take( date ); } /// Returns the total effort for all registered dates Duration effort() const; QDate firstDate() const { return m_actual.firstKey(); } QDate lastDate() const { return m_actual.lastKey(); } QMap actualEffortMap() const { return m_actual; } /// Load from document bool loadXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save to document void saveXML(QDomElement &element) const; bool contains( QDate date ) const { return m_actual.contains( date ); } private: QMap m_actual; }; typedef QMap DateUsedEffortMap; class PLANKERNEL_EXPORT Entry { public: Entry() : percentFinished( 0 ), remainingEffort( Duration::zeroDuration ), totalPerformed( Duration::zeroDuration ) {} Entry( int percent, Duration remaining, Duration performed ) : percentFinished( percent ), remainingEffort( remaining ), totalPerformed( performed ) {} Entry( const Entry &e ) { copy( e ); } bool operator==( const Entry &e ) const { return percentFinished == e.percentFinished && remainingEffort == e.remainingEffort && totalPerformed == e.totalPerformed && note == e.note; } bool operator!=( const Entry &e ) const { return ! operator==( e ); } Entry &operator=(const Entry &e ) { copy( e ); return *this; } int percentFinished; Duration remainingEffort; Duration totalPerformed; QString note; protected: void copy( const Entry &e ) { percentFinished = e.percentFinished; remainingEffort = e.remainingEffort; totalPerformed = e.totalPerformed; note = e.note; } }; typedef QMap EntryList; typedef QHash ResourceUsedEffortMap; explicit Completion( Node *node = 0 ); // review * or &, or at all? Completion( const Completion © ); virtual ~Completion(); bool operator==(const Completion &p); bool operator!=(Completion &p) { return !(*this == p); } Completion &operator=(const Completion &p); /// Load from document bool loadXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save to document void saveXML(QDomElement &element) const; bool startIsValid() const { return m_started && m_startTime.isValid(); } bool isStarted() const { return m_started; } void setStarted( bool on ); bool finishIsValid() const { return m_finished && m_finishTime.isValid(); } bool isFinished() const { return m_finished; } void setFinished( bool on ); DateTime startTime() const { return m_startTime; } void setStartTime( const DateTime &dt ); DateTime finishTime() const { return m_finishTime; } void setFinishTime( const DateTime &dt ); void setPercentFinished( QDate date, int value ); void setRemainingEffort( QDate date, Duration value ); void setActualEffort( QDate date, Duration value ); /// Return a list of the resource that has done any work on this task - QList resources() { return m_usedEffort.keys(); } + QList resources() const { return m_usedEffort.keys(); } const EntryList &entries() const { return m_entries; } void addEntry( QDate date, Entry *entry ); Entry *takeEntry( QDate date ) { return m_entries.take( date ); changed(); } Entry *entry( QDate date ) const { return m_entries[ date ]; } /// Returns the date of the latest entry QDate entryDate() const; /// Returns the percentFinished of the latest entry int percentFinished() const; /// Returns the percentFinished on @p date int percentFinished( QDate date ) const; /// Returns the estimated remaining effort Duration remainingEffort() const; /// Returns the estimated remaining effort on @p date Duration remainingEffort( QDate date ) const; /// Returns the total actual effort Duration actualEffort() const; /// Returns the total actual effort on @p date Duration actualEffort( QDate date ) const; /// Returns the total actual effort upto and including @p date Duration actualEffortTo( QDate date ) const; /// Returns the actual effort for @p resource on @p date Duration actualEffort( const Resource *resource, QDate date ) const; /// TODO QString note() const; /// TODO void setNote( const QString &str ); /// Returns the total actual cost double actualCost() const; /// Returns the actual cost for @p resource double actualCost( const Resource *resource ) const; /// Returns the actual cost on @p date double actualCost( QDate date ) const; /// Returns the total actual cost for @p resource on @p date double actualCost( const Resource *resource, QDate date ) const; /// Returns the total actual effort and cost upto and including @p date EffortCost actualCostTo( long int id, QDate date ) const; /** * Returns a map of all actual effort and cost entered */ virtual EffortCostMap actualEffortCost( long id, EffortCostCalculationType type = ECCT_All ) const; void addUsedEffort( const Resource *resource, UsedEffort *value = 0 ); UsedEffort *takeUsedEffort( const Resource *r ) { return m_usedEffort.take( const_cast( r ) ); changed(); } UsedEffort *usedEffort( const Resource *r ) const { return m_usedEffort.value( const_cast( r ) ); } const ResourceUsedEffortMap &usedEffortMap() const { return m_usedEffort; } void changed( int property = -1 ); Node *node() const { return m_node; } void setNode( Node *node ) { m_node = node; } enum Entrymode { FollowPlan, EnterCompleted, EnterEffortPerTask, EnterEffortPerResource }; void setEntrymode( Entrymode mode ) { m_entrymode = mode; } Entrymode entrymode() const { return m_entrymode; } void setEntrymode( const QString &mode ); QString entryModeToString() const; QStringList entrymodeList() const; EffortCostMap effortCostPrDay(QDate start, QDate end, long id = -1 ) const; /// Returns the actual effort and cost pr day used by @p resource EffortCostMap effortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE ) const; protected: void copy( const Completion ©); double averageCostPrHour( QDate date, long id ) const; std::pair actualStartEndDates() const; private: Node *m_node; bool m_started, m_finished; DateTime m_startTime, m_finishTime; EntryList m_entries; ResourceUsedEffortMap m_usedEffort; Entrymode m_entrymode; #ifndef NDEBUG public: void printDebug( const QByteArray &ident ) const; #endif }; /** * The WorkPackage class controls work flow for a task */ class PLANKERNEL_EXPORT WorkPackage { public: /// @enum WPTransmitionStatus describes if this package was sent or received enum WPTransmitionStatus { TS_None, /// Not sent nor received TS_Send, /// Package was sent to resource - TS_Receive /// Package was received from resource + TS_Receive, /// Package was received from resource + TS_Rejected /// Recieved package was rejected by project manager }; explicit WorkPackage( Task *task = 0 ); explicit WorkPackage( const WorkPackage &wp ); virtual ~WorkPackage(); Task *parentTask() const { return m_task; } void setParentTask( Task *task ) { m_task = task; } /// Returns the transmission status of this package WPTransmitionStatus transmitionStatus() const { return m_transmitionStatus; } void setTransmitionStatus( WPTransmitionStatus sts ) { m_transmitionStatus = sts; } static QString transmitionStatusToString( WPTransmitionStatus sts, bool trans = false ); static WPTransmitionStatus transmitionStatusFromString( const QString &sts ); /// Load from document virtual bool loadXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save the full workpackage virtual void saveXML(QDomElement &element) const; /// Load from document virtual bool loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save the full workpackage virtual void saveLoggedXML(QDomElement &element) const; /// Set schedule manager void setScheduleManager( ScheduleManager *sm ); /// Return schedule manager ScheduleManager *scheduleManager() const { return m_manager; } /// Return the schedule id, or NOTSCHEDULED if no schedule manager is set long id() const { return m_manager ? m_manager->scheduleId() : NOTSCHEDULED; } Completion &completion(); const Completion &completion() const; void addLogEntry( DateTime &dt, const QString &str ); QMap log() const; QStringList log(); /// Return a list of resources fetched from the appointments or requests /// merged with resources added to completion QList fetchResources(); /// Return a list of resources fetched from the appointments or requests /// merged with resources added to completion QList fetchResources( long id ); /// Returns id of the resource that owns this package. If empty, task leader owns it. QString ownerId() const { return m_ownerId; } /// Set the resource that owns this package to @p owner. If empty, task leader owns it. void setOwnerId( const QString &id ) { m_ownerId = id; } /// Returns the name of the resource that owns this package. QString ownerName() const { return m_ownerName; } /// Set the name of the resource that owns this package. void setOwnerName( const QString &name ) { m_ownerName = name; } DateTime transmitionTime() const { return m_transmitionTime; } void setTransmitionTime( const DateTime &dt ) { m_transmitionTime = dt; } /// Clear workpackage data void clear(); private: Task *m_task; ScheduleManager *m_manager; Completion m_completion; QString m_ownerName; QString m_ownerId; WPTransmitionStatus m_transmitionStatus; DateTime m_transmitionTime; QMap m_log; }; class PLANKERNEL_EXPORT WorkPackageSettings { public: WorkPackageSettings(); bool loadXML( const KoXmlElement &element ); void saveXML( QDomElement &element) const; bool operator==( WorkPackageSettings settings ) const; bool operator!=( WorkPackageSettings settings ) const; bool usedEffort; bool progress; bool documents; + bool remainingEffort; }; /** * A task in the scheduling software is represented by this class. A task * can be anything from 'build house' to 'drill hole' It will always mean * an activity. */ class PLANKERNEL_EXPORT Task : public Node { Q_OBJECT public: explicit Task(Node *parent = 0); explicit Task(const Task &task, Node *parent = 0); ~Task(); /// Return task type. Can be Type_Task, Type_Summarytask ot Type_Milestone. virtual int type() const; /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ Duration *getRandomDuration(); /** * Return the resource request made to group * (There should be only one) */ ResourceGroupRequest *resourceGroupRequest(const ResourceGroup *group) const; void clearResourceRequests(); void addRequest(ResourceGroup *group, int numResources); void addRequest(ResourceGroupRequest *request); void takeRequest(ResourceGroupRequest *request); void makeAppointments(); virtual QStringList requestNameList() const; virtual QList requestedResources() const; virtual bool containsRequest( const QString &/*identity*/ ) const; virtual ResourceRequest *resourceRequest( const QString &/*name*/ ) const; /// Return the list of resources assigned to this task virtual QStringList assignedNameList( long id = CURRENTSCHEDULE ) const; /** * Calculates if the assigned resource is overbooked * within the duration of this task */ void calcResourceOverbooked(); /// Load from document virtual bool load(KoXmlElement &element, XMLLoaderObject &status ); /// Save to document virtual void save(QDomElement &element, const XmlSaveContext &context) const; /// Save appointments for schedule with id virtual void saveAppointments(QDomElement &element, long id) const; /// Save a workpackage document with schedule identity @p id void saveWorkPackageXML( QDomElement &element, long id ) const; /** * Returns a list of planned effort and cost for this task * for the interval start, end inclusive */ virtual EffortCostMap plannedEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /** * Returns a list of planned effort and cost for the @p resource * for the interval @p start, @p end inclusive, useng schedule with identity @p id */ virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for @p resource on this task (or subtasks) virtual Duration plannedEffort( const Resource *resource, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this task (or subtasks) virtual Duration plannedEffort( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this task (or subtasks) on date virtual Duration plannedEffort(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for @p resource on this task (or subtasks) on date virtual Duration plannedEffort( const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the planned effort up to and including date virtual Duration plannedEffortTo(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the planned effort for @p resource up to and including date virtual Duration plannedEffortTo( const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total actual effort for this task (or subtasks) virtual Duration actualEffort() const; /// Returns the total actual effort for this task (or subtasks) on date virtual Duration actualEffort(QDate date ) const; /// Returns the actual effort up to and including date virtual Duration actualEffortTo(QDate date ) const; /** * Returns the total planned cost for this task (or subtasks) */ virtual EffortCost plannedCost( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Planned cost up to and including date virtual double plannedCostTo(QDate /*date*/, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns actual effort and cost up to and including @p date virtual EffortCost actualCostTo( long int id, QDate date ) const; /** * Returns a list of actual effort and cost for this task * for the interval start, end inclusive */ virtual EffortCostMap actualEffortCostPrDay( QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the actual effort and cost pr day used by @p resource virtual EffortCostMap actualEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the effort planned to be used to reach the actual percent finished virtual Duration budgetedWorkPerformed( QDate date, long id = CURRENTSCHEDULE ) const; /// Returns the cost planned to be used to reach the actual percent finished virtual double budgetedCostPerformed( QDate date, long id = CURRENTSCHEDULE ) const; using Node::bcwsPrDay; /// Return map of Budgeted Cost of Work Scheduled pr day virtual EffortCostMap bcwsPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Budgeted Cost of Work Scheduled virtual double bcws( QDate date, long id = CURRENTSCHEDULE ) const; using Node::bcwpPrDay; /// Return map of Budgeted Cost of Work Performed pr day (also includes bcwsPrDay) virtual EffortCostMap bcwpPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Budgeted Cost of Work Performed virtual double bcwp( long id = CURRENTSCHEDULE ) const; /// Budgeted Cost of Work Performed ( up to @p date ) virtual double bcwp( QDate date, long id = CURRENTSCHEDULE ) const; using Node::acwp; /// Map of Actual Cost of Work Performed virtual EffortCostMap acwp( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Actual Cost of Work Performed up to dat virtual EffortCost acwp( QDate date, long id = CURRENTSCHEDULE ) const; /// Effort based performance index virtual double effortPerformanceIndex( QDate date, long id = CURRENTSCHEDULE ) const; /// Schedule performance index virtual double schedulePerformanceIndex( QDate date, long id = CURRENTSCHEDULE ) const; /// Cost performance index virtual double costPerformanceIndex( long int id, QDate date, bool *error=0 ) const; /** * Return the duration that an activity's start can be delayed * without affecting the project completion date. * An activity with positive float is not on the critical path. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration positiveFloat( long id = CURRENTSCHEDULE ) const; void setPositiveFloat( Duration fl, long id = CURRENTSCHEDULE ) const; /** * Return the duration by which the duration of an activity or path * has to be reduced in order to fulfil a timing- or dependency constraint. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration negativeFloat( long id = CURRENTSCHEDULE ) const; void setNegativeFloat( Duration fl, long id = CURRENTSCHEDULE ) const; /** * Return the duration by which an activity can be delayed or extended * without affecting the start of any succeeding activity. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration freeFloat( long id = CURRENTSCHEDULE ) const; void setFreeFloat( Duration fl, long id = CURRENTSCHEDULE ) const; /** * Return the duration from Early Start to Late Start. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration startFloat( long id = CURRENTSCHEDULE ) const; /** * Return the duration from Early Finish to Late Finish. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration finishFloat( long id = CURRENTSCHEDULE ) const; /** * A task is critical if positive float equals 0 * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ virtual bool isCritical( long id = CURRENTSCHEDULE ) const; /** * Set current schedule to schedule with identity id, for me and my children. * @param id Schedule identity */ virtual void setCurrentSchedule(long id); /** * The assigned resources can not fulfil the estimated effort. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ virtual bool effortMetError( long id = CURRENTSCHEDULE ) const; /// @return true if this task has been started bool isStarted() const; Completion &completion() { return m_workPackage.completion(); } const Completion &completion() const { return m_workPackage.completion(); } WorkPackage &workPackage() { return m_workPackage; } const WorkPackage &workPackage() const { return m_workPackage; } int workPackageLogCount() const { return m_packageLog.count(); } QList workPackageLog() const { return m_packageLog; } void addWorkPackage( WorkPackage *wp ); void removeWorkPackage( WorkPackage *wp ); WorkPackage *workPackageAt( int index ) const; QString wpOwnerName() const; WorkPackage::WPTransmitionStatus wpTransmitionStatus() const; DateTime wpTransmitionTime() const; /** * Returns the state of the task * @param id The identity of the schedule used when calculating the state */ virtual uint state( long id = CURRENTSCHEDULE ) const; /// Check if this node has any dependent child nodes virtual bool isEndNode() const; /// Check if this node has any dependent parent nodes virtual bool isStartNode() const; QList parentProxyRelations() const { return m_parentProxyRelations; } QList childProxyRelations() const { return m_childProxyRelations; } /** * Calculates and returns the duration of the node. * Uses the correct expected-, optimistic- or pessimistic effort * dependent on @p use. * @param time Where to start calculation. * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @param backward If true, time specifies when the task should end. */ virtual Duration duration(const DateTime &time, int use, bool backward); /** * Return the duration calculated on bases of the estimates calendar */ Duration length(const DateTime &time, Duration duration, bool backward); Duration length(const DateTime &time, Duration uration, Schedule *sch, bool backward); /// Copy info from parent schedule void copySchedule(); /// Copy intervals from parent schedule void copyAppointments(); /// Copy intervals from parent schedule in the range @p start, @p end void copyAppointments( const DateTime &start, const DateTime &end = DateTime() ); Q_SIGNALS: void workPackageToBeAdded(KPlato::Node *node, int row); void workPackageAdded(KPlato::Node *node); void workPackageToBeRemoved(KPlato::Node *node, int row); void workPackageRemoved(KPlato::Node *node); public: virtual void initiateCalculation(MainSchedule &sch); /** * Sets up the lists used for calculation. * This includes adding summarytasks relations to subtasks * and lists for start- and endnodes. */ virtual void initiateCalculationLists(MainSchedule &sch); /** * Calculates early start and early finish, first for all predeccessors, * then for this task. * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateForward(int use); /** * Calculates ref m_durationForward from ref earliestStart and * returns the resulting end time (early finish), * which will be used as the successors ref earliestStart. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateEarlyFinish(int use); /** * Calculates late start and late finish, first for all successors, * then for this task. * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateBackward(int use); /** * Calculates ref m_durationBackward from ref latestFinish and * returns the resulting start time (late start), * which will be used as the predecessors ref latestFinish. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateLateStart(int use); /** * Schedules the task within the limits of earliestStart and latestFinish. * Calculates ref m_startTime, ref m_endTime and ref m_duration, * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param earliest The task is not scheduled to start earlier than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks endtime which can be used for scheduling the successor. */ virtual DateTime scheduleForward(const DateTime &earliest, int use); /** * Schedules the task within the limits of start time and latestFinish, * Calculates end time and duration. * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks endtime which can be used for scheduling the successor. */ virtual DateTime scheduleFromStartTime(int use); /** * Schedules the task within the limits of earliestStart and latestFinish. * Calculates ref m_startTime, ref m_endTime and ref m_duration, * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param latest The task is not scheduled to end later than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks starttime which can be used for scheduling the predeccessor. */ virtual DateTime scheduleBackward(const DateTime &latest, int use); /** * Schedules the task within the limits of end time and latestFinish. * Calculates endTime and duration. * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks starttime which can be used for scheduling the predeccessor. */ virtual DateTime scheduleFromEndTime(int use); /** * Summarytasks (with milestones) need special treatment because * milestones are always 'glued' to their predecessors. */ virtual void adjustSummarytask(); /// Calculate the critical path virtual bool calcCriticalPath(bool fromEnd); virtual void calcFreeFloat(); // Proxy relations are relations to/from summarytasks. // These relations are distributed to the child tasks before calculation. virtual void clearProxyRelations(); virtual void addParentProxyRelations( const QList & ); virtual void addChildProxyRelations( const QList & ); virtual void addParentProxyRelation(Node *, const Relation *); virtual void addChildProxyRelation(Node *, const Relation *); public: DateTime earlyStartDate(); void setEarlyStartDate(DateTime value); DateTime earlyFinishDate(); void setEarlyFinishDate(DateTime value); DateTime lateStartDate(); void setLateStartDate(DateTime value); DateTime lateFinishDate(); void setLateFinishDate(DateTime value); int activitySlack(); void setActivitySlack(int value); int activityFreeMargin(); void setActivityFreeMargin(int value); protected: /** * Return the duration calculated on bases of the requested resources */ Duration calcDuration(const DateTime &time, Duration effort, bool backward); private: DateTime calculateSuccessors(const QList &list, int use); DateTime calculatePredeccessors(const QList &list, int use); DateTime scheduleSuccessors(const QList &list, int use); DateTime schedulePredeccessors(const QList &list, int use); /// Fixed duration: Returns @p dt /// Duration with calendar: Returns first available after @p dt /// Has working resource(s) allocated: Returns the earliest time a resource can start work after @p dt, and checks appointments if @p sch is not null. DateTime workTimeAfter(const DateTime &dt, Schedule *sch = 0) const; /// Fixed duration: Returns @p dt /// Duration with calendar: Returns first available before @p dt /// Has working resource(s) allocated: Returns the latest time a resource can finish work, and checks appointments if @p sch is not null. DateTime workTimeBefore(const DateTime &dt, Schedule *sch = 0) const; private: QList m_resource; QList m_parentProxyRelations; QList m_childProxyRelations; // This list store pointers to linked task QList m_requiredTasks; WorkPackage m_workPackage; QList m_packageLog; bool m_calculateForwardRun; bool m_calculateBackwardRun; bool m_scheduleForwardRun; bool m_scheduleBackwardRun; }; } //KPlato namespace Q_DECLARE_METATYPE( KPlato::Completion::UsedEffort::ActualEffort ) #ifndef QT_NO_DEBUG_STREAM PLANKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae ); #endif #endif diff --git a/src/libs/models/kptnodeitemmodel.cpp b/src/libs/models/kptnodeitemmodel.cpp index 5450661c..f70bf651 100644 --- a/src/libs/models/kptnodeitemmodel.cpp +++ b/src/libs/models/kptnodeitemmodel.cpp @@ -1,5240 +1,5240 @@ /* 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: return xi18nc("@info:tooltip", "%1: %2%3", wbsCode(node, Qt::DisplayRole).toString(), node->name(), description(node, Qt::DisplayRole).toString()); 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("") ); + 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::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; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraint: // constraint type 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; } 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; } 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; } break; } case NodeModel::NodeDescription: // description 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< 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 "kptworkpackagemodel.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptflatproxymodel.h" #include "kptnodeitemmodel.h" #include "kptnode.h" #include "kpttask.h" #include "kptproject.h" #include "kptschedule.h" #include "kptdebug.h" #include #include #include +#include namespace KPlato { QVariant WorkPackageModel::nodeName( const WorkPackage *wp, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return wp->parentTask() ? wp->parentTask()->name() : ""; case Qt::EditRole: return wp->parentTask() ? wp->parentTask()->name() : ""; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant WorkPackageModel::ownerName( const WorkPackage *wp, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return wp->ownerName(); case Qt::EditRole: return wp->ownerName(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant WorkPackageModel::transmitionStatus( const WorkPackage *wp, int role ) const { switch ( role ) { case Qt::DisplayRole: return wp->transmitionStatusToString( wp->transmitionStatus(), true ); case Qt::EditRole: return wp->transmitionStatus(); case Qt::ToolTipRole: { int sts = wp->transmitionStatus(); if ( sts == WorkPackage::TS_Send ) { return i18n( "Sent to %1 at %2", wp->ownerName(), transmitionTime( wp, Qt::DisplayRole ).toString() ); } if ( sts == WorkPackage::TS_Receive ) { return i18n( "Received from %1 at %2", wp->ownerName(), transmitionTime( wp, Qt::DisplayRole ).toString() ); } return i18n( "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant WorkPackageModel::transmitionTime( const WorkPackage *wp, int role ) const { if ( ! wp ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return QLocale().toString( wp->transmitionTime(), QLocale::ShortFormat ); case Qt::EditRole: return wp->transmitionTime(); case Qt::ToolTipRole: { int sts = wp->transmitionStatus(); QString t = QLocale().toString( wp->transmitionTime(), QLocale::LongFormat ); if ( sts == WorkPackage::TS_Send ) { return i18n( "Work package sent at: %1", t ); } if ( sts == WorkPackage::TS_Receive ) { return i18n( "Work package transmission received at: %1", t ); } return i18n( "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant WorkPackageModel::completion( const WorkPackage *wp, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( wp->transmitionStatus() == WorkPackage::TS_Receive ) { return wp->completion().percentFinished(); } break; case Qt::EditRole: if ( wp->transmitionStatus() == WorkPackage::TS_Receive ) { return wp->completion().percentFinished(); } break; case Qt::ToolTipRole: if ( wp->transmitionStatus() == WorkPackage::TS_Receive ) { return i18n( "Task reported %1% completed", wp->completion().percentFinished() ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant WorkPackageModel::data( const WorkPackage *wp, int column, int role ) const { switch ( column ) { case NodeModel::WPOwnerName: case NodeModel::NodeName: return ownerName( wp, role ); case NodeModel::WPTransmitionStatus: case NodeModel::NodeStatus: return transmitionStatus( wp, role ); case NodeModel::NodeCompleted: return completion( wp, role ); case NodeModel::WPTransmitionTime: case NodeModel::NodeActualStart: return transmitionTime( wp, role ); default: break; } return QVariant(); } //----------------------------- bool WPSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if ( sourceModel()->index( source_row, NodeModel::NodeType, source_parent ).data( Qt::EditRole ).toInt() != Node::Type_Task ) { return false; } if ( sourceModel()->index( source_row, NodeModel::NodeStatus, source_parent ).data( Qt::EditRole ).toInt() & Node::State_NotScheduled ) { return false; } return true; } WorkPackageProxyModel::WorkPackageProxyModel( QObject *parent ) : QAbstractProxyModel( parent ) { m_proxies << new WPSortFilterProxyModel( this ); m_proxies << new FlatProxyModel( this ); m_nodemodel = new NodeItemModel( this ); QAbstractProxyModel *p = this; foreach ( QAbstractProxyModel *m, m_proxies ) { p->setSourceModel( m ); p = m; } p->setSourceModel( m_nodemodel ); } Qt::ItemFlags WorkPackageProxyModel::flags(const QModelIndex &index) const { if ( isWorkPackageIndex( index ) ) { - return Qt::ItemIsEnabled; + return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; } - return QAbstractProxyModel::flags( index ); + return QAbstractProxyModel::flags( index ) | Qt::ItemIsDropEnabled; +} + +Qt::DropActions WorkPackageProxyModel::supportedDropActions() const +{ + return Qt::CopyAction; +} + +bool WorkPackageProxyModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(action); + Q_UNUSED(row); + Q_UNUSED(parent); + + if (data->hasFormat("text/uri-list")) { + for (const QString &f : QString(data->data("text/uri-list")).split("\r\n", QString::SkipEmptyParts)) { + if (f.endsWith(".planwork")) { + return true; + } + } + } + return false; +} + +bool WorkPackageProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (!canDropMimeData(data, action, row, column, parent)) { + return false; + } + if (action == Qt::IgnoreAction) { + return true; + } + if (data->hasFormat("text/uri-list")) { + QList files; + for (const QString &f : QString(data->data("text/uri-list")).split("\r\n", QString::SkipEmptyParts)) { + if (f.endsWith(".planwork")) { + files << f; + } + emit loadWorkPackage(files); + } + return true; + } + return false; +} + +QStringList WorkPackageProxyModel::mimeTypes () const +{ + return QStringList() << "application/x-vnd.kde.plan.work"; } void WorkPackageProxyModel::setSourceModel( QAbstractItemModel *model ) { if ( sourceModel() ) { disconnect(sourceModel(), &QAbstractItemModel::dataChanged, this, &WorkPackageProxyModel::sourceDataChanged); /* disconnect(sourceModel(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(sourceHeaderDataChanged(Qt::Orientation,int,int)));*/ disconnect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &QAbstractItemModel::layoutChanged); disconnect(sourceModel(), &QAbstractItemModel::layoutAboutToBeChanged, this, &QAbstractItemModel::layoutAboutToBeChanged); disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeInserted, this, &WorkPackageProxyModel::sourceRowsAboutToBeInserted); disconnect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &WorkPackageProxyModel::sourceRowsInserted); disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, &WorkPackageProxyModel::sourceRowsAboutToBeRemoved); disconnect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, &WorkPackageProxyModel::sourceRowsAboutToBeRemoved); /* disconnect(sourceModel(), SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(sourceColumnsAboutToBeInserted(QModelIndex,int,int))); disconnect(sourceModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(sourceColumnsInserted(QModelIndex,int,int))); disconnect(sourceModel(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); disconnect(sourceModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(sourceColumnsRemoved(QModelIndex,int,int))); */ disconnect(sourceModel(), &QAbstractItemModel::modelAboutToBeReset, this, &WorkPackageProxyModel::sourceModelAboutToBeReset); disconnect(sourceModel(), &QAbstractItemModel::modelReset, this, &WorkPackageProxyModel::sourceModelReset); disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeMoved, this, &WorkPackageProxyModel::sourceRowsAboutToBeMoved); disconnect(sourceModel(), &QAbstractItemModel::rowsMoved, this, &WorkPackageProxyModel::sourceRowsMoved); /* disconnect(sourceModel(), SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); disconnect(sourceModel(), SIGNAL(columnsMoved(QModelIndex&parent,int,int,QModelIndex,int)), this, SLOT(sourceColumnsMoved(QModelIndex&parent,int,int,QModelIndex,int)));*/ } QAbstractProxyModel::setSourceModel( model ); if ( model ) { connect(model, &QAbstractItemModel::dataChanged, this, &WorkPackageProxyModel::sourceDataChanged); /* connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(sourceHeaderDataChanged(Qt::Orientation,int,int)));*/ connect(model, &QAbstractItemModel::layoutChanged, this, &QAbstractItemModel::layoutChanged); connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &QAbstractItemModel::layoutAboutToBeChanged); connect(model, &QAbstractItemModel::rowsAboutToBeInserted, this, &WorkPackageProxyModel::sourceRowsAboutToBeInserted); connect(model, &QAbstractItemModel::rowsInserted, this, &WorkPackageProxyModel::sourceRowsInserted); connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &WorkPackageProxyModel::sourceRowsAboutToBeRemoved); connect(model, &QAbstractItemModel::rowsRemoved, this, &WorkPackageProxyModel::sourceRowsRemoved); /* connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(sourceColumnsAboutToBeInserted(QModelIndex,int,int))); connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(sourceColumnsInserted(QModelIndex,int,int))); connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(sourceColumnsAboutToBeRemoved(QModelIndex,int,int))); connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(sourceColumnsRemoved(QModelIndex,int,int))); */ connect(model, &QAbstractItemModel::modelAboutToBeReset, this, &WorkPackageProxyModel::sourceModelAboutToBeReset); connect(model, &QAbstractItemModel::modelReset, this, &WorkPackageProxyModel::sourceModelReset); connect(model, &QAbstractItemModel::rowsAboutToBeMoved, this, &WorkPackageProxyModel::sourceRowsAboutToBeMoved); connect(model, &QAbstractItemModel::rowsMoved, this, &WorkPackageProxyModel::sourceRowsMoved); /* connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); connect(model, SIGNAL(columnsMoved(QModelIndex&parent,int,int,QModelIndex,int)), this, SLOT(sourceColumnsMoved(QModelIndex&parent,int,int,QModelIndex,int)));*/ } } void WorkPackageProxyModel::sourceDataChanged(const QModelIndex &start, const QModelIndex &end) { emit dataChanged( mapFromSource( start ), mapFromSource( end ) ); } void WorkPackageProxyModel::sourceModelAboutToBeReset() { // debugPlan; beginResetModel(); detachTasks(); } void WorkPackageProxyModel::sourceModelReset() { // debugPlan; attachTasks(); +#if 0 for ( int r = 0; r < rowCount(); ++r ) { debugPlan< 0; } int WorkPackageProxyModel::rowCount( const QModelIndex &parent ) const { int rows = 0; if ( ! parent.isValid() ) { rows = sourceModel()->rowCount(); } else if ( isTaskIndex( parent ) ) { Task *task = taskFromIndex( parent ); rows = task ? task->workPackageLogCount() : 0; } // debugPlan<columnCount(); } QModelIndex WorkPackageProxyModel::mapToSource( const QModelIndex &proxyIndex ) const { if ( ! proxyIndex.isValid() ) { return QModelIndex(); } if ( isWorkPackageIndex( proxyIndex ) ) { // workpackage, not mapped to source model return QModelIndex(); } return sourceModel()->index( proxyIndex.row(), proxyIndex.column() ); } QModelIndex WorkPackageProxyModel::mapFromSource( const QModelIndex &sourceIndex ) const { // index from source model is always a node return createIndex( sourceIndex.row(), sourceIndex.column() ); } QModelIndex WorkPackageProxyModel::parent( const QModelIndex &child ) const { QModelIndex idx; if ( isWorkPackageIndex( child ) ) { // only work packages have parent idx = m_nodemodel->index( static_cast( child.internalPointer() ) ); idx = mapFromBaseModel( idx ); } // debugPlan<workPackageAt( idx.row() ), idx.column(), role ); } } // debugPlan<( idx.internalPointer() ); } else if ( idx.isValid() ) { QVariant obj = data( idx, Role::Object ); task = qobject_cast( obj.value() ); } // debugPlan<index( node ) ); } QModelIndex WorkPackageProxyModel::mapFromBaseModel( const QModelIndex &idx ) const { if ( ! idx.isValid() ) { return QModelIndex(); } QModelIndex in = idx; for ( int i = m_proxies.count() -1; i >= 0; --i ) { in = m_proxies.at( i )->mapFromSource( in ); } return mapFromSource( in ); } void WorkPackageProxyModel::setProject( Project *project ) { debugPlan<setProject( project ); } void WorkPackageProxyModel::setScheduleManager(ScheduleManager *sm) { debugPlan<setScheduleManager( sm ); } NodeItemModel *WorkPackageProxyModel::baseModel() const { return m_nodemodel; } void WorkPackageProxyModel::detachTasks( Task *task ) { if ( task ) { disconnect(task, &Task::workPackageToBeAdded, this, &WorkPackageProxyModel::workPackageToBeAdded); disconnect(task, &Task::workPackageAdded, this, &WorkPackageProxyModel::workPackageAdded); disconnect(task, &Task::workPackageToBeRemoved, this, &WorkPackageProxyModel::workPackageToBeRemoved); disconnect(task, &Task::workPackageRemoved, this, &WorkPackageProxyModel::workPackageRemoved); // debugPlan< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef WORKPACKAGEMODEL_H #define WORKPACKAGEMODEL_H #include "planmodels_export.h" #include class QModelIndex; class QVariant; +class QMimeData; /// The main namespace namespace KPlato { class WorkPackage; class Node; class Task; class Project; class ScheduleManager; class NodeItemModel; class PLANMODELS_EXPORT WorkPackageModel : public QObject { Q_OBJECT public: explicit WorkPackageModel( QObject *parent = 0 ) : QObject( parent ) {} ~WorkPackageModel() {} - + QVariant data( const WorkPackage *wp, int column, int role = Qt::DisplayRole ) const; protected: QVariant nodeName(const WorkPackage *wp, int role ) const; QVariant ownerName(const WorkPackage *wp, int role ) const; QVariant transmitionStatus(const WorkPackage *wp, int role ) const; QVariant transmitionTime(const WorkPackage *wp, int role ) const; QVariant completion( const WorkPackage *wp, int role ) const; }; /** The WPSortFilterProxyModel only accepts scheduled tasks. */ class WPSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit WPSortFilterProxyModel(QObject *parent = 0) : QSortFilterProxyModel( parent ) {} protected: /// Only accept scheduled tasks bool filterAcceptsRow(int source_row, const QModelIndex &sourceParent) const; }; /** The WorkPackageProxyModel offers a flat list of tasks with workpackage log entries as children. The tasks is fetched from the WPSortFilterProxyModel, the work packages is added by this model. It uses the NodeItemModel to get the tasks, the FlatProxyModel to convert to a flat list, and the WPSortFilterProxyModel to accept only scheduled tasks. It depends on the fact that the WPSortFilterProxyModel holds a flat list. */ class PLANMODELS_EXPORT WorkPackageProxyModel : public QAbstractProxyModel { Q_OBJECT public: explicit WorkPackageProxyModel(QObject *parent = 0); Qt::ItemFlags flags(const QModelIndex &index) const; + + Qt::DropActions supportedDropActions() const; + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + virtual QStringList mimeTypes () const; + void setSourceModel( QAbstractItemModel *sourceModel ); bool hasChildren(const QModelIndex &parent) const; int rowCount( const QModelIndex &parent = QModelIndex() ) const; int columnCount( const QModelIndex &parent = QModelIndex() ) const; QModelIndex parent( const QModelIndex &child ) const; QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const; QVariant data( const QModelIndex &idx, int role = Qt::DisplayRole ) const; NodeItemModel *baseModel() const; QModelIndex mapToSource(const QModelIndex &proxyIndex) const; QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; Task *taskFromIndex( const QModelIndex &idx ) const; QModelIndex indexFromTask( const Node *node ) const; +Q_SIGNALS: + void loadWorkPackage(QList); + public Q_SLOTS: void setProject(KPlato::Project *project); void setScheduleManager(KPlato::ScheduleManager *sm); protected Q_SLOTS: void sourceDataChanged(const QModelIndex& start, const QModelIndex& end); void sourceModelAboutToBeReset(); void sourceModelReset(); void sourceRowsAboutToBeInserted(const QModelIndex&, int, int ); void sourceRowsInserted(const QModelIndex&, int, int end); void sourceRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); void sourceRowsRemoved(const QModelIndex& parent, int start, int); void sourceRowsAboutToBeMoved( const QModelIndex&, int sourceStart, int sourceEnd, const QModelIndex&, int destStart ); void sourceRowsMoved( const QModelIndex&, int , int , const QModelIndex&, int ); void workPackageToBeAdded(KPlato::Node*, int); void workPackageAdded(KPlato::Node*); void workPackageToBeRemoved(KPlato::Node*, int); void workPackageRemoved(KPlato::Node*); protected: QModelIndex mapFromBaseModel( const QModelIndex &idx ) const; void detachTasks( Task *task = 0 ); void attachTasks( Task *task = 0 ); inline bool isTaskIndex( const QModelIndex &idx ) const { return idx.isValid() && ! idx.internalPointer(); } inline bool isWorkPackageIndex( const QModelIndex &idx ) const { return idx.isValid() && idx.internalPointer(); } private: WorkPackageModel m_model; NodeItemModel *m_nodemodel; QList m_proxies; }; } //namespace KPlato #endif //WORKPACKAGEMODEL_H diff --git a/src/libs/ui/kptmainprojectpanel.cpp b/src/libs/ui/kptmainprojectpanel.cpp index 20f77953..b5ecb703 100644 --- a/src/libs/ui/kptmainprojectpanel.cpp +++ b/src/libs/ui/kptmainprojectpanel.cpp @@ -1,321 +1,357 @@ /* This file is part of the KDE project Copyright (C) 2004-2007, 2011, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptmainprojectpanel.h" #include "kptdebug.h" #include #ifdef PLAN_KDEPIMLIBS_FOUND #include #include #include #endif #include "kptproject.h" #include "kptcommand.h" #include "kptschedule.h" #include "kpttaskdescriptiondialog.h" #include "kptdocumentspanel.h" #include namespace KPlato { MainProjectPanel::MainProjectPanel(Project &p, QWidget *parent) : QWidget(parent), project(p) { setupUi(this); #ifndef PLAN_KDEPIMLIBS_FOUND chooseLeader->hide(); #endif // FIXME // [Bug 311940] New: Plan crashes when typing a text in the filter textbox before the textbook is fully loaded when selecting a contact from the adressbook chooseLeader->hide(); QString s = i18n( "The Work Breakdown Structure introduces numbering for all tasks in the project, according to the task structure.\nThe WBS code is auto-generated.\nYou can define the WBS code pattern using the Define WBS Pattern command in the Tools menu." ); wbslabel->setWhatsThis( s ); wbs->setWhatsThis( s ); namefield->setText(project.name()); leaderfield->setText(project.leader()); // useSharedResources->setEnabled(!project.isSharedResourcesLoaded()); useSharedResources->setChecked(project.useSharedResources()); resourcesFile->setText(project.sharedResourcesFile()); projectsPlace->setText(project.sharedProjectsUrl().toDisplayString()); - m_documents = new DocumentsPanel(p, tabWidget->widget(1)); - tabWidget->widget(1)->layout()->addWidget(m_documents); - - m_description = new TaskDescriptionPanel(p, tabWidget->widget(2)); + const Project::WorkPackageInfo wpi = p.workPackageInfo(); + ui_CheckForWorkPackages->setChecked(wpi.checkForWorkPackages); + ui_RetrieveUrl->setUrl(wpi.retrieveUrl); + ui_DeleteFile->setChecked(wpi.deleteAfterRetrieval); + ui_ArchiveFile->setChecked(wpi.archiveAfterRetrieval); + ui_ArchiveUrl->setUrl(wpi.archiveUrl); + ui_PublishUrl->setUrl(wpi.publishUrl); + + ui_RetrieveUrl->setMode(KFile::Directory); + ui_ArchiveUrl->setMode(KFile::Directory); + ui_PublishUrl->setMode(KFile::Directory); + + m_documents = new DocumentsPanel(p, ui_documents); + ui_documents->layout()->addWidget(m_documents); + + m_description = new TaskDescriptionPanel(p, ui_description); m_description->namefield->hide(); m_description->namelabel->hide(); - tabWidget->widget(2)->layout()->addWidget(m_description); + ui_description->layout()->addWidget(m_description); wbs->setText(project.wbsCode()); if ( wbs->text().isEmpty() ) { wbslabel->hide(); wbs->hide(); } DateTime st = project.constraintStartTime(); DateTime et = project.constraintEndTime(); startDate->setDate(st.date()); startTime->setTime( QTime( st.time().hour(), st.time().minute(), 0 ) ); endDate->setDate(et.date()); endTime->setTime( QTime( et.time().hour(), et.time().minute(), 0 ) ); enableDateTime(); namefield->setFocus(); useSharedResources->setToolTip(xi18nc("@info:tooltip", "Enables sharing resources with other projects")); useSharedResources->setWhatsThis(xi18nc("@info:whatsthis", "Shared resources" "Resources can be shared between projects" " to avoid overbooking resources across projects." " Shared resources must be defined in a separate file," " and you must have at least read access to it." " The projects that share the resources must also be" " accessible by you." )); s = xi18nc("@info:tooltip", "File where shared resources are defined"); resourcesLabel->setToolTip(s); resourcesType->setToolTip(s); resourcesFile->setToolTip(s); s = xi18nc("@info:tooltip", "Directory where all the projects that share resources can be found"); projectsLabel->setToolTip(s); projectsType->setToolTip(s); projectsPlace->setToolTip(s); projectsLoadAtStartup->setChecked(project.loadProjectsAtStartup()); projectsLoadAtStartup->setToolTip(xi18nc("@info:tooltip", "Load shared resource assignments at startup")); projectsLoadBtn->setToolTip(xi18nc("@info:tooltip", "Load (or re-load) shared resource assignments")); projectsClearBtn->setToolTip(xi18nc("@info:tooltip", "Clear shared resource assignments")); // signals and slots connections connect( m_documents, &DocumentsPanel::changed, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( m_description, &TaskDescriptionPanelImpl::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( endDate, &QDateTimeEdit::dateChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( endTime, &QDateTimeEdit::timeChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( startDate, &QDateTimeEdit::dateChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( startTime, &QDateTimeEdit::timeChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( namefield, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( leaderfield, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( useSharedResources, &QGroupBox::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( resourcesFile, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect( projectsPlace, &QLineEdit::textChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled ); connect(projectsLoadAtStartup, &QAbstractButton::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); connect( chooseLeader, &QAbstractButton::clicked, this, &MainProjectPanel::slotChooseLeader ); connect(resourcesBrowseBtn, &QAbstractButton::clicked, this, &MainProjectPanel::openResourcesFile); connect(projectsBrowseBtn, &QAbstractButton::clicked, this, &MainProjectPanel::openProjectsPlace); connect(projectsLoadBtn, &QAbstractButton::clicked, this, &MainProjectPanel::loadProjects); connect(projectsClearBtn, &QAbstractButton::clicked, this, &MainProjectPanel::clearProjects); + + connect(ui_CheckForWorkPackages, &QCheckBox::stateChanged, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_RetrieveUrl, &KUrlRequester::textEdited, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_RetrieveUrl, &KUrlRequester::urlSelected, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_DeleteFile, &QRadioButton::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_ArchiveFile, &QRadioButton::toggled, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_ArchiveUrl, &KUrlRequester::textEdited, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_ArchiveUrl, &KUrlRequester::urlSelected, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_PublishUrl, &KUrlRequester::textEdited, this, &MainProjectPanel::slotCheckAllFieldsFilled); + connect(ui_PublishUrl, &KUrlRequester::urlSelected, this, &MainProjectPanel::slotCheckAllFieldsFilled); } bool MainProjectPanel::ok() { if (useSharedResources->isChecked() && resourcesFile->text().isEmpty()) { return false; } return true; } MacroCommand *MainProjectPanel::buildCommand() { MacroCommand *m = 0; KUndo2MagicString c = kundo2_i18n("Modify main project"); if (project.name() != namefield->text()) { if (!m) m = new MacroCommand(c); m->addCommand(new NodeModifyNameCmd(project, namefield->text())); } if (project.leader() != leaderfield->text()) { if (!m) m = new MacroCommand(c); m->addCommand(new NodeModifyLeaderCmd(project, leaderfield->text())); } if (startDateTime() != project.constraintStartTime()) { if (!m) m = new MacroCommand(c); m->addCommand(new ProjectModifyStartTimeCmd(project, startDateTime())); } if (endDateTime() != project.constraintEndTime()) { if (!m) m = new MacroCommand(c); m->addCommand(new ProjectModifyEndTimeCmd(project, endDateTime())); } if (project.useSharedResources() != useSharedResources->isChecked()) { if (!m) m = new MacroCommand(c); m->addCommand(new UseSharedResourcesCmd(&project, useSharedResources->isChecked())); } if (project.sharedResourcesFile() != resourcesFile->text()) { if (!m) m = new MacroCommand(c); m->addCommand(new SharedResourcesFileCmd( &project, resourcesFile->text())); } QString place = projectsPlace->text(); if (projectsType->currentIndex() == 0 /*dir*/ && !place.isEmpty() && !place.endsWith('/')) { place.append('/'); } QUrl sharedProjectsUrl(place); if (project.sharedProjectsUrl() != sharedProjectsUrl) { if (!m) m = new MacroCommand(c); m->addCommand(new SharedProjectsUrlCmd( &project, sharedProjectsUrl)); } if (project.loadProjectsAtStartup() != projectsLoadAtStartup->isChecked()) { if (!m) m = new MacroCommand(c); m->addCommand(new LoadProjectsAtStartupCmd( &project, projectsLoadAtStartup->isChecked())); } MacroCommand *cmd = m_description->buildCommand(); if ( cmd ) { if (!m) m = new MacroCommand(c); m->addCommand( cmd ); } cmd = m_documents->buildCommand(); if ( cmd ) { if (!m) m = new MacroCommand(c); m->addCommand( cmd ); } + + Project::WorkPackageInfo wpi; + wpi.checkForWorkPackages = ui_CheckForWorkPackages->isChecked(); + wpi.retrieveUrl = ui_RetrieveUrl->url(); + wpi.deleteAfterRetrieval = ui_DeleteFile->isChecked(); + wpi.archiveAfterRetrieval = ui_ArchiveFile->isChecked(); + wpi.archiveUrl = ui_ArchiveUrl->url(); + wpi.publishUrl = ui_PublishUrl->url(); + if (wpi != project.workPackageInfo()) { + ProjectModifyWorkPackageInfoCmd *cmd = new ProjectModifyWorkPackageInfoCmd(project, wpi); + if (!m) m = new MacroCommand(c); + m->addCommand( cmd ); + } + return m; } void MainProjectPanel::slotCheckAllFieldsFilled() { emit changed(); bool state = !namefield->text().isEmpty(); if (state && useSharedResources->isChecked()) { state = !resourcesFile->text().isEmpty(); if (state && projectsLoadAtStartup->isChecked()) { state = !projectsPlace->text().isEmpty(); } } emit obligatedFieldsFilled(state); } void MainProjectPanel::slotChooseLeader() { #ifdef PLAN_KDEPIMLIBS_FOUND QPointer dlg = new Akonadi::EmailAddressSelectionDialog( this ); if ( dlg->exec() && dlg ) { QStringList names; const Akonadi::EmailAddressSelection::List selections = dlg->selectedAddresses(); foreach ( const Akonadi::EmailAddressSelection &selection, selections ) { QString s = selection.name(); if ( ! selection.email().isEmpty() ) { if ( ! selection.name().isEmpty() ) { s += " <"; } s += selection.email(); if ( ! selection.name().isEmpty() ) { s += '>'; } if ( ! s.isEmpty() ) { names << s; } } } if ( ! names.isEmpty() ) { leaderfield->setText( names.join( ", " ) ); } } #endif } void MainProjectPanel::slotStartDateClicked() { enableDateTime(); } void MainProjectPanel::slotEndDateClicked() { enableDateTime(); } void MainProjectPanel::enableDateTime() { debugPlan; startTime->setEnabled(true); startDate->setEnabled(true); endTime->setEnabled(true); endDate->setEnabled(true); } QDateTime MainProjectPanel::startDateTime() { return QDateTime(startDate->date(), startTime->time(), Qt::LocalTime); } QDateTime MainProjectPanel::endDateTime() { return QDateTime(endDate->date(), endTime->time(), Qt::LocalTime); } void MainProjectPanel::openResourcesFile() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Resources"), "", tr("Resources file (*.plan)")); resourcesFile->setText(fileName); } void MainProjectPanel::openProjectsPlace() { if (projectsType->currentIndex() == 0 /*Directory*/) { qInfo()<setText(dirName); return; } if (projectsType->currentIndex() == 1 /*File*/) { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Projects"), "", tr("Projects file (*)")); projectsPlace->setText(fileName); return; } Q_ASSERT(false); // Unimplemented projects type } bool MainProjectPanel::loadSharedResources() const { return useSharedResources->isChecked(); } void MainProjectPanel::loadProjects() { QString place = projectsPlace->text(); if (projectsType->currentIndex() == 0 /*dir*/ && !place.isEmpty() && !place.endsWith('/')) { place.append('/'); } QUrl url(place); emit loadResourceAssignments(url); } void MainProjectPanel::clearProjects() { emit clearResourceAssignments(); } } //KPlato namespace diff --git a/src/libs/ui/kptmainprojectpanel.h b/src/libs/ui/kptmainprojectpanel.h index 899a7bbc..c1a40e3f 100644 --- a/src/libs/ui/kptmainprojectpanel.h +++ b/src/libs/ui/kptmainprojectpanel.h @@ -1,81 +1,81 @@ /* This file is part of the KDE project Copyright (C) 2004-2007 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTMAINPROJECTPANEL_H #define KPTMAINPROJECTPANEL_H #include "planui_export.h" #include "ui_kptmainprojectpanelbase.h" #include class QDateTime; namespace KPlato { class Project; class MacroCommand; class TaskDescriptionPanel; class DocumentsPanel; class MainProjectPanel : public QWidget, public Ui_MainProjectPanelBase { Q_OBJECT public: explicit MainProjectPanel(Project &project, QWidget *parent=0); virtual QDateTime startDateTime(); virtual QDateTime endDateTime(); MacroCommand *buildCommand(); - + bool ok(); bool loadSharedResources() const; public Q_SLOTS: virtual void slotCheckAllFieldsFilled(); virtual void slotChooseLeader(); virtual void slotStartDateClicked(); virtual void slotEndDateClicked(); virtual void enableDateTime(); private Q_SLOTS: void openResourcesFile(); void openProjectsPlace(); void loadProjects(); void clearProjects(); Q_SIGNALS: void obligatedFieldsFilled(bool); void changed(); void loadResourceAssignments(QUrl url); void clearResourceAssignments(); private: + Project &project; DocumentsPanel *m_documents; TaskDescriptionPanel *m_description; - Project &project; }; } //KPlato namespace #endif // MAINPROJECTPANEL_H diff --git a/src/libs/ui/kptmainprojectpanelbase.ui b/src/libs/ui/kptmainprojectpanelbase.ui index 13575889..b43ec057 100644 --- a/src/libs/ui/kptmainprojectpanelbase.ui +++ b/src/libs/ui/kptmainprojectpanelbase.ui @@ -1,409 +1,525 @@ Dag Andersen <danders@get2net.dk> KPlato::MainProjectPanelBase 0 0 501 520 0 - + General WBS: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 20 0 QFrame::StyledPanel QFrame::Sunken false The project name. &Name: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false namefield 0 0 The project name. The project leader. &Manager: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false leaderfield 0 0 The project leader. Choose a project leader from your address book. &Choose... Scheduling Range Earliest start: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Latest finish: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Qt::Horizontal 40 20 &Use shared resources true false Resources: File Browse... .. Projects: Directory Browse... .. Load resource assignments at startup Load .. Clear .. Qt::Vertical 20 18 - + Documents false - + + + Work Package + + + + + + Publish + + + + + + + + Publish: + + + + + + + + + + + + + + + Retrieve + + + + + + Check for work packages + + + + + + + + + Retrieve: + + + + + + + + + + + + + + + Archive + + + + + + Delete file after it has been retrieved + + + + + + + Archive file after it has been retrieved + + + + + + + + + Archive: + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 170 + + + + + + + Description 0 0 0 0 KLineEdit QLineEdit
klineedit.h
1
+ + KUrlRequester + QFrame +
kurlrequester.h
+ 1 +
namefield leaderfield chooseLeader startDate startTime endDate endTime useSharedResources toggled(bool) resourcesWidget setEnabled(bool) 242 330 242 344
diff --git a/src/libs/ui/kpttaskeditor.cpp b/src/libs/ui/kpttaskeditor.cpp index 52bb1f83..85b5d69e 100644 --- a/src/libs/ui/kpttaskeditor.cpp +++ b/src/libs/ui/kpttaskeditor.cpp @@ -1,1733 +1,1761 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2010, 2012 Dag Andersen + Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskeditor.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptnodeitemmodel.h" #include "kptcommand.h" #include "kptproject.h" #include "kptitemviewsettup.h" #include "kptworkpackagesenddialog.h" #include "kptworkpackagesendpanel.h" #include "kptdatetime.h" #include "kptdebug.h" #include "kptresourcemodel.h" #include "kptresourceallocationmodel.h" #include "ResourceAllocationView.h" #include "kpttaskdialog.h" #include "TasksEditController.h" #include "Help.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------- TaskEditorItemModel::TaskEditorItemModel( QObject *parent ) : NodeItemModel( parent ) { } Qt::ItemFlags TaskEditorItemModel::flags( const QModelIndex &index ) const { if ( index.column() == NodeModel::NodeType ) { if ( ! m_readWrite || isColumnReadOnly( index.column() ) ) { return QAbstractItemModel::flags( index ); } Node *n = node( index ); bool baselined = n ? n->isBaselined() : false; if ( n && ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { return QAbstractItemModel::flags( index ) | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; } return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled; } return NodeItemModel::flags( index ); } QVariant TaskEditorItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && section == NodeModel::NodeType ) { if ( role == Qt::ToolTipRole ) { return xi18nc( "@info:tooltip", "The type of task or the estimate type of the task" ); } else if ( role == Qt::WhatsThisRole ) { return xi18nc( "@info:whatsthis", "

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

" "The type can be set to Milestone, Effort or Duration." "If the type is Summary or Project the type is not editable."); } } return NodeItemModel::headerData(section, orientation, role); } QVariant TaskEditorItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return NodeItemModel::data( index, role ); } Node *n = node( index ); if ( n != 0 && index.column() == NodeModel::NodeType ) { return type( n, role ); } return NodeItemModel::data( index, role ); } bool TaskEditorItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { Node *n = node( index ); if ( n != 0 && role == Qt::EditRole && index.column() == NodeModel::NodeType ) { return setType( n, value, role ); } return NodeItemModel::setData( index, value, role ); } QVariant TaskEditorItemModel::type( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return node->typeToString( true ); } case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: { if ( node->type() == Node::Type_Task ) { return xi18nc( "@info:tooltip", "Task with estimate type: %1", node->estimate()->typeToString( true ) ); } return xi18nc( "@info:tooltip", "Task type: %1", node->typeToString( true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::EnumListValue: { if ( node->type() == Node::Type_Milestone ) { return 0; } if ( node->type() == Node::Type_Task ) { return node->estimate()->type() + 1; } return -1; } case Role::EnumList: { QStringList lst; lst << Node::typeToString( Node::Type_Milestone, true ); lst += Estimate::typeToStringList( true ); return lst; } } return QVariant(); } bool TaskEditorItemModel::setType( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( node->type() == Node::Type_Summarytask ) { return false; } int v = value.toInt(); switch ( v ) { case 0: { // Milestone NamedCommand *cmd = 0; if ( node->constraint() == Node::FixedInterval ) { cmd = new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime(), kundo2_i18n( "Set type to Milestone" ) ); } else { cmd = new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 0.0, kundo2_i18n( "Set type to Milestone" ) ); } emit executeCommand( cmd ); return true; } default: { // Estimate --v; MacroCommand *m = new MacroCommand( kundo2_i18n( "Set type to %1", Estimate::typeToString( (Estimate::Type)v, true ) ) ); m->addCommand( new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v ) ); if ( node->type() == Node::Type_Milestone ) { if ( node->constraint() == Node::FixedInterval ) { m->addCommand( new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime().addDays( 1 ) ) ); } else { m->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), Duration::Unit_d ) ); m->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 1.0 ) ); } } emit executeCommand( m ); return true; } } break; } default: break; } return false; } //-------------------- TaskEditorTreeView::TaskEditorTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("view-task").pixmap(32)); TaskEditorItemModel *m = new TaskEditorItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setDefaultDropAction(Qt::CopyAction); createItemDelegates( m ); setItemDelegateForColumn( NodeModel::NodeType, new EnumDelegate( this ) ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &TaskEditorTreeView::slotDropAllowed ); } NodeItemModel *TaskEditorTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void TaskEditorTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } void TaskEditorTreeView::editPaste() { QModelIndex idx = m_leftview->currentIndex(); const QMimeData *data = QGuiApplication::clipboard()->mimeData(); model()->dropMimeData(data, Qt::CopyAction, idx.row()+1, 0, idx.parent()); } //-------------------- NodeTreeView::NodeTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("view-task").pixmap(32)); NodeItemModel *m = new NodeItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &NodeTreeView::slotDropAllowed ); } NodeItemModel *NodeTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void NodeTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } //----------------------------------- TaskEditor::TaskEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ) { debugPlan<<"----------------- Create TaskEditor ----------------------"; QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new TaskEditorTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); debugPlan<actionSplitView(); setupGui(); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); m_view->setAcceptDropsOnView( true ); QList lst1; lst1 << 1 << -1; // only display column 0 (NodeName) in left view QList show; show << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeType << NodeModel::NodePriority << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; QList lst2; for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } for ( int i = 0; i < show.count(); ++i ) { int sec = m_view->slaveView()->header()->visualIndex( show[ i ] ); //debugPlan<<"move section:"<slaveView()->header()->moveSection( sec, i ); } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << NodeModel::NodeName ); m_view->slaveView()->setDefaultColumns( show ); connect( model(), SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskEditor::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskEditor::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskEditor::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(baseModel(), &NodeItemModel::projectShownChanged, this, &TaskEditor::slotProjectShown); connect(model(), &QAbstractItemModel::rowsMoved, this, &TaskEditor::slotEnableActions); Help::add(this, xi18nc("@info:whatsthis", "Task Editor" "" "The Task Editor is used to create, edit, and delete tasks. " "Tasks are organized into a Work Breakdown Structure (WBS) to any depth." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Editor"))); } void TaskEditor::slotProjectShown( bool on ) { debugPlan<rowCount() > 0 ) { idx = proxyModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } } else if ( baseModel() && baseModel()->rowCount() > 0 ) { idx = baseModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } if ( on && idx.isValid() ) { m_view->masterView()->expand( idx ); } slotEnableActions(); } void TaskEditor::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskEditor::setProject( Project *project ) { debugPlan<setProject( project ); ViewBase::setProject( project ); } void TaskEditor::createDockers() { // Add dockers DockWidget *ds = 0; { ds = new DockWidget( this, "Allocations", xi18nc( "@title resource allocations", "Allocations" ) ); QTreeView *x = new QTreeView( ds ); AllocatedResourceItemModel *m1 = new AllocatedResourceItemModel( x ); x->setModel( m1 ); m1->setProject( project() ); // x->setHeaderHidden( true ); x->setSelectionBehavior( QAbstractItemView::SelectRows ); x->setSelectionMode( QAbstractItemView::ExtendedSelection ); x->expandAll(); x->resizeColumnToContents( 0 ); x->setDragDropMode( QAbstractItemView::DragOnly ); x->setDragEnabled ( true ); ds->setWidget( x ); connect(this, &ViewBase::projectChanged, m1, &AllocatedResourceItemModel::setProject); connect(this, &TaskEditor::taskSelected, m1, &AllocatedResourceItemModel::setTask); connect(m1, &AllocatedResourceItemModel::expandAll, x, &QTreeView::expandAll); connect(m1, &AllocatedResourceItemModel::resizeColumnToContents, x, &QTreeView::resizeColumnToContents); addDocker( ds ); } { ds = new DockWidget( this, "Resources", xi18nc( "@title", "Resources" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag resources into the Task Editor" " and drop into the allocations- or responsible column" ) ); ResourceAllocationView *e = new ResourceAllocationView(part(), ds ); ResourceItemModel *m = new ResourceItemModel( e ); e->setModel( m ); m->setProject( project() ); m->setReadWrite( isReadWrite() ); QList show; show << ResourceModel::ResourceName; for ( int i = m->columnCount() - 1; i >= 0; --i ) { e->setColumnHidden( i, ! show.contains( i ) ); } e->setHeaderHidden( true ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::ExtendedSelection ); e->expandAll(); e->resizeColumnToContents( ResourceModel::ResourceName ); e->setDragDropMode( QAbstractItemView::DragOnly ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, e, &ResourceAllocationView::setSelectedTasks); connect(this, SIGNAL(projectChanged(KPlato::Project*)), m, SLOT(setProject(KPlato::Project*))); connect(this, &ViewBase::readWriteChanged, m, &ItemModelBase::setReadWrite); connect(m, &ItemModelBase::executeCommand, part(), &KoDocument::addCommand); addDocker( ds ); } { ds = new DockWidget( this, "Taskmodules", xi18nc( "@title", "Task Modules" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag a task module into the Task Editor to add it to the project" ) ); ds->setLocation( Qt::LeftDockWidgetArea ); ds->setShown(false); // hide by default QTreeView *e = new QTreeView( ds ); QSortFilterProxyModel *sf = new QSortFilterProxyModel(e); TaskModuleModel *m = new TaskModuleModel(sf); sf->setSourceModel(m); e->setModel(sf); e->sortByColumn(0, Qt::AscendingOrder); e->setSortingEnabled(true); e->setHeaderHidden( true ); e->setRootIsDecorated( false ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::SingleSelection ); // e->resizeColumnToContents( 0 ); e->setDragDropMode( QAbstractItemView::DragDrop ); e->setAcceptDrops( true ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(e, &QAbstractItemView::doubleClicked, this, &TaskEditor::taskModuleDoubleClicked); connect(this, &TaskEditor::loadTaskModules, m, &TaskModuleModel::loadTaskModules); connect(m, &TaskModuleModel::saveTaskModule, this, &TaskEditor::saveTaskModule); connect(m, &TaskModuleModel::removeTaskModule, this, &TaskEditor::removeTaskModule); addDocker( ds ); } } void TaskEditor::taskModuleDoubleClicked(QModelIndex idx) { QUrl url = idx.data(Qt::UserRole).toUrl(); if (url.isValid()) { emit openDocument(url); } } void TaskEditor::setTaskModules(const QStringList& files) { emit loadTaskModules( files ); } void TaskEditor::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskEditor::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<( selectedNode() ) ); } QModelIndexList TaskEditor::selectedRows() const { #if 0 // Qt bug? return m_view->selectionModel()->selectedRows(); #else QModelIndexList lst; foreach ( QModelIndex i, m_view->selectionModel()->selectedIndexes() ) { if ( i.column() == 0 ) { lst << i; } } return lst; #endif } int TaskEditor::selectedRowCount() const { return selectedRows().count(); } QList TaskEditor::selectedNodes() const { QList lst; foreach ( const QModelIndex &i, selectedRows() ) { Node * n = m_view->baseModel()->node( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskEditor::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskEditor::currentNode() const { Node * n = m_view->baseModel()->node( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskEditor::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos, const QModelIndexList &rows ) { QString name; if (rows.count() > 1) { debugPlan< summarytasks; QList tasks; QList milestones; for (const QModelIndex &idx : rows) { Node *node = m_view->baseModel()->node( idx ); if (node) { switch ( node->type() ) { case Node::Type_Task: tasks << static_cast(node); break; case Node::Type_Milestone: milestones << static_cast(node); break; case Node::Type_Summarytask: summarytasks << static_cast(node); break; default: break; } } } if (!tasks.isEmpty()) { editTasks(tasks, pos); return; } return; } Node *node = m_view->baseModel()->node( index ); if ( node == 0 ) { return; } debugPlan<name()<<" :"<type() ) { case Node::Type_Project: name = "task_edit_popup"; break; case Node::Type_Task: name = node->isScheduled( baseModel()->id() ) ? "task_popup" : "task_edit_popup"; break; case Node::Type_Milestone: name = node->isScheduled( baseModel()->id() ) ? "taskeditor_milestone_popup" : "task_edit_popup"; break; case Node::Type_Summarytask: name = "summarytask_popup"; break; default: name = "node_popup"; break; } m_view->setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } debugPlan<setContextMenuIndex(QModelIndex()); } void TaskEditor::editTasks(const QList &tasks, const QPoint &pos) { QList lst; QAction tasksEdit(i18n( "Edit..."), nullptr); if (!tasks.isEmpty()) { TasksEditController *ted = new TasksEditController(*project(), tasks, this); connect(&tasksEdit, &QAction::triggered, ted, &TasksEditController::activate); connect(ted, &TasksEditController::addCommand, koDocument(), &KoDocument::addCommand); lst << &tasksEdit; } lst += contextActionList(); if (!lst.isEmpty()) { QMenu::exec( lst, pos, lst.first() ); } } void TaskEditor::setScheduleManager( ScheduleManager *sm ) { if (!sm && scheduleManager()) { // we should only get here if the only schedule manager is scheduled, // or when last schedule manager is deleted m_domdoc.clear(); QDomElement element = m_domdoc.createElement("expanded"); m_domdoc.appendChild(element); m_view->masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskEditor::slotEnableActions() { updateActionsEnabled( isReadWrite() ); } void TaskEditor::updateActionsEnabled( bool on ) { // debugPlan<setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } int selCount = selectedRowCount(); if ( selCount == 0 ) { if ( currentNode() ) { // there are tasks but none is selected menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } else { // we need to be able to add the first task menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } return; } Node *n = selectedNode(); // 0 if not a single task, summarytask or milestone if ( selCount == 1 && n == 0 ) { // only project selected menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( true ); actionAddSubtask->setEnabled( true ); actionAddSubMilestone->setEnabled( true ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } bool baselined = false; Project *p = m_view->project(); if ( p && p->isBaselined() ) { foreach ( Node *n, selectedNodes() ) { if ( n->isBaselined() ) { baselined = true; break; } } } if ( selCount == 1 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubtask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubMilestone->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionDeleteTask->setEnabled( ! baselined ); Node *s = n->siblingBefore(); actionMoveTaskUp->setEnabled( s ); actionMoveTaskDown->setEnabled( n->siblingAfter() ); s = n->siblingBefore(); actionIndentTask->setEnabled( ! baselined && s && ! s->isBaselined() ); actionUnindentTask->setEnabled( ! baselined && n->level() > 1 ); return; } // selCount > 1 menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( ! baselined ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } void TaskEditor::setupGui() { QString name = "taskeditor_add_list"; menuAddTask = new KActionMenu(koIcon("view-task-add"), i18n("Add Task"), this); actionCollection()->addAction("add_task", menuAddTask ); connect( menuAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); addAction( name, menuAddTask ); actionAddTask = new QAction( i18n( "Add Task" ), this); actionAddTask->setShortcut( Qt::CTRL + Qt::Key_I ); connect( actionAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); menuAddTask->addAction( actionAddTask ); actionAddMilestone = new QAction( i18n( "Add Milestone" ), this ); actionAddMilestone->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_I ); connect( actionAddMilestone, &QAction::triggered, this, &TaskEditor::slotAddMilestone ); menuAddTask->addAction( actionAddMilestone ); menuAddSubTask = new KActionMenu(koIcon("view-task-child-add"), i18n("Add Sub-Task"), this); actionCollection()->addAction("add_subtask", menuAddTask ); connect( menuAddSubTask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); addAction( name, menuAddSubTask ); actionAddSubtask = new QAction( i18n( "Add Sub-Task" ), this ); actionAddSubtask->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_I ); connect( actionAddSubtask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); menuAddSubTask->addAction( actionAddSubtask ); actionAddSubMilestone = new QAction( i18n( "Add Sub-Milestone" ), this ); actionAddSubMilestone->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_I ); connect( actionAddSubMilestone, &QAction::triggered, this, &TaskEditor::slotAddSubMilestone ); menuAddSubTask->addAction( actionAddSubMilestone ); actionDeleteTask = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); actionCollection()->setDefaultShortcut( actionDeleteTask, Qt::Key_Delete ); actionCollection()->addAction("delete_task", actionDeleteTask ); connect( actionDeleteTask, &QAction::triggered, this, &TaskEditor::slotDeleteTask ); addAction( name, actionDeleteTask ); name = "taskeditor_move_list"; actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask ); connect(actionIndentTask, &QAction::triggered, this, &TaskEditor::slotIndentTask); addAction( name, actionIndentTask ); actionUnindentTask = new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask ); connect(actionUnindentTask, &QAction::triggered, this, &TaskEditor::slotUnindentTask); addAction( name, actionUnindentTask ); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp ); connect(actionMoveTaskUp, &QAction::triggered, this, &TaskEditor::slotMoveTaskUp); addAction( name, actionMoveTaskUp ); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown ); connect(actionMoveTaskDown, &QAction::triggered, this, &TaskEditor::slotMoveTaskDown); addAction( name, actionMoveTaskDown ); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction( actionShowProject ); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskEditor::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); createDockers(); } void TaskEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void TaskEditor::slotAddTask() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddMilestone() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { // None selected or only project selected: insert under main project m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); // sibling if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubMilestone() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubtask() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::edit( const QModelIndex &i ) { if ( i.isValid() ) { m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->setParentsExpanded( i, true ); // in case treeview does not have focus m_view->edit( i ); } } void TaskEditor::slotDeleteTask() { //debugPlan; QList lst = selectedNodes(); while ( true ) { // remove children of selected tasks, as parents delete their children Node *ch = 0; foreach ( Node *n1, lst ) { foreach ( Node *n2, lst ) { if ( n2->isChildOf( n1 ) ) { ch = n2; break; } } if ( ch != 0 ) { break; } } if ( ch == 0 ) { break; } lst.removeAt( lst.indexOf( ch ) ); } //foreach ( Node* n, lst ) { debugPlan<name(); } emit deleteTaskList( lst ); QModelIndex i = m_view->selectionModel()->currentIndex(); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotIndentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit indentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->setParentsExpanded( i, true ); } } void TaskEditor::slotUnindentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit unindentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskUp() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskUp(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskDown() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskDown(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } bool TaskEditor::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? bool res = m_view->loadContext( baseModel()->columnMap(), context ); return res; } void TaskEditor::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( baseModel()->columnMap(), context ); } KoPrintJob *TaskEditor::createPrintJob() { return m_view->createPrintJob( this ); } void TaskEditor::slotEditCopy() { m_view->editCopy(); } void TaskEditor::slotEditPaste() { m_view->editPaste(); } //----------------------------------- TaskView::TaskView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new NodeTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); NodeSortFilterProxyModel *p = new NodeSortFilterProxyModel( m_view->baseModel(), m_view ); m_view->setModel( p ); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode(QAbstractItemView::DragOnly); m_view->setDropIndicatorShown( false ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( false ); m_view->setAcceptDropsOnView( false ); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodePerformanceIndex << NodeModel::NodeBCWS << NodeModel::NodeBCWP << NodeModel::NodeACWP << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Task Execution View" "" "The view is used to edit and inspect task progress during project execution." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Execution_View"))); } void TaskView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskView::draw( Project &project ) { m_view->setProject( &project ); } void TaskView::draw() { } void TaskView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( i ) ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskView::currentNode() const { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( m_view->selectionModel()->currentIndex() ) ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = m_view->baseModel()->node( proxyModel()->mapToSource( index ) ); if ( node ) { switch ( node->type() ) { case Node::Type_Project: name = "taskview_project_popup"; break; case Node::Type_Task: name = "taskview_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager() && sm != scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskView::slotEnableActions() { updateActionsEnabled( true ); } void TaskView::updateActionsEnabled( bool /*on*/ ) { } void TaskView::setupGui() { // KActionCollection *coll = actionCollection(); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction( actionShowProject ); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool TaskView::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskView::createPrintJob() { return m_view->createPrintJob( this ); } void TaskView::slotEditCopy() { m_view->editCopy(); } //--------------------------------- WorkPackageTreeView::WorkPackageTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { debugPlan<<"----------"<baseModel(); } void WorkPackageTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { Q_UNUSED(index); Q_UNUSED(dropIndicatorPosition); Q_UNUSED(event); /* QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); }*/ } //-------------------------------- TaskWorkPackageView::TaskWorkPackageView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ), m_cmd( 0 ) { QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new WorkPackageTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); - m_view->setDragDropMode( QAbstractItemView::InternalMove ); - m_view->setDropIndicatorShown( false ); + m_view->setDragDropMode( QAbstractItemView::DragDrop ); + m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); - m_view->setAcceptDrops( false ); - m_view->setAcceptDropsOnView( false ); + m_view->setAcceptDrops( true ); + m_view->setAcceptDropsOnView( true ); + m_view->setDefaultDropAction(Qt::CopyAction); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskWorkPackageView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskWorkPackageView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskWorkPackageView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); + + connect(m_view->proxyModel(), &WorkPackageProxyModel::loadWorkPackage, this, &TaskWorkPackageView::slotLoadWorkPackage); } Project *TaskWorkPackageView::project() const { return m_view->project(); } void TaskWorkPackageView::setProject( Project *project ) { m_view->setProject( project ); } WorkPackageProxyModel *TaskWorkPackageView::proxyModel() const { return m_view->proxyModel(); } void TaskWorkPackageView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskWorkPackageView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskWorkPackageView::slotRefreshView() { emit checkForWorkPackages(false); } void TaskWorkPackageView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskWorkPackageView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = proxyModel()->taskFromIndex( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskWorkPackageView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskWorkPackageView::currentNode() const { Node * n = proxyModel()->taskFromIndex( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskWorkPackageView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = proxyModel()->taskFromIndex( index ); if ( node ) { switch ( node->type() ) { case Node::Type_Task: name = "workpackage_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskWorkPackageView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<baseModel()->setScheduleManager( sm ); } void TaskWorkPackageView::slotEnableActions() { updateActionsEnabled( true ); } void TaskWorkPackageView::updateActionsEnabled( bool on ) { bool o = ! selectedNodes().isEmpty(); actionMailWorkpackage->setEnabled( o && on ); } void TaskWorkPackageView::setupGui() { // KActionCollection *coll = actionCollection(); QString name = "workpackage_list"; actionMailWorkpackage = new QAction(koIcon("mail-send"), i18n("Send..."), this); actionCollection()->setDefaultShortcut( actionMailWorkpackage, Qt::CTRL + Qt::Key_M ); actionCollection()->addAction("send_workpackage", actionMailWorkpackage ); connect( actionMailWorkpackage, &QAction::triggered, this, &TaskWorkPackageView::slotMailWorkpackage ); addAction( name, actionMailWorkpackage ); + actionOpenWorkpackages = new QAction(koIcon("view-task"), i18n("Work Packages..."), this); + actionCollection()->setDefaultShortcut( actionOpenWorkpackages, Qt::CTRL + Qt::Key_O ); + actionCollection()->addAction("open_workpackages", actionOpenWorkpackages ); + actionOpenWorkpackages->setEnabled(false); + connect( actionOpenWorkpackages, &QAction::triggered, this, &TaskWorkPackageView::openWorkpackages ); + addAction( name, actionOpenWorkpackages ); + // Add the context menu actions for the view options connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskWorkPackageView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskWorkPackageView::slotMailWorkpackage() { QList lst = selectedNodes(); if ( ! lst.isEmpty() ) { // TODO find a better way to log to avoid undo/redo m_cmd = new MacroCommand( kundo2_i18n( "Log Send Workpackage" ) ); QPointer dlg = new WorkPackageSendDialog( lst, scheduleManager(), this ); - connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::mailWorkpackages ); + connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::publishWorkpackages ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::slotWorkPackageSent ); dlg->exec(); delete dlg; if ( ! m_cmd->isEmpty() ) { part()->addCommand( m_cmd ); m_cmd = 0; } delete m_cmd; m_cmd = 0; } } void TaskWorkPackageView::slotWorkPackageSent( const QList &nodes, Resource *resource ) { foreach ( Node *n, nodes ) { WorkPackage *wp = new WorkPackage( static_cast( n )->workPackage() ); wp->setOwnerName( resource->name() ); wp->setOwnerId( resource->id() ); wp->setTransmitionTime( DateTime::currentDateTime() ); wp->setTransmitionStatus( WorkPackage::TS_Send ); m_cmd->addCommand( new WorkPackageAddCmd( static_cast( n->projectNode() ), n, wp ) ); } } void TaskWorkPackageView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskWorkPackageView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool TaskWorkPackageView::loadContext( const KoXmlElement &context ) { debugPlan; ViewBase::loadContext( context ); return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskWorkPackageView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskWorkPackageView::createPrintJob() { return m_view->createPrintJob( this ); } void TaskWorkPackageView::slotEditCopy() { m_view->editCopy(); } +void TaskWorkPackageView::slotWorkpackagesAvailable(bool value) +{ + actionOpenWorkpackages->setEnabled(value); +} + +void TaskWorkPackageView::slotLoadWorkPackage(QList files) +{ + QList urls; + for (const QString &f : files) { + QUrl url = QUrl(f); + if (url.isValid()) { + urls << url; + } + } + emit loadWorkPackageUrl(m_view->project(), urls); +} + } // namespace KPlato diff --git a/src/libs/ui/kpttaskeditor.h b/src/libs/ui/kpttaskeditor.h index ca3b7dbb..bf64ddcc 100644 --- a/src/libs/ui/kpttaskeditor.h +++ b/src/libs/ui/kpttaskeditor.h @@ -1,362 +1,367 @@ /* This file is part of the KDE project Copyright (C) 2006 -20010 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASKEDITOR_H #define KPTTASKEDITOR_H #include "planui_export.h" #include "kptglobal.h" #include "kptnodeitemmodel.h" #include "kptviewbase.h" class KoDocument; class KActionMenu; namespace KPlato { class Project; class Node; class NodeItemModel; class MacroCommand; class PLANUI_EXPORT TaskEditorItemModel : public NodeItemModel { Q_OBJECT public: explicit TaskEditorItemModel( QObject *parent = 0 ); virtual Qt::ItemFlags flags( const QModelIndex & index ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex & index, const QVariant &value, int role = Qt::EditRole ); protected: QVariant type( const Node *node, int role ) const; virtual bool setType( Node *node, const QVariant &value, int role ); }; class PLANUI_EXPORT TaskEditorTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit TaskEditorTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); NodeItemModel *baseModel() const; NodeSortFilterProxyModel *proxyModel() const { return qobject_cast( model() ); } Project *project() const { return baseModel()->project(); } void setProject( Project *project ) { baseModel()->setProject( project ); } void editPaste(); Q_SIGNALS: void currentColumnChanged( const QModelIndex&, const QModelIndex& ); protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); }; class PLANUI_EXPORT NodeTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit NodeTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); NodeItemModel *baseModel() const; NodeSortFilterProxyModel *proxyModel() const { return qobject_cast( model() ); } Project *project() const { return baseModel()->project(); } void setProject( Project *project ) { baseModel()->setProject( project ); } Q_SIGNALS: void currentColumnChanged( const QModelIndex&, const QModelIndex& ); protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); }; class PLANUI_EXPORT TaskEditor : public ViewBase { Q_OBJECT public: TaskEditor(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); void setProject( Project *project ); Project *project() const { return m_view->project(); } virtual void createDockers(); virtual Node *currentNode() const; QList selectedNodes() const ; Node *selectedNode() const; virtual void updateReadWrite( bool readwrite ); NodeItemModel *baseModel() const { return m_view->baseModel(); } NodeSortFilterProxyModel *proxyModel() const { return m_view->proxyModel(); } QAbstractItemModel *model() const { return m_view->model(); } /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; virtual KoPrintJob *createPrintJob(); Q_SIGNALS: void taskSelected(KPlato::Task *task); void openNode(); void addTask(); void addMilestone(); void addSubtask(); void addSubMilestone(); void deleteTaskList(const QList&); void moveTaskUp(); void moveTaskDown(); void indentTask(); void unindentTask(); void loadTaskModules( const QStringList &files ); void saveTaskModule(const QUrl &url, KPlato::Project *project); void removeTaskModule( const QUrl &url ); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void setScheduleManager(KPlato::ScheduleManager *sm); void setTaskModules( const QStringList &files ); void slotEditCopy(); void slotEditPaste(); protected: void updateActionsEnabled( bool on ); int selectedRowCount() const; QModelIndexList selectedRows() const; void editTasks(const QList &tasks, const QPoint &pos); protected Q_SLOTS: virtual void slotOptions(); private Q_SLOTS: void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested(const QModelIndex &index, const QPoint& pos , const QModelIndexList &rows); void slotEnableActions(); void slotAddTask(); void slotAddSubtask(); void slotAddMilestone(); void slotAddSubMilestone(); void slotDeleteTask(); void slotIndentTask(); void slotUnindentTask(); void slotMoveTaskUp(); void slotMoveTaskDown(); void slotSplitView(); void slotProjectShown( bool ); void taskModuleDoubleClicked(QModelIndex idx); private: void edit( const QModelIndex &index ); private: TaskEditorTreeView *m_view; KActionMenu *menuAddTask; KActionMenu *menuAddSubTask; QAction *actionAddTask; QAction *actionAddMilestone; QAction *actionAddSubtask; QAction *actionAddSubMilestone; QAction *actionDeleteTask; QAction *actionMoveTaskUp; QAction *actionMoveTaskDown; QAction *actionIndentTask; QAction *actionUnindentTask; QAction *actionShowProject; QDomDocument m_domdoc; }; class PLANUI_EXPORT TaskView : public ViewBase { Q_OBJECT public: TaskView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); Project *project() const { return m_view->project(); } virtual void draw( Project &project ); virtual void draw(); NodeItemModel *baseModel() const { return m_view->baseModel(); } NodeSortFilterProxyModel *proxyModel() const { return m_view->proxyModel(); } virtual Node *currentNode() const; QList selectedNodes() const ; Node *selectedNode() const; virtual void updateReadWrite( bool readwrite ); /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; KoPrintJob *createPrintJob(); Q_SIGNALS: void openNode(); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void setScheduleManager(KPlato::ScheduleManager *sm); void slotEditCopy(); protected: void updateActionsEnabled( bool on ); int selectedNodeCount() const; protected Q_SLOTS: virtual void slotOptions(); private Q_SLOTS: void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotEnableActions(); void slotSplitView(); private: NodeTreeView *m_view; QAction *actionShowProject; QDomDocument m_domdoc; }; //----------------------------------- class WorkPackageTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit WorkPackageTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); NodeItemModel *baseModel() const; WorkPackageProxyModel *proxyModel() const { return m; } Project *project() const { return baseModel()->project(); } void setProject( Project *project ) { m->setProject( project ); } ScheduleManager *scheduleManager() const { return baseModel()->manager(); } Q_SIGNALS: void currentColumnChanged( const QModelIndex&, const QModelIndex& ); protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); protected: WorkPackageProxyModel *m; }; class PLANUI_EXPORT TaskWorkPackageView : public ViewBase { Q_OBJECT public: TaskWorkPackageView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); Project *project() const; void setProject( Project *project ); ScheduleManager *scheduleManager() const { return m_view->scheduleManager(); } WorkPackageProxyModel *proxyModel() const; virtual Node *currentNode() const; QList selectedNodes() const ; Node *selectedNode() const; virtual void updateReadWrite( bool readwrite ); /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; KoPrintJob *createPrintJob(); - + Q_SIGNALS: void mailWorkpackage(KPlato::Node *n, KPlato::Resource *r = 0); - void mailWorkpackages(const QList &nodes, KPlato::Resource *r); + void publishWorkpackages(const QList &nodes, KPlato::Resource *r, bool mailTo); + void openWorkpackages(); void checkForWorkPackages(bool); + void loadWorkPackageUrl(KPlato::Project *project, QList &urls); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void slotRefreshView(); void setScheduleManager(KPlato::ScheduleManager *sm); void slotEditCopy(); + void slotWorkpackagesAvailable(bool value); protected: void updateActionsEnabled( bool on ); int selectedNodeCount() const; protected Q_SLOTS: virtual void slotOptions(); void slotMailWorkpackage(); void slotWorkPackageSent(const QList &nodes, KPlato::Resource *resource); + void slotLoadWorkPackage(QList); private Q_SLOTS: void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotEnableActions(); void slotSplitView(); private: WorkPackageTreeView *m_view; MacroCommand *m_cmd; QAction *actionMailWorkpackage; - + QAction *actionOpenWorkpackages; }; } //namespace KPlato #endif diff --git a/src/libs/ui/kpttaskprogresspanel.cpp b/src/libs/ui/kpttaskprogresspanel.cpp index 3bfd2955..d884afc5 100644 --- a/src/libs/ui/kpttaskprogresspanel.cpp +++ b/src/libs/ui/kpttaskprogresspanel.cpp @@ -1,423 +1,424 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007, 2012 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskprogresspanel.h" #include "kptusedefforteditor.h" #include #include #include #include #include "kpttask.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptresource.h" #include "kptdurationspinbox.h" #include "kptschedule.h" #include "kptproject.h" #include "kptdebug.h" using namespace KPlato; int toEditMode(Completion::Entrymode m) { const QList modes = QList() << Completion::EnterEffortPerResource << Completion::EnterEffortPerTask << Completion::EnterCompleted; return qBound(0, modes.indexOf(m), 1); } Completion::Entrymode fromEditMode(int m) { const QList modes = QList() << Completion::EnterEffortPerResource << Completion::EnterEffortPerTask; return modes.value(m); } //----------------- TaskProgressPanel::TaskProgressPanel( Task &task, ScheduleManager *sm, StandardWorktime *workTime, QWidget *parent ) : TaskProgressPanelImpl( task, parent ) { Q_UNUSED(workTime); debugPlan; started->setChecked(m_completion.isStarted()); finished->setChecked(m_completion.isFinished()); startTime->setDateTime(m_completion.startTime()); finishTime->setDateTime(m_completion.finishTime()); finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); scheduledEffort = task.estimate()->expectedValue(); setYear( QDate::currentDate().year() ); if ( m_completion.usedEffortMap().isEmpty() || m_task.requests().isEmpty() ) { foreach ( ResourceGroupRequest *g, m_task.requests().requests() ) { foreach ( ResourceRequest *r, g->resourceRequests() ) { m_completion.addUsedEffort( r->resource() ); } } } enableWidgets(); started->setFocus(); connect( weekNumber, SIGNAL(currentIndexChanged(int)), SLOT(slotWeekNumberChanged(int)) ); connect(ui_resourceCombo, SIGNAL(activated(QString)), resourceTable->model(), SLOT(addResource(QString))); connect( addEntryBtn, &QAbstractButton::clicked, entryTable, &CompletionEntryEditor::addEntry ); connect( removeEntryBtn, &QAbstractButton::clicked, entryTable, &CompletionEntryEditor::removeEntry ); entryTable->model()->setManager( sm ); entryTable->model()->setTask( &task ); entryTable->setCompletion( &m_completion ); connect( entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanel::slotEntryAdded ); resourceTable->setProject( static_cast( task.projectNode() ) ); resourceTable->setCompletion( &m_completion ); slotWeekNumberChanged( weekNumber->currentIndex() ); updateResourceCombo(); connect(resourceTable->model(), &UsedEffortItemModel::rowInserted, this, &TaskProgressPanelImpl::updateResourceCombo); //resourceTable->resizeColumnsToContents(); connect(started, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotStartedChanged); connect(started, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotChanged); connect(finished, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotFinishedChanged); connect(finished, &QAbstractButton::toggled, this, &TaskProgressPanelImpl::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotChanged); connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotStartTimeChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotChanged); connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskProgressPanelImpl::slotFinishTimeChanged); } MacroCommand *TaskProgressPanel::buildCommand() { Project *project = dynamic_cast( m_task.projectNode() ); if ( project == 0 ) { return 0; } return buildCommand( *project, m_original, m_completion ); } MacroCommand *TaskProgressPanel::buildCommand( const Project &project, Completion &org, Completion &curr ) { MacroCommand *cmd = 0; KUndo2MagicString c = kundo2_i18n("Modify task completion"); if ( org.entrymode() != curr.entrymode() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionEntrymodeCmd(org, curr.entrymode() ) ); } if ( org.startTime() != curr.startTime() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionStartTimeCmd(org, curr.startTime() ) ); } if ( org.finishTime() != curr.finishTime() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionFinishTimeCmd(org, curr.finishTime() ) ); } if ( org.isStarted() != curr.isStarted() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionStartedCmd(org, curr.isStarted() ) ); } if ( org.isFinished() != curr.isFinished() ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new ModifyCompletionFinishedCmd(org, curr.isFinished() ) ); } QList orgdates = org.entries().keys(); QList currdates = curr.entries().keys(); foreach ( const QDate &d, orgdates ) { if ( currdates.contains( d ) ) { if ( curr.entry( d ) == org.entry( d ) ) { continue; } if ( cmd == 0 ) cmd = new MacroCommand( c ); debugPlan<<"modify entry "<addCommand( new ModifyCompletionEntryCmd(org, d, e ) ); } else { if ( cmd == 0 ) cmd = new MacroCommand( c ); debugPlan<<"remove entry "<addCommand( new RemoveCompletionEntryCmd(org, d ) ); } } foreach ( const QDate &d, currdates ) { if ( ! orgdates.contains( d ) ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); Completion::Entry *e = new Completion::Entry( * ( curr.entry( d ) ) ); debugPlan<<"add entry "<addCommand( new AddCompletionEntryCmd(org, d, e ) ); } } const Completion::ResourceUsedEffortMap &map = curr.usedEffortMap(); Completion::ResourceUsedEffortMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { Resource *r = project.findResource(it.key()->id()); if ( r == 0 ) { warnPlan<<"Can't find resource:"<id()<name(); continue; } Completion::UsedEffort *ue = map[ r ]; if ( ue == 0 ) { continue; } if ( org.usedEffort( r ) == 0 || *ue != *(org.usedEffort( r )) ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); cmd->addCommand( new AddCompletionUsedEffortCmd( org, r, new Completion::UsedEffort( *ue ) ) ); } } return cmd; } void TaskProgressPanel::slotWeekNumberChanged( int index ) { debugPlan<setCurrentMonday( date ); } void TaskProgressPanel::slotEntryAdded( const QDate &date ) { debugPlan<setEnabled( false ); editmode->setCurrentIndex(toEditMode(m_original.entrymode())); connect( editmode, SIGNAL(currentIndexChanged(int)), SLOT(slotEditmodeChanged(int)) ); connect( editmode, SIGNAL(activated(int)), SLOT(slotChanged()) ); connect(resourceTable, &UsedEffortEditor::changed, this, &TaskProgressPanelImpl::slotChanged ); connect(resourceTable, &UsedEffortEditor::resourceAdded, this, &TaskProgressPanelImpl::slotChanged ); connect(resourceTable->model(), &UsedEffortItemModel::effortChanged, this, &TaskProgressPanelImpl::slotEffortChanged); connect(entryTable, &CompletionEntryEditor::changed, this, &TaskProgressPanelImpl::slotChanged ); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanelImpl::slotChanged ); connect(entryTable, &CompletionEntryEditor::changed, this, &TaskProgressPanelImpl::slotEntryChanged ); connect(entryTable, &CompletionEntryEditor::rowInserted, this, &TaskProgressPanelImpl::slotEntryChanged ); connect(entryTable, &CompletionEntryEditor::rowRemoved, this, &TaskProgressPanelImpl::slotEntryChanged ); connect( prevWeekBtn, &QAbstractButton::clicked, this, &TaskProgressPanelImpl::slotPrevWeekBtnClicked ); connect( nextWeekBtn, &QAbstractButton::clicked, this, &TaskProgressPanelImpl::slotNextWeekBtnClicked ); connect ( ui_year, SIGNAL(valueChanged(int)), SLOT(slotFillWeekNumbers(int)) ); int y = 0; int wn = QDate::currentDate().weekNumber( &y ); setYear( y ); weekNumber->setCurrentIndex( wn - m_weekOffset ); } void TaskProgressPanelImpl::slotEffortChanged(const QDate &date) { if (date.isValid()) { entryTable->insertEntry(date); } } void TaskProgressPanelImpl::slotChanged() { emit changed(); } void TaskProgressPanelImpl::slotEditmodeChanged( int idx ) { m_completion.setEntrymode(fromEditMode(idx)); entryTable->model()->slotDataChanged(); enableWidgets(); } void TaskProgressPanelImpl::slotStartedChanged(bool state) { m_completion.setStarted( state ); if (state) { QTime t = QTime::currentTime(); t.setHMS( t.hour(), t.minute(), 0 ); m_completion.setStartTime( QDateTime(QDate::currentDate(), t, Qt::LocalTime) ); startTime->setDateTime( m_completion.startTime() ); slotCalculateEffort(); } enableWidgets(); } void TaskProgressPanelImpl::setFinished() { QTime t = QTime::currentTime(); t.setHMS( t.hour(), t.minute(), 0 ); finishTime->setDateTime( QDateTime(QDate::currentDate(), t, Qt::LocalTime) ); slotFinishTimeChanged( finishTime->dateTime() ); } void TaskProgressPanelImpl::slotFinishedChanged(bool state) { debugPlan<dateTime(); slotCalculateEffort(); } enableWidgets(); } void TaskProgressPanelImpl::slotFinishTimeChanged( const QDateTime &dt ) { if ( ! m_completion.isFinished() ) { return; } m_completion.setFinishTime( dt ); if ( m_completion.percentFinished() < 100 ) { m_completion.setPercentFinished( dt.date(), 100 ); } entryTable->setCompletion( &m_completion ); // for refresh } void TaskProgressPanelImpl::slotStartTimeChanged( const QDateTime &dt ) { m_completion.setStartTime( dt ); finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); } void TaskProgressPanelImpl::slotEntryChanged() { finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); } void TaskProgressPanelImpl::updateResourceCombo() { ui_resourceCombo->blockSignals(true); ui_resourceCombo->clear(); ui_resourceCombo->addItems(resourceTable->model()->freeResources().keys()); ui_resourceCombo->blockSignals(false); } void TaskProgressPanelImpl::enableWidgets() { editmode->setEnabled( !finished->isChecked() ); started->setEnabled(!finished->isChecked()); finished->setEnabled(started->isChecked()); finishTime->setEnabled(finished->isChecked()); startTime->setEnabled(started->isChecked() && !finished->isChecked()); addEntryBtn->setEnabled( started->isChecked() && !finished->isChecked() ); removeEntryBtn->setEnabled( ! entryTable->selectionModel()->selectedIndexes().isEmpty() && started->isChecked() && ! finished->isChecked() ); ui_resourceFrame->setVisible(m_completion.entrymode() == Completion::EnterEffortPerResource); resourceTable->model()->setReadOnly( ( ! started->isChecked() ) || finished->isChecked() || editmode->currentIndex() != 0 ); } void TaskProgressPanelImpl::slotPercentFinishedChanged( int ) { slotCalculateEffort(); } void TaskProgressPanelImpl::slotCalculateEffort() { } void TaskProgressPanelImpl::slotPrevWeekBtnClicked() { debugPlan; int i = weekNumber->currentIndex(); if ( i == 0 ) { debugPlan<value() - 1 ); if ( m_lastIsNextYear ) { decr = 2; } weekNumber->setCurrentIndex( weekNumber->count() - decr ); } else { weekNumber->setCurrentIndex( i - 1 ); } } void TaskProgressPanelImpl::slotNextWeekBtnClicked() { int i = weekNumber->currentIndex(); debugPlan<count(); if ( i == weekNumber->count() - 1 ) { debugPlan<value() + 1 ); if ( m_firstIsPrevYear ) { index = 1; } weekNumber->setCurrentIndex( index ); } else { weekNumber->setCurrentIndex( i + 1 ); } } void TaskProgressPanelImpl::setYear( int year ) { debugPlan; ui_year->setValue( year ); } void TaskProgressPanelImpl::slotFillWeekNumbers( int year ) { debugPlan; weekNumber->clear(); m_year = year; m_weekOffset = 1; int y = 0; QDate date( year, 1, 1 ); int wn = date.weekNumber( &y ); m_firstIsPrevYear = false; debugPlan<addItem( i18nc( "Week number (year)", "Week %1 (%2)", wn, y ) ); m_weekOffset = 0; m_firstIsPrevYear = true; debugPlan<<"Added last week of prev year"; } for ( int i=1; i <= 52; ++i ) { weekNumber->addItem( i18nc( "Week number", "Week %1", i ) ); } date = QDate( year, 12, 31 ); wn = date.weekNumber( &y ); debugPlan<addItem( i18nc( "Week number", "Week %1", wn ) ); } else if ( wn == 1 ) { weekNumber->addItem( i18nc( "Week number (year)", "Week %1 (%2)", wn, y ) ); m_lastIsNextYear = true; } } void TaskProgressPanelImpl::slotSelectionChanged( const QItemSelection &sel ) { removeEntryBtn->setEnabled( ! sel.isEmpty() && started->isChecked() && ! finished->isChecked() ); } diff --git a/src/libs/ui/kpttaskprogresspanel.h b/src/libs/ui/kpttaskprogresspanel.h index 34fe666b..ff290bc3 100644 --- a/src/libs/ui/kpttaskprogresspanel.h +++ b/src/libs/ui/kpttaskprogresspanel.h @@ -1,104 +1,105 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASKPROGRESSPANEL_H #define KPTTASKPROGRESSPANEL_H #include "planui_export.h" #include "ui_kpttaskprogresspanelbase.h" #include "kpttask.h" #include namespace KPlato { class StandardWorktime; class Duration; class ScheduleManager; class MacroCommand; //------------------------ -class TaskProgressPanelImpl : public QWidget, public Ui_TaskProgressPanelBase { +class PLANUI_EXPORT TaskProgressPanelImpl : public QWidget, public Ui_TaskProgressPanelBase { Q_OBJECT public: explicit TaskProgressPanelImpl( Task &task, QWidget *parent=0 ); void enableWidgets(); void setYear( int year ); Q_SIGNALS: void changed(); public Q_SLOTS: void slotChanged(); void slotEditmodeChanged( int idx ); void slotStartedChanged(bool state); void slotFinishedChanged(bool state); void slotPercentFinishedChanged(int value); void slotStartTimeChanged( const QDateTime &dt ); void slotFinishTimeChanged( const QDateTime &dt ); void slotEntryChanged(); void slotSelectionChanged( const QItemSelection &sel ); void slotPrevWeekBtnClicked(); void slotNextWeekBtnClicked(); void updateResourceCombo(); void slotEffortChanged(const QDate &date); protected Q_SLOTS: void slotCalculateEffort(); void slotFillWeekNumbers( int year ); protected: void setFinished(); Task &m_task; Completion &m_original; Completion m_completion; int m_dayLength; Duration scheduledEffort; int m_weekOffset; int m_year; bool m_firstIsPrevYear; bool m_lastIsNextYear; }; class PLANUI_EXPORT TaskProgressPanel : public TaskProgressPanelImpl { Q_OBJECT public: explicit TaskProgressPanel( Task &task, ScheduleManager *sm, StandardWorktime *workTime=0, QWidget *parent=0 ); MacroCommand *buildCommand(); static MacroCommand *buildCommand( const Project &project, Completion &org, Completion &curr ); protected Q_SLOTS: void slotWeekNumberChanged( int ); void slotEntryAdded( const QDate &date); }; } //KPlato namespace #endif // TASKPROGRESSPANEL_H diff --git a/src/libs/ui/kpttaskprogresspanelbase.ui b/src/libs/ui/kpttaskprogresspanelbase.ui index b72ed3bf..dc6bcb4f 100644 --- a/src/libs/ui/kpttaskprogresspanelbase.ui +++ b/src/libs/ui/kpttaskprogresspanelbase.ui @@ -1,297 +1,339 @@ Dag Andersen <danders@get2net.dk> KPlato::TaskProgressPanelBase 0 0 887 427 QFrame::StyledPanel QFrame::Raised Started: true Finished: true Qt::Horizontal 36 20 - - - Edit mode: - - - - - - - - Per resource + + + + 0 - - - - Per task + + 0 - + + 0 + + + 0 + + + + + Edit mode: + + + + + + + + Per resource + + + + + Per task + + + + + QFrame::StyledPanel QFrame::Raised 0 0 0 0 1900 2999 Prev .. Next .. Qt::Horizontal 16 25 - - - Add resource: - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Add resource: + + + + + + + - - - - + 0 0 false + + 70 + + + true + QFrame::StyledPanel QFrame::Raised Add Entry .. Remove Entry .. Qt::Horizontal 40 20 0 0 0 100 false KComboBox QComboBox
kcombobox.h
CompletionEntryEditor QTableView
kptusedefforteditor.h
UsedEffortEditor QTableView
kptusedefforteditor.h
started startTime finished finishTime addEntryBtn removeEntryBtn ui_year prevWeekBtn weekNumber nextWeekBtn
diff --git a/src/libs/ui/kptusedefforteditor.cpp b/src/libs/ui/kptusedefforteditor.cpp index 5b131d60..76e8af71 100644 --- a/src/libs/ui/kptusedefforteditor.cpp +++ b/src/libs/ui/kptusedefforteditor.cpp @@ -1,927 +1,929 @@ /* This file is part of the KDE project Copyright (C) 2007, 2011, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptusedefforteditor.h" #include "kptitemmodelbase.h" #include #include #include #include #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdebug.h" namespace KPlato { UsedEffortItemModel::UsedEffortItemModel ( QWidget *parent ) : QAbstractItemModel( parent ), m_completion( 0 ), m_readonly( false ) { m_headers << i18n( "Resource" ); QLocale locale; for ( int i = 1; i <= 7; ++i ) { m_headers << locale.dayName( i, QLocale::ShortFormat ); } m_headers << i18n( "This Week" ); } Qt::ItemFlags UsedEffortItemModel::flags ( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); if ( m_readonly || ! index.isValid() || index.column() == 8 ) { return flags; } if ( index.column() == 0 ) { const Resource *r = resource( index ); if ( r ) { if ( m_resourcelist.contains( r ) && ! m_completion->usedEffortMap().contains( r ) ) { return flags | Qt::ItemIsEditable; } } return flags; } return flags | Qt::ItemIsEditable; } QVariant UsedEffortItemModel::data ( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: { if ( index.column() == 0 ) { const Resource *r = resource( index ); //debugPlan<name(); } break; } Completion::UsedEffort *ue = usedEffort( index ); if ( ue == 0 ) { return QVariant(); } if ( index.column() == 8 ) { // Total //debugPlan<effort( d ); res += e.normalEffort().toDouble( Duration::Unit_h ); } return QLocale().toString(res, 'f', 1 ); } Completion::UsedEffort::ActualEffort e = ue->effort( m_dates.value( index.column() - 1 ) ); double res = e.normalEffort().toDouble( Duration::Unit_h ); return QLocale().toString(res, 'f', 1 ); } case Qt::EditRole: { if ( index.column() == 8 ) { return QVariant(); } if ( index.column() == 0 ) { const Resource *r = resource( index ); //debugPlan<name(); } } else { Completion::UsedEffort *ue = usedEffort( index ); if ( ue == 0 ) { return QVariant(); } Completion::UsedEffort::ActualEffort e = ue->effort( m_dates.value( index.column() - 1 ) ); double res = e.normalEffort().toDouble( Duration::Unit_h ); return QLocale().toString(res, 'f', 1 ); } break; } case Role::EnumList: { if ( index.column() == 0 ) { QStringList lst = m_editlist.keys(); return lst; } break; } case Role::EnumListValue: { if ( index.column() == 0 ) { return m_editlist.values().indexOf( resource( index ) ); // clazy:exclude=container-anti-pattern } break; } default: break; } return QVariant(); } bool UsedEffortItemModel::setData ( const QModelIndex &idx, const QVariant &value, int role ) { debugPlan; switch ( role ) { case Qt::EditRole: { if ( idx.column() == 8 ) { return false; } if ( idx.column() == 0 ) { const Resource *er = resource( idx ); Q_ASSERT( er != 0 ); Q_ASSERT ( m_editlist.count() > value.toInt() ); const Resource *v = m_editlist.values().value( value.toInt() ); // clazy:exclude=container-anti-pattern Q_ASSERT( v != 0 ); int x = m_resourcelist.indexOf( er ); Q_ASSERT( x != -1 ); m_resourcelist.replace( x, v ); m_completion->addUsedEffort( v ); emit dataChanged( createIndex( idx.row(), 1 ), createIndex( idx.row(), columnCount() - 1 ) ); emit rowInserted( createIndex( idx.row(), 0 ) ); return true; } Completion::UsedEffort *ue = usedEffort( idx ); if ( ue == 0 ) { return false; } QDate d = m_dates.value( idx.column() - 1 ); Completion::UsedEffort::ActualEffort e = ue->effort( d ); e.setNormalEffort( Duration( value.toDouble(), Duration::Unit_h ) ); ue->setEffort( d, e ); emit effortChanged(d); emit dataChanged( idx, idx ); return true; } default: break; } return false; } bool UsedEffortItemModel::submit() { debugPlan; return QAbstractItemModel::submit(); } void UsedEffortItemModel::revert() { debugPlan; QList lst = m_resourcelist; foreach ( const Resource *r, lst ) { if ( ! m_completion->usedEffortMap().contains( r ) ) { int row = m_resourcelist.indexOf( r ); if ( row != -1 ) { beginRemoveRows( QModelIndex(), row, row ); m_resourcelist.removeAt( row ); endRemoveRows(); } } } } QVariant UsedEffortItemModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Vertical ) { return data(index(section, 0), role); } if ( section < 0 || section >= m_headers.count() ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_headers.at( section ); case Qt::ToolTipRole: { if ( section >= 1 && section <= 7 ) { return QLocale().toString(m_dates.at( section - 1 ), QLocale::LongFormat); } if ( section == 8 ) { return i18n( "Total effort this week" ); } break; } + case Qt::TextAlignmentRole: + return Qt::AlignLeading; default: break; } return QVariant(); } int UsedEffortItemModel::columnCount(const QModelIndex & parent ) const { int c = 0; if ( m_completion && ! parent.isValid() ) { c = 9; } return c; } int UsedEffortItemModel::rowCount(const QModelIndex & ) const { int rows = 0; if ( m_completion ) { rows = m_resourcelist.count(); } return rows; } QModelIndex UsedEffortItemModel::index ( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column ); } void UsedEffortItemModel::setCompletion( Completion *completion ) { beginResetModel(); m_completion = completion; m_resourcelist.clear(); QMap lst; const Completion::ResourceUsedEffortMap &map = completion->usedEffortMap(); Completion::ResourceUsedEffortMap::const_iterator it; for (it = map.constBegin(); it != map.constEnd(); ++it) { lst.insertMulti(it.key()->name(), it.key()); } m_resourcelist = lst.values(); endResetModel(); } const Resource *UsedEffortItemModel::resource(const QModelIndex &index ) const { int row = index.row(); if ( m_completion == 0 || row < 0 || row >= m_resourcelist.count() ) { return 0; } return m_resourcelist.value( row ); } Completion::UsedEffort *UsedEffortItemModel::usedEffort(const QModelIndex &index ) const { const Resource *r = resource( index ); if ( r == 0 ) { return 0; } return m_completion->usedEffort( r ); } void UsedEffortItemModel::setCurrentMonday( const QDate &date ) { beginResetModel(); m_dates.clear(); for ( int i = 0; i < 7; ++i ) { m_dates << date.addDays( i ); } endResetModel(); emit headerDataChanged ( Qt::Horizontal, 1, 7 ); } QModelIndex UsedEffortItemModel::addRow() { if ( m_project == 0 ) { return QModelIndex(); } m_editlist.clear(); m_editlist = freeResources(); if ( m_editlist.isEmpty() ) { return QModelIndex(); } int row = rowCount(); beginInsertRows( QModelIndex(), row, row ); m_resourcelist.append(m_editlist.first()); endInsertRows(); return createIndex(row, 0, const_cast(m_editlist.first())); } void UsedEffortItemModel::addResource(const QString &name) { const Resource *resource = freeResources().value(name); if (!resource) { return; } m_editlist.clear(); m_editlist.insert(name, resource); int row = rowCount(); beginInsertRows(QModelIndex(), row, row); m_resourcelist.append(resource); endInsertRows(); setData(createIndex(row, 0, const_cast(resource)), 0, Qt::EditRole); } QMap UsedEffortItemModel::freeResources() const { QMap map; foreach ( Resource *r, m_project->resourceList() ) { if ( ! m_resourcelist.contains( r ) ) { map.insertMulti( r->name(), r ); } } return map; } //----------- UsedEffortEditor::UsedEffortEditor( QWidget *parent ) : QTableView( parent ) { UsedEffortItemModel *m = new UsedEffortItemModel(this ); setModel( m ); setItemDelegateForColumn ( 1, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 2, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 3, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 4, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 5, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 6, new DoubleSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 7, new DoubleSpinBoxDelegate( this ) ); connect ( model(), &QAbstractItemModel::dataChanged, this, &UsedEffortEditor::changed ); connect ( m, &UsedEffortItemModel::rowInserted, this, &UsedEffortEditor::resourceAdded ); connect (m, &UsedEffortItemModel::rowInserted, this, &UsedEffortEditor::scrollToBottom); } bool UsedEffortEditor::hasFreeResources() const { return ! model()->freeResources().isEmpty(); } void UsedEffortEditor::setProject( Project *p ) { model()->setProject( p ); } void UsedEffortEditor::setCompletion( Completion *completion ) { model()->setCompletion(completion); setColumnHidden(0, true); } void UsedEffortEditor::setCurrentMonday( const QDate &date ) { static_cast( model() )->setCurrentMonday( date ); } void UsedEffortEditor::addResource() { UsedEffortItemModel *m = model(); QModelIndex i = m->addRow(); if ( i.isValid() ) { setCurrentIndex( i ); edit( i ); } } //---------------------------------------- CompletionEntryItemModel::CompletionEntryItemModel ( QObject *parent ) : QAbstractItemModel( parent ), m_node( 0 ), m_project( 0 ), m_manager( 0 ), m_completion( 0 ) { m_headers << i18n( "Date" ) // xgettext: no-c-format << i18n( "% Completed" ) << i18n( "Used Effort" ) << i18n( "Remaining Effort" ) << i18n( "Planned Effort" ); m_flags.insert( Property_Date, Qt::NoItemFlags ); m_flags.insert( Property_Completion, Qt::ItemIsEditable ); m_flags.insert( Property_UsedEffort, Qt::NoItemFlags ); m_flags.insert( Property_RemainingEffort, Qt::ItemIsEditable ); m_flags.insert( Property_PlannedEffort, Qt::NoItemFlags ); } void CompletionEntryItemModel::setTask( Task *t ) { m_node = t; m_project = 0; if ( m_node && m_node->projectNode() ) { m_project = static_cast( m_node->projectNode() ); } } void CompletionEntryItemModel::slotDataChanged() { refresh(); } void CompletionEntryItemModel::setManager( ScheduleManager *sm ) { m_manager = sm; refresh(); } Qt::ItemFlags CompletionEntryItemModel::flags ( const QModelIndex &index ) const { if ( index.isValid() && index.column() < m_flags.count() ) { return QAbstractItemModel::flags( index ) | m_flags[ index.column() ]; } return QAbstractItemModel::flags( index ); } QVariant CompletionEntryItemModel::date ( int row, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return m_datelist.value( row ); default: break; } return QVariant(); } QVariant CompletionEntryItemModel::percentFinished ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return e->percentFinished; default: break; } return QVariant(); } QVariant CompletionEntryItemModel::remainingEffort ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: { return e->remainingEffort.format(); } case Qt::EditRole: return e->remainingEffort.toDouble( Duration::Unit_h ); case Role::DurationScales: { QVariantList lst; // TODO: week if ( m_node && m_project ) { if ( m_node->estimate()->type() == Estimate::Type_Effort ) { lst.append( m_project->standardWorktime()->day() ); } } if ( lst.isEmpty() ) { lst.append( 24.0 ); } lst << 60.0 << 60.0 << 1000.0; return lst; } case Role::DurationUnit: return static_cast( Duration::Unit_h ); 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 CompletionEntryItemModel::actualEffort ( int row, int role ) const { Completion::Entry *e = m_completion->entry( date( row ).toDate() ); if ( e == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: { Duration v; if ( m_completion->entrymode() == Completion::EnterEffortPerResource ) { v = m_completion->actualEffortTo( date( row ).toDate() ); } else { v = e->totalPerformed; } //debugPlan<name()<<": "<totalPerformed.toDouble( Duration::Unit_h ); case Role::DurationScales: { QVariantList lst; // TODO: week if ( m_node && m_project ) { if ( m_node->estimate()->type() == Estimate::Type_Effort ) { lst.append( m_project->standardWorktime()->day() ); } } if ( lst.isEmpty() ) { lst.append( 24 ); } lst << 60 << 60 << 1000; return lst; } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::ToolTipRole: return xi18nc("@info:tooltip", "Accumulated effort %1", actualEffort(row, Qt::DisplayRole).toString()); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant CompletionEntryItemModel::plannedEffort ( int /*row*/, int role ) const { if ( m_node == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: { Duration v = m_node->plannedEffort( id(), ECCT_EffortWork ); //debugPlan<name()<<": "<estimate()->type() == Estimate::Type_Effort ) { lst.append( m_project->standardWorktime()->day() ); } } if ( lst.isEmpty() ) { lst.append( 24.0 ); } lst << 60.0 << 60.0 << 1000.0; return lst; } case Role::DurationUnit: return static_cast( Duration::Unit_h ); 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 CompletionEntryItemModel::data ( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } switch ( index.column() ) { case Property_Date: return date( index.row(), role ); case Property_Completion: return percentFinished( index.row(), role ); case Property_UsedEffort: return actualEffort( index.row(), role ); case Property_RemainingEffort: return remainingEffort( index.row(), role ); case Property_PlannedEffort: return plannedEffort( index.row(), role ); default: break; } return QVariant(); } QList CompletionEntryItemModel::scales() const { QList lst; if ( m_node && m_project ) { if ( m_node->estimate()->type() == Estimate::Type_Effort ) { lst = m_project->standardWorktime()->scales(); } } if ( lst.isEmpty() ) { lst = Estimate::defaultScales(); } //debugPlan<entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } e->percentFinished = value.toInt(); if ( m_completion->entrymode() != Completion::EnterEffortPerResource && m_node ) { // calculate used/remaining Duration est = m_node->plannedEffort( id(), ECCT_EffortWork ); e->totalPerformed = est * e->percentFinished / 100; e->remainingEffort = est - e->totalPerformed; } emit dataChanged( idx, createIndex( idx.row(), 3 ) ); return true; } if ( idx.column() == Property_UsedEffort ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } double v( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration d = Estimate::scale( v, unit, scales() ); if ( d == e->totalPerformed ) { return false; } e->totalPerformed = d; emit dataChanged( idx, idx ); return true; } if ( idx.column() == Property_RemainingEffort ) { Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); if ( e == 0 ) { return false; } double v( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration d = Estimate::scale( v, unit, scales() ); if ( d == e->remainingEffort ) { return false; } e->remainingEffort = d; emit dataChanged( idx, idx ); return true; } } default: break; } return false; } bool CompletionEntryItemModel::submit() { debugPlan<= m_headers.count() ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_headers.at( section ); case Qt::ToolTipRole: switch (section) { case Property_UsedEffort: if ( m_completion->entrymode() == Completion::EnterEffortPerResource ) { return xi18nc("@info", "Accumulated used effort.Used effort must be entered per resource"); } return xi18nc("@info:tooltip", "Accumulated used effort"); case Property_RemainingEffort: return xi18nc("@info:tooltip", "Remaining effort to complete the task"); case Property_PlannedEffort: return xi18nc("@info:tooltip", "Planned effort accumulated until date"); } break; default: break; } return QVariant(); } int CompletionEntryItemModel::columnCount(const QModelIndex & /*parent */) const { return 5; } int CompletionEntryItemModel::rowCount(const QModelIndex &idx ) const { if ( m_completion == 0 || idx.isValid() ) { return 0; } return m_datelist.count(); } QModelIndex CompletionEntryItemModel::index ( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column ); } void CompletionEntryItemModel::setCompletion( Completion *completion ) { m_completion = completion; refresh(); } void CompletionEntryItemModel::refresh() { beginResetModel(); m_datelist.clear(); m_flags[ Property_UsedEffort ] = Qt::NoItemFlags; if ( m_completion ) { m_datelist = m_completion->entries().keys(); if ( m_completion->entrymode() != Completion::EnterEffortPerResource ) { m_flags[ Property_UsedEffort ] = Qt::ItemIsEditable; } } debugPlan< 0 && d <= m_datelist.last() ) { d = m_datelist.last().addDays( 1 ); } beginInsertRows( QModelIndex(), row, row ); m_datelist.append( d ); endInsertRows(); return createIndex( row, 0 ); } void CompletionEntryItemModel::removeEntry( const QDate& date ) { removeRow( m_datelist.indexOf( date ) ); } void CompletionEntryItemModel::removeRow( int row ) { debugPlan<= rowCount() ) { return; } QDate date = m_datelist.value( row ); beginRemoveRows( QModelIndex(), row, row ); m_datelist.removeAt( row ); endRemoveRows(); debugPlan<takeEntry( date ); emit rowRemoved( date ); emit changed(); } void CompletionEntryItemModel::addEntry( const QDate& date ) { debugPlan<entries().isEmpty() ) { if ( m_node ) { e->remainingEffort = m_node->plannedEffort( id(), ECCT_EffortWork ); } } else { e->percentFinished = m_completion->percentFinished(); e->totalPerformed = m_completion->actualEffort(); e->remainingEffort = m_completion->remainingEffort(); } m_completion->addEntry( date, e ); refresh(); int i = m_datelist.indexOf( date ); if ( i != -1 ) { emit rowInserted( date ); emit dataChanged( createIndex( i, 1 ), createIndex( i, rowCount() - 1 ) ); } else errorPlan<<"Failed to find added entry: "<date(i).toDate() == date) { const QModelIndex idx1 = index(i, Property_UsedEffort); const QModelIndex idx2 = index(rowCount()-1, Property_UsedEffort); emit dataChanged(idx1, idx2); return; } } addEntry(date); } //----------- CompletionEntryEditor::CompletionEntryEditor( QWidget *parent ) : QTableView( parent ) { verticalHeader()->hide(); CompletionEntryItemModel *m = new CompletionEntryItemModel(this ); setItemDelegateForColumn ( 1, new ProgressBarDelegate( this ) ); setItemDelegateForColumn ( 2, new DurationSpinBoxDelegate( this ) ); setItemDelegateForColumn ( 3, new DurationSpinBoxDelegate( this ) ); setCompletionModel( m ); resizeColumnToContents(1); resizeColumnToContents(2); resizeColumnToContents(3); resizeColumnToContents(4); } void CompletionEntryEditor::setCompletionModel( CompletionEntryItemModel *m ) { if ( model() ) { disconnect(model(), &CompletionEntryItemModel::rowInserted, this, &CompletionEntryEditor::rowInserted); disconnect(model(), &CompletionEntryItemModel::rowRemoved, this, &CompletionEntryEditor::rowRemoved); disconnect(model(), &QAbstractItemModel::dataChanged, this, &CompletionEntryEditor::changed); disconnect(model(), &CompletionEntryItemModel::changed, this, &CompletionEntryEditor::changed); disconnect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &CompletionEntryEditor::selectedItemsChanged); } setModel( m ); if ( model() ) { connect(model(), &CompletionEntryItemModel::rowInserted, this, &CompletionEntryEditor::rowInserted); connect(model(), &CompletionEntryItemModel::rowRemoved, this, &CompletionEntryEditor::rowRemoved); connect(model(), &QAbstractItemModel::dataChanged, this, &CompletionEntryEditor::changed); connect(model(), &CompletionEntryItemModel::changed, this, &CompletionEntryEditor::changed); connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &CompletionEntryEditor::selectedItemsChanged); } } void CompletionEntryEditor::setCompletion( Completion *completion ) { model()->setCompletion( completion ); } void CompletionEntryEditor::insertEntry(const QDate &date) { model()->addRow(date); } void CompletionEntryEditor::addEntry() { debugPlan<addRow(); if ( i.isValid() ) { model()->setFlags( i.column(), Qt::ItemIsEditable ); setCurrentIndex( i ); emit selectedItemsChanged(QItemSelection(), QItemSelection()); //hmmm, control removeEntryBtn scrollTo( i ); edit( i ); model()->setFlags( i.column(), Qt::NoItemFlags ); } } void CompletionEntryEditor::removeEntry() { //debugPlan; QModelIndexList lst = selectedIndexes(); debugPlan< rows; while ( ! lst.isEmpty() ) { QModelIndex idx = lst.takeFirst(); rows[ idx.row() ] = 0; } QList r = rows.uniqueKeys(); debugPlan<= 0; --i ) { model()->removeRow( r.at( i ) ); } } } //KPlato namespace diff --git a/src/libs/ui/kptusedefforteditor.h b/src/libs/ui/kptusedefforteditor.h index 12c7f031..317dd08f 100644 --- a/src/libs/ui/kptusedefforteditor.h +++ b/src/libs/ui/kptusedefforteditor.h @@ -1,211 +1,217 @@ /* This file is part of the KDE project Copyright (C) 2007, 2011 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. */ #ifndef KPTUSEDEFFORTEDITOR_H #define KPTUSEDEFFORTEDITOR_H #include "planui_export.h" #include #include #include "kpttask.h" class QDate; namespace KPlato { class Completion; class Resource; class Project; class PLANUI_EXPORT UsedEffortItemModel : public QAbstractItemModel { Q_OBJECT public: explicit UsedEffortItemModel(QWidget *parent = 0); void setProject( Project *project ) { m_project = project; } virtual Qt::ItemFlags flags( const QModelIndex & index ) const; virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex &index, const QVariant & value, int role = Qt::EditRole ); virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; virtual QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; void setCompletion( Completion *completion ); const Resource *resource(const QModelIndex &index ) const; Completion::UsedEffort *usedEffort(const QModelIndex &index ) const; void setCurrentMonday( const QDate &date ); QModelIndex addRow(); QMap freeResources() const; void setReadOnly( bool ro ) { m_readonly = ro; } bool readOnly() const { return m_readonly; } Q_SIGNALS: void rowInserted( const QModelIndex& ); void changed(); void effortChanged(const QDate &date); public Q_SLOTS: bool submit(); void revert(); void addResource(const QString &name); private: Project *m_project; Completion *m_completion; QList m_dates; QStringList m_headers; QList m_resourcelist; QMap m_editlist; bool m_readonly; }; class PLANUI_EXPORT UsedEffortEditor : public QTableView { Q_OBJECT public: explicit UsedEffortEditor(QWidget *parent); void setProject( Project *project ); void setCompletion( Completion *completion ); void setCurrentMonday( const QDate &date ); void addResource(); bool hasFreeResources() const; UsedEffortItemModel *model() const { return static_cast( QTableView::model() ); } + QSize sizeHint() const { return m_size.isValid() ? m_size : QTableView::sizeHint(); } + void setSizeHint(const QSize &size) { m_size = size; } + Q_SIGNALS: void changed(); void resourceAdded(); + +private: + QSize m_size; }; //-------------------------------------------- class PLANUI_EXPORT CompletionEntryItemModel : public QAbstractItemModel { Q_OBJECT public: enum Properties { Property_Date, /// Date of entry Property_Completion, /// % Completed Property_UsedEffort, /// Used Effort Property_RemainingEffort, /// Remaining Effort Property_PlannedEffort /// Planned Effort }; explicit CompletionEntryItemModel(QObject *parent = 0); void setTask( Task *t ); virtual Qt::ItemFlags flags( const QModelIndex & index ) const; virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex &index, const QVariant & value, int role = Qt::EditRole ); virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; virtual QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; void setCompletion( Completion *completion ); const Resource *resource(const QModelIndex &index ) const; Completion::UsedEffort *usedEffort(const QModelIndex &index ) const; void setCurrentMonday( const QDate &date ); QModelIndex addRow(); void removeRow( int row ); /// These flags are in addition to flags return from QAbstractItemModel::flags() void setFlags( int col, Qt::ItemFlags flags ) { m_flags[ col ] = flags; } long id() const { return m_manager == 0 ? -1 : m_manager->scheduleId(); } void addRow(const QDate &date); Q_SIGNALS: void rowInserted( const QDate& ); void rowRemoved( const QDate& ); void changed(); public Q_SLOTS: bool submit(); void revert(); void slotDataChanged(); void setManager(KPlato::ScheduleManager *sm); protected: QVariant date ( int row, int role = Qt::DisplayRole ) const; QVariant percentFinished ( int row, int role ) const; QVariant remainingEffort ( int row, int role ) const; virtual QVariant actualEffort ( int row, int role ) const; QVariant plannedEffort ( int row, int role ) const; void removeEntry( const QDate& date ); void addEntry( const QDate& date ); void refresh(); QList scales() const; protected: Task *m_node; Project *m_project; ScheduleManager *m_manager; Completion *m_completion; QList m_dates; QStringList m_headers; QList m_datelist; QList m_flags; }; class PLANUI_EXPORT CompletionEntryEditor : public QTableView { Q_OBJECT public: explicit CompletionEntryEditor(QWidget *parent); void setCompletion( Completion *completion ); CompletionEntryItemModel *model() const { return static_cast( QTableView::model() ); } void setCompletionModel( CompletionEntryItemModel *m ); void insertEntry(const QDate &date); Q_SIGNALS: void changed(); void rowInserted( const QDate& ); void rowRemoved( const QDate& ); void selectedItemsChanged(const QItemSelection&, const QItemSelection&); public Q_SLOTS: void addEntry(); void removeEntry(); private: }; } //KPlato namespace #endif // KPTUSEDEFFORTEDITOR_H diff --git a/src/libs/ui/kptviewbase.cpp b/src/libs/ui/kptviewbase.cpp index eb1144bc..44e834d8 100644 --- a/src/libs/ui/kptviewbase.cpp +++ b/src/libs/ui/kptviewbase.cpp @@ -1,2737 +1,2736 @@ /* This file is part of the KDE project Copyright (C) 2006 -2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptviewbase.h" #include "kptitemmodelbase.h" #include "kptproject.h" #include "kptdebug.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 #include #include #include #include #include namespace KPlato { DockWidget::DockWidget( ViewBase *v, const QString &identity, const QString &title ) : QDockWidget( v ), view( v ), id( identity ), location( Qt::RightDockWidgetArea ), editor( false ), m_shown( true ) { setWindowTitle( title ); setObjectName( v->objectName() + '-' + identity ); toggleViewAction()->setObjectName( objectName() ); connect(this, &QDockWidget::dockLocationChanged, this, &DockWidget::setLocation); } void DockWidget::activate( KoMainWindow *mainWindow ) { connect(this, &QDockWidget::visibilityChanged, this, &DockWidget::setShown); setVisible( m_shown ); mainWindow->addDockWidget( location, this ); foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { a->addAction( toggleViewAction() ); break; } } } void DockWidget::deactivate( KoMainWindow *mainWindow ) { disconnect(this, &QDockWidget::visibilityChanged, this, &DockWidget::setShown); mainWindow->removeDockWidget( this ); // activation re-parents to QMainWindow, so re-parent back to view setParent( const_cast( view ) ); foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { a->removeAction( toggleViewAction() ); break; } } } void DockWidget::setShown( bool show ) { m_shown = show; setVisible( show ); } bool KPlato::DockWidget::shown() const { return m_shown; } void DockWidget::setLocation( Qt::DockWidgetArea area ) { location = area; } bool DockWidget::saveXml( QDomElement &context ) const { QDomElement e = context.ownerDocument().createElement( "docker" ); context.appendChild( e ); e.setAttribute( "id", id ); e.setAttribute( "location", QString::number(location) ); e.setAttribute( "floating", QString::number(isFloating()) ); e.setAttribute( "visible", QString::number(m_shown) ); return true; } void DockWidget::loadXml(const KoXmlElement& context) { location = static_cast( context.attribute( "location", "0" ).toInt() ); setFloating( (bool) context.attribute( "floating", "0" ).toInt() ); m_shown = context.attribute( "visible", "1" ).toInt(); } //------------------------ bool PrintingOptions::loadXml( KoXmlElement &element ) { KoXmlElement e; forEachElement( e, element ) { if ( e.tagName() == "header" ) { headerOptions.group = e.attribute( "group", "0" ).toInt(); headerOptions.project = static_cast( e.attribute( "project", "0" ).toInt() ); headerOptions.date = static_cast( e.attribute( "date", "0" ).toInt() ); headerOptions.manager = static_cast( e.attribute( "manager", "0" ).toInt() ); headerOptions.page = static_cast( e.attribute( "page", "0" ).toInt() ); } else if ( e.tagName() == "footer" ) { footerOptions.group = e.attribute( "group", "0" ).toInt(); footerOptions.project = static_cast( e.attribute( "project", "0" ).toInt() ); footerOptions.date = static_cast( e.attribute( "date", "0" ).toInt() ); footerOptions.manager = static_cast( e.attribute( "manager", "0" ).toInt() ); footerOptions.page = static_cast( e.attribute( "page", "0" ).toInt() ); } } return true; } void PrintingOptions::saveXml( QDomElement &element ) const { QDomElement me = element.ownerDocument().createElement( "printing-options" ); element.appendChild( me ); QDomElement h = me.ownerDocument().createElement( "header" ); me.appendChild( h ); h.setAttribute( "group", QString::number(headerOptions.group) ); h.setAttribute( "project", QString::number(headerOptions.project) ); h.setAttribute( "date", QString::number(headerOptions.date) ); h.setAttribute( "manager", QString::number(headerOptions.manager) ); h.setAttribute( "page", QString::number(headerOptions.page) ); QDomElement f = me.ownerDocument().createElement( "footer" ); me.appendChild( f ); f.setAttribute( "group", QString::number(footerOptions.group) ); f.setAttribute( "project", QString::number(footerOptions.project) ); f.setAttribute( "date", QString::number(footerOptions.date) ); f.setAttribute( "manager", QString::number(footerOptions.manager) ); f.setAttribute( "page", QString::number(footerOptions.page) ); } //---------------------- PrintingHeaderFooter::PrintingHeaderFooter( const PrintingOptions &opt, QWidget *parent ) : QWidget( parent ) { setupUi( this ); setWindowTitle( i18n("Header and Footer" )); setOptions( opt ); connect(ui_header, &QGroupBox::toggled, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerProject, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerPage, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerManager, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerDate, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footer, &QGroupBox::toggled, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerProject, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerPage, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerManager, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerDate, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); } PrintingHeaderFooter::~PrintingHeaderFooter() { //debugPlan; } void PrintingHeaderFooter::slotChanged() { debugPlan; emit changed( options() ); } void PrintingHeaderFooter::setOptions( const PrintingOptions &options ) { m_options = options; ui_header->setChecked( m_options.headerOptions.group ); ui_headerProject->setCheckState( m_options.headerOptions.project ); ui_headerDate->setCheckState( m_options.headerOptions.date ); ui_headerManager->setCheckState( m_options.headerOptions.manager ); ui_headerPage->setCheckState( m_options.headerOptions.page ); ui_footer->setChecked( m_options.footerOptions.group ); ui_footerProject->setCheckState( m_options.footerOptions.project ); ui_footerDate->setCheckState( m_options.footerOptions.date ); ui_footerManager->setCheckState( m_options.footerOptions.manager ); ui_footerPage->setCheckState( m_options.footerOptions.page ); } PrintingOptions PrintingHeaderFooter::options() const { //debugPlan; PrintingOptions opt; opt.headerOptions.group = ui_header->isChecked(); opt.headerOptions.project = ui_headerProject->checkState(); opt.headerOptions.date = ui_headerDate->checkState(); opt.headerOptions.manager = ui_headerManager->checkState(); opt.headerOptions.page = ui_headerPage->checkState(); opt.footerOptions.group = ui_footer->isChecked(); opt.footerOptions.project = ui_footerProject->checkState(); opt.footerOptions.date = ui_footerDate->checkState( ); opt.footerOptions.manager = ui_footerManager->checkState(); opt.footerOptions.page = ui_footerPage->checkState(); return opt; } PrintingDialog::PrintingDialog( ViewBase *view ) : KoPrintingDialog( view ), m_view( view ), m_widget( 0 ) { setPrinterPageLayout( view->pageLayout() ); QImage px( 100, 600, QImage::Format_Mono ); int dpm = printer().resolution() * 40; px.setDotsPerMeterX( dpm ); px.setDotsPerMeterY( dpm ); QPainter p( &px ); m_textheight = p.boundingRect( QRectF(), Qt::AlignTop, "Aj" ).height(); debugPlan<<"textheight:"<printingOptions(); } void PrintingDialog::setPrintingOptions( const PrintingOptions &opt ) { debugPlan; m_view->setPrintingOptions( opt ); emit changed( opt ); emit changed(); } void PrintingDialog::setPrinterPageLayout( const KoPageLayout &pagelayout ) { QPrinter &p = printer(); QPrinter::Orientation o; switch ( pagelayout.orientation ) { case KoPageFormat::Portrait: o = QPrinter::Portrait; break; case KoPageFormat::Landscape: o = QPrinter::Landscape; break; default: o = QPrinter::Portrait; break; } p.setOrientation( o ); p.setPaperSize( KoPageFormat::printerPageSize( pagelayout.format ) ); p.setPageMargins( pagelayout.leftMargin, pagelayout.topMargin, pagelayout.rightMargin, pagelayout.bottomMargin, QPrinter::Point ); } void PrintingDialog::startPrinting(RemovePolicy removePolicy ) { setPrinterPageLayout( m_view->pageLayout() ); // FIXME: Something resets printer().paperSize() to A4 ! KoPrintingDialog::startPrinting( removePolicy ); } QWidget *PrintingDialog::createPageLayoutWidget() const { QWidget *w = ViewBase::createPageLayoutWidget( m_view ); KoPageLayoutWidget *pw = w->findChild(); connect(pw, SIGNAL(layoutChanged(KoPageLayout)), m_view, SLOT(setPageLayout(KoPageLayout))); connect(pw, &KoPageLayoutWidget::layoutChanged, this, &PrintingDialog::setPrinterPageLayout); connect(pw, SIGNAL(layoutChanged(KoPageLayout)), this, SIGNAL(changed())); return w; } QList PrintingDialog::createOptionWidgets() const { //debugPlan; PrintingHeaderFooter *w = new PrintingHeaderFooter( printingOptions() ); connect(w, &PrintingHeaderFooter::changed, this, &PrintingDialog::setPrintingOptions); const_cast( this )->m_widget = w; return QList() << w; } /*QList PrintingDialog::shapesOnPage(int) { return QList(); }*/ void PrintingDialog::drawRect( QPainter &p, const QRect &r, Qt::Edges edges ) { p.save(); QPen pen = p.pen(); pen.setColor(Qt::gray); p.setPen(pen); if (edges & Qt::LeftEdge) { p.drawLine( r.topLeft(), r.bottomLeft() ); } if (edges & Qt::BottomEdge) { p.drawLine( r.bottomLeft(), r.bottomRight() ); } if (edges & Qt::TopEdge) { p.drawLine( r.topRight(), r.bottomRight() ); } if (edges & Qt::RightEdge) { p.drawLine( r.topRight(), r.bottomRight() ); } p.restore(); } QRect PrintingDialog::headerRect() const { PrintingOptions options = m_view->printingOptions(); if ( options.headerOptions.group == false ) { return QRect(); } int height = headerFooterHeight( options.headerOptions ); return QRect( 0, 0, const_cast( this )->printer().pageRect().width(), height ); } QRect PrintingDialog::footerRect() const { PrintingOptions options = m_view->printingOptions(); if ( options.footerOptions.group == false ) { return QRect(); } int height = headerFooterHeight( options.footerOptions ); QRect r = const_cast( this )->printer().pageRect(); return QRect( 0, r.height() - height, r.width(), height ); } int PrintingDialog::headerFooterHeight( const PrintingOptions::Data &options ) const { int height = 0.0; if ( options.page == Qt::Checked || options.project == Qt::Checked || options.manager == Qt::Checked || options.date == Qt::Checked ) { height += m_textheight * 1.5; } if ( options.project == Qt::Checked && options.manager == Qt::Checked && ( options.date == Qt::Checked || options.page == Qt::Checked ) ) { height *= 2.0; } debugPlan< pageRect.left()) { pageRect.setLeft(r.left()); } p.restore(); if ( options.project == Qt::Checked || options.manager == Qt::Checked || options.date == Qt::Checked ) { p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } } if ( options.date == Qt::Checked ) { p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Date:") ); p.restore(); if ( ( options.project == Qt::Checked && options.manager != Qt::Checked ) || ( options.project != Qt::Checked && options.manager == Qt::Checked ) ) { dateRect = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, date ); dateRect.setHeight( rect_1.height() ); rect_1.setRight( dateRect.left() - 2 ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } else if ( options.project == Qt::Checked && options.manager == Qt::Checked ) { dateRect = p.boundingRect( rect_2, Qt::AlignRight|Qt::AlignTop, date ); dateRect.setHeight( rect_2.height() ); rect_2.setRight( dateRect.left() - 2 ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } p.drawLine( rect_2.topRight(), rect_2.bottomRight() ); } else { dateRect = p.boundingRect( rect_2, Qt::AlignLeft|Qt::AlignTop, date ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } dateRect.setHeight( rect_2.height() ); rect_2.setRight( dateRect.left() - 2 ); if ( rect_2.left() != rect.left() ) { p.drawLine( rect_2.topRight(), rect_2.bottomRight() ); } } } if ( options.manager == Qt::Checked ) { p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Manager:") ); p.restore(); if ( options.project != Qt::Checked ) { managerRect = p.boundingRect( rect_1, Qt::AlignLeft|Qt::AlignTop, manager ); managerRect.setHeight( rect_1.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } } else if ( options.date != Qt::Checked && options.page != Qt::Checked ) { managerRect = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, manager ); managerRect.setHeight( rect_1.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } rect_1.setRight( managerRect.left() - 2 ); p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } else { managerRect = p.boundingRect( rect_2, Qt::AlignLeft|Qt::AlignTop, manager ); managerRect.setHeight( rect_2.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } } } if ( options.project == Qt::Checked ) { projectRect = p.boundingRect( rect_1, Qt::AlignLeft|Qt::AlignTop, projectName ); projectRect.setHeight( rect_1.height() ); p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Project:") ); if (rct.right() > projectRect.right()) { projectRect.setRight(rct.right()); } p.restore(); } if ( options.page == Qt::Checked ) { p.drawText( pageRect, Qt::AlignHCenter|Qt::AlignBottom, page ); } if ( options.project == Qt::Checked ) { p.drawText( projectRect.adjusted(3, 0, 3, 0), Qt::AlignLeft|Qt::AlignBottom, projectName ); } if ( options.date == Qt::Checked ) { p.drawText( dateRect, Qt::AlignHCenter|Qt::AlignBottom, date ); } if ( options.manager == Qt::Checked ) { p.drawText( managerRect.adjusted(3, 0, 3, 0), Qt::AlignLeft|Qt::AlignBottom, manager ); } QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); if ( options.page == Qt::Checked ) { p.drawText( pageRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Page:" ) ); } if ( options.project == Qt::Checked ) { p.drawText( projectRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Project:" ) ); } if ( options.date == Qt::Checked ) { p.drawText( dateRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Date:" ) ); } if ( options.manager == Qt::Checked ) { p.drawText( managerRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Manager:" ) ); } p.restore(); } //-------------- ViewBase::ViewBase(KoPart *part, KoDocument *doc, QWidget *parent) : KoView(part, doc, parent), m_readWrite( false ), m_proj( 0 ), m_schedulemanager( 0 ), m_singleTreeView(nullptr), m_doubleTreeView(nullptr) { } ViewBase::~ViewBase() { if ( koDocument() ) { //HACK to avoid ~View to access koDocument() setDocumentDeleted(); } } void ViewBase::setProject( Project *project ) { m_proj = project; emit projectChanged( project ); } KoDocument *ViewBase::part() const { return koDocument(); } KoPageLayout ViewBase::pageLayout() const { return m_pagelayout; } void ViewBase::setPageLayout( const KoPageLayout &layout ) { m_pagelayout = layout; } bool ViewBase::isActive() const { if ( hasFocus() ) { return true; } foreach ( QWidget *v, findChildren() ) { if ( v->hasFocus() ) { return true; } } return false; } void ViewBase::updateReadWrite( bool readwrite ) { m_readWrite = readwrite; emit readWriteChanged( readwrite ); } void ViewBase::setGuiActive( bool active ) // virtual slot { //debugPlan<setWindowTitle( xi18nc( "@title:tab", "Page Layout" ) ); QHBoxLayout *lay = new QHBoxLayout(widget); KoPageLayoutWidget *w = new KoPageLayoutWidget( widget, view->pageLayout() ); w->showPageSpread( false ); lay->addWidget( w, 1 ); KoPagePreviewWidget *prev = new KoPagePreviewWidget( widget ); prev->setPageLayout( view->pageLayout() ); lay->addWidget( prev, 1 ); connect (w, &KoPageLayoutWidget::layoutChanged, prev, &KoPagePreviewWidget::setPageLayout); return widget; } /*static*/ PrintingHeaderFooter *ViewBase::createHeaderFooterWidget( ViewBase *view ) { PrintingHeaderFooter *widget = new PrintingHeaderFooter( view->printingOptions() ); widget->setWindowTitle( xi18nc( "@title:tab", "Header and Footer" ) ); widget->setOptions( view->printingOptions() ); return widget; } void ViewBase::slotHeaderContextMenuRequested( const QPoint &pos ) { debugPlan; QList lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } void ViewBase::createOptionActions(int actions) { QAction *action; action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionExpand) { action = new QAction(koIcon("arrow-down"), i18n("Expand All"), this); connect(action, &QAction::triggered, this, &ViewBase::expandAll); addContextAction(action); } if (actions & OptionExpand) { action = new QAction(koIcon("arrow-up"), i18n("Collapse All"), this); connect(action, &QAction::triggered, this, &ViewBase::collapseAll); addContextAction(action); } action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionPrint) { action = KStandardAction::create(KStandardAction::Print, mainWindow(), SLOT(slotFilePrint()), this); action->setObjectName("print"); addContextAction(action); } if (actions & OptionPrintPreview) { action = KStandardAction::create(KStandardAction::PrintPreview, mainWindow(), SLOT(slotFilePrintPreview()), this); action->setObjectName("print preview"); addContextAction(action); } if (actions & OptionPrintPdf) { action = new QAction(koIcon("application-pdf"), i18n("Print to PDF..."), this); action->setObjectName("print pdf"); connect(action, SIGNAL(triggered()), mainWindow(), SLOT(exportToPdf())); addContextAction(action); } if (actions & OptionPrintConfig) { action = new QAction(koIcon("configure"), i18n("Print Options..."), this); action->setObjectName("print options"); connect(action, &QAction::triggered, this, &ViewBase::slotOptions); addContextAction(action); } action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionViewConfig) { action = new QAction(koIcon("configure"), i18n("Configure View..."), this); action->setObjectName("configure view"); connect(action, &QAction::triggered, this, &ViewBase::slotOptions); addContextAction(action); } } void ViewBase::slotOptionsFinished( int result ) { if ( result == QDialog::Accepted ) { emit optionsModified(); } if ( sender() ) { sender()->deleteLater(); } } bool ViewBase::loadContext( const KoXmlElement &context ) { KoXmlElement me; forEachElement( me, context ) { if ( me.tagName() == "page-layout" ) { m_pagelayout.format = KoPageFormat::formatFromString( me.attribute( "format" ) ); m_pagelayout.orientation = me.attribute( "orientation" ) == "landscape" ? KoPageFormat::Landscape : KoPageFormat::Portrait; m_pagelayout.width = me.attribute( "width", "0.0" ).toDouble(); m_pagelayout.height = me.attribute( "height", "0.0" ).toDouble(); m_pagelayout.leftMargin = me.attribute( "left-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.rightMargin = me.attribute( "right-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.topMargin = me.attribute( "top-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.bottomMargin = me.attribute( "bottom-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); } else if ( me.tagName() == "printing-options" ) { m_printingOptions.loadXml( me ); } else if ( me.tagName() == "dockers" ) { KoXmlElement e; forEachElement ( e, me ) { DockWidget *ds = findDocker( e.attribute( "id" ) ); if ( ds ) { ds->loadXml( e ); } } } } return true; } void ViewBase::saveContext( QDomElement &context ) const { QDomElement me = context.ownerDocument().createElement( "page-layout" ); context.appendChild( me ); me.setAttribute( "format", KoPageFormat::formatString( m_pagelayout.format ) ); me.setAttribute( "orientation", m_pagelayout.orientation == KoPageFormat::Portrait ? "portrait" : "landscape" ); me.setAttribute( "width", QString::number(m_pagelayout.width) ); me.setAttribute( "height",QString::number(m_pagelayout. height) ); me.setAttribute( "left-margin", QString::number(m_pagelayout.leftMargin) ); me.setAttribute( "right-margin", QString::number(m_pagelayout.rightMargin) ); me.setAttribute( "top-margin", QString::number(m_pagelayout.topMargin) ); me.setAttribute( "bottom-margin",QString::number( m_pagelayout.bottomMargin) ); m_printingOptions.saveXml( context ); if ( ! m_dockers.isEmpty() ) { QDomElement e = context.ownerDocument().createElement( "dockers" ); context.appendChild( e ); foreach ( const DockWidget *ds, m_dockers ) { ds->saveXml( e ); } } } void ViewBase::addDocker( DockWidget *ds ) { //addAction( "view_docker_list", ds->toggleViewAction() ); m_dockers << ds; } QList ViewBase::dockers() const { return m_dockers; } DockWidget* ViewBase::findDocker( const QString &id ) const { foreach ( DockWidget *ds, m_dockers ) { if ( ds->id == id ) { return ds; } } return 0; } void ViewBase::setViewSplitMode(bool split) { if (m_doubleTreeView) { m_doubleTreeView->setViewSplitMode(split); } } void ViewBase::showColumns(const QList &left, const QList &right) { TreeViewBase *view1 = m_singleTreeView; TreeViewBase *view2 = nullptr; if (m_doubleTreeView) { view1 = m_doubleTreeView->masterView(); view2 = m_doubleTreeView->slaveView(); m_doubleTreeView->setViewSplitMode(!right.isEmpty()); } - qInfo()<model(); if (model) { int count = model->columnCount(); for (int i = 0; i < count; ++i) { view1->setColumnHidden(i, !left.contains(i)); if (view2) { view2->setColumnHidden(i, !right.contains(i)); } } // sort columns for (int i = 0; i < left.count(); ++i) { view1->mapToSection(left.at(i), i); } if (view2) { for (int i = 0; i < right.count(); ++i) { view2->mapToSection(right.at(i), i); } } } } } //---------------------- TreeViewPrintingDialog::TreeViewPrintingDialog( ViewBase *view, TreeViewBase *treeview, Project *project ) : PrintingDialog( view ), m_tree( treeview ), m_project( project ), m_firstRow( -1 ) { printer().setFromTo( documentFirstPage(), documentLastPage() ); } int TreeViewPrintingDialog::documentLastPage() const { int page = documentFirstPage(); while ( firstRow( page ) != -1 ) { ++page; } if ( page > documentFirstPage() ) { --page; } return page; } int TreeViewPrintingDialog::firstRow( int page ) const { debugPlan<header(); int height = mh->height(); int hHeight = headerRect().height(); int fHeight = footerRect().height(); QRect pageRect = const_cast( this )->printer().pageRect(); int gap = 8; int pageHeight = pageRect.height() - height; if ( hHeight > 0 ) { pageHeight -= ( hHeight + gap ); } if ( fHeight > 0 ) { pageHeight -= ( fHeight + gap ); } int rowsPrPage = pageHeight / height; int rows = m_tree->model()->rowCount(); int row = -1; for ( int i = 0; i < rows; ++i ) { if ( ! m_tree->isRowHidden( i, QModelIndex() ) ) { row = i; break; } } if ( row != -1 ) { QModelIndex idx = m_tree->model()->index( row, 0, QModelIndex() ); row = 0; while ( idx.isValid() ) { if ( row >= rowsPrPage * pageNumber ) { debugPlan<indexBelow( idx ); } if ( ! idx.isValid() ) { row = -1; } } debugPlan<<"Page"< TreeViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void TreeViewPrintingDialog::printPage( int page, QPainter &painter ) { m_firstRow = firstRow( page ); QHeaderView *mh = m_tree->header(); int length = mh->length(); int height = mh->height(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); QRect paperRect = printer().paperRect(); QAbstractItemModel *model = m_tree->model(); debugPlan<printingOptions(), page, *(m_project) ); } int gap = 8; int pageHeight = pageRect.height() - height; if ( hRect.isValid() ) { pageHeight -= ( hRect.height() + gap ); } if ( fRect.isValid() ) { pageHeight -= ( fRect.height() + gap ); } int rowsPrPage = pageHeight / height; double sx = pageRect.width() > length ? 1.0 : (double)pageRect.width() / (double)length; double sy = 1.0; painter.scale( sx, sy ); int h = 0; painter.translate( 0, hRect.height() + gap ); h = hRect.height() + gap; painter.setPen(Qt::black); painter.setBrush( Qt::lightGray ); int higestIndex = 0; int rightpos = 0; for ( int i = 0; i < mh->count(); ++i ) { QString text = model->headerData( i, Qt::Horizontal ).toString(); QVariant a = model->headerData( i, Qt::Horizontal, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r = QRect( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); // FIXME There is a bug somewhere, the text somehow overwites the rect outline for the first column! painter.save(); painter.setBrush(QBrush()); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); painter.drawRect( r ); painter.restore(); } //debugPlan<isSectionHidden( i )<sectionPosition( i ); } if ( m_firstRow == -1 ) { debugPlan<<"No data"; return; } painter.setBrush( QBrush() ); QModelIndex idx = model->index( m_firstRow, 0, QModelIndex() ); int numRows = 0; //debugPlan<count(); ++i ) { if ( mh->isSectionHidden( i ) ) { continue; } Qt::Edges edges = Qt::BottomEdge | Qt::LeftEdge; QModelIndex index = model->index( idx.row(), i, idx.parent() ); QString text = model->data( index ).toString(); QVariant a = model->data( index, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); QRect r( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1) , align, text ); } ++numRows; idx = m_tree->indexBelow( idx ); } } /** * TreeViewBase is a QTreeView adapted for operation by keyboard only and as components in DoubleTreeViewBase. * Note that keyboard navigation and selection behavior may not be fully compliant with QTreeView. * If you use other settings than QAbstractItemView::ExtendedSelection and QAbstractItemView::SelectRows, * you should have a look at the implementation keyPressEvent() and updateSelection(). */ TreeViewBase::TreeViewBase( QWidget *parent ) : QTreeView( parent ), m_arrowKeyNavigation( true ), m_acceptDropsOnView( false ), m_readWrite( false ), m_handleDrag(true) { m_dragPixmap = koIcon("application-x-vnd.kde.plan").pixmap(32); setDefaultDropAction( Qt::MoveAction ); setItemDelegate( new ItemDelegate( this ) ); setAlternatingRowColors ( true ); header()->setContextMenuPolicy( Qt::CustomContextMenu ); connect( header(), &QWidget::customContextMenuRequested, this, &TreeViewBase::slotHeaderContextMenuRequested ); } void TreeViewBase::dropEvent( QDropEvent *e ) { debugPlan; QTreeView::dropEvent( e ); } KoPrintJob * TreeViewBase::createPrintJob( ViewBase *parent ) { TreeViewPrintingDialog *dia = new TreeViewPrintingDialog( parent, this, parent->project() ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } void TreeViewBase::setReadWrite( bool rw ) { m_readWrite = rw; if ( model() ) { model()->setData( QModelIndex(), rw, Role::ReadWrite ); } } void TreeViewBase::createItemDelegates( ItemModelBase *model ) { for ( int c = 0; c < model->columnCount(); ++c ) { QAbstractItemDelegate *delegate = model->createDelegate( c, this ); if ( delegate ) { setItemDelegateForColumn( c, delegate ); } } } void TreeViewBase::slotHeaderContextMenuRequested( const QPoint& pos ) { //debugPlan; emit headerContextMenuRequested( header()->mapToGlobal( pos ) ); } void TreeViewBase::setColumnsHidden( const QList &lst ) { //debugPlan< xlst; foreach ( int c, lst ) { if ( c == -1 ) { // hide rest for ( int i = prev+1; i < model()->columnCount(); ++i ) { if ( ! lst.contains( i ) ) { xlst << i; } } break; } xlst << c; prev = c; } for ( int c = 0; c < model()->columnCount(); ++c ) { setColumnHidden( c, xlst.contains( c ) ); } } QModelIndex TreeViewBase::firstColumn( int row, const QModelIndex &parent ) { int s; for ( s = 0; s < header()->count(); ++s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { break; } } if ( s == -1 ) { return QModelIndex(); } return model()->index( row, header()->logicalIndex( s ), parent ); } QModelIndex TreeViewBase::lastColumn( int row, const QModelIndex &parent ) { int s; for ( s = header()->count() - 1; s >= 0; --s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { break; } } if ( s == -1 ) { return QModelIndex(); } return model()->index( row, header()->logicalIndex( s ), parent ); } QModelIndex TreeViewBase::nextColumn( const QModelIndex &curr ) { return moveCursor( curr, QAbstractItemView::MoveRight ); } QModelIndex TreeViewBase::previousColumn( const QModelIndex &curr ) { return moveCursor( curr, QAbstractItemView::MoveLeft ); } QModelIndex TreeViewBase::firstEditable( int row, const QModelIndex &parent ) { QModelIndex index = firstColumn( row, parent ); if ( model()->flags( index ) & Qt::ItemIsEditable ) { return index; } return moveToEditable( index, QAbstractItemView::MoveRight ); } QModelIndex TreeViewBase::lastEditable( int row, const QModelIndex &parent ) { QModelIndex index = lastColumn( row, parent ); if ( model()->flags( index ) & Qt::ItemIsEditable ) { return index; } return moveToEditable( index, QAbstractItemView::MoveLeft ); } // Reimplemented to fix qt bug 160083: Doesn't scroll horizontally. void TreeViewBase::scrollTo(const QModelIndex &index, ScrollHint hint) { //debugPlan<width(); int horizontalOffset = header()->offset(); int horizontalPosition = header()->sectionPosition(index.column()); int cellWidth = header()->sectionSize(index.column()); if (hint == PositionAtCenter) { horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2)); } else { if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth) horizontalScrollBar()->setValue(horizontalPosition); else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth) horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth); } } void TreeViewBase::focusInEvent(QFocusEvent *event) { //debugPlan<reason(); QAbstractScrollArea::focusInEvent(event); //NOTE: not QTreeView if ( event->reason() == Qt::MouseFocusReason ) { return; } QModelIndex curr = currentIndex(); if ( ! curr.isValid() || ! isIndexHidden( curr ) ) { return; } QModelIndex idx = curr; for ( int s = 0; s < header()->count(); ++s) { idx = model()->index( curr.row(), header()->logicalIndex( s ), curr.parent() ); if ( ! isIndexHidden( idx ) ) { selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate); scrollTo( idx ); break; } } } /*! \reimp */ void TreeViewBase::keyPressEvent(QKeyEvent *event) { //debugPlan<key()<<","<key()) { case Qt::Key_Right: { QModelIndex nxt = moveCursor( MoveRight, Qt::NoModifier ); if ( nxt.isValid() ) { selectionModel()->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); } else { emit moveAfterLastColumn( current ); } event->accept(); return; break; } case Qt::Key_Left: { QModelIndex prv = moveCursor( MoveLeft, Qt::NoModifier ); if ( prv.isValid() ) { selectionModel()->setCurrentIndex( prv, QItemSelectionModel::NoUpdate ); } else { emit moveBeforeFirstColumn( current ); } event->accept(); return; break; } case Qt::Key_Down: { QModelIndex i = moveCursor( MoveDown, Qt::NoModifier ); updateSelection( current, i, event ); event->accept(); return; break; } case Qt::Key_Up: { QModelIndex i = moveCursor( MoveUp, Qt::NoModifier ); updateSelection( current, i, event ); event->accept(); return; break; } default: break; } } QTreeView::keyPressEvent(event); } void TreeViewBase::updateSelection( const QModelIndex &oldidx, const QModelIndex &newidx, QKeyEvent *event ) { if ( newidx == oldidx || ! newidx.isValid() ) { return; } if ( !hasFocus() && QApplication::focusWidget() == indexWidget(oldidx) ) { setFocus(); } QItemSelectionModel::SelectionFlags command; // NoUpdate on Key movement and Ctrl Qt::KeyboardModifiers modifiers = static_cast(event)->modifiers(); switch (static_cast(event)->key()) { case Qt::Key_Backtab: modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab Q_FALLTHROUGH(); case Qt::Key_Down: case Qt::Key_Up: case Qt::Key_Left: case Qt::Key_Right: if (modifiers & Qt::ControlModifier) command = QItemSelectionModel::NoUpdate; else if (modifiers & Qt::ShiftModifier) command = QItemSelectionModel::Select | selectionBehaviorFlags(); else command = QItemSelectionModel::ClearAndSelect | selectionBehaviorFlags(); break; default: break; } selectionModel()->setCurrentIndex( newidx, command ); } void TreeViewBase::mousePressEvent(QMouseEvent *event) { // If the mouse is pressed outside any item, the current item should be/remain selected QPoint pos = event->pos(); QModelIndex index = indexAt(pos); debugPlan<pos(); if ( ! index.isValid() ) { index = selectionModel()->currentIndex(); if ( index.isValid() && ! selectionModel()->isSelected( index ) ) { pos = visualRect( index ).center(); QMouseEvent e( event->type(), pos, mapToGlobal( pos ), event->button(), event->buttons(), event->modifiers() ); QTreeView::mousePressEvent( &e ); event->setAccepted( e.isAccepted() ); debugPlan<( sender() ); if ( delegate == 0 ) { warnPlan<<"Not a KPlato::ItemDelegate, try standard treatment"<endEditHint(); // Close editor, do nothing else QTreeView::closeEditor( editor, QAbstractItemDelegate::NoHint ); QModelIndex index; switch ( endHint ) { case Delegate::EditLeftItem: index = moveToEditable( currentIndex(), MoveLeft ); break; case Delegate::EditRightItem: index = moveToEditable( currentIndex(), MoveRight ); break; case Delegate::EditDownItem: index = moveToEditable( currentIndex(), MoveDown ); break; case Delegate::EditUpItem: index = moveToEditable( currentIndex(), MoveUp ); break; default: //debugPlan<<"Standard treatment"<setCurrentIndex(persistent, flags); // currentChanged signal would have already started editing if (!(editTriggers() & QAbstractItemView::CurrentChanged)) { edit(persistent); } } } QModelIndex TreeViewBase::moveToEditable( const QModelIndex &index, CursorAction cursorAction ) { QModelIndex ix = index; do { ix = moveCursor( ix, cursorAction ); } while ( ix.isValid() && ! ( model()->flags( ix ) & Qt::ItemIsEditable ) ); //debugPlan<= model()->columnCount(ix.parent()) ) { //debugPlan<columnCount(ix.parent())<index( ix.row(), col, ix.parent() ); } // else Here we could go to the top return ix; } case MoveUp: { // TODO: span // Fetch the index above current. // This should be the previous non-hidden row, same column as current, // that has a column in current.column() ix = indexAbove( current ); while ( ix.isValid() && col >= model()->columnCount(ix.parent()) ) { ix = indexAbove( ix ); } if ( ix.isValid() ) { ix = model()->index( ix.row(), col, ix.parent() ); } // else Here we could go to the bottom return ix; } case MovePrevious: case MoveLeft: { for ( int s = header()->visualIndex( col ) - 1; s >= 0; --s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { ix = model()->index( current.row(), header()->logicalIndex( s ), current.parent() ); break; } } return ix; } case MoveNext: case MoveRight: { for ( int s = header()->visualIndex( col ) + 1; s < header()->count(); ++s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { ix = model()->index( current.row(), header()->logicalIndex( s ), current.parent() ); break; } } return ix; } case MovePageUp: case MovePageDown: { ix = QTreeView::moveCursor( cursorAction, modifiers ); // Now we are at the correct row, so move to correct column if ( ix.isValid() ) { ix = model()->index( ix.row(), col, ix.parent() ); } // else Here we could go to the bottom return ix; } case MoveHome: { if ( ( modifiers & Qt::ControlModifier ) == 0 ) { ix = QTreeView::moveCursor( cursorAction, modifiers ); // move to first row } else { //stay at this row ix = current; } for ( int s = 0; s < header()->count(); ++s ) { int logicalIndex = header()->logicalIndex( s ); if ( ! isColumnHidden( logicalIndex ) ) { ix = model()->index( ix.row(), header()->logicalIndex( s ), ix.parent() ); break; } } return ix; } case MoveEnd: { if ( ( modifiers & Qt::ControlModifier ) == 0 ) { ix = QTreeView::moveCursor( cursorAction, modifiers ); // move to last row } else { //stay at this row ix = current; } for ( int s = header()->count() - 1; s >= 0; --s ) { int logicalIndex = header()->logicalIndex( s ); if ( ! isColumnHidden( logicalIndex ) ) { ix = model()->index( ix.row(), logicalIndex, ix.parent() ); break; } } return ix; } default: break; } return ix; } void TreeViewBase::contextMenuEvent ( QContextMenuEvent *event ) { debugPlan<selectedRows(); emit contextMenuRequested( indexAt(event->pos()), event->globalPos(), selectionModel()->selectedRows() ); } void TreeViewBase::slotCurrentChanged( const QModelIndex ¤t, const QModelIndex & ) { if ( current.isValid() ) { scrollTo( current ); } } void TreeViewBase::setModel( QAbstractItemModel *model ) { if ( selectionModel() ) { disconnect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } QTreeView::setModel( model ); if ( selectionModel() ) { connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } setReadWrite( m_readWrite ); } void TreeViewBase::setSelectionModel( QItemSelectionModel *model ) { if ( selectionModel() ) { disconnect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } QTreeView::setSelectionModel( model ); if ( selectionModel() ) { connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } } void TreeViewBase::setStretchLastSection( bool mode ) { header()->setStretchLastSection( mode ); } void TreeViewBase::mapToSection( int col, int section ) { header()->moveSection( header()->visualIndex( col ), section ); } int TreeViewBase::section( int col ) const { return header()->visualIndex( col ); } void TreeViewBase::dragMoveEvent(QDragMoveEvent *event) { //debugPlan; if (dragDropMode() == InternalMove && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) { //debugPlan<<"Internal:"<isAccepted(); return; } QTreeView::dragMoveEvent( event ); if ( dropIndicatorPosition() == QAbstractItemView::OnViewport ) { if ( ! m_acceptDropsOnView ) { event->ignore(); } debugPlan<<"On viewport:"<isAccepted(); } else { QModelIndex index = indexAt( event->pos() ); if ( index.isValid() ) { emit dropAllowed( index, dropIndicatorPosition(), event ); } else { event->ignore(); debugPlan<<"Invalid index:"<isAccepted(); } } if ( event->isAccepted() ) { if ( viewport()->cursor().shape() == Qt::ForbiddenCursor ) { viewport()->unsetCursor(); } } else if ( viewport()->cursor().shape() != Qt::ForbiddenCursor ) { viewport()->setCursor( Qt::ForbiddenCursor ); } debugPlan<isAccepted()<cursor().shape(); } QModelIndex TreeViewBase::firstVisibleIndex( const QModelIndex &idx ) const { int count = model()->columnCount(); for ( int c = 0; c < count; ++c ) { if ( ! isColumnHidden( c ) ) { return model()->index( idx.row(), c, model()->parent( idx ) ); } } return QModelIndex(); } bool TreeViewBase::loadContext( const QMetaEnum &map, const KoXmlElement &element, bool expand ) { //debugPlan<setStretchLastSection( (bool)( element.attribute( "stretch-last-column", "1" ).toInt() ) ); KoXmlElement e = element.namedItem( "columns" ).toElement(); if ( ! e.isNull() ) { if ( ! map.isValid() ) { // try numbers debugPlan<<"invalid map"; for ( int i = model()->columnCount() - 1; i >= 0; --i ) { QString s = e.attribute( QString( "column-%1" ).arg( i ), "" ); if ( s == "hidden" ) { hideColumn( i ); } else if ( s == "shown" ) { showColumn( i ); } else debugPlan<columnCount() - 1; i >= 0; --i ) { QString n = map.key( i ); //debugPlan<count(); ++i ) { if ( e.hasAttribute( s.arg( i ) ) ) { int index = e.attribute( s.arg( i ), "-1" ).toInt(); if ( index >= 0 && index < h->count() ) { header()->moveSection( h->visualIndex( index ), i ); } } } } else { QMap m; // QMap for ( int i = 0; i < h->count(); ++i ) { QString n = e.attribute( s.arg( i ) ); if ( n.isEmpty() ) { continue; } int col = map.keyToValue( n.toUtf8() ); if ( col >= 0 && col < h->count() ) { m.insert( i, col ); } } for ( QMap::const_iterator it = m.constBegin(); it != m.constEnd(); ++it ) { QString n = e.attribute( s.arg( it.key() ) ); int current = h->visualIndex( it.value() ); header()->moveSection( current, it.key() ); } } } if (expand) { loadExpanded(element); } return true; } void TreeViewBase::saveContext( const QMetaEnum &map, QDomElement &element, bool expand ) const { //debugPlan<stretchLastSection()) ); QDomElement e = element.ownerDocument().createElement( "columns" ); element.appendChild( e ); for ( int i = 0; i < model()->columnCount(); ++i ) { bool h = isColumnHidden( i ); if ( ! map.isValid() ) { debugPlan<<"invalid map"; e.setAttribute( QString( "column-%1" ).arg( i ), h ? "hidden" : "shown" ); } else { QString n = map.key( i ); //debugPlan<count(); ++i ) { if ( ! isColumnHidden( h->logicalIndex( i ) ) ) { if ( ! map.isValid() ) { e.setAttribute( QString( "section-%1" ).arg( i ), h->logicalIndex( i ) ); } else { QString n = map.key( h->logicalIndex( i ) ); if ( ! n.isEmpty() ) { e.setAttribute( QString( "section-%1" ).arg( i ), n ); } } } } if (expand) { QDomElement expanded = element.ownerDocument().createElement("expanded"); element.appendChild(expanded); saveExpanded(expanded); } } ItemModelBase *TreeViewBase::itemModel() const { QAbstractItemModel *m = model(); QAbstractProxyModel *p = qobject_cast( m ); while ( p ) { m = p->sourceModel(); p = qobject_cast( m ); } return qobject_cast( m ); } void TreeViewBase::expandRecursive(const QModelIndex &idx, bool xpand) { int rowCount = model()->rowCount(idx); if (rowCount == 0) { return; } xpand ? expand(idx) : collapse(idx); for (int r = 0; r < rowCount; ++r) { QModelIndex i = model()->index(r, 0, idx); Q_ASSERT(i.isValid()); expandRecursive(i, xpand); } } void TreeViewBase::slotExpand() { // NOTE: Do not use this, KGantt does not like it // if (!m_contextMenuIndex.isValid()) { // expandAll(); // return; // } QModelIndex idx = m_contextMenuIndex; if (idx.column() > 0) { idx = idx.model()->index(idx.row(), idx.column(), idx.parent()); } expandRecursive(idx, true); } void TreeViewBase::slotCollapse() { // NOTE: Do not use this, KGantt does not like it // if (!m_contextMenuIndex.isValid()) { // collapseAll(); // return; // } QModelIndex idx = m_contextMenuIndex; if (idx.column() > 0) { idx = idx.model()->index(idx.row(), 0, idx.parent()); } expandRecursive(idx, false); } void TreeViewBase::setContextMenuIndex(const QModelIndex &idx) { m_contextMenuIndex = idx; } void TreeViewBase::loadExpanded(const KoXmlElement &element) { // we get here on loadContext() m_loadContextDoc.clear(); KoXmlElement expanded = element.namedItem("expanded").toElement(); if (expanded.isNull()) { return; } KoXml::asQDomElement(m_loadContextDoc, expanded); // FIXME: // if data is dependent on schedule manger // we cannot do anything until schedulemanger is set, // so we wait a bit and hope everything is ok QTimer::singleShot(500, this, &TreeViewBase::doContextExpanded); } void TreeViewBase::expandRecursivly(QDomElement element, const QModelIndex &parent) { if (element.isNull()) { return; } for(QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.tagName() != "item") { continue; } int childRow = e.attribute("row", "-1").toInt(); if (childRow > -1) { QModelIndex idx = model()->index(childRow, 0, parent); if (idx.isValid()) { setExpanded(idx, true); expandRecursivly(e, idx); } } } } void TreeViewBase::doExpand(QDomDocument &doc) { // we get here on setScheduleManager() m_expandDoc = doc; QTimer::singleShot(0, this, &TreeViewBase::doExpanded); } void TreeViewBase::doContextExpanded() { expandRecursivly(m_loadContextDoc.documentElement()); } void TreeViewBase::doExpanded() { expandRecursivly(m_expandDoc.documentElement()); } void TreeViewBase::saveExpanded(QDomElement &element, const QModelIndex &parent) const { for (int r = 0; r < model()->rowCount(parent); ++r) { QModelIndex idx = model()->index(r, 0, parent); if (isExpanded(idx)) { QDomElement e = element.ownerDocument().createElement("item"); e.setAttribute("row", r); element.appendChild(e); saveExpanded(e, idx); } } } void TreeViewBase::setHandleDrag(bool state) { m_handleDrag = state; } void TreeViewBase::startDrag(Qt::DropActions supportedActions) { Qt::DropAction defaultDropAction = Qt::IgnoreAction; if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) { defaultDropAction = this->defaultDropAction(); } else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) { defaultDropAction = Qt::CopyAction; } if (m_handleDrag) { QModelIndexList indexes = selectionModel()->selectedRows(); if (!indexes.isEmpty()) { QMimeData *data = model()->mimeData(indexes); if (!data) { debugPlan<<"No mimedata"; return; } QDrag *drag = new QDrag(this); drag->setPixmap(m_dragPixmap); drag->setMimeData(data); drag->exec(supportedActions, defaultDropAction); } } else { static_cast(parent())->handleDrag(supportedActions, defaultDropAction); } } QList TreeViewBase::visualColumns() const { if (!isVisible()) { return QList(); } QMap columns; for (int i = 0; i < model()->columnCount(); ++i) { if (!isColumnHidden(i)) { columns.insert(header()->visualIndex(i), i); } } return columns.values(); } void TreeViewBase::setDragPixmap(const QPixmap &pixmap) { m_dragPixmap = pixmap; } QPixmap TreeViewBase::dragPixmap() const { return m_dragPixmap; } void TreeViewBase::editCopy() { } void TreeViewBase::editPaste() { } QModelIndexList TreeViewBase::selectedIndexes() const { QModelIndexList viewSelected; QModelIndexList modelSelected; if (selectionModel()) modelSelected = selectionModel()->selectedIndexes(); for (int i = 0; i < modelSelected.count(); ++i) { // check that neither the parents nor the index is hidden before we add QModelIndex index = modelSelected.at(i); while (index.isValid() && !isIndexHidden(index)) { int column = index.column(); index = index.parent(); if (index.isValid() && column != index.column()) { index = index.sibling(index.row(), column); } } if (index.isValid()) continue; viewSelected.append(modelSelected.at(i)); } return viewSelected; } //---------------------- DoubleTreeViewPrintingDialog::DoubleTreeViewPrintingDialog( ViewBase *view, DoubleTreeViewBase *treeview, Project *project ) : PrintingDialog( view ), m_tree( treeview ), m_project( project ), m_firstRow( -1 ) { printer().setFromTo( documentFirstPage(), documentLastPage() ); } int DoubleTreeViewPrintingDialog::documentLastPage() const { debugPlan<pageLayout().format ); int page = documentFirstPage(); while ( firstRow( page ) != -1 ) { ++page; } if ( page > documentFirstPage() ) { --page; } return page; } int DoubleTreeViewPrintingDialog::firstRow( int page ) const { debugPlan<masterView()->header(); QHeaderView *sh = m_tree->slaveView()->header(); int height = mh->height() > sh->height() ? mh->height() : sh->height(); int hHeight = headerRect().height(); int fHeight = footerRect().height(); QRect pageRect = const_cast( this )->printer().pageRect(); int gap = 8; int pageHeight = pageRect.height() - height; if ( hHeight > 0 ) { pageHeight -= ( hHeight + gap ); } if ( fHeight > 0 ) { pageHeight -= ( fHeight + gap ); } int rowsPrPage = pageHeight / height; debugPlan<<"rowsPrPage"< 0 ); int rows = m_tree->model()->rowCount(); int row = -1; for ( int i = 0; i < rows; ++i ) { if ( ! m_tree->masterView()->isRowHidden( i, QModelIndex() ) ) { row = i; break; } } if ( row != -1 ) { QModelIndex idx = m_tree->model()->index( row, 0, QModelIndex() ); row = 0; while ( idx.isValid() ) { if ( row >= rowsPrPage * pageNumber ) { debugPlan<masterView()->indexBelow( idx ); } if ( ! idx.isValid() ) { row = -1; } } debugPlan<<"Page"< DoubleTreeViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void DoubleTreeViewPrintingDialog::printPage( int page, QPainter &painter ) { debugPlan<pageLayout() ); qreal t, l, b, r; printer().getPageMargins( &l, &t, &r, &b, QPrinter::Point ); debugPlan<masterView()->header(); QHeaderView *sh = m_tree->slaveView()->header(); int length = mh->length() + sh->length(); int height = mh->height() > sh->height() ? mh->height() : sh->height(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); QRect paperRect = printer().paperRect(); QAbstractItemModel *model = m_tree->model(); Q_ASSERT( model != 0 ); debugPlan< length ? 1.0 : (double)pageRect.width() / (double)length; double sy = 1.0; painter.scale( sx, sy ); int h = 0; painter.translate( 0, hRect.height() + gap ); h = hRect.height() + gap; painter.setPen(Qt::black); painter.setBrush( Qt::lightGray ); int higestIndex = 0; int rightpos = 0; for ( int i = 0; i < mh->count(); ++i ) { QString text = model->headerData( i, Qt::Horizontal ).toString(); QVariant a = model->headerData( i, Qt::Horizontal, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r = QRect( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); // FIXME There is a bug somewhere, the text somehow overwites the rect outline for the first column! painter.save(); painter.setBrush(QBrush()); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); painter.drawRect( r ); painter.restore(); } if ( ! sh->isSectionHidden( i ) ) { QRect r = QRect( sh->sectionPosition( i ) + mh->length(), 0, sh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); } //debugPlan<isSectionHidden( i )<sectionPosition( i ); } if ( m_firstRow == -1 || model->rowCount() == 0 ) { debugPlan<<"No data"; painter.restore(); return; } painter.setBrush( QBrush() ); QModelIndex idx = model->index( 0, 0 ); for ( int r = 0; r < m_firstRow && idx.isValid(); ++r ) { idx = m_tree->masterView()->indexBelow( idx ); } int numRows = 0; //debugPlan<count(); ++i ) { if ( mh->isSectionHidden( i ) && sh->isSectionHidden( i ) ) { continue; } Qt::Edges edges = Qt::BottomEdge | Qt::LeftEdge; QModelIndex index = model->index( idx.row(), i, idx.parent() ); QString text = model->data( index ).toString(); QVariant a = model->data( index, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1) , align, text ); } if ( ! sh->isSectionHidden( i ) ) { QRect r( sh->sectionPosition( i ) + mh->length(), 0, sh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); } } ++numRows; idx = m_tree->masterView()->indexBelow( idx ); } painter.restore(); } /** * DoubleTreeViewBase is a QSplitter containing two treeviews. * This makes it possible to keep columns visible in one view when scrolling the other view horizontally. */ DoubleTreeViewBase::DoubleTreeViewBase( bool /*mode*/, QWidget *parent ) : QSplitter( parent ), m_rightview( 0 ), m_selectionmodel( 0 ), m_readWrite( false ), m_mode( false ) { init(); } DoubleTreeViewBase::DoubleTreeViewBase( QWidget *parent ) : QSplitter( parent ), m_rightview( 0 ), m_selectionmodel( 0 ), m_mode( false ) { init(); } DoubleTreeViewBase::~DoubleTreeViewBase() { } KoPrintJob *DoubleTreeViewBase::createPrintJob( ViewBase *parent ) { DoubleTreeViewPrintingDialog *dia = new DoubleTreeViewPrintingDialog( parent, this, parent->project() ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } void DoubleTreeViewBase::slotExpand() { m_leftview->slotExpand(); } void DoubleTreeViewBase::slotCollapse() { m_leftview->slotCollapse(); } void DoubleTreeViewBase::setParentsExpanded( const QModelIndex &idx, bool expanded ) { //debugPlan<isExpanded( idx )<isExpanded( idx ); QModelIndex p = model()->parent( idx ); QList lst; while ( p.isValid() ) { lst << p; p = model()->parent( p ); } while ( ! lst.isEmpty() ) { p = lst.takeLast(); m_leftview->setExpanded( p, expanded ); m_rightview->setExpanded( m_rightview->firstVisibleIndex( p ), expanded ); //HACK: qt can't handle that column 0 is hidden! //debugPlan<isExpanded( p )<isExpanded( p ); } } void DoubleTreeViewBase::init() { setOrientation( Qt::Horizontal ); setHandleWidth( 3 ); m_leftview = new TreeViewBase(this); m_leftview->setObjectName("Left view"); m_leftview->setHandleDrag(false); addWidget( m_leftview ); setStretchFactor( 0, 1 ); m_rightview = new TreeViewBase(this); m_rightview->setObjectName("Right view"); m_rightview->setHandleDrag(false); addWidget( m_rightview ); setStretchFactor( 1, 1 ); m_leftview->setTreePosition(-1); // always visual index 0 connect( m_leftview, &TreeViewBase::contextMenuRequested, this, &DoubleTreeViewBase::contextMenuRequested ); connect( m_leftview, &TreeViewBase::headerContextMenuRequested, this, &DoubleTreeViewBase::slotLeftHeaderContextMenuRequested ); connect( m_rightview, &TreeViewBase::contextMenuRequested, this, &DoubleTreeViewBase::contextMenuRequested ); connect( m_rightview, &TreeViewBase::headerContextMenuRequested, this, &DoubleTreeViewBase::slotRightHeaderContextMenuRequested ); connect( m_leftview->verticalScrollBar(), &QAbstractSlider::valueChanged, m_rightview->verticalScrollBar(), &QAbstractSlider::setValue ); connect( m_rightview->verticalScrollBar(), &QAbstractSlider::valueChanged, m_leftview->verticalScrollBar(), &QAbstractSlider::setValue ); connect( m_leftview, &TreeViewBase::moveAfterLastColumn, this, &DoubleTreeViewBase::slotToRightView ); connect( m_rightview, &TreeViewBase::moveBeforeFirstColumn, this, &DoubleTreeViewBase::slotToLeftView ); connect( m_leftview, &TreeViewBase::editAfterLastColumn, this, &DoubleTreeViewBase::slotEditToRightView ); connect( m_rightview, &TreeViewBase::editBeforeFirstColumn, this, &DoubleTreeViewBase::slotEditToLeftView ); connect( m_leftview, &QTreeView::expanded, m_rightview, &QTreeView::expand ); connect( m_leftview, &QTreeView::collapsed, m_rightview, &QTreeView::collapse ); connect( m_rightview, &QTreeView::expanded, m_leftview, &QTreeView::expand ); connect( m_rightview, &QTreeView::collapsed, m_leftview, &QTreeView::collapse ); connect( m_leftview, &TreeViewBase::dropAllowed, this, &DoubleTreeViewBase::dropAllowed ); connect( m_rightview, &TreeViewBase::dropAllowed, this, &DoubleTreeViewBase::dropAllowed ); m_actionSplitView = new QAction(koIcon("view-split-left-right"), QString(), this); setViewSplitMode( true ); connect( m_leftview->header(), &QHeaderView::sortIndicatorChanged, this, &DoubleTreeViewBase::slotLeftSortIndicatorChanged ); connect( m_rightview->header(), &QHeaderView::sortIndicatorChanged, this, &DoubleTreeViewBase::slotRightSortIndicatorChanged ); } void DoubleTreeViewBase::slotLeftSortIndicatorChanged( int logicalIndex, Qt::SortOrder /*order*/ ) { QSortFilterProxyModel *sf = qobject_cast( model() ); if ( sf ) { ItemModelBase *m = m_rightview->itemModel(); if ( m ) { sf->setSortRole( m->sortRole( logicalIndex ) ); } } m_leftview->header()->setSortIndicatorShown( true ); // sorting controlled by left treeview, turn right off m_rightview->header()->setSortIndicatorShown( false ); } void DoubleTreeViewBase::slotRightSortIndicatorChanged( int logicalIndex, Qt::SortOrder /*order*/ ) { QSortFilterProxyModel *sf = qobject_cast( model() ); if ( sf ) { ItemModelBase *m = m_rightview->itemModel(); if ( m ) { sf->setSortRole( m->sortRole( logicalIndex ) ); } } m_rightview->header()->setSortIndicatorShown( true ); // sorting controlled by right treeview, turn left off m_leftview->header()->setSortIndicatorShown( false ); } QList DoubleTreeViewBase::expandColumnList( const QList &lst ) const { QList mlst = lst; if ( ! mlst.isEmpty() ) { int v = 0; if ( mlst.last() == -1 && mlst.count() > 1 ) { v = mlst[ mlst.count() - 2 ] + 1; mlst.removeLast(); } for ( int c = v; c < model()->columnCount(); ++c ) { mlst << c; } } return mlst; } void DoubleTreeViewBase::hideColumns( TreeViewBase *view, const QList &list ) { view->setColumnsHidden( list ); } void DoubleTreeViewBase::hideColumns( const QList &masterList, const QList &slaveList ) { m_leftview->setColumnsHidden( masterList ); m_rightview->setColumnsHidden( slaveList ); if ( m_rightview->isHidden() ) { QList mlst = expandColumnList( masterList ); QList slst = expandColumnList( slaveList ); QList lst; for ( int c = 0; c < model()->columnCount(); ++c ) { // only hide columns hidden in *both* views //debugPlan<= 0) && (slst.indexOf( c ) >= 0) ) { lst << c; } } //debugPlan<setColumnsHidden( lst ); } else { setStretchFactors(); } } void DoubleTreeViewBase::slotToRightView( const QModelIndex &index ) { //debugPlan<firstColumn( index.row(), model()->parent( index ) ); m_rightview->setFocus(); if ( nxt.isValid() ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); } } void DoubleTreeViewBase::slotToLeftView( const QModelIndex &index ) { //debugPlan<lastColumn( index.row(), model()->parent( index ) ); m_leftview->setFocus(); if ( prv.isValid() ) { m_selectionmodel->setCurrentIndex( prv, QItemSelectionModel::NoUpdate ); } } void DoubleTreeViewBase::slotEditToRightView( const QModelIndex &index ) { //debugPlan<isHidden() ) { return; } m_rightview->setFocus(); QModelIndex nxt = m_rightview->firstEditable( index.row(), model()->parent ( index ) ); if ( nxt.isValid() && ( model()->flags( nxt ) & Qt::ItemIsEditable ) ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); m_rightview->edit( nxt ); } else { slotToRightView( index ); } } void DoubleTreeViewBase::slotEditToLeftView( const QModelIndex &index ) { //debugPlan<isHidden() ) { return; } m_leftview->setFocus(); QModelIndex nxt = m_leftview->lastEditable( index.row(), model()->parent ( index ) ); if ( nxt.isValid() && ( model()->flags( nxt ) & Qt::ItemIsEditable ) ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); m_leftview->edit( nxt ); } else { slotToLeftView( index ); } } void DoubleTreeViewBase::setReadWrite( bool rw ) { m_readWrite = rw; m_leftview->setReadWrite( rw ); m_rightview->setReadWrite( rw ); } void DoubleTreeViewBase::closePersistentEditor( const QModelIndex &index ) { m_leftview->closePersistentEditor( index ); m_rightview->closePersistentEditor( index ); } void DoubleTreeViewBase::setModel( QAbstractItemModel *model ) { m_leftview->setModel( model ); m_rightview->setModel( model ); if ( m_selectionmodel ) { disconnect( m_selectionmodel, &QItemSelectionModel::selectionChanged, this, &DoubleTreeViewBase::slotSelectionChanged ); disconnect( m_selectionmodel, &QItemSelectionModel::currentChanged, this, &DoubleTreeViewBase::currentChanged ); } m_selectionmodel = m_leftview->selectionModel(); m_rightview->setSelectionModel( m_selectionmodel ); connect( m_selectionmodel, &QItemSelectionModel::selectionChanged, this, &DoubleTreeViewBase::slotSelectionChanged ); connect( m_selectionmodel, &QItemSelectionModel::currentChanged, this, &DoubleTreeViewBase::currentChanged ); setReadWrite( m_readWrite ); } QAbstractItemModel *DoubleTreeViewBase::model() const { return m_leftview->model(); } void DoubleTreeViewBase::slotSelectionChanged( const QItemSelection &sel, const QItemSelection & ) { emit selectionChanged( sel.indexes() ); } void DoubleTreeViewBase::setSelectionModel( QItemSelectionModel *model ) { m_leftview->setSelectionModel( model ); m_rightview->setSelectionModel( model ); } void DoubleTreeViewBase::setSelectionMode( QAbstractItemView::SelectionMode mode ) { m_leftview->setSelectionMode( mode ); m_rightview->setSelectionMode( mode ); } void DoubleTreeViewBase::setSelectionBehavior( QAbstractItemView::SelectionBehavior mode ) { m_leftview->setSelectionBehavior( mode ); m_rightview->setSelectionBehavior( mode ); } void DoubleTreeViewBase::setItemDelegateForColumn( int col, QAbstractItemDelegate * delegate ) { m_leftview->setItemDelegateForColumn( col, delegate ); m_rightview->setItemDelegateForColumn( col, delegate ); } void DoubleTreeViewBase::createItemDelegates( ItemModelBase *model ) { m_leftview->createItemDelegates( model ); m_rightview->createItemDelegates( model ); } void DoubleTreeViewBase::setEditTriggers( QAbstractItemView::EditTriggers mode ) { m_leftview->setEditTriggers( mode ); m_rightview->setEditTriggers( mode ); } QAbstractItemView::EditTriggers DoubleTreeViewBase::editTriggers() const { return m_leftview->editTriggers(); } void DoubleTreeViewBase::setStretchLastSection( bool mode ) { m_rightview->header()->setStretchLastSection( mode ); if ( m_rightview->isHidden() ) { m_leftview->header()->setStretchLastSection( mode ); } } void DoubleTreeViewBase::edit( const QModelIndex &index ) { if ( ! m_leftview->isColumnHidden( index.column() ) ) { m_leftview->edit( index ); } else if ( ! m_rightview->isHidden() && ! m_rightview->isColumnHidden( index.column() ) ) { m_rightview->edit( index ); } } void DoubleTreeViewBase::setDragDropMode( QAbstractItemView::DragDropMode mode ) { m_leftview->setDragDropMode( mode ); m_rightview->setDragDropMode( mode ); } void DoubleTreeViewBase::setDragDropOverwriteMode( bool mode ) { m_leftview->setDragDropOverwriteMode( mode ); m_rightview->setDragDropOverwriteMode( mode ); } void DoubleTreeViewBase::setDropIndicatorShown( bool mode ) { m_leftview->setDropIndicatorShown( mode ); m_rightview->setDropIndicatorShown( mode ); } void DoubleTreeViewBase::setDragEnabled ( bool mode ) { m_leftview->setDragEnabled( mode ); m_rightview->setDragEnabled( mode ); } void DoubleTreeViewBase::setAcceptDrops( bool mode ) { m_leftview->setAcceptDrops( mode ); m_rightview->setAcceptDrops( mode ); } void DoubleTreeViewBase::setAcceptDropsOnView( bool mode ) { m_leftview->setAcceptDropsOnView( mode ); m_rightview->setAcceptDropsOnView( mode ); } void DoubleTreeViewBase::setDefaultDropAction( Qt::DropAction action ) { m_leftview->setDefaultDropAction( action ); m_rightview->setDefaultDropAction( action ); } void DoubleTreeViewBase::slotRightHeaderContextMenuRequested( const QPoint &pos ) { //debugPlan; emit slaveHeaderContextMenuRequested( pos ); emit headerContextMenuRequested( pos ); } void DoubleTreeViewBase::slotLeftHeaderContextMenuRequested( const QPoint &pos ) { //debugPlan; emit masterHeaderContextMenuRequested( pos ); emit headerContextMenuRequested( pos ); } void DoubleTreeViewBase::setStretchFactors() { int lc = m_leftview->header()->count() - m_leftview->header()->hiddenSectionCount(); int rc = m_rightview->header()->count() - m_rightview->header()->hiddenSectionCount(); setStretchFactor( indexOf( m_rightview ), qMax( 1, qMin( 4, rc / qMax( 1, lc ) ) ) ); //debugPlan<loadContext(map, slave, false); } KoXmlElement master = element.namedItem("master").toElement(); if (!master.isNull()) { m_leftview->loadContext(map, master); } return true; } void DoubleTreeViewBase::saveContext( const QMetaEnum &map, QDomElement &element ) const { QDomElement master = element.ownerDocument().createElement( "master" ); element.appendChild(master); m_leftview->saveContext(map, master); QDomElement slave = element.ownerDocument().createElement( "slave" ); element.appendChild(slave); if (m_rightview->isHidden()) { slave.setAttribute("hidden", "true"); } m_rightview->saveContext(map, slave, false); } void DoubleTreeViewBase::setViewSplitMode( bool split ) { if ( split ) { m_actionSplitView->setText( i18n( "Unsplit View" ) ); m_actionSplitView->setIcon(koIcon("view-close")); } else { m_actionSplitView->setText( i18n( "Split View" ) ); m_actionSplitView->setIcon(koIcon("view-split-left-right")); } if ( m_mode == split ) { return; } m_mode = split; if ( split ) { m_leftview->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_leftview->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); if ( model() ) { m_rightview->setColumnHidden( 0, true ); m_leftview->resizeColumnToContents( 0 ); for ( int c = 1; c < m_rightview->model()->columnCount(); ++c ) { if ( m_leftview->isColumnHidden( c ) ) { m_rightview->setColumnHidden( c, true ); } else { m_rightview->setColumnHidden( c, false ); m_rightview->mapToSection( c, m_leftview->section( c ) ); m_leftview->setColumnHidden( c, true ); m_rightview->resizeColumnToContents( c ); } } } m_rightview->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); m_rightview->show(); } else { m_rightview->hide(); if ( model() ) { int offset = m_rightview->isColumnHidden( 0 ) ? 1 : 0; for ( int c = 0; c < model()->columnCount(); ++c ) { if ( ! m_rightview->isColumnHidden( c ) ) { m_leftview->setColumnHidden( c, false ); m_leftview->mapToSection( c, m_rightview->section( c ) + offset ); m_leftview->resizeColumnToContents( c ); } } } m_leftview->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_leftview->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } } void DoubleTreeViewBase::setRootIsDecorated ( bool show ) { m_leftview->setRootIsDecorated( show ); m_rightview->setRootIsDecorated( show ); } QModelIndex DoubleTreeViewBase::indexAt( const QPoint &pos ) const { QModelIndex idx = m_leftview->indexAt( pos ); if ( ! idx.isValid() ) { idx = m_rightview->indexAt( pos ); } return idx; } void DoubleTreeViewBase::setContextMenuIndex(const QModelIndex &idx) { m_leftview->setContextMenuIndex(idx); m_rightview->setContextMenuIndex(idx); } void sort(QTreeView *view, QModelIndexList &list) { QModelIndexList i; i << list.takeFirst(); for (QModelIndex idx = view->indexAbove(i.first()); idx.isValid() && !list.isEmpty(); idx = view->indexAbove(idx)) { if (list.contains(idx)) { i.prepend(idx); list.removeOne(idx); } } for (QModelIndex idx = view->indexBelow(i.last()); idx.isValid() && !list.isEmpty(); idx = view->indexBelow(idx)) { if (list.contains(idx)) { i.append(idx); list.removeOne(idx); } } list = i; } QMimeData *DoubleTreeViewBase::mimeData() const { QModelIndexList rows = selectionModel()->selectedRows(); sort(m_leftview, rows); if (rows.isEmpty()) { debugPlan<<"No rows selected"; return 0; } QList columns;; columns = m_leftview->visualColumns() + m_rightview->visualColumns(); QModelIndexList indexes; for (int r = 0; r < rows.count(); ++r) { int row = rows.at(r).row(); const QModelIndex &parent = rows.at(r).parent(); for (int i = 0; i < columns.count(); ++i) { indexes << model()->index(row, columns.at(i), parent); } } return model()->mimeData(indexes); } void DoubleTreeViewBase::handleDrag(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction) { QMimeData *data = mimeData(); if (!data) { debugPlan<<"No mimedata"; return; } QDrag *drag = new QDrag(this); drag->setPixmap(m_leftview->dragPixmap()); drag->setMimeData(data); Qt::DropAction a = drag->exec(supportedActions, defaultDropAction); } void DoubleTreeViewBase::setDragPixmap(const QPixmap &pixmap) { m_leftview->setDragPixmap(pixmap); } QPixmap DoubleTreeViewBase::dragPixmap() const { return m_leftview->dragPixmap(); } void DoubleTreeViewBase::editCopy() { QMimeData *data = mimeData(); if (!data) { debugPlan<<"No mimedata"; return; } QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setMimeData(data); } void DoubleTreeViewBase::editPaste() { } } // namespace KPlato diff --git a/src/libs/ui/kptworkpackagemergedialog.cpp b/src/libs/ui/kptworkpackagemergedialog.cpp index a499e129..0b1117ca 100644 --- a/src/libs/ui/kptworkpackagemergedialog.cpp +++ b/src/libs/ui/kptworkpackagemergedialog.cpp @@ -1,190 +1,449 @@ /* This file is part of the KDE project Copyright (C) 2011 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptworkpackagemergedialog.h" #include "kptpackage.h" #include "kptproject.h" #include "kpttask.h" +#include "kpttaskprogresspanel.h" +#include "kptcommand.h" +#include "kptduration.h" #include #include +#include #include #include #include #include #include #include #include #include #include #include "kmessagebox_copy.cpp" // themedMessageBoxIcon() -namespace KPlato +using namespace KPlato; + + +WorkPackageMergePanel::WorkPackageMergePanel( QWidget *parent ) + : QWidget( parent ) { + setupUi( this ); +} -PackageInfoWidget::PackageInfoWidget( Package *package, QWidget* parent) - : QFrame( parent ), - m_package( package ) +WorkPackageMergeDialog::WorkPackageMergeDialog(Project *project, const QMap &list, QWidget *parent ) + : KoDialog( parent ) + , m_project(project) + , m_packages( list.values() ) + , m_currentPackage(-1) + , m_cmd(nullptr) + , m_progressPanel(nullptr) { - setFrameStyle( QFrame::Sunken | QFrame::StyledPanel ); - QHBoxLayout *l = new QHBoxLayout( this ); - l->addSpacing( 20 ); - QCheckBox *w = new QCheckBox( this ); - w->setText( i18n( "Used Effort" ) ); - w->setCheckState( package->settings.usedEffort ? Qt::Checked : Qt::Unchecked ); - l->addWidget( w ); - connect(w, &QCheckBox::stateChanged, this, &PackageInfoWidget::slotUsedEffortChanged); - w = new QCheckBox( this ); - w->setText( i18n( "Task Progress" ) ); - w->setCheckState( package->settings.progress ? Qt::Checked : Qt::Unchecked ); - l->addWidget( w ); - connect(w, &QCheckBox::stateChanged, this, &PackageInfoWidget::slotProgressChanged); - w = new QCheckBox( this ); - w->setText( i18n("&Documents") ); - w->setCheckState( package->settings.documents ? Qt::Checked : Qt::Unchecked ); - l->addWidget( w ); - connect(w, &QCheckBox::stateChanged, this, &PackageInfoWidget::slotDocumentsChanged); + setButtons(KoDialog::User1|KoDialog::User2); + panel.ui_completionView->setRootIsDecorated(false); + m_model = new QStandardItemModel(0, 4, panel.ui_completionView); + m_model->setHeaderData(0, Qt::Horizontal, xi18nc("@title", "Date")); + m_model->setHeaderData(1, Qt::Horizontal, xi18nc("@title", "Completion")); + m_model->setHeaderData(2, Qt::Horizontal, xi18nc("@title", "Used Effort")); + m_model->setHeaderData(3, Qt::Horizontal, xi18nc("@title", "Remaining Effort")); + panel.ui_completionView->setModel(m_model); + + panel.ui_updateStarted->setToolTip(xi18nc("@info:tooltip", "Update started")); + panel.ui_updateFinished->setToolTip(xi18nc("@info:tooltip", "Update finished")); + + setMainWidget(&panel); + + nextPackage(); } -void PackageInfoWidget::slotUsedEffortChanged( int s ) +WorkPackageMergeDialog::~WorkPackageMergeDialog() { - m_package->settings.usedEffort = (bool)s; + if (m_cmd) { + m_cmd->undo(); + delete m_cmd; + } } -void PackageInfoWidget::slotProgressChanged( int s ) +void WorkPackageMergeDialog::setPage(QWidget *page) { - m_package->settings.progress = (bool)s; + disconnect(this, &KoDialog::user1Clicked, this, nullptr); + disconnect(this, &KoDialog::user2Clicked, this, nullptr); + panel.ui_stackedWidget->setCurrentWidget(page); + if (page == panel.ui_packagePage) { + setButtonText(KoDialog::User1, i18n("Merge")); + setButtonIcon(KoDialog::User1, koIcon("go-next")); + setButtonToolTip(KoDialog::User1, xi18nc("@info:tooltip", "Merge the changes and display the resulting progress information")); + setButtonText(KoDialog::User2, i18n("Reject")); + setButtonIcon(KoDialog::User2, koIcon("edit-delete")); + setButtonToolTip(KoDialog::User2, xi18nc("@info:tooltip", "Reject this package and goto next package")); + connect(this, &KoDialog::user1Clicked, this, &WorkPackageMergeDialog::slotMerge); + connect(this, &KoDialog::user2Clicked, this, &WorkPackageMergeDialog::slotReject); + } else if (page == panel.ui_progressPage) { + setButtonText(KoDialog::User1, i18n("Next")); + setButtonIcon(KoDialog::User1, koIcon("go-next")); + setButtonToolTip(KoDialog::User1, xi18nc("@info:tooltip", "Commit the changes and go to next package")); + setButtonText(KoDialog::User2, i18n("Back")); + setButtonIcon(KoDialog::User2, koIcon("go-previous")); + setButtonToolTip(KoDialog::User2, xi18nc("@info:tooltip", "Revert the changes and go back to the current package")); + connect(this, &KoDialog::user1Clicked, this, &WorkPackageMergeDialog::slotApply); + connect(this, &KoDialog::user2Clicked, this, &WorkPackageMergeDialog::slotBack); + } else if (page == panel.ui_finishPage) { + setButtons(KoDialog::Close); + } } -void PackageInfoWidget::slotDocumentsChanged( int s ) +void WorkPackageMergeDialog::nextPackage() { - m_package->settings.documents = (bool)s; + ++m_currentPackage; + Package *package = m_packages.value(m_currentPackage); + if (package) { + panel.ui_taskName->setText(package->task->name()); + panel.ui_ownerName->setText(package->ownerName); + panel.ui_packageTime->setText(package->timeTag.toString(Qt::SystemLocaleShortDate)); + fillCompletionModel(package); + setPage(panel.ui_packagePage); + } else { + gotoFinish(); + } } -WorkPackageMergePanel::WorkPackageMergePanel( QWidget *parent ) - : QWidget( parent ) +void WorkPackageMergeDialog::gotoFinish() { - setupUi( this ); + setPage(panel.ui_finishPage); } -WorkPackageMergeDialog::WorkPackageMergeDialog( const QString &text, const QMap &list, QWidget *parent ) - : KoDialog( parent ), - m_packages( list.values() ) +void WorkPackageMergeDialog::gotoProgress() { - panel.ui_text->setText( text ); + Package *package = m_packages.value(m_currentPackage); + Q_ASSERT(package); + if (package) { + delete m_progressPanel; + m_progressPanel = new TaskProgressPanel( *(package->toTask), nullptr, nullptr, panel.ui_stackedWidget ); + panel.ui_progressLayout->addWidget(m_progressPanel); + setPage(panel.ui_progressPage); + } else { + gotoFinish(); + } +} - QIcon icon = themedMessageBoxIcon( QMessageBox::Information ); - if ( ! icon.isNull() ) { - QStyleOption option; - option.initFrom( this ); - panel.ui_icon->setPixmap( icon.pixmap( style()->pixelMetric( QStyle::PM_MessageBoxIconSize, &option, this ) ) ); +void WorkPackageMergeDialog::fillCompletionModel(Package *package) +{ + Q_ASSERT(package->project); + Q_ASSERT(package->task); + Q_ASSERT(package->toTask); + QStandardItemModel *m = m_model; + m->setRowCount(0); + if (!package) { + return; + } + const Completion &newCompletion = package->task->completion(); + const Resource *resource1 = nullptr; + foreach(const Resource *r, newCompletion.resources()) { + if (r->id() == package->ownerId) { + resource1 = r; + break; + } } + Resource *resource2 = m_project->findResource(package->ownerId); + Q_ASSERT(resource2); + const Completion &currCompletion = package->toTask->completion(); - setButtons( KoDialog::Yes | KoDialog::No ); + bool started = !currCompletion.isStarted() && newCompletion.isStarted(); + panel.ui_updateStarted->setChecked(started); + if (newCompletion.isStarted()) { + panel.ui_started->setText(newCompletion.startTime().toString(Qt::DefaultLocaleShortDate)); + } else { + panel.ui_started->setText(xi18nc("@info", "Task is not started")); + } - panel.ui_view->setHeaderHidden( true ); - panel.ui_view->setRootIsDecorated( false ); - m_delegate = new KExtendableItemDelegate( panel.ui_view ); - panel.ui_view->setItemDelegate( m_delegate ); + bool finished = !currCompletion.isFinished() && newCompletion.isFinished(); + panel.ui_updateFinished->setChecked(finished); + if (newCompletion.isFinished()) { + panel.ui_finished->setText(newCompletion.finishTime().toString(Qt::DefaultLocaleShortDate)); + } else { + panel.ui_finished->setText(xi18nc("@info", "Task is not finished")); + } - m_model = new QStandardItemModel( panel.ui_view ); - foreach( Package *p, m_packages ) { - QList items; - items << new QStandardItem(); - items << new QStandardItem( p->project->childNode( 0 )->name() ); - items << new QStandardItem( static_cast( p->project->childNode( 0 ) )->workPackage().ownerName() ); - items << new QStandardItem( QLocale().toString(p->timeTag, QLocale::ShortFormat) ); + const Completion::EntryList &fromEntries = newCompletion.entries(); + Completion::EntryList::const_iterator it; + for (it = fromEntries.constBegin(); it != fromEntries.constEnd(); ++it) { + QList lst; - if ( p->toTask ) { - items[ CheckColumn ]->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable ); - items[ CheckColumn ]->setCheckState( Qt::Checked ); + lst << new QStandardItem(it.key().toString(Qt::DefaultLocaleShortDate)); + lst.last()->setData(it.key().toString(Qt::ISODate)); + if (currCompletion.entries().contains(it.key())) { + lst.last()->setToolTip(xi18nc("@info:tooltip", "Existing entry")); + } else { + lst.last()->setToolTip(xi18nc("@info:tooltip", "New entry")); + } - items[ TaskNameColumn ]->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); + lst << new QStandardItem(QString("%1% (%2%)").arg(it.value()->percentFinished).arg(currCompletion.percentFinished(it.key()))); + lst.last()->setData(it.value()->percentFinished); + lst.last()->setToolTip(xi18nc("@info:tooltip", "New value: %1%Current value: %2%", it.value()->percentFinished, currCompletion.percentFinished(it.key()))); + lst.last()->setCheckable(true); + if (package->settings.progress && it.value()->percentFinished != currCompletion.percentFinished(it.key())) { + lst.last()->setCheckState(Qt::Checked); + } - items[ OwnerNameColumn ]->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); + Duration ne = newCompletion.actualEffort(resource1, it.key()); + Duration oe = currCompletion.actualEffort(resource2, it.key()); + QString nv = ne.toString(Duration::Format_Hour); + QString ov = oe.toString(Duration::Format_Hour); + lst << new QStandardItem(QString("%1 (%2)").arg(nv, ov)); + lst.last()->setData(ne.toDouble()); + lst.last()->setToolTip(xi18nc("@info:tooltip", "New value: %1Current value: %2", nv, ov)); + lst.last()->setCheckable(true); + if (package->settings.usedEffort && ne != oe) { + lst.last()->setCheckState(Qt::Checked); + } + + nv = it.value()->remainingEffort.toString(Duration::Format_Hour); + ov = currCompletion.remainingEffort(it.key()).toString(Duration::Format_Hour); + lst << new QStandardItem(QString("%1 (%2)").arg(nv, ov)); + lst.last()->setData(it.value()->remainingEffort.toDouble()); + lst.last()->setToolTip(xi18nc("@info:tooltip", "New value: %1Current value: %2", nv, ov)); + lst.last()->setCheckable(true); + if (package->settings.remainingEffort && it.value()->remainingEffort != currCompletion.remainingEffort(it.key())) { + lst.last()->setCheckState(Qt::Checked); + } - items[DateTimeColumn]->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); - items[ DateTimeColumn ]->setTextAlignment( Qt::AlignCenter ); + m->appendRow(lst); + } + if (package->documents.isEmpty()) { + panel.ui_documents->setText(i18n("No documents")); + panel.ui_updateDocuments->setChecked(false); + } else { + panel.ui_documents->setText(i18n("Documents")); + panel.ui_updateDocuments->setChecked(true); + if (package->settings.documents) { + panel.ui_updateDocuments->setChecked(true); } - m_model->appendRow( items ); } + for (int i = 0; i < m_model->columnCount(); ++i) { + panel.ui_completionView->resizeColumnToContents(i); + } +} + +void WorkPackageMergeDialog::slotMerge() +{ + Package *package = m_packages.value(m_currentPackage); + if (package) { + acceptPackage(package); + } + gotoProgress(); +} - // sort on datetime first - QSortFilterProxyModel *dtsort = new QSortFilterProxyModel( panel.ui_view ); - dtsort->setSourceModel( m_model ); - dtsort->sort( DateTimeColumn, Qt::DescendingOrder ); +void WorkPackageMergeDialog::slotReject() +{ + Package *package = m_packages.value(m_currentPackage); + if (package) { + // TODO: undo command + WorkPackage *wp = new WorkPackage(package->task->workPackage()); + wp->setParentTask(package->toTask); + wp->setTransmitionStatus(WorkPackage::TS_Rejected); + package->toTask->addWorkPackage(wp); + emit terminateWorkPackage(package); + } + nextPackage(); +} - // then on task name - QSortFilterProxyModel *tasksort = new QSortFilterProxyModel( panel.ui_view ); - tasksort->setSourceModel( dtsort ); - tasksort->sort( TaskNameColumn, Qt::AscendingOrder ); +void WorkPackageMergeDialog::slotApply() +{ + applyPackage(); + nextPackage(); +} - panel.ui_view->setModel( tasksort ); - for ( int c = 0; c < m_model->columnCount(); ++c ) { - panel.ui_view->resizeColumnToContents( c ); +void WorkPackageMergeDialog::slotBack() +{ + if (m_cmd) { + m_cmd->undo(); + delete m_cmd; + m_cmd = nullptr; } - setMainWidget( &panel ); + setPage(panel.ui_packagePage); +} + +bool WorkPackageMergeDialog::updateStarted() const +{ + return panel.ui_updateStarted->isChecked(); +} - slotChanged(); +bool WorkPackageMergeDialog::updateFinished() const +{ + return panel.ui_updateFinished->isChecked(); +} - connect(panel.ui_view->model(), &QAbstractItemModel::dataChanged, this, &WorkPackageMergeDialog::slotChanged); - connect(panel.ui_view, &QAbstractItemView::activated, this, &WorkPackageMergeDialog::slotActivated); +bool WorkPackageMergeDialog::updateEntry(int row) const +{ + return updateProgress(row) || updateUsedEffort(row); } -WorkPackageMergeDialog::~WorkPackageMergeDialog() +bool WorkPackageMergeDialog::updateProgress(int row) const { - m_delegate->contractAll(); + return m_model->data(m_model->index(row, CompletionColumn), Qt::CheckStateRole).toBool(); } -void WorkPackageMergeDialog::slotActivated( const QModelIndex &idx ) +bool WorkPackageMergeDialog::updateUsedEffort(int row) const { - QModelIndex i = idx; - if ( i.column() >= CheckColumn ) { - i = i.model()->index( i.row(), TaskNameColumn, i.parent() ); - } - if ( i.column() != TaskNameColumn ) { - return; - } - if ( m_delegate->isExtended( i ) ) { - m_delegate->contractItem( i ); - } else { - m_delegate->extendItem( new PackageInfoWidget( m_packages.at( idx.row() ) ), i ); - } + return m_model->data(m_model->index(row, UsedEffortColumn), Qt::CheckStateRole).toBool(); } -QList WorkPackageMergeDialog::checkedList() const +bool WorkPackageMergeDialog::updateRemainingEffort(int row) const { - QList lst; - int count = m_model->rowCount(); - for ( int i = 0; i < count; ++i ) { - if ( m_model->index( i, 0 ).data( Qt::CheckStateRole ).toInt() == Qt::Checked ) { - lst << i; + return m_model->data(m_model->index(row, RemainingEffortColumn), Qt::CheckStateRole).toBool(); +} + +bool WorkPackageMergeDialog::updateDocuments() const +{ + return panel.ui_updateDocuments->isChecked(); +} + +QDate WorkPackageMergeDialog::date(int row) const +{ + return m_model->data(m_model->index(row, DateColumn), Qt::UserRole+1).toDate(); +} + +int WorkPackageMergeDialog::completion(int row) const +{ + return m_model->data(m_model->index(row, CompletionColumn), Qt::UserRole+1).toInt(); +} + +Duration WorkPackageMergeDialog::usedEffort(int row) const +{ + return Duration(m_model->data(m_model->index(row, UsedEffortColumn), Qt::UserRole+1).toDouble()); +} + +Duration WorkPackageMergeDialog::remainingEffort(int row) const +{ + return Duration(m_model->data(m_model->index(row, RemainingEffortColumn), Qt::UserRole+1).toDouble()); +} + +void WorkPackageMergeDialog::acceptPackage(const Package *package) +{ + Task *to = package->toTask; + const Task *from = package->task; + + Resource *resource = m_project->findResource( package->ownerId ); + if ( resource == 0 ) { + KMessageBox::error( 0, i18n( "The package owner '%1' is not a resource in this project. You must handle this manually.", package->ownerName ) ); + return; + } + + m_cmd = new MacroCommand( kundo2_noi18n("Merge workpackage") ); + Completion &org = to->completion(); + const Completion &curr = from->completion(); + + if (updateStarted()) { + if ( org.isStarted() != curr.isStarted() ) { + m_cmd->addCommand( new ModifyCompletionStartedCmd(org, curr.isStarted() ) ); + } + if ( org.startTime() != curr.startTime() ) { + m_cmd->addCommand( new ModifyCompletionStartTimeCmd( org, curr.startTime() ) ); + } + } + if (updateFinished()) { + if ( org.isFinished() != curr.isFinished() ) { + m_cmd->addCommand( new ModifyCompletionFinishedCmd( org, curr.isFinished() ) ); } + if ( org.finishTime() != curr.finishTime() ) { + m_cmd->addCommand( new ModifyCompletionFinishTimeCmd( org, curr.finishTime() ) ); + } + } + bool docsaved = false; + if (updateDocuments()) { + //TODO: handle remote files + QMap::const_iterator it = package->documents.constBegin(); + QMap::const_iterator end = package->documents.constEnd(); + for ( ; it != end; ++it ) { + const QUrl src = QUrl::fromLocalFile(it.key()); + KIO::CopyJob *job = KIO::move( src, it.value(), KIO::Overwrite ); + if ( job->exec() ) { + docsaved = true; + //TODO: async + debugPlan<<"Moved file:"<rowCount(); ++r) { + QDate d = date(r); + Q_ASSERT(d.isValid()); + if (!d.isValid()) { + continue; + } + if (updateEntry(r)) { + Completion::Entry *e = nullptr; + if (org.entries().contains(d)) { + e = new Completion::Entry(*(org.entry(d))); + m_cmd->addCommand(new RemoveCompletionEntryCmd(org, d)); + } else { + e = new Completion::Entry(); + } + if (updateProgress(r)) { + e->percentFinished = completion(r); + } + if (updateRemainingEffort(r)) { + e->remainingEffort = remainingEffort(r); + } + m_cmd->addCommand(new AddCompletionEntryCmd(org, d, e)); + } + // actual effort per resource on date (but not entry) + if (updateUsedEffort(r)) { + Completion::UsedEffort *ue = org.usedEffort(resource); + if (!ue) { + ue = new Completion::UsedEffort(); + m_cmd->addCommand(new AddCompletionUsedEffortCmd(org, resource, ue)); + } + m_cmd->addCommand(new AddCompletionActualEffortCmd(*ue, d, Completion::UsedEffort::ActualEffort(usedEffort(r), Duration::zeroDuration))); + } + } + if ( ! docsaved && m_cmd->isEmpty() ) { + //KMessageBox::information( 0, i18n( "Nothing to save from this package" ) ); + } + // add a copy to our tasks list of transmitted packages + WorkPackage *wp = new WorkPackage( from->workPackage() ); + wp->setParentTask( to ); + if ( ! wp->transmitionTime().isValid() ) { + wp->setTransmitionTime( package->timeTag ); } - return lst; + wp->setTransmitionStatus( WorkPackage::TS_Receive ); + + m_cmd->addCommand(new WorkPackageAddCmd(m_project, to, wp)); + m_cmd->redo(); } -void WorkPackageMergeDialog::slotChanged() + +void WorkPackageMergeDialog::applyPackage() { - enableButton( KoDialog::Yes, m_model->rowCount() > 0 ); + MacroCommand *cmd = new MacroCommand(m_cmd->text()); + emit executeCommand(cmd); + cmd->addCommand(m_cmd); + m_cmd = nullptr; + // user might have made manual changes, must go after m_cmd + MacroCommand *mc = m_progressPanel->buildCommand(); + if (mc) { + mc->redo(); + cmd->addCommand(mc); + } + emit terminateWorkPackage(m_packages.value(m_currentPackage)); } -} // namespace KPlato diff --git a/src/libs/ui/kptworkpackagemergedialog.h b/src/libs/ui/kptworkpackagemergedialog.h index 8851f998..c805a325 100644 --- a/src/libs/ui/kptworkpackagemergedialog.h +++ b/src/libs/ui/kptworkpackagemergedialog.h @@ -1,88 +1,112 @@ /* This file is part of the KDE project Copyright (C) 2011 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WORKPACKAGEMERGEDIALOG_H #define WORKPACKAGEMERGEDIALOG_H #include "planui_export.h" #include "ui_kptworkpackagemergepanel.h" #include #include #include -#include class KExtendableItemDelegate; class QStandardItemModel; +class KUndo2Command; + namespace KPlato { class Package; - -class PackageInfoWidget : public QFrame -{ - Q_OBJECT -public: - explicit PackageInfoWidget( Package *package, QWidget *parent = 0 ); - -protected Q_SLOTS: - void slotUsedEffortChanged( int state ); - void slotProgressChanged( int state ); - void slotDocumentsChanged( int state ); - -protected: - Package *m_package; -}; +class TaskProgressPanel; +class Project; +class Duration; +class MacroCommand; class WorkPackageMergePanel : public QWidget, public Ui::WorkPackageMergePanel { Q_OBJECT public: explicit WorkPackageMergePanel(QWidget *parent = 0); }; class PLANUI_EXPORT WorkPackageMergeDialog : public KoDialog { Q_OBJECT public: - enum Columns { CheckColumn = 0, TaskNameColumn, OwnerNameColumn, DateTimeColumn }; + enum Columns { DateColumn = 0, CompletionColumn, UsedEffortColumn, RemainingEffortColumn }; - WorkPackageMergeDialog( const QString &text, const QMap &list, QWidget *parent = 0 ); + WorkPackageMergeDialog(Project *project, const QMap &list, QWidget *parent = 0 ); ~WorkPackageMergeDialog(); QList checkedList() const; +Q_SIGNALS: + void executeCommand(KUndo2Command *cmd); + void terminateWorkPackage(const KPlato::Package *package); + protected Q_SLOTS: - void slotActivated( const QModelIndex &idx ); - void slotChanged(); + void slotMerge(); + void slotReject(); + void slotApply(); + void slotBack(); + +protected: + void nextPackage(); + void gotoProgress(); + void gotoFinish(); + void fillCompletionModel(Package *package); + void acceptPackage(const Package *package); + void applyPackage(); + + bool updateStarted() const; + bool updateFinished() const; + bool updateEntry(int row) const; + bool updateProgress(int row) const; + bool updateUsedEffort(int row) const; + bool updateRemainingEffort(int row) const; + bool updateDocuments() const; + + QDate date(int row) const; + int completion(int row) const; + Duration usedEffort(int row) const; + Duration remainingEffort(int row) const; + + void setPage(QWidget *page); private: + Project *m_project; WorkPackageMergePanel panel; KExtendableItemDelegate *m_delegate; QList m_packages; + int m_currentPackage; QStandardItemModel *m_model; + MacroCommand *m_cmd; + TaskProgressPanel *m_progressPanel; }; } // namespace KPlato #endif diff --git a/src/libs/ui/kptworkpackagemergepanel.ui b/src/libs/ui/kptworkpackagemergepanel.ui index 566c4ef3..b8d9ff2d 100644 --- a/src/libs/ui/kptworkpackagemergepanel.ui +++ b/src/libs/ui/kptworkpackagemergepanel.ui @@ -1,55 +1,263 @@ KPlato::WorkPackageMergePanel 0 0 - 512 - 331 + 619 + 356 - - - 0 - - - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + + 0 + 0 + + + + + 0 + 0 + + + + Form + + - + + + 0 + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + <owner name> + + + + + + + + 0 + 0 + + + + Task: + + + + + + + Generated: + + + + + + + From: + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + <package time> + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + <task name> + + + + + + + + + Qt::Horizontal + + + + + + + + + Started + + + + + + Update + + + + + + + <started> + + + + + + + + + + Finished + + + + + + Update + + + + + + + <finished> + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Documents + + + + + + Update + + + + + + + <documents>l + + + + + + + + + + + + Qt::Vertical + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + No more data. Press Close to finish. + + + Qt::AlignCenter + + + + + + diff --git a/src/libs/ui/kptworkpackagesenddialog.cpp b/src/libs/ui/kptworkpackagesenddialog.cpp index c2c3108b..4c768a6a 100644 --- a/src/libs/ui/kptworkpackagesenddialog.cpp +++ b/src/libs/ui/kptworkpackagesenddialog.cpp @@ -1,45 +1,46 @@ /* This file is part of the KDE project Copyright (C) 2007 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; 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 "kptworkpackagesenddialog.h" #include "kptworkpackagesendpanel.h" #include "kptdocumentspanel.h" #include "kpttask.h" #include -namespace KPlato -{ +using namespace KPlato; WorkPackageSendDialog::WorkPackageSendDialog( const QList &tasks, ScheduleManager *sm, QWidget *p) : KoDialog(p) { setCaption( xi18nc( "@title:window", "Send Work Packages") ); setButtons( Close ); setDefaultButton( Close ); showButtonSeparator( true ); m_wp = new WorkPackageSendPanel( tasks, sm, this); setMainWidget( m_wp ); } - -} //KPlato namespace +QSize WorkPackageSendDialog::sizeHint() const +{ + return QSize(350, 300); +} diff --git a/src/libs/ui/kptworkpackagesenddialog.h b/src/libs/ui/kptworkpackagesenddialog.h index a93b00ae..3696f1af 100644 --- a/src/libs/ui/kptworkpackagesenddialog.h +++ b/src/libs/ui/kptworkpackagesenddialog.h @@ -1,49 +1,51 @@ /* This file is part of the KDE project Copyright (C) 2007 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTWORKPACKAGESENDDIALOG_H #define KPTWORKPACKAGESENDDIALOG_H #include "planui_export.h" #include namespace KPlato { class WorkPackageSendPanel; class Node; class ScheduleManager; class PLANUI_EXPORT WorkPackageSendDialog : public KoDialog { Q_OBJECT public: explicit WorkPackageSendDialog( const QList &tasks, ScheduleManager *sm, QWidget *parent=0); WorkPackageSendPanel *panel() const { return m_wp; } + QSize sizeHint() const; + private: WorkPackageSendPanel *m_wp; }; } //KPlato namespace #endif // KPTWORKPACKAGESENDDIALOG_H diff --git a/src/libs/ui/kptworkpackagesendpanel.cpp b/src/libs/ui/kptworkpackagesendpanel.cpp index cf556018..27aa26bd 100644 --- a/src/libs/ui/kptworkpackagesendpanel.cpp +++ b/src/libs/ui/kptworkpackagesendpanel.cpp @@ -1,74 +1,87 @@ /* This file is part of the KDE project Copyright (C) 2007 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptworkpackagesendpanel.h" #include "kpttaskeditor.h" #include "kptproject.h" #include "kpttask.h" #include "kptschedule.h" #include #include #include #include +#include +#include #include +#include +#include -namespace KPlato -{ +using namespace KPlato; WorkPackageSendPanel::WorkPackageSendPanel( const QList &tasks, ScheduleManager *sm, QWidget *p ) : QWidget(p) { setupUi( this ); + QStandardItemModel *m = new QStandardItemModel(0, 1, ui_treeView); + m->setHeaderData(0, Qt::Horizontal, xi18nc("@title", "Resource")); + ui_treeView->setModel(m); + long id = sm ? sm->scheduleId() : NOTSCHEDULED; foreach ( Node *n, tasks ) { Task *t = qobject_cast( n ); if ( t == 0 ) { continue; } foreach ( Resource *r, t->workPackage().fetchResources( id ) ) { - m_resMap[ r ] << n; + m_resMap[r->name()] = r; + m_nodeMap[ r ] << n; } } - QMap >::const_iterator it; + QMap::const_iterator it; for ( it = m_resMap.constBegin(); it != m_resMap.constEnd(); ++it ) { - QPushButton *pb = new QPushButton(koIcon("mail-send"), i18n("Send To..."), scrollArea); - QLineEdit *le = new QLineEdit( it.key()->name(), scrollArea ); - le->setReadOnly( true ); - formLayout->addRow( pb, le ); - - connect( pb, &QAbstractButton::clicked, this, &WorkPackageSendPanel::slotSendClicked ); - m_pbMap[ pb ] = it.key(); + QStandardItem *item = new QStandardItem(it.key()); + m->appendRow(item); } - + connect(ui_publish, &QAbstractButton::clicked, this, &WorkPackageSendPanel::slotSendClicked); + connect(ui_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WorkPackageSendPanel::slotSelectionChanged); } void WorkPackageSendPanel::slotSendClicked() { - Resource *r = m_pbMap[ qobject_cast( sender() ) ]; - emit sendWorkpackages( m_resMap[ r ], r ); +// Resource *r = m_pbMap[ qobject_cast( sender() ) ]; + QModelIndex idx = ui_treeView->selectionModel()->selectedIndexes().value(0); + if (idx.isValid()) { + Resource *r = m_resMap.value(idx.data().toString()); + Q_ASSERT(r); + emit sendWorkpackages( m_nodeMap[ r ], r, true /*by mail for now*/); // FIXME + ui_treeView->model()->setData(idx, Qt::Checked, Qt::CheckStateRole); + } } - -} //KPlato namespace +void WorkPackageSendPanel::slotSelectionChanged() +{ + ui_publish->setEnabled(ui_treeView->selectionModel()->selectedIndexes().value(0).isValid()); +} diff --git a/src/libs/ui/kptworkpackagesendpanel.h b/src/libs/ui/kptworkpackagesendpanel.h index cffb419d..c3f0398e 100644 --- a/src/libs/ui/kptworkpackagesendpanel.h +++ b/src/libs/ui/kptworkpackagesendpanel.h @@ -1,58 +1,60 @@ /* This file is part of the KDE project Copyright (C) 2007 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTWORKPACKAGESENDPANEL_H #define KPTWORKPACKAGESENDPANEL_H #include "planui_export.h" #include "ui_kptworkpackagesendpanel.h" #include #include class QPushButton; namespace KPlato { class Resource; class Node; class ScheduleManager; class PLANUI_EXPORT WorkPackageSendPanel : public QWidget, public Ui_WorkPackageSendPanel { Q_OBJECT public: explicit WorkPackageSendPanel( const QList &tasks, ScheduleManager *sm, QWidget *parent=0 ); Q_SIGNALS: - void sendWorkpackages(const QList&, KPlato::Resource*); + void sendWorkpackages(const QList&, KPlato::Resource*, bool); protected Q_SLOTS: void slotSendClicked(); + void slotSelectionChanged(); protected: - QMap > m_resMap; - QMap m_pbMap; + QMap m_resMap; + QMap > m_nodeMap; }; } //KPlato namespace #endif // KPTWORKPACKAGESENDPANEL_H diff --git a/src/libs/ui/kptworkpackagesendpanel.ui b/src/libs/ui/kptworkpackagesendpanel.ui index 0a2a984d..f2a5ad3e 100644 --- a/src/libs/ui/kptworkpackagesendpanel.ui +++ b/src/libs/ui/kptworkpackagesendpanel.ui @@ -1,46 +1,58 @@ KPlato::WorkPackageSendPanel 0 0 - 447 - 247 + 470 + 279 - - 0 - - - - QFrame::NoFrame + + + false - - true + + false - - - - 0 - 0 - 447 - 247 - - - - - QFormLayout::ExpandingFieldsGrow - - - + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + Publish... + + + + + + + + diff --git a/src/workpackage/main.cpp b/src/workpackage/main.cpp index 4bc646ee..dc75d3f4 100644 --- a/src/workpackage/main.cpp +++ b/src/workpackage/main.cpp @@ -1,66 +1,66 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander 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 "commandlineparser.h" #include #include #include #include #include extern "C" Q_DECL_EXPORT int kdemain( int argc, char **argv ) { QApplication app(argc, argv); #ifdef Q_OS_MACOS // app.applicationName() will return "Plan Work" because of the nice name // set in the Info.plist. DBus doesn't like the resulting space in the // service name, so reset the application name: app.setApplicationName("calligraplanwork"); #endif KDBusService service(KDBusService::Unique); // we come here only once... /** * Disable debug output by default, only log warnings. * Debug logs can be controlled by the environment variable QT_LOGGING_RULES. * * For example, to get full debug output, run the following: * QT_LOGGING_RULES="calligra.planwork=true" calligraplan * * See: http://doc.qt.io/qt-5/qloggingcategory.html */ - QLoggingCategory::setFilterRules("calligra.*.debug=false\n" - "calligra.*.warning=true"); + QLoggingCategory::setFilterRules("calligra.plan*.debug=false\n" + "calligra.plan*.warning=true"); // Migrate data from kde4 to kf5 locations Calligra2Migration m("calligraplanwork", "planwork"); m.setConfigFiles(QStringList() << QStringLiteral("planworkrc")); m.setUiFiles(QStringList() << QStringLiteral("planwork.rc") << QStringLiteral("planwork_readonly.rc") << QStringLiteral("planworkui.rc")); m.migrate(); CommandLineParser cmd; QObject::connect(&service, &KDBusService::activateRequested, &cmd, &CommandLineParser::handleActivateRequest); cmd.handleCommandLine(QDir::current()); return app.exec(); } diff --git a/src/workpackage/taskcompletiondialog.cpp b/src/workpackage/taskcompletiondialog.cpp index 76704b36..a7374de6 100644 --- a/src/workpackage/taskcompletiondialog.cpp +++ b/src/workpackage/taskcompletiondialog.cpp @@ -1,490 +1,104 @@ /* This file is part of the KDE project Copyright (C) 2009, 2011, 2012 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "taskcompletiondialog.h" #include "workpackage.h" +#include "kpttaskprogresspanel.h" #include "kptusedefforteditor.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include #include #include +#include #include "debugarea.h" -using namespace KPlato; +using namespace KPlatoWork; -namespace KPlatoWork -{ -TaskCompletionDialog::TaskCompletionDialog(WorkPackage &p, ScheduleManager *sm, QWidget *parent) +TaskCompletionDialog::TaskCompletionDialog(WorkPackage &package, ScheduleManager *sm, QWidget *parent) : KoDialog(parent) { setCaption(i18n("Task Progress") ); setButtons( Ok|Cancel ); setDefaultButton( Ok ); showButtonSeparator( true ); - m_panel = new TaskCompletionPanel( p, sm, this); + m_panel = new TaskCompletionPanel( package, sm, this); setMainWidget(m_panel); enableButtonOk(false); connect(m_panel, &TaskCompletionPanel::changed, this, &TaskCompletionDialog::slotChanged); } -void TaskCompletionDialog::slotChanged( bool ) +void TaskCompletionDialog::slotChanged( bool state ) { - enableButtonOk( true ); + enableButtonOk( state ); } KUndo2Command *TaskCompletionDialog::buildCommand() { //debugPlanWork; return m_panel->buildCommand(); } - -TaskCompletionPanel::TaskCompletionPanel(WorkPackage &p, ScheduleManager *sm, QWidget *parent) - : QWidget(parent), - m_package( &p ) +TaskCompletionPanel::TaskCompletionPanel(WorkPackage &package, ScheduleManager *sm, QWidget *parent) + : QWidget(parent) { - //debugPlanWork; - setupUi(this); - - addEntryBtn->setIcon(koIcon("list-add")); - removeEntryBtn->setIcon(koIcon("list-remove")); - - CompletionEntryItemModel *m = new CompletionEntryItemModel( this ); - entryTable->setItemDelegateForColumn ( 1, new ProgressBarDelegate( this ) ); - entryTable->setItemDelegateForColumn ( 2, new DurationSpinBoxDelegate( this ) ); - entryTable->setItemDelegateForColumn ( 3, new DurationSpinBoxDelegate( this ) ); - entryTable->setCompletionModel( m ); - - Task *task = qobject_cast( p.node() ); - m_completion = task->completion(); - started->setChecked(m_completion.isStarted()); - finished->setChecked(m_completion.isFinished()); - startTime->setDateTime(m_completion.startTime()); - finishTime->setDateTime(m_completion.finishTime()); - finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); - - scheduledEffort = p.node()->estimate()->expectedValue(); - - if ( m_completion.usedEffortMap().isEmpty() || task->requests().isEmpty() ) { - foreach ( ResourceGroupRequest *g, task->requests().requests() ) { - foreach ( ResourceRequest *r, g->resourceRequests() ) { - m_completion.addUsedEffort( r->resource() ); + Q_ASSERT(sm); + if (package.task()->completion().entrymode() != KPlato::Completion::EnterEffortPerResource) { + package.task()->completion().setEntrymode(KPlato::Completion::EnterEffortPerResource); + } + if (package.task()->completion().resources().isEmpty()) { + foreach (Resource *r, package.task()->assignedResources(sm->scheduleId())) { + if (r->id() == package.task()->workPackage().ownerId()) { + package.task()->completion().addUsedEffort(r); } } } - - enableWidgets(); - started->setFocus(); - - m->setManager( sm ); - m->setTask( task ); - Resource *r = p.project()->findResource( task->workPackage().ownerId() ); - m->setSource( r, task, &m_completion ); - - entryTable->horizontalHeader()->swapSections( CompletionEntryItemModel::Property_PlannedEffort, CompletionEntryItemModel::Property_ActualAccumulated ); - - Duration pr = task->plannedEffort( r ); - Duration tr = task->plannedEffort(); - ui_plannedLabel->setText(i18n("Planned effort for %1: %2. Total planned effort for task: %3", r->name(), pr.format(), tr.format())); - - entryTable->scrollToBottom(); - entryTable->resizeColumnsToContents(); - - connect( addEntryBtn, &QAbstractButton::clicked, this, &TaskCompletionPanel::slotAddEntry ); - connect( removeEntryBtn, &QAbstractButton::clicked, entryTable, &KPlato::CompletionEntryEditor::removeEntry ); - - connect( entryTable, &KPlato::CompletionEntryEditor::rowInserted, this, &TaskCompletionPanel::slotEntryAdded ); - connect(entryTable, &KPlato::CompletionEntryEditor::changed, this, &TaskCompletionPanel::slotChanged ); - connect(entryTable, &KPlato::CompletionEntryEditor::changed, this, &TaskCompletionPanel::slotEntryChanged ); - connect(entryTable, &KPlato::CompletionEntryEditor::rowInserted, this, &TaskCompletionPanel::slotChanged ); - connect(entryTable, &KPlato::CompletionEntryEditor::rowInserted, this, &TaskCompletionPanel::slotEntryChanged ); - connect(entryTable, &KPlato::CompletionEntryEditor::rowRemoved, this, &TaskCompletionPanel::slotEntryChanged ); - connect(entryTable, &KPlato::CompletionEntryEditor::selectedItemsChanged, this, &TaskCompletionPanel::slotSelectionChanged ); - - - connect(started, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotStartedChanged); - connect(started, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotChanged); - connect(finished, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotFinishedChanged); - connect(finished, &QAbstractButton::toggled, this, &TaskCompletionPanel::slotChanged); - - connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotChanged); - connect(startTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotStartTimeChanged); - connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotChanged); - connect(finishTime, &QDateTimeEdit::dateTimeChanged, this, &TaskCompletionPanel::slotFinishTimeChanged); - - removeEntryBtn->setEnabled( false ); -} - -QSize TaskCompletionPanel::sizeHint() const -{ - return QWidget::sizeHint().expandedTo( QSize( 610, 0 ) ); + QVBoxLayout *l = new QVBoxLayout(this); + m_panel = new KPlato::TaskProgressPanel(*(package.task()), sm, nullptr, this); + m_panel->editModeWidget->setVisible(false); + m_panel->addResourceWidget->setVisible(false); + m_panel->resourceTable->verticalHeader()->hide(); + QSize size = m_panel->resourceTable->sizeHint(); + size.setHeight(120); + m_panel->resourceTable->setSizeHint(size); + l->addWidget(m_panel); + connect(m_panel, &KPlato::TaskProgressPanelImpl::changed, this, &TaskCompletionPanel::slotChanged); } KUndo2Command *TaskCompletionPanel::buildCommand() { - MacroCommand *cmd = new MacroCommand( kundo2_i18n("Modify task completion") ); - Completion &org = m_package->task()->completion(); - if ( org.entrymode() != m_completion.entrymode() ) { - cmd->addCommand( new ModifyCompletionEntrymodeCmd(org, m_completion.entrymode() ) ); - } - if ( org.isStarted() != m_completion.isStarted() ) { - cmd->addCommand( new ModifyCompletionStartedCmd(org, m_completion.isStarted() ) ); - } - if ( org.isFinished() != m_completion.isFinished() ) { - cmd->addCommand( new ModifyCompletionFinishedCmd(org, m_completion.isFinished() ) ); - } - if ( org.startTime() != m_completion.startTime() ) { - cmd->addCommand( new ModifyCompletionStartTimeCmd(org, m_completion.startTime() ) ); - } - if ( org.finishTime() != m_completion.finishTime() ) { - cmd->addCommand( new ModifyCompletionFinishTimeCmd(org, m_completion.finishTime() ) ); - } - QList orgdates = org.entries().keys(); - QList m_completiondates = m_completion.entries().keys(); - foreach ( const QDate &d, orgdates ) { - if ( m_completiondates.contains( d ) ) { - if ( m_completion.entry( d ) == org.entry( d ) ) { - continue; - } - Completion::Entry *e = new Completion::Entry( *( m_completion.entry( d ) ) ); - cmd->addCommand( new ModifyCompletionEntryCmd(org, d, e ) ); - } else { - cmd->addCommand( new RemoveCompletionEntryCmd(org, d ) ); - } - } - foreach ( const QDate &d, m_completiondates ) { - if ( ! orgdates.contains( d ) ) { - Completion::Entry *e = new Completion::Entry( * ( m_completion.entry( d ) ) ); - cmd->addCommand( new AddCompletionEntryCmd(org, d, e ) ); - } - } - - if ( cmd->isEmpty() ) { - delete cmd; - return 0; - } - return cmd; + return m_panel->buildCommand(); } void TaskCompletionPanel::slotChanged() { emit changed( true ); //FIXME } -void TaskCompletionPanel::slotStartedChanged(bool state) { - m_completion.setStarted( state ); - if (state) { - m_completion.setStartTime( QDateTime::currentDateTime() ); - startTime->setDateTime( m_completion.startTime() ); - slotCalculateEffort(); - } - enableWidgets(); -} - -void TaskCompletionPanel::setFinished() { - finishTime->setDateTime( QDateTime::currentDateTime() ); - slotFinishTimeChanged( finishTime->dateTime() ); -} - -void TaskCompletionPanel::slotFinishedChanged(bool state) { - debugPlanWork<node()->plannedEffort() ); - m_completion.addEntry( m_completion.finishTime().date(), e ); - entryTable->setCompletion( &m_completion ); - debugPlanWork<<"Entry added:"<model()->rowCount() - 1; - QModelIndex idx = entryTable->model()->index( row, CompletionEntryItemModel::Property_Completion ); - entryTable->model()->setData( idx, 100 ); - } - } - enableWidgets(); -} - -void TaskCompletionPanel::slotFinishTimeChanged( const QDateTime &dt ) -{ - m_completion.setFinishTime( dt ); -} - -void TaskCompletionPanel::slotStartTimeChanged( const QDateTime &dt ) -{ - m_completion.setStartTime( dt ); - finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); -} - -void TaskCompletionPanel::slotAddEntry() -{ - CompletionEntryItemModel *m = static_cast( entryTable->model() ); - int col = KPlato::CompletionEntryItemModel::Property_UsedEffort; - - entryTable->addEntry(); - - m_completion.setEntrymode( Completion::EnterEffortPerTask ); - m->setFlags( col, Qt::ItemIsEditable ); -} - -void TaskCompletionPanel::slotEntryChanged() -{ - finishTime->setMinimumDateTime( qMax( startTime->dateTime(), QDateTime(m_completion.entryDate(), QTime(), Qt::LocalTime) ) ); - if ( ! finished->isChecked() && ! m_completion.isFinished() && m_completion.percentFinished() == 100 ) { - finished->setChecked( true ); - } -} - -void TaskCompletionPanel::enableWidgets() { - started->setEnabled(!finished->isChecked()); - finished->setEnabled(started->isChecked()); - finishTime->setEnabled(finished->isChecked()); - startTime->setEnabled(started->isChecked() && !finished->isChecked()); -} - - -void TaskCompletionPanel::slotPercentFinishedChanged( int ) { - slotCalculateEffort(); -} - -void TaskCompletionPanel::slotCalculateEffort() -{ -} - -void TaskCompletionPanel::slotEntryAdded( const QDate& date ) -{ - debugPlanWork<setEnabled( !sel.isEmpty() ); -} - -void TaskCompletionPanel::slotEditmodeChanged( int index ) -{ - Q_UNUSED( index ); - -} - -//------------------- -CompletionEntryItemModel::CompletionEntryItemModel( QObject *parent ) - : KPlato::CompletionEntryItemModel( parent ), - m_calculate( false ), - m_resource( 0 ), - m_task( 0 ) -{ - m_headers << i18nc("@title:column", "Total"); -} - -void CompletionEntryItemModel::setSource( Resource *resource, Task *task, Completion *completion ) -{ - m_resource = resource; - m_task = task; - setCompletion( completion ? completion : &(task->completion()) ); -} - -QVariant CompletionEntryItemModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) { - switch (role) { - case Qt::ToolTipRole: - switch (section) { - case Property_UsedEffort: - return xi18nc("@info:tooltip", "Used effort since previous entry"); - case Property_ActualAccumulated: - return xi18nc("@info:tooltip", "Accumulated used effort"); - default: break; - } - default: break; - } - } - return KPlato::CompletionEntryItemModel::headerData(section, orientation, role); -} - -int CompletionEntryItemModel::columnCount( const QModelIndex& ) const -{ - return 6; -} - -QVariant CompletionEntryItemModel::actualEffort ( int row, int role ) const -{ - Completion::Entry *e = m_completion->entry( date( row ).toDate() ); - if ( e == 0 ) { - return QVariant(); - } - switch ( role ) { - case Qt::DisplayRole: - case Qt::ToolTipRole: { - Duration v = e->totalPerformed; - if ( row > 0 ) { - v -= m_completion->entry( date( row - 1 ).toDate() )->totalPerformed; - } - //debugPlanWork<name()<<": "<totalPerformed; - if ( row > 0 ) { - v -= m_completion->entry( date( row - 1 ).toDate() )->totalPerformed; - } - //debugPlanWork<name()<<": "<( Duration::Unit_h ); - case Role::Minimum: - return static_cast( Duration::Unit_h ); - case Role::Maximum: - return static_cast( Duration::Unit_h ); - case Qt::StatusTipRole: - case Qt::WhatsThisRole: - return QVariant(); - default: - break; - } - return QVariant(); -} - -QVariant CompletionEntryItemModel::data( const QModelIndex &idx, int role ) const -{ - if ( idx.column() == Property_PlannedEffort && m_resource ) { - switch ( role ) { - case Qt::DisplayRole: { - Duration v = m_task->plannedEffortTo( m_resource, date( idx.row() ).toDate() ); - return v.format(); - } - default: - return QVariant(); - } - } else if ( idx.column() == Property_ActualAccumulated ) { - switch ( role ) { - case Qt::DisplayRole: { - Duration v; - Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); - if ( e ) { - v = e->totalPerformed; - } - return v.format(); - } - default: - return QVariant(); - } - } - return KPlato::CompletionEntryItemModel::data( idx, role ); -} - -bool CompletionEntryItemModel::setData( const QModelIndex &idx, const QVariant &value, int role ) -{ - //debugPlanWork; - switch ( role ) { - case Qt::EditRole: { - if ( idx.column() == Property_Date ) { - QDate od = date( idx.row() ).toDate(); - removeEntry( od ); - addEntry( value.toDate() ); - // emit dataChanged( idx, idx ); - m_calculate = true; - return true; - } - if ( idx.column() == Property_Completion ) { - Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); - if ( e == 0 ) { - return false; - } - e->percentFinished = value.toInt(); - if ( m_calculate && m_node && idx.row() == rowCount() - 1 ) { - // calculate used/remaining - Duration est = m_node->plannedEffort( id(), ECCT_EffortWork ); - e->totalPerformed = est * e->percentFinished / 100; - e->remainingEffort = est - e->totalPerformed; - } else if ( e->percentFinished == 100 && e->remainingEffort != 0 ) { - e->remainingEffort = Duration::zeroDuration; - } - emit dataChanged( idx, createIndex( idx.row(), 3 ) ); - return true; - } - if ( idx.column() == Property_ActualEffort ) { - Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); - if ( e == 0 ) { - return false; - } - m_calculate = false; - Duration prev; - if ( idx.row() > 0 ) { - Completion::Entry *pe = m_completion->entry( date( idx.row() - 1 ).toDate() ); - if ( pe ) { - prev = pe->totalPerformed; - } - } - double v( value.toList()[0].toDouble() ); - Duration::Unit unit = static_cast( value.toList()[1].toInt() ); - Duration d = Estimate::scale( v, unit, scales() ); - if ( d + prev == e->totalPerformed ) { - return false; - } - e->totalPerformed = d + prev; - emit dataChanged( idx, idx ); - return true; - } - if ( idx.column() == Property_RemainigEffort ) { - Completion::Entry *e = m_completion->entry( date( idx.row() ).toDate() ); - if ( e == 0 ) { - return false; - } - m_calculate = false; - double v( value.toList()[0].toDouble() ); - Duration::Unit unit = static_cast( value.toList()[1].toInt() ); - Duration d = Estimate::scale( v, unit, scales() ); - if ( d == e->remainingEffort ) { - return false; - } - e->remainingEffort = d; - debugPlanWork<remainingEffort.format(); - emit dataChanged( idx, idx ); - return true; - } - break; - } - default: break; - } - return false; -} - -} //KPlatoWork namespace diff --git a/src/workpackage/taskcompletiondialog.h b/src/workpackage/taskcompletiondialog.h index 9e9a0046..b048d023 100644 --- a/src/workpackage/taskcompletiondialog.h +++ b/src/workpackage/taskcompletiondialog.h @@ -1,137 +1,85 @@ /* This file is part of the KDE project Copyright (C) 2009, 2011 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPLATOWORK_TASKCOMPLETIONDIALOG_H #define KPLATOWORK_TASKCOMPLETIONDIALOG_H #include "planwork_export.h" #include "ui_taskcompletionpanel.h" #include "workpackage.h" #include "kptusedefforteditor.h" #include #include class KUndo2Command; namespace KPlato { class ScheduleManager; + class TaskProgressPanel; } namespace KPlatoWork { class TaskCompletionPanel; class PLANWORK_EXPORT TaskCompletionDialog : public KoDialog { Q_OBJECT public: - explicit TaskCompletionDialog( WorkPackage &p, ScheduleManager *sm, QWidget *parent=0 ); + explicit TaskCompletionDialog( WorkPackage &package, ScheduleManager *sm, QWidget *parent=0 ); KUndo2Command *buildCommand(); protected Q_SLOTS: void slotChanged( bool ); private: TaskCompletionPanel *m_panel; }; -class PLANWORK_EXPORT TaskCompletionPanel : public QWidget, public Ui::TaskCompletionPanel +class PLANWORK_EXPORT TaskCompletionPanel : public QWidget { Q_OBJECT public: - explicit TaskCompletionPanel( WorkPackage &p, ScheduleManager *sm, QWidget *parent=0 ); + explicit TaskCompletionPanel( WorkPackage &package, ScheduleManager *sm, QWidget *parent=0 ); KUndo2Command *buildCommand(); void enableWidgets(); - QSize sizeHint() const; - Q_SIGNALS: void changed( bool ); public Q_SLOTS: void slotChanged(); - void slotStartedChanged(bool state); - void slotFinishedChanged(bool state); - void slotPercentFinishedChanged(int value); - void slotStartTimeChanged( const QDateTime &dt ); - void slotFinishTimeChanged( const QDateTime &dt ); - void slotAddEntry(); - void slotEntryChanged(); - void slotSelectionChanged( const QItemSelection &sel ); - - void slotEntryAdded( const QDate& date ); - - void slotEditmodeChanged( int ); - -protected Q_SLOTS: - void slotCalculateEffort(); - -protected: - void setFinished(); - - WorkPackage *m_package; - Completion m_completion; - int m_dayLength; - - Duration scheduledEffort; -}; - -class CompletionEntryItemModel : public KPlato::CompletionEntryItemModel -{ - Q_OBJECT -public: - enum Properties { - Property_Date = KPlato::CompletionEntryItemModel::Property_Date, - Property_Completion = KPlato::CompletionEntryItemModel::Property_Completion, - Property_ActualEffort = KPlato::CompletionEntryItemModel::Property_UsedEffort, - Property_RemainigEffort = KPlato::CompletionEntryItemModel::Property_RemainingEffort, - Property_PlannedEffort = KPlato::CompletionEntryItemModel::Property_PlannedEffort, - Property_ActualAccumulated - }; - - explicit CompletionEntryItemModel(QObject *parent = 0); - - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - int columnCount( const QModelIndex &idx = QModelIndex() ) const; - QVariant data( const QModelIndex &idx, int role ) const; - bool setData( const QModelIndex &idx, const QVariant &value, int role ); - - void setSource( Resource *resource, Task *task, Completion *completion = 0 ); - -protected: - virtual QVariant actualEffort( int row, int role ) const; private: - bool m_calculate; // opens for calculating used-/remaining effort - Resource *m_resource; - Task *m_task; + KPlato::TaskProgressPanel *m_panel; }; } //KPlatoWork namespace #endif diff --git a/src/workpackage/view.cpp b/src/workpackage/view.cpp index d26f30c2..cfec5f11 100644 --- a/src/workpackage/view.cpp +++ b/src/workpackage/view.cpp @@ -1,520 +1,528 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2009, 2011, 2012 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "view.h" #include "mainwindow.h" #include "taskworkpackageview.h" #include "workpackage.h" #include "packagesettings.h" #include "taskcompletiondialog.h" #include "calligraplanworksettings.h" #include "kpttaskeditor.h" #include "kpttaskdescriptiondialog.h" #include "kptcommonstrings.h" #include "KoDocumentInfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "part.h" #include "factory.h" #include "kptviewbase.h" #include "kptdocumentseditor.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptcommand.h" #include "kptdocuments.h" #include "kpttaskprogressdialog.h" #include "kptcalendar.h" #include #include "debugarea.h" namespace KPlatoWork { View::View( Part *part, QWidget *parent, KActionCollection *collection ) : QStackedWidget( parent ), m_part( part ), m_scheduleActionGroup( new QActionGroup( this ) ), m_manager( 0 ) { m_readWrite = part->isReadWrite(); debugPlanWork<addAction("package_remove_selected", actionRemoveSelectedPackages ); connect( actionRemoveSelectedPackages, &QAction::triggered, this, &View::slotRemoveSelectedPackages ); actionRemoveCurrentPackage = new QAction(koIcon("edit-delete"), i18n("Remove Package"), this); collection->addAction("package_remove_current", actionRemoveCurrentPackage ); connect( actionRemoveCurrentPackage, &QAction::triggered, this, &View::slotRemoveCurrentPackage ); actionViewList = new QAction(koIcon("view-list-tree"), i18n("List"), this); actionViewList->setToolTip( i18nc( "@info:tooltip", "Select task list" ) ); collection->addAction("view_list", actionViewList ); connect( actionViewList, &QAction::triggered, this, &View::slotViewList ); actionViewGantt = new QAction(koIcon("view-time-schedule"), i18n("Gantt"), this); actionViewGantt->setToolTip( i18nc( "@info:tooltip", "Select timeline" ) ); collection->addAction("view_gantt", actionViewGantt ); connect( actionViewGantt, &QAction::triggered, this, &View::slotViewGantt ); // actionTaskProgress = new QAction(koIcon("document-edit"), i18n("Progress..."), this); // collection->addAction("task_progress", actionTaskProgress ); // connect( actionTaskProgress, SIGNAL(triggered(bool)), SLOT(slotTaskProgress()) ); //------ Settings actionConfigure = new QAction(koIcon("configure"), i18n("Configure PlanWork..."), this); collection->addAction("configure", actionConfigure ); connect( actionConfigure, &QAction::triggered, this, &View::slotConfigure ); //------ Popups actionEditDocument = new QAction(koIcon("document-edit"), i18n("Edit..."), this); collection->addAction("edit_document", actionEditDocument ); connect( actionEditDocument, SIGNAL(triggered(bool)), SLOT(slotEditDocument()) ); actionViewDocument = new QAction(koIcon("document-preview"), i18nc( "@verb", "View..."), this); collection->addAction("view_document", actionViewDocument ); connect( actionViewDocument, &QAction::triggered, this, &View::slotViewDocument ); // FIXME remove UndoText::removeDocument() when string freeze is lifted actionRemoveDocument = new QAction(koIcon("list-remove"), UndoText::removeDocument().toString(), this); collection->addAction("remove_document", actionRemoveDocument ); connect( actionRemoveDocument, &QAction::triggered, this, &View::slotRemoveDocument ); actionSendPackage = new QAction(koIcon("mail-send"), i18n("Send Package..."), this); collection->addAction("edit_sendpackage", actionSendPackage ); connect( actionSendPackage, &QAction::triggered, this, &View::slotSendPackage ); actionPackageSettings = new QAction(koIcon("document-properties"), i18n("Package Settings..."), this); collection->addAction("edit_packagesettings", actionPackageSettings ); connect( actionPackageSettings, &QAction::triggered, this, &View::slotPackageSettings ); actionTaskCompletion = new QAction(koIcon("document-edit"), i18n("Edit Progress..."), this); collection->addAction("task_progress", actionTaskCompletion ); connect( actionTaskCompletion, &QAction::triggered, this, &View::slotTaskCompletion ); actionViewDescription = new QAction(/*koIcon("document_view"),*/ i18n("View Description..."), this); collection->addAction("task_description", actionViewDescription ); connect( actionViewDescription, &QAction::triggered, this, &View::slotTaskDescription ); updateReadWrite( m_readWrite ); //debugPlanWork<<" end"; loadContext(); slotCurrentChanged( currentIndex() ); connect( this, &QStackedWidget::currentChanged, this, &View::slotCurrentChanged ); slotSelectionChanged(); } View::~View() { saveContext(); } void View::slotCurrentChanged( int index ) { actionViewList->setEnabled( index != 0 ); actionViewGantt->setEnabled( index != 1 ); saveContext(); } void View::slotViewList() { debugPlanWork; setCurrentIndex( 0 ); } void View::slotViewGantt() { debugPlanWork; setCurrentIndex( 1 ); } void View::createViews() { QWidget *v = createTaskWorkPackageView(); addWidget( v ); v = createGanttView(); addWidget( v ); } TaskWorkPackageView *View::createTaskWorkPackageView() { TaskWorkPackageView *v = new TaskWorkPackageView( part(), this ); connect( v, &AbstractView::requestPopupMenu, this, &View::slotPopupMenu ); connect( v, &AbstractView::selectionChanged, this, &View::slotSelectionChanged ); v->updateReadWrite( m_readWrite ); v->loadContext(); return v; } TaskWPGanttView *View::createGanttView() { TaskWPGanttView *v = new TaskWPGanttView( part(), this ); connect( v, &AbstractView::requestPopupMenu, this, &View::slotPopupMenu ); connect( v, &AbstractView::selectionChanged, this, &View::slotSelectionChanged ); v->updateReadWrite( m_readWrite ); v->loadContext(); return v; } void View::setupPrinter( QPrinter &/*printer*/, QPrintDialog &/*printDialog */) { //debugPlanWork; } void View::print( QPrinter &/*printer*/, QPrintDialog &/*printDialog*/ ) { } void View::slotSelectionChanged() { bool enable = ! currentView()->selectedNodes().isEmpty(); actionRemoveSelectedPackages->setEnabled( enable ); actionRemoveCurrentPackage->setEnabled( enable ); } void View::slotEditCut() { //debugPlanWork; } void View::slotEditCopy() { //debugPlanWork; } void View::slotEditPaste() { //debugPlanWork; } void View::slotProgressChanged( int ) { } void View::slotConfigure() { } ScheduleManager *View::currentScheduleManager() const { - return 0; // atm we always work with default manager + WorkPackage *wp = m_part->findWorkPackage( currentNode() ); + return wp ? wp->project()->scheduleManagers().value(0) : nullptr; } void View::updateReadWrite( bool readwrite ) { debugPlanWork<"<setEnabled( readwrite ); emit sigUpdateReadWrite( readwrite ); } Part *View::part() const { return m_part; } void View::slotPopupMenu( const QString& name, const QPoint & pos ) { Q_ASSERT( m_part->factory() ); if ( m_part->factory() == 0 ) { return; } QMenu *menu = ( ( QMenu* ) m_part->factory() ->container( name, m_part ) ); if ( menu == 0 ) { return; } QList lst; AbstractView *v = currentView(); if ( v ) { lst = v->contextActionList(); debugPlanWork<addSeparator(); foreach ( QAction *a, lst ) { menu->addAction( a ); } } } menu->exec( pos ); foreach ( QAction *a, lst ) { menu->removeAction( a ); } } bool View::loadContext() { debugPlanWork; setCurrentIndex( PlanWorkSettings::self()->currentView() ); return true; } void View::saveContext() const { debugPlanWork; PlanWorkSettings::self()->setCurrentView( currentIndex() ); PlanWorkSettings::self()->save(); } void View::slotEditDocument() { slotEditDocument( currentDocument() ); } void View::slotEditDocument( Document *doc ) { debugPlanWork<type() != Document::Type_Product ) { KMessageBox::error( 0, i18n( "This file is not editable" ) ); return; } part()->editWorkpackageDocument( doc ); } void View::slotViewDocument() { emit viewDocument( currentDocument() ); } void View::slotRemoveDocument() { part()->removeDocument( currentDocument() ); } void View::slotPackageSettings() { WorkPackage *wp = part()->findWorkPackage( currentNode() ); if ( wp == 0 ) { return; } QPointer dia = new PackageSettingsDialog( *wp, this ); if ( dia->exec() == QDialog::Accepted && dia ) { KUndo2Command *cmd = dia->buildCommand(); if ( cmd ) { debugPlanWork; part()->addCommand( cmd ); } } delete dia; } int View::openPackageSettings() { int result = QDialog::Rejected; WorkPackage *wp = part()->findWorkPackage( currentNode() ); if ( wp == 0 ) { return result; } QPointer dia = new PackageSettingsDialog( *wp, this, true ); dia->enableButtonOk(true); if ( dia->exec() == QDialog::Accepted && dia ) { result = QDialog::Accepted; KUndo2Command *cmd = dia->buildCommand(); if ( cmd ) { debugPlanWork; part()->addCommand( cmd ); } } delete dia; return result; } void View::slotSendPackage() { Node *node = currentNode(); if ( node == 0 ) { KMessageBox::error(0, i18n("No work package is selected" ) ); return; } debugPlanWork<name(); WorkPackage *wp = part()->findWorkPackage( node ); if ( wp == 0 ) { KMessageBox::error(0, i18n("Cannot find work package" ) ); return; } /* if ( wp->isModified() ) { int r = KMessageBox::questionYesNoCancel( 0, i18n("This work package has been modified.\nDo you want to save it before sending?" ), node->name() ); switch ( r ) { case KMessageBox::Cancel: return; case KMessageBox::Yes: wp->saveToProjects( part() ); break; default: break; } }*/ - if (openPackageSettings() != QDialog::Accepted) { - return; - } - - QTemporaryFile temp(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); - temp.setAutoRemove( false ); - if ( ! temp.open() ) { - KMessageBox::error( 0, i18n("Could not open temporary file. Sending is aborted." ) ); - return; - } bool wasmodified = wp->isModified(); - wp->saveNativeFormat( part(), temp.fileName() ); - wp->setModified( wasmodified ); + if (wp->sendUrl().isValid()) { + QTemporaryFile temp(wp->sendUrl().toLocalFile() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); + temp.setAutoRemove( false ); + if ( ! temp.open() ) { + KMessageBox::error( 0, i18n("Could not open file. Sending is aborted." ) ); + return; + } + wp->saveNativeFormat( part(), temp.fileName() ); + } else { + QTemporaryFile temp(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); + temp.setAutoRemove( false ); + if ( ! temp.open() ) { + KMessageBox::error( 0, i18n("Could not open temporary file. Sending is aborted." ) ); + return; + } + wp->saveNativeFormat( part(), temp.fileName() ); - QStringList attachURLs; - attachURLs << temp.fileName(); + QStringList attachURLs; + attachURLs << temp.fileName(); - QString to = node->projectNode()->leader(); - QString cc; - QString bcc; - QString subject = i18n( "Work Package: %1", node->name() ); - QString body = node->projectNode()->name(); - QString messageFile; + QString to = node->projectNode()->leader(); + QString cc; + QString bcc; + QString subject = i18n( "Work Package: %1", node->name() ); + QString body = node->projectNode()->name(); + QString messageFile; - KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); + KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); + } + wp->setModified( wasmodified ); } void View::slotTaskDescription() { Task *node = qobject_cast( currentNode() ); if ( node == 0 ) { return; } QPointer dlg = new TaskDescriptionDialog( *node, this, true ); dlg->exec(); delete dlg; } AbstractView *View::currentView() const { return qobject_cast( currentWidget() ); } Node *View::currentNode() const { AbstractView *v = currentView(); return v ? v->currentNode() : 0; } Document *View::currentDocument() const { AbstractView *v = currentView(); return v ? v->currentDocument() : 0; } void View::slotTaskProgress() { debugPlanWork; Task *n = qobject_cast( currentNode() ); if ( n == 0 ) { return; } StandardWorktime *w = qobject_cast( n->projectNode() )->standardWorktime(); QPointer dlg = new TaskProgressDialog( *n, currentScheduleManager(), w, this ); if ( dlg->exec() == QDialog::Accepted && dlg ) { KUndo2Command *cmd = dlg->buildCommand(); if ( cmd ) { cmd->redo(); //FIXME m_part->addCommand( cmd ); } } } void View::slotTaskCompletion() { debugPlanWork; WorkPackage *wp = m_part->findWorkPackage( currentNode() ); if ( wp == 0 ) { return; } QPointer dlg = new TaskCompletionDialog( *wp, currentScheduleManager(), this ); if ( dlg->exec() == QDialog::Accepted && dlg ) { KUndo2Command *cmd = dlg->buildCommand(); if ( cmd ) { m_part->addCommand( cmd ); } } delete dlg; } void View::slotRemoveSelectedPackages() { debugPlanWork; QList lst = currentView()->selectedNodes(); if ( lst.isEmpty() ) { return; } m_part->removeWorkPackages( lst ); } void View::slotRemoveCurrentPackage() { debugPlanWork; Node *n = currentNode(); if ( n == 0 ) { return; } m_part->removeWorkPackage( n ); } } //KPlatoWork namespace diff --git a/src/workpackage/view.h b/src/workpackage/view.h index 790faac3..598405a6 100644 --- a/src/workpackage/view.h +++ b/src/workpackage/view.h @@ -1,181 +1,182 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2009, 2011 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPLATOWORK_VIEW #define KPLATOWORK_VIEW #include #include class QPrinter; class QPrintDialog; class QMenu; class QActionGroup; class KActionCollection; class KoStore; class KoView; class KPlatoWork_MainWindow; namespace KPlato { class Document; class GanttView; class ScheduleManager; class Node; class Schedule; class Resource; } using namespace KPlato; /// Then namespace for the KPlato work package handler namespace KPlatoWork { class Part; class View; class AbstractView; class TaskWorkPackageView; class TaskWPGanttView; //------------- class View : public QStackedWidget { Q_OBJECT public: explicit View( Part* part, QWidget *parent, KActionCollection *collection ); ~View(); Part *part() const; virtual void setupPrinter( QPrinter &printer, QPrintDialog &printDialog ); virtual void print( QPrinter &printer, QPrintDialog &printDialog ); QMenu *popupMenu( const QString& name, const QPoint &pos ); // virtual ViewAdaptor* dbusObject(); virtual bool loadContext(); virtual void saveContext() const; ScheduleManager *currentScheduleManager() const; long currentScheduleId() const; TaskWorkPackageView *createTaskWorkPackageView(); TaskWPGanttView *createGanttView(); KPlatoWork_MainWindow *kplatoWorkMainWindow() const; Node *currentNode() const; Document *currentDocument() const; int openPackageSettings(); Q_SIGNALS: void currentScheduleManagerChanged(KPlato::ScheduleManager *sm); void openInternalDocument( KoStore * ); void sigUpdateReadWrite( bool ); void viewDocument(KPlato::Document *doc); public Q_SLOTS: void slotEditCut(); void slotEditCopy(); void slotEditPaste(); void slotConfigure(); void slotPopupMenu( const QString& menuname, const QPoint &pos ); void slotTaskProgress(); void slotTaskCompletion(); protected Q_SLOTS: void slotCurrentChanged( int index ); void slotProgressChanged( int value ); void slotEditDocument(); void slotEditDocument(KPlato::Document *doc); void slotViewDocument(); void slotRemoveDocument(); void slotSendPackage(); void slotPackageSettings(); void slotTaskDescription(); void slotRemoveCurrentPackage(); void slotRemoveSelectedPackages(); void slotSelectionChanged(); void slotViewList(); void slotViewGantt(); protected: virtual void updateReadWrite( bool readwrite ); QAction *addScheduleAction( Schedule *sch ); void setLabel(); AbstractView *currentView() const; private: void createViews(); private: Part *m_part; QActionGroup *m_scheduleActionGroup; QMap m_scheduleActions; ScheduleManager *m_manager; bool m_readWrite; // ------ Edit QAction *actionCut; QAction *actionCopy; QAction *actionPaste; QAction *actionRemoveSelectedPackages; // ------ View QAction *actionViewList; QAction *actionViewGantt; // ------ Settings QAction *actionConfigure; QAction *actionViewDocument; QAction *actionEditDocument; QAction *actionRemoveDocument; QAction *actionSendPackage; QAction *actionPackageSettings; QAction *actionTaskCompletion; QAction *actionViewDescription; QAction *actionRemoveCurrentPackage; }; } //KplatoWork namespace #endif diff --git a/src/workpackage/workpackage.cpp b/src/workpackage/workpackage.cpp index da606133..25cdf635 100644 --- a/src/workpackage/workpackage.cpp +++ b/src/workpackage/workpackage.cpp @@ -1,830 +1,833 @@ /* This file is part of the KDE project Copyright (C) 2009, 2012 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "workpackage.h" #include "KPlatoXmlLoader.h" //NOTE: this file should probably be moved #include "part.h" #include "kptglobal.h" #include "kptnode.h" #include "kptproject.h" #include "kptdocuments.h" #include "kptcommand.h" #include "kptxmlloaderobject.h" #include "XmlSaveContext.h" #include "kptconfigbase.h" #include "kptcommonstrings.h" #include #include #include #include #include #include #include #include #include #include #include "debugarea.h" using namespace KPlato; namespace KPlatoWork { WorkPackage::WorkPackage( bool fromProjectStore ) : m_project( new Project() ), m_fromProjectStore( fromProjectStore ), m_modified( false) { m_project->setConfig( &m_config ); } WorkPackage::WorkPackage( Project *project, bool fromProjectStore ) : m_project( project ), m_fromProjectStore( fromProjectStore ), m_modified( false) { Q_ASSERT( project ); Q_ASSERT ( project->childNode( 0 ) ); m_project->setConfig( &m_config ); if ( ! project->scheduleManagers().isEmpty() ) { // should be only one manager, so just get the first const QList &lst = m_project->scheduleManagers(); project->setCurrentSchedule(lst.first()->scheduleId()); } connect( project, &KPlato::Project::projectChanged, this, &WorkPackage::projectChanged ); } WorkPackage::~WorkPackage() { delete m_project; qDeleteAll( m_childdocs ); } void WorkPackage::setSettings( const WorkPackageSettings &settings ) { if ( m_settings != settings ) { m_settings = settings; setModified( true ); } } //TODO find a way to know when changes are undone void WorkPackage::projectChanged() { debugPlanWork; setModified( true ); } bool WorkPackage::addChild( Part */*part*/, const Document *doc ) { DocumentChild *ch = findChild( doc ); if ( ch ) { if ( ch->isOpen() ) { KMessageBox::error( 0, i18n( "Document is already open" ) ); return false; } } else { ch = new DocumentChild( this ); if ( ! ch->setDoc( doc ) ) { delete ch; return false; } } if ( ! ch->editDoc() ) { delete ch; return false; } if ( ! m_childdocs.contains( ch ) ) { m_childdocs.append( ch ); connect( ch, &DocumentChild::fileModified, this, &WorkPackage::slotChildModified ); } return true; } void WorkPackage::slotChildModified( bool mod ) { debugPlanWork<documents().contains( doc ) : false; } DocumentChild *WorkPackage::findChild( const Document *doc ) const { foreach ( DocumentChild *c, m_childdocs ) { if ( c->doc() == doc ) { return c; } } return 0; } bool WorkPackage::loadXML( const KoXmlElement &element, XMLLoaderObject &status ) { bool ok = false; KoXmlNode n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); debugPlanWork<load( e, status ) ) ) { status.addMsg( XMLLoaderObject::Errors, "Loading of work package failed" ); KMessageBox::error( 0, i18n( "Failed to load project: %1" , m_project->name() ) ); } } } if ( ok ) { KoXmlNode n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); debugPlanWork<( m_project->childNode( 0 ) ); t->workPackage().setOwnerName( e.attribute( "owner" ) ); t->workPackage().setOwnerId( e.attribute( "owner-id" ) ); + m_sendUrl = QUrl(e.attribute("save-url")); + m_fetchUrl = QUrl(e.attribute("load-url")); Resource *r = m_project->findResource( t->workPackage().ownerId() ); if ( r == 0 ) { debugPlanWork<<"Cannot find resource id!!"<workPackage().ownerId()<workPackage().ownerName(); } debugPlanWork<<"is this me?"<workPackage().ownerName(); KoXmlNode ch = e.firstChild(); for ( ; ! ch.isNull(); ch = ch.nextSibling() ) { if ( ! ch.isElement() ) { continue; } KoXmlElement el = ch.toElement(); debugPlanWork<scheduleManagers().isEmpty() ) { // should be only one manager const QList &lst = m_project->scheduleManagers(); m_project->setCurrentSchedule(lst.first()->scheduleId()); } return ok; } bool WorkPackage::loadKPlatoXML( const KoXmlElement &element, XMLLoaderObject &status ) { bool ok = false; KoXmlNode n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); debugPlanWork<name() ) ); } } } if ( ok ) { KoXmlNode n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); debugPlanWork<( m_project->childNode( 0 ) ); t->workPackage().setOwnerName( e.attribute( "owner" ) ); t->workPackage().setOwnerId( e.attribute( "owner-id" ) ); Resource *r = m_project->findResource( t->workPackage().ownerId() ); if ( r == 0 ) { debugPlanWork<<"Cannot find resource id!!"<workPackage().ownerId()<workPackage().ownerName(); } debugPlanWork<<"is this me?"<workPackage().ownerName(); KoXmlNode ch = e.firstChild(); for ( ; ! ch.isNull(); ch = ch.nextSibling() ) { if ( ! ch.isElement() ) { continue; } KoXmlElement el = ch.toElement(); debugPlanWork<scheduleManagers().isEmpty() ) { // should be only one manager const QList &lst = m_project->scheduleManagers(); m_project->setCurrentSchedule(lst.first()->scheduleId()); } return ok; } bool WorkPackage::saveToStream( QIODevice * dev ) { QDomDocument doc = saveXML(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) warnPlanWork << "wrote " << nwritten << "- expected" << s.size(); return nwritten == (int)s.size(); } bool WorkPackage::saveNativeFormat( Part */*part*/, const QString &path ) { if ( path.isEmpty() ) { KMessageBox::error( 0, i18n("Cannot save to empty filename") ); return false; } debugPlanWork<name()<bad()) { KMessageBox::error( 0, i18n("Could not create the file for saving") ); delete store; return false; } if (store->open("root")) { KoStoreDevice dev(store); if ( ! saveToStream(&dev) || ! store->close() ) { debugPlanWork << "saveToStream failed"; delete store; return false; } } else { KMessageBox::error( 0, i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml") ) ); delete store; return false; } if (!completeSaving(store)) { delete store; return false; } if (!store->finalize()) { delete store; return false; } // Success delete store; m_modified = false; return true; } bool WorkPackage::completeSaving( KoStore *store ) { debugPlanWork; KoStore *oldstore = KoStore::createStore( filePath(), KoStore::Read, "", KoStore::Zip ); if ( oldstore->bad() ) { KMessageBox::error( 0, i18n( "Failed to open store:\n %1", filePath() ) ); return false; } if (oldstore->hasFile( "documentinfo.xml" ) ) { copyFile( oldstore, store, "documentinfo.xml" ); } if (oldstore->hasFile( "preview.png" ) ) { copyFile( oldstore, store, "preview.png" ); } // First get all open documents debugPlanWork<saveToStore( store ) ) { } } // Then get new files foreach ( const Document *doc, node()->documents().documents() ) { if ( m_newdocs.contains( doc ) ) { store->addLocalFile( m_newdocs[ doc ].path(), doc->url().fileName() ); m_newdocs.remove( doc ); // TODO remove temp file ?? } } // Then get files from the old store copied to the new store foreach ( Document *doc, node()->documents().documents() ) { if ( doc->sendAs() != Document::SendAs_Copy ) { continue; } if ( ! store->hasFile( doc->url().fileName() ) ) { copyFile( oldstore, store, doc->url().fileName() ); } } return true; } QString WorkPackage::fileName( const Part *part ) const { Q_UNUSED(part); if ( m_project == 0 ) { warnPlanWork<<"No project in this package"; return QString(); } Node *n = node(); if ( n == 0 ) { warnPlanWork<<"No node in this project"; return QString(); } QString projectName = m_project->name().remove( ' ' ); // FIXME: workaround: KoResourcePaths::saveLocation( "projects", projectName + '/' ); const QString path = KoResourcePaths::saveLocation( "appdata", "projects/" + projectName + '/' ); QString wpName = QString( n->name().remove( ' ' ) + '_' + n->id() + ".planwork" ); return path + wpName; } void WorkPackage::removeFile() { QFile file( m_filePath ); if ( ! file.exists() ) { warnPlanWork<<"No project in this package"; return; } file.remove(); } void WorkPackage::saveToProjects( Part *part ) { debugPlanWork; QString path = fileName( part ); debugPlanWork<name(); if ( saveNativeFormat( part, path ) ) { m_fromProjectStore = true; m_filePath = path; } else { KMessageBox::error( 0, i18n( "Cannot save to projects store:\n%1" , path ) ); } return; } bool WorkPackage::isModified() const { if ( m_modified ) { return true; } foreach ( DocumentChild *ch, m_childdocs ) { if ( ch->isModified() || ch->isFileModified() ) { return true; } } return false; } QString WorkPackage::name() const { QString n; Task *t = task(); return t ? t->name() : QString(); } Node *WorkPackage::node() const { return m_project == 0 ? 0 : m_project->childNode( 0 ); } Task *WorkPackage::task() const { Task *task = qobject_cast( node() ); Q_ASSERT( task ); return task; } bool WorkPackage::removeDocument( Part *part, Document *doc ) { Node *n = node(); if ( n == 0 ) { return false; } part->addCommand( new DocumentRemoveCmd( n->documents(), doc, UndoText::removeDocument() ) ); return true; } bool WorkPackage::copyFile( KoStore *from, KoStore *to, const QString &filename ) { QByteArray data; if ( ! from->extractFile( filename , data ) ) { KMessageBox::error( 0, i18n( "Failed read file:\n %1", filename ) ); return false; } if ( ! to->addDataToFile( data, filename ) ) { KMessageBox::error( 0, i18n( "Failed write file:\n %1", filename ) ); return false; } debugPlanWork<<"Copied file:"<( node() ); if ( t ) { wp.setAttribute( "owner", t->workPackage().ownerName() ); wp.setAttribute( "owner-id", t->workPackage().ownerId() ); } doc.appendChild( wp ); m_project->save( doc, XmlSaveContext() ); return document; } void WorkPackage::merge( Part *part, const WorkPackage *wp, KoStore *store ) { debugPlanWork; const Node *from = wp->node(); Node *to = node(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Merge data" ) ); if ( to->name() != from->name() ) { m->addCommand( new NodeModifyNameCmd( *to, from->name() ) ); } if ( to->description() != from->description() ) { m->addCommand( new NodeModifyDescriptionCmd( *to, from->description() ) ); } if ( to->startTime() != from->startTime() && from->startTime().isValid() ) { m->addCommand( new NodeModifyStartTimeCmd( *to, from->startTime() ) ); } if ( to->endTime() != from->endTime() && from->endTime().isValid() ) { m->addCommand( new NodeModifyEndTimeCmd( *to, from->endTime() ) ); } if ( to->leader() != from->leader() ) { m->addCommand( new NodeModifyLeaderCmd( *to, from->leader() ) ); } if ( from->type() == Node::Type_Task && from->type() == Node::Type_Task ) { if ( static_cast( to )->workPackage().ownerId() != static_cast( from )->workPackage().ownerId() ) { debugPlanWork<<"merge:"<<"different owners"<( from )->workPackage().ownerName()<( to )->workPackage().ownerName(); if ( static_cast( to )->workPackage().ownerId().isEmpty() ) { //TODO cmd static_cast( to )->workPackage().setOwnerId( static_cast( from )->workPackage().ownerId() ); static_cast( to )->workPackage().setOwnerName( static_cast( from )->workPackage().ownerName() ); } } foreach ( Document *doc, from->documents().documents() ) { Document *org = to->documents().findDocument( doc->url() ); if ( org ) { // TODO: also handle modified type, sendas // update ? what if open, modified ... if ( doc->type() == Document::Type_Product ) { //### FIXME. user feedback warnPlanWork<<"We do not update existing deliverables (except name change)"; if ( doc->name() != org->name() ) { m->addCommand( new DocumentModifyNameCmd( org, doc->name() ) ); } } else { if ( doc->name() != org->name() ) { m->addCommand( new DocumentModifyNameCmd( org, doc->name() ) ); } if ( doc->sendAs() != org->sendAs() ) { m->addCommand( new DocumentModifySendAsCmd( org, doc->sendAs() ) ); } if ( doc->sendAs() == Document::SendAs_Copy ) { debugPlanWork<<"Update existing doc:"<url(); openNewDocument( org, store ); } } } else { debugPlanWork<<"new document:"<typeToString(doc->type())<url(); Document *newdoc = new Document( *doc ); m->addCommand( new DocumentAddCmd( to->documents(), newdoc ) ); if ( doc->sendAs() == Document::SendAs_Copy ) { debugPlanWork<<"Copy file"; openNewDocument( newdoc, store ); } } } } const Project *fromProject = wp->project(); Project *toProject = m_project; const ScheduleManager *fromSm = fromProject->scheduleManagers().value( 0 ); Q_ASSERT( fromSm ); ScheduleManager *toSm = toProject->scheduleManagers().value( 0 ); Q_ASSERT( toSm ); if ( fromSm->managerId() != toSm->managerId() || fromSm->scheduleId() != toSm->scheduleId() ) { // rescheduled, update schedules m->addCommand( new CopySchedulesCmd( *fromProject, *toProject ) ); } if ( m->isEmpty() ) { delete m; } else { part->addCommand( m ); } } void WorkPackage::openNewDocument( const Document *doc, KoStore *store ) { const QUrl url = extractFile( doc, store ); if ( url.url().isEmpty() ) { KMessageBox::error( 0, i18n( "Could not extract document from storage:
%1", doc->url().path() ) ); return; } if ( ! url.isValid() ) { KMessageBox::error( 0, i18n( "Invalid URL:
%1", url.path() ) ); return; } m_newdocs.insert( doc, url ); } int WorkPackage::queryClose( Part *part ) { debugPlanWork<name(); QStringList lst; if ( ! m_childdocs.isEmpty() ) { foreach ( DocumentChild *ch, m_childdocs ) { if ( ch->isOpen() && ch->doc()->sendAs() == Document::SendAs_Copy ) { lst << ch->doc()->url().fileName(); } } } if ( ! lst.isEmpty() ) { KMessageBox::ButtonCode result = KMessageBox::warningContinueCancelList( 0, i18np( "

The work package '%2' has an open document.

Data may be lost if you continue.

", "

The work package '%2' has open documents.

Data may be lost if you continue.

", lst.count(), name ), lst ); switch (result) { case KMessageBox::Continue: { debugPlanWork<<"Continue"; break; } default: // case KMessageBox::Cancel : debugPlanWork<<"Cancel"; return KMessageBox::Cancel; break; } } if ( ! isModified() ) { return KMessageBox::Yes; } KMessageBox::ButtonCode res = KMessageBox::warningYesNoCancel( 0, i18n("

The work package '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes: { debugPlanWork<<"Yes"; saveToProjects( part ); break; } case KMessageBox::No: debugPlanWork<<"No"; break; default: // case KMessageBox::Cancel : debugPlanWork<<"Cancel"; break; } return res; } QUrl WorkPackage::extractFile( const Document *doc ) { KoStore *store = KoStore::createStore( m_filePath, KoStore::Read, "", KoStore::Zip ); if ( store->bad() ) { KMessageBox::error( 0, i18n( "

Work package '%1'

Could not open store:

%2

", node()->name(), m_filePath ) ); delete store; return QUrl(); } const QUrl url = extractFile( doc, store ); delete store; return url; } QUrl WorkPackage::extractFile( const Document *doc, KoStore *store ) { //FIXME: should use a special tmp dir QString tmp = QDir::tempPath() + QLatin1Char('/') + doc->url().fileName(); const QUrl url = QUrl::fromLocalFile( tmp ); debugPlanWork<<"Extract: "<url().fileName()<<" -> "<extractFile( doc->url().fileName(), url.path() ) ) { KMessageBox::error( 0, i18n( "

Work package '%1'

Could not extract file:

%2

", node()->name(), doc->url().fileName() ) ); return QUrl(); } return url; } QString WorkPackage::id() const { QString id; if ( node() ) { id = m_project->id() + node()->id(); } return id; } //-------------------------------- PackageRemoveCmd::PackageRemoveCmd( Part *part, WorkPackage *value, const KUndo2MagicString& name ) : NamedCommand( name ), m_part( part ), m_value( value ), m_mine( false ) { } PackageRemoveCmd::~PackageRemoveCmd() { if ( m_mine ) { m_value->removeFile(); delete m_value; } } void PackageRemoveCmd::execute() { m_part->removeWorkPackage( m_value ); m_mine = true; } void PackageRemoveCmd::unexecute() { m_part->addWorkPackage( m_value ); m_mine = false; } //--------------------- ModifyPackageSettingsCmd::ModifyPackageSettingsCmd( WorkPackage *wp, WorkPackageSettings &value, const KUndo2MagicString& name ) : NamedCommand( name ), m_wp( wp ), m_value( value ), m_oldvalue( wp->settings() ) { } void ModifyPackageSettingsCmd::execute() { m_wp->setSettings( m_value ); } void ModifyPackageSettingsCmd::unexecute() { m_wp->setSettings( m_oldvalue ); } //--------------------- CopySchedulesCmd::CopySchedulesCmd( const Project &fromProject, Project &toProject, const KUndo2MagicString &name ) : NamedCommand( name ), m_project( toProject ) { QDomDocument olddoc; QDomElement e = olddoc.createElement( "old" ); olddoc.appendChild( e ); toProject.save( e, XmlSaveContext() ); m_olddoc = olddoc.toString(); QDomDocument newdoc; e = newdoc.createElement( "new" ); newdoc.appendChild( e ); fromProject.save( e, XmlSaveContext() ); m_newdoc = newdoc.toString(); } void CopySchedulesCmd::execute() { load( m_newdoc ); } void CopySchedulesCmd::unexecute() { load( m_olddoc ); } void CopySchedulesCmd::load( const QString &doc ) { clearSchedules(); KoXmlDocument d; d.setContent( doc ); KoXmlElement proj = d.documentElement().namedItem( "project").toElement(); Q_ASSERT( ! proj.isNull() ); KoXmlElement task = proj.namedItem( "task").toElement(); Q_ASSERT( ! task.isNull() ); KoXmlElement ts = task.namedItem( "schedules").namedItem( "schedule").toElement(); Q_ASSERT( ! ts.isNull() ); KoXmlElement ps = proj.namedItem( "schedules").namedItem( "plan" ).toElement(); Q_ASSERT( ! ps.isNull() ); XMLLoaderObject status; status.setProject( &m_project ); status.setVersion( PLAN_FILE_SYNTAX_VERSION ); // task first NodeSchedule *ns = new NodeSchedule(); if ( ns->loadXML( ts, status ) ) { debugPlanWork<name()<type()<id(); ns->setNode( m_project.childNode( 0 ) ); m_project.childNode( 0 )->addSchedule( ns ); } else { Q_ASSERT( false ); delete ns; } // schedule manager next (includes main schedule and resource schedule) ScheduleManager *sm = new ScheduleManager( m_project ); if ( sm->loadXML( ps, status ) ) { m_project.addScheduleManager( sm ); } else { Q_ASSERT( false ); delete sm; } if ( sm ) { m_project.setCurrentSchedule( sm->scheduleId() ); } m_project.childNode( 0 )->changed(); } void CopySchedulesCmd::clearSchedules() { foreach ( Schedule *s, m_project.schedules() ) { m_project.takeSchedule( s ); } foreach ( Schedule *s, m_project.childNode( 0 )->schedules() ) { foreach ( Appointment *a, s->appointments() ) { if ( a->resource() && a->resource()->resource() ) { a->resource()->resource()->takeSchedule( a->resource() ); } } m_project.childNode( 0 )->takeSchedule( s ); } foreach ( ScheduleManager *sm, m_project.scheduleManagers() ) { m_project.takeScheduleManager( sm ); delete sm; } } } //KPlatoWork namespace diff --git a/src/workpackage/workpackage.h b/src/workpackage/workpackage.h index 9f940559..63652611 100644 --- a/src/workpackage/workpackage.h +++ b/src/workpackage/workpackage.h @@ -1,217 +1,222 @@ /* This file is part of the KDE project Copyright (C) 2009 Dag Andersen - + Copyright (C) 2019 Dag Andersen + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPLATOWORK_WORKPACKAGE_H #define KPLATOWORK_WORKPACKAGE_H #include "kptxmlloaderobject.h" #include "kptcommand.h" #include "kpttask.h" #include #include #include class KoStore; class QDomDocument; namespace KPlato { class Project; class Document; class XMLLoaderObject; } using namespace KPlato; /// The main namespace for KPlato WorkPackage Handler namespace KPlatoWork { class Part; class WorkPackage; class DocumentChild; /** A work package consists of a Project node and one Task node along with scheduling information and assigned resources. */ class WorkPackage : public QObject { Q_OBJECT public: explicit WorkPackage(bool fromProjectStore); WorkPackage( Project *project, bool fromProjectStore ); ~WorkPackage(); /// @return Package name QString name() const; DocumentChild *findChild( const Document *doc ) const; /// Called when loading a work package. Saves to Project store. /// Asks to save/overwrite if already there. /// Does nothing if opened from Projects store. void saveToProjects( Part *part ); bool contains( const DocumentChild* child ) const { return m_childdocs.contains( const_cast( child ) ); } QList childDocs() { return m_childdocs; } bool addChild( Part *part, const Document *doc ); void removeChild( DocumentChild *child ); bool contains( const Document *doc ) const; QString nodeId() const; /// Load the Plan work package document bool loadXML( const KoXmlElement &element, XMLLoaderObject &status ); /// Load the old KPlato work package file format bool loadKPlatoXML( const KoXmlElement &element, XMLLoaderObject &status ); QDomDocument saveXML(); bool saveNativeFormat( Part *part, const QString &path ); bool saveDocumentsToStore( KoStore *store ); bool completeSaving( KoStore *store ); Node *node() const; Task *task() const; Project *project() const { return m_project; } /// Remove document @p doc bool removeDocument( Part *part, Document *doc ); /// Set the file path to this package void setFilePath( const QString &name ) { m_filePath = name; } /// Return the file path to this package QString filePath() const { return m_filePath; } /// Construct file path to projects store QString fileName( const Part *part ) const; /// Remove work package file void removeFile(); /// Merge data from work package @p wp void merge( Part *part, const WorkPackage *wp, KoStore *store ); bool isModified() const; int queryClose( Part *part ); QUrl extractFile( const Document *doc ); QUrl extractFile( const Document *doc, KoStore *store ); QString id() const; bool isValid() const { return m_project && node(); } WorkPackageSettings &settings() { return m_settings; } void setSettings( const WorkPackageSettings &settings ); QMap newDocuments() const { return m_newdocs; } void removeNewDocument( const Document *doc ) { m_newdocs.remove( doc ); } + QUrl sendUrl() const { return m_sendUrl; } + QUrl fetchUrl() const { return m_fetchUrl; } + Q_SIGNALS: void modified( bool ); void saveWorkPackage(KPlatoWork::WorkPackage* ); public Q_SLOTS: void setModified( bool on ) { m_modified = on; } protected Q_SLOTS: void projectChanged(); void slotChildModified( bool mod ); protected: /// Copy file @p filename from old store @p from, to the new store @p to bool copyFile( KoStore *from, KoStore *to, const QString &filename ); bool saveToStream( QIODevice * dev ); void openNewDocument( const Document *doc, KoStore *store ); protected: Project *m_project; QString m_filePath; bool m_fromProjectStore; QList m_childdocs; - QMap m_newdocs; // new documents that does not exists in the project store (yet) - + QMap m_newdocs; /// new documents that does not exists in the project store (yet) + QUrl m_sendUrl; /// Where to put the package. If not valid, transmit by mail + QUrl m_fetchUrl; /// Plan will store package here bool m_modified; WorkPackageSettings m_settings; ConfigBase m_config; }; //----------------------------- class PackageRemoveCmd : public NamedCommand { public: PackageRemoveCmd( Part *part, WorkPackage *value, const KUndo2MagicString &name = KUndo2MagicString() ); ~PackageRemoveCmd(); void execute(); void unexecute(); private: Part *m_part; WorkPackage *m_value; bool m_mine; }; //----------------------------- class ModifyPackageSettingsCmd : public NamedCommand { public: ModifyPackageSettingsCmd( WorkPackage *wp, WorkPackageSettings &value, const KUndo2MagicString &name = KUndo2MagicString() ); void execute(); void unexecute(); private: WorkPackage *m_wp; WorkPackageSettings m_value, m_oldvalue; }; //----------------------------- class CopySchedulesCmd : public NamedCommand { public: CopySchedulesCmd( const Project &fromProject, Project &toProject, const KUndo2MagicString &name = KUndo2MagicString() ); void execute(); void unexecute(); private: void load( const QString &doc ); void clean( const QDomDocument &doc ); void clearSchedules(); private: Project &m_project; QString m_olddoc; QString m_newdoc; }; } //KPlatoWork namespace #endif