diff --git a/src/calligraplan.rc b/src/calligraplan.rc index 677302e7..8a786122 100644 --- a/src/calligraplan.rc +++ b/src/calligraplan.rc @@ -1,188 +1,192 @@ - + &File &Edit &View &Tools &Settings &Help Project - + + + + + diff --git a/src/kptview.cpp b/src/kptview.cpp index 126b4dde..acbe1a88 100644 --- a/src/kptview.cpp +++ b/src/kptview.cpp @@ -1,3323 +1,3314 @@ /* 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 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 "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 "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_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::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::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::checkForWorkPackages, getPart(), &MainDocument::checkForWorkPackages); 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::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 ; + qInfo()<type() ) { - case Node::Type_Project: { - break; - } case Node::Type_Subproject: //TODO break; + case Node::Type_Project: case Node::Type_Task: case Node::Type_Milestone: case Node::Type_Summarytask: { - Task *task = dynamic_cast( node ); - Q_ASSERT( task ); - TaskDescriptionDialog *dia = new TaskDescriptionDialog( *task, this ); + 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_Project: { - // TODO - break; - } case Node::Type_Subproject: //TODO break; + case Node::Type_Project: case Node::Type_Summarytask: - //TODO case Node::Type_Task: case Node::Type_Milestone: { - Task *task = dynamic_cast( node ); - Q_ASSERT( task ); - DocumentsDialog *dia = new DocumentsDialog( *task, this ); + 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 ) { return getPart()->loadWorkPackage( project, url ); } 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(); } 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 ) { debugPlan; if ( resource == 0 ) { warnPlan<<"No resource, we don't handle node->leader() yet"; return; } QString to = resource->name() + " <" + resource->email() + '>'; QString subject = i18n( "Work Package for project: %1", getProject().name() ); QString body; QStringList attachURLs; foreach ( Node *n, nodes ) { 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, 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'; } QString cc; QString bcc; QString messageFile; 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/libs/kernel/kptproject.cpp b/src/libs/kernel/kptproject.cpp index 31384ce4..61415408 100644 --- a/src/libs/kernel/kptproject.cpp +++ b/src/libs/kernel/kptproject.cpp @@ -1,2958 +1,2962 @@ /* 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 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); } } QList cals; QString s; bool ok = false; setName( element.attribute( "name" ) ); removeId( m_id ); m_id = element.attribute( "id" ); registerNodeId( this ); 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); + 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::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< * * 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 "kptdocumentsdialog.h" #include "kptdocumentspanel.h" -#include "kpttask.h" +#include "kptnode.h" #include "kptcommand.h" #include #include using namespace KPlato; -DocumentsDialog::DocumentsDialog(Task &task, QWidget *p, bool readOnly) +DocumentsDialog::DocumentsDialog(Node &node, QWidget *p, bool readOnly) : KoDialog(p) - , m_panel(new DocumentsPanel(task, this)) + , m_panel(new DocumentsPanel(node, this)) { setCaption(i18n("Task Documents")); if (readOnly) { setButtons(Close); } else { setButtons(Ok|Cancel); setDefaultButton(Ok); } showButtonSeparator(true); setMainWidget(m_panel); enableButtonOk(false); connect(m_panel, &DocumentsPanel::changed, this, &KoDialog::enableButtonOk); } MacroCommand *DocumentsDialog::buildCommand() { return m_panel->buildCommand(); } void DocumentsDialog::slotButtonClicked(int button) { if (button == KoDialog::Ok) { accept(); } else { KoDialog::slotButtonClicked(button); } } diff --git a/src/libs/ui/kptdocumentsdialog.h b/src/libs/ui/kptdocumentsdialog.h index f22420f5..10018fbf 100644 --- a/src/libs/ui/kptdocumentsdialog.h +++ b/src/libs/ui/kptdocumentsdialog.h @@ -1,59 +1,58 @@ /* This file is part of the KDE project * 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 KPTDOCUMENTSDIALOG_H #define KPTDOCUMENTSDIALOG_H #include "planui_export.h" #include "PlanMacros.h" #include namespace KPlato { class DocumentsPanel; -class Task; class Node; class MacroCommand; class PLANUI_EXPORT DocumentsDialog : public KoDialog { Q_OBJECT public: /** * The constructor for the documents dialog. - * @param task the task to show documents for + * @param node the task or project to show documents for * @param parent parent widget * @param readOnly determines whether the data are read-only */ - explicit DocumentsDialog( Task &task, QWidget *parent = 0, bool readOnly = false ); + explicit DocumentsDialog( Node &node, QWidget *parent = 0, bool readOnly = false ); MacroCommand *buildCommand(); protected Q_SLOTS: void slotButtonClicked( int button ); protected: DocumentsPanel *m_panel; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kptganttview.cpp b/src/libs/ui/kptganttview.cpp index 4831c65c..40dfd403 100644 --- a/src/libs/ui/kptganttview.cpp +++ b/src/libs/ui/kptganttview.cpp @@ -1,1539 +1,1543 @@ /* This file is part of the KDE project Copyright (C) 2002 - 2007, 2012 Dag Andersen Copyright (C) 2006 Raphael Langerhorst Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptganttview.h" #include "kptnodeitemmodel.h" #include "kptappointment.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptrelation.h" #include "kptschedule.h" #include "kptviewbase.h" #include "kptitemviewsettup.h" #include "kptduration.h" #include "kptdatetime.h" #include "kptresourceappointmentsmodel.h" #include "Help.h" #include "kptdebug.h" #include "gantt/DateTimeTimeLine.h" #include "gantt/DateTimeGrid.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// The main namespace namespace KPlato { class GanttItemDelegate; //------------------------------------------------- GanttChartDisplayOptionsPanel::GanttChartDisplayOptionsPanel( GanttViewBase *gantt, GanttItemDelegate *delegate, QWidget *parent ) : QWidget( parent ) , m_delegate( delegate ) , m_gantt(gantt) { setupUi( this ); setValues( *delegate ); connect( ui_showTaskName, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showResourceNames, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showDependencies, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showPositiveFloat, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showNegativeFloat, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showCriticalPath, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showCriticalTasks, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showCompletion, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showSchedulingError, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); connect( ui_showTimeConstraint, &QCheckBox::stateChanged, this, &GanttChartDisplayOptionsPanel::changed ); } void GanttChartDisplayOptionsPanel::slotOk() { m_delegate->showTaskName = ui_showTaskName->checkState() == Qt::Checked; m_delegate->showResources = ui_showResourceNames->checkState() == Qt::Checked; m_delegate->showTaskLinks = ui_showDependencies->checkState() == Qt::Checked; m_delegate->showPositiveFloat = ui_showPositiveFloat->checkState() == Qt::Checked; m_delegate->showNegativeFloat = ui_showNegativeFloat->checkState() == Qt::Checked; m_delegate->showCriticalPath = ui_showCriticalPath->checkState() == Qt::Checked; m_delegate->showCriticalTasks = ui_showCriticalTasks->checkState() == Qt::Checked; m_delegate->showProgress = ui_showCompletion->checkState() == Qt::Checked; m_delegate->showSchedulingError = ui_showSchedulingError->checkState() == Qt::Checked; m_delegate->showTimeConstraint = ui_showTimeConstraint->checkState() == Qt::Checked; DateTimeTimeLine *timeline = m_gantt->timeLine(); timeline->setInterval(ui_timeLineInterval->value() * 60000); QPen pen; pen.setWidth(ui_timeLineStroke->value()); pen.setColor(ui_timeLineColor->color()); timeline->setPen(pen); DateTimeTimeLine::Options opt = timeline->options(); opt.setFlag(DateTimeTimeLine::Foreground, ui_timeLineForeground->isChecked()); opt.setFlag(DateTimeTimeLine::Background, ui_timeLineBackground->isChecked()); opt.setFlag(DateTimeTimeLine::UseCustomPen, ui_timeLineUseCustom->isChecked()); timeline->setOptions(opt); } void GanttChartDisplayOptionsPanel::setValues( const GanttItemDelegate &del ) { ui_showTaskName->setCheckState( del.showTaskName ? Qt::Checked : Qt::Unchecked ); ui_showResourceNames->setCheckState( del.showResources ? Qt::Checked : Qt::Unchecked ); ui_showDependencies->setCheckState( del.showTaskLinks ? Qt::Checked : Qt::Unchecked ); ui_showPositiveFloat->setCheckState( del.showPositiveFloat ? Qt::Checked : Qt::Unchecked ); ui_showNegativeFloat->setCheckState( del.showNegativeFloat ? Qt::Checked : Qt::Unchecked ); ui_showCriticalPath->setCheckState( del.showCriticalPath ? Qt::Checked : Qt::Unchecked ); ui_showCriticalTasks->setCheckState( del.showCriticalTasks ? Qt::Checked : Qt::Unchecked ); ui_showCompletion->setCheckState( del.showProgress ? Qt::Checked : Qt::Unchecked ); ui_showSchedulingError->setCheckState( del.showSchedulingError ? Qt::Checked : Qt::Unchecked ); ui_showTimeConstraint->setCheckState( del.showTimeConstraint ? Qt::Checked : Qt::Unchecked ); DateTimeTimeLine *timeline = m_gantt->timeLine(); ui_timeLineInterval->setValue(timeline->interval() / 60000); QPen pen = timeline->pen(); ui_timeLineStroke->setValue(pen.width()); ui_timeLineColor->setColor(pen.color()); ui_timeLineHide->setChecked(true); DateTimeTimeLine::Options opt = timeline->options(); ui_timeLineBackground->setChecked(opt & DateTimeTimeLine::Background); ui_timeLineForeground->setChecked(opt & DateTimeTimeLine::Foreground); ui_timeLineUseCustom->setChecked(opt & DateTimeTimeLine::UseCustomPen); } void GanttChartDisplayOptionsPanel::setDefault() { GanttItemDelegate del; setValues( del ); } //---- GanttViewSettingsDialog::GanttViewSettingsDialog( GanttViewBase *gantt, GanttItemDelegate *delegate, ViewBase *view, bool selectPrint ) : ItemViewSettupDialog( view, gantt->treeView(), true, view ), m_gantt( gantt ) { GanttChartDisplayOptionsPanel *panel = new GanttChartDisplayOptionsPanel( gantt, delegate ); /*KPageWidgetItem *page = */insertWidget( 1, panel, i18n( "Chart" ), i18n( "Gantt Chart Settings" ) ); QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_printingoptions = new GanttPrintingOptionsWidget( this ); m_printingoptions->setOptions( gantt->printingOptions() ); tab->addTab( m_printingoptions, m_printingoptions->windowTitle() ); KPageWidgetItem *page = insertWidget( 2, tab, i18n( "Printing" ), i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, SIGNAL(accepted()), this, SLOT(slotOk()) ); connect( this, &QDialog::accepted, panel, &GanttChartDisplayOptionsPanel::slotOk ); connect( button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, panel, &GanttChartDisplayOptionsPanel::setDefault ); } void GanttViewSettingsDialog::slotOk() { debugPlan; m_gantt->setPrintingOptions( m_printingoptions->options()); ItemViewSettupDialog::slotOk(); } //------------------------- GanttPrintingOptions::GanttPrintingOptions() : printRowLabels( true ), singlePage( true ) { } bool GanttPrintingOptions::loadContext( const KoXmlElement &settings ) { KoXmlElement e = settings.namedItem( "print-options" ).toElement(); if ( ! e.isNull() ) { printRowLabels = (bool)( e.attribute( "print-rowlabels", "0" ).toInt() ); singlePage = (bool)( e.attribute( "print-singlepage", "0" ).toInt() ); } debugPlan <<"..........."<treeView()->header()->height(); // same header hight m_sceneRect = gantt->graphicsView()->sceneRect(); m_horPages = 1; qreal c = m_sceneRect.width() - printer().pageRect().width(); while ( c > 0 ) { ++m_horPages; c -= printer().pageRect().width(); } m_vertPages = 1; c = m_sceneRect.height() - printer().pageRect().height() - m_headerHeight; while ( c > 0 ) { ++m_vertPages; c -= printer().pageRect().height(); } debugPlan< pages; if ( printer().fromPage() > 0 ) { pages << printer().fromPage(); if ( ! m_gantt->m_printOptions.singlePage ) { int last = printer().toPage(); for ( int i = pages.first() + 1; i <= last; ++i ) { pages << i; } if (m_vertPages > 1) { m_image = QImage(m_sceneRect.width(), m_sceneRect.height() + m_headerHeight, QImage::Format_ARGB32); m_image.fill(Qt::white); QPainter p(&m_image); m_gantt->print(&p, m_image.rect(), m_gantt->m_printOptions.printRowLabels, true); } } } setPageRange( pages ); PrintingDialog::startPrinting( removePolicy ); } QList GanttPrintingDialog::createOptionWidgets() const { //debugPlan; GanttPrintingOptionsWidget *w = new GanttPrintingOptionsWidget(); w->setPrintRowLabels( m_gantt->m_printOptions.printRowLabels ); connect(w->ui_printRowLabels, &QAbstractButton::toggled, this, &GanttPrintingDialog::slotPrintRowLabelsToogled); w->setSinglePage( m_gantt->m_printOptions.singlePage ); connect(w->ui_singlePage, &QAbstractButton::toggled, this, &GanttPrintingDialog::slotSinglePageToogled); const_cast( this )->m_options = w; return QList() << createPageLayoutWidget() << m_options; } void GanttPrintingDialog::slotPrintRowLabelsToogled( bool on ) { m_gantt->m_printOptions.printRowLabels = on; } void GanttPrintingDialog::slotSinglePageToogled( bool on ) { m_gantt->m_printOptions.singlePage = on; printer().setFromTo( documentFirstPage(), documentLastPage() ); } int GanttPrintingDialog::documentLastPage() const { //debugPlan<m_printOptions.singlePage<m_printOptions.singlePage ? documentFirstPage() : m_horPages * m_vertPages; } void GanttPrintingDialog::printPage( int page, QPainter &painter ) { debugPlan<<"page:"<m_printOptions.singlePage; int vert = singlePage ? 0 : p / m_horPages; int hor = singlePage ? 0 : p % m_horPages; // painter.setClipRect( pageRect.adjusted( -1.0, -1.0, 1.0, 1.0 ) ); if (singlePage) { // single page: use KGantt m_gantt->print( &painter, m_sceneRect.left(), m_sceneRect.right(), pageRect, m_gantt->m_printOptions.printRowLabels, true ); } else if (m_vertPages == 1) { // single vertical page: use KGantt qreal hh = vert == 0 ? m_headerHeight : 0; qreal ho = vert > 0 ? m_headerHeight : 0; QRectF sourceRect = QRectF( m_sceneRect.x() + ( pageRect.width() * hor ), m_sceneRect.y() + ( ( pageRect.height() * vert ) - ho ), pageRect.width(), pageRect.height() - hh ); debugPlan<print( &painter, sourceRect.left(), sourceRect.right(), pageRect, hor == 0 && m_gantt->m_printOptions.printRowLabels, true ); } else { // print on multiple vertical pages: use pixmap // QT5TODO Make KGantt able to print multiple pages vertically QRectF sourceRect = m_image.rect(); qreal hh = vert == 0 ? m_headerHeight : 0; qreal ho = vert > 0 ? m_headerHeight : 0; sourceRect = QRectF( sourceRect.x() + ( pageRect.width() * hor ), sourceRect.y() + ( ( pageRect.height() * vert ) - ho ), pageRect.width(), pageRect.height() - hh ); debugPlan<setContextMenuPolicy( Qt::CustomContextMenu ); connect( header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderContextMenuRequested(QPoint)) ); } //------------------------------------------- GanttZoomWidget::GanttZoomWidget( QWidget *parent ) : QSlider( parent ), m_hide( true ), m_grid( 0 ) { setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); setGeometry( 0, 0, 200, minimumSizeHint().height() ); setContextMenuPolicy( Qt::PreventContextMenu ); setOrientation( Qt::Horizontal ); setPageStep( 5 ); setMaximum( 125 ); connect(this, &QAbstractSlider::valueChanged, this, &GanttZoomWidget::sliderValueChanged); } void GanttZoomWidget::setEnableHideOnLeave( bool hide ) { m_hide = hide; } void GanttZoomWidget::setGrid( KGantt::DateTimeGrid *grid ) { m_grid = grid; if ( grid ) { int pos = -1; // daywidth always >= 0.1 for ( qreal dw = grid->dayWidth(); dw >= 0.1 && pos < maximum(); ++pos ) { dw *= 1.0 / 1.1; } blockSignals( true ); setValue( pos ); blockSignals( false ); } } void GanttZoomWidget::leaveEvent( QEvent *e ) { if ( m_hide ) { setVisible( false ); } QSlider::leaveEvent( e ); } void GanttZoomWidget::sliderValueChanged( int value ) { //debugPlan<setDayWidth( v ); } } //------------------------------------------- GanttViewBase::GanttViewBase( QWidget *parent ) : KGantt::View( parent ) { setGrid(new DateTimeGrid()); DateTimeGrid *g = static_cast( grid() ); g->setUserDefinedUpperScale( new KGantt::DateTimeScaleFormatter(KGantt::DateTimeScaleFormatter::Month, QString::fromLatin1("yyyy-MMMM"))); g->setUserDefinedLowerScale( new KGantt::DateTimeScaleFormatter(KGantt::DateTimeScaleFormatter::Day, QString::fromLatin1("ddd"))); g->timeNow()->setInterval(5000); QLocale locale; g->setWeekStart( locale.firstDayOfWeek() ); const QList weekdays = locale.weekdays(); QSet fd; for ( int i = Qt::Monday; i <= Qt::Sunday; ++i ) { if (!weekdays.contains(static_cast(i))) { fd << static_cast( i ); } } g->setFreeDays( fd ); m_zoomwidget = new GanttZoomWidget( graphicsView() ); m_zoomwidget->setGrid( g ); m_zoomwidget->setEnableHideOnLeave( true ); m_zoomwidget->hide(); m_zoomwidget->move( 6, 6 ); graphicsView()->installEventFilter(this); graphicsView()->setMouseTracking(true); } GanttViewBase::~GanttViewBase() { // HACK: avoid crash due to access of graphicsview scrollbar after death // KGantt tries to sync leftview scrollbar with graphicsview scrollbar // and seems sometimes graphicsview has already been deleted. // Note: this will be fixed in next KGantt release leftView()->verticalScrollBar()->disconnect(); } DateTimeTimeLine *GanttViewBase::timeLine() const { DateTimeGrid *g = static_cast( grid() ); return g->timeNow(); } GanttTreeView *GanttViewBase::treeView() const { GanttTreeView *tv = qobject_cast(const_cast(leftView())); Q_ASSERT(tv); return tv; } bool GanttViewBase::eventFilter(QObject *obj, QEvent *event) { if (obj != graphicsView()) { return false; } if (event->type() == QEvent::HoverMove) { QHoverEvent *e = static_cast( event ); if (e->pos().y() > 7 && e->pos().y() < m_zoomwidget->height() + 5 && e->pos().x() > 7 && e->pos().x() < m_zoomwidget->width() + 5 ) { if ( !m_zoomwidget->isVisible()) { m_zoomwidget->show(); m_zoomwidget->setFocus(); } return true; } } return false; } bool GanttViewBase::loadContext( const KoXmlElement &settings ) { KGantt::DateTimeGrid *g = static_cast( grid() ); g->setScale( static_cast( settings.attribute( "chart-scale", "0" ).toInt() ) ); g->setDayWidth( settings.attribute( "chart-daywidth", "30" ).toDouble() ); DateTimeTimeLine::Options opt; opt.setFlag(DateTimeTimeLine::Foreground, settings.attribute("timeline-foreground").toInt()); opt.setFlag(DateTimeTimeLine::Background, settings.attribute("timeline-background").toInt()); opt.setFlag(DateTimeTimeLine::UseCustomPen, settings.attribute("timeline-custom").toInt()); timeLine()->setOptions(opt); timeLine()->setInterval(settings.attribute("timeline-interval", 0).toInt() * 60000); QPen pen; pen.setWidth(settings.attribute("timeline-width").toInt()); pen.setColor(QColor(settings.attribute("timeline-color"))); timeLine()->setPen(pen); return true; } void GanttViewBase::saveContext( QDomElement &settings ) const { KGantt::DateTimeGrid *g = static_cast( grid() ); settings.setAttribute( "chart-scale", QString::number(g->scale()) ); settings.setAttribute( "chart-daywidth", QString::number(g->dayWidth()) ); settings.setAttribute("timeline-foreground", timeLine()->options() & DateTimeTimeLine::Foreground); settings.setAttribute("timeline-background", timeLine()->options() & DateTimeTimeLine::Background); settings.setAttribute("timeline-interval", timeLine()->interval() / 60000); settings.setAttribute("timeline-custom", timeLine()->options() & DateTimeTimeLine::UseCustomPen); settings.setAttribute("timeline-width", timeLine()->pen().width()); settings.setAttribute("timeline-color", timeLine()->pen().color().name()); } //------------------------------------------- NodeGanttViewBase::NodeGanttViewBase( QWidget *parent ) : GanttViewBase( parent ), m_project( 0 ), m_ganttdelegate( new GanttItemDelegate( this ) ) { debugPlan<<"------------------- create NodeGanttViewBase -----------------------"; graphicsView()->setItemDelegate( m_ganttdelegate ); GanttTreeView *tv = new GanttTreeView( this ); tv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); tv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tv->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); // needed since qt 4.2 setLeftView( tv ); m_rowController = new KGantt::TreeViewRowController( tv, ganttProxyModel() ); setRowController( m_rowController ); tv->header()->setStretchLastSection( true ); NodeSortFilterProxyModel *m = new NodeSortFilterProxyModel(&m_defaultModel, this, true); KGantt::View::setModel( m ); } NodeGanttViewBase::~NodeGanttViewBase() { delete m_rowController; } NodeSortFilterProxyModel *NodeGanttViewBase::sfModel() const { return static_cast( KGantt::View::model() ); } void NodeGanttViewBase::setItemModel( ItemModelBase *model ) { sfModel()->setSourceModel( model ); } ItemModelBase *NodeGanttViewBase::model() const { return sfModel()->itemModel(); } void NodeGanttViewBase::setProject( Project *project ) { model()->setProject( project ); m_project = project; } bool NodeGanttViewBase::loadContext( const KoXmlElement &settings ) { treeView()->loadContext( model()->columnMap(), settings ); KoXmlElement e = settings.namedItem( "ganttchart" ).toElement(); if ( ! e.isNull() ) { m_ganttdelegate->showTaskLinks = (bool)( e.attribute( "show-dependencies", "0" ).toInt() ); m_ganttdelegate->showTaskName = (bool)( e.attribute( "show-taskname", "0" ).toInt() ); m_ganttdelegate->showResources = (bool)( e.attribute( "show-resourcenames", "0" ).toInt() ); m_ganttdelegate->showProgress = (bool)( e.attribute( "show-completion", "0" ).toInt() ); m_ganttdelegate->showCriticalPath = (bool)( e.attribute( "show-criticalpath", "0" ).toInt() ); m_ganttdelegate->showCriticalTasks = (bool)( e.attribute( "show-criticaltasks", "0" ).toInt() ); m_ganttdelegate->showPositiveFloat = (bool)( e.attribute( "show-positivefloat", "0" ).toInt() ); m_ganttdelegate->showSchedulingError = (bool)( e.attribute( "show-schedulingerror", "0" ).toInt() ); m_ganttdelegate->showTimeConstraint = (bool)( e.attribute( "show-timeconstraint", "0" ).toInt() ); m_ganttdelegate->showNegativeFloat = (bool)( e.attribute( "show-negativefloat", "0" ).toInt() ); GanttViewBase::loadContext( e ); m_printOptions.loadContext( e ); } return true; } void NodeGanttViewBase::saveContext( QDomElement &settings ) const { debugPlan; treeView()->saveContext( model()->columnMap(), settings ); QDomElement e = settings.ownerDocument().createElement( "ganttchart" ); settings.appendChild( e ); e.setAttribute( "show-dependencies", QString::number(m_ganttdelegate->showTaskLinks) ); e.setAttribute( "show-taskname", QString::number(m_ganttdelegate->showTaskName) ); e.setAttribute( "show-resourcenames", QString::number(m_ganttdelegate->showResources) ); e.setAttribute( "show-completion", QString::number(m_ganttdelegate->showProgress) ); e.setAttribute( "show-criticalpath", QString::number(m_ganttdelegate->showCriticalPath) ); e.setAttribute( "show-criticaltasks",QString::number(m_ganttdelegate->showCriticalTasks) ); e.setAttribute( "show-positivefloat", QString::number(m_ganttdelegate->showPositiveFloat) ); e.setAttribute( "show-schedulingerror", QString::number(m_ganttdelegate->showSchedulingError) ); e.setAttribute( "show-timeconstraint", QString::number(m_ganttdelegate->showTimeConstraint) ); e.setAttribute( "show-negativefloat", QString::number(m_ganttdelegate->showNegativeFloat) ); GanttViewBase::saveContext( e ); m_printOptions.saveContext( e ); } void NodeGanttViewBase::setShowUnscheduledTasks(bool show) { NodeSortFilterProxyModel *m = qobject_cast(KGantt::View::model()); if (m) { m->setFilterUnscheduled(!show); } } //------------------------------------------- MyKGanttView::MyKGanttView( QWidget *parent ) : NodeGanttViewBase( parent ), m_manager( 0 ) { debugPlan<<"------------------- create MyKGanttView -----------------------"; GanttItemModel *gm = new GanttItemModel( this ); setItemModel( gm ); treeView()->createItemDelegates( gm ); QList show; show << NodeModel::NodeName << NodeModel::NodeCompleted << NodeModel::NodeStartTime << NodeModel::NodeEndTime; treeView()->setDefaultColumns( show ); for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { treeView()->hideColumn( i ); } } setConstraintModel( new KGantt::ConstraintModel( this ) ); KGantt::ProxyModel *m = static_cast( ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); // To provide correct format m->setRole( KGantt::StartTimeRole, Qt::EditRole ); // To provide correct format m->setRole( KGantt::EndTimeRole, Qt::EditRole ); // To provide correct format m->removeColumn( Qt::DisplayRole ); m->setColumn( KGantt::ItemTypeRole, NodeModel::NodeType ); m->setColumn( KGantt::StartTimeRole, NodeModel::NodeStartTime ); m->setColumn( KGantt::EndTimeRole, NodeModel::NodeEndTime ); m->setColumn( KGantt::TaskCompletionRole, NodeModel::NodeCompleted ); KGantt::DateTimeGrid *g = static_cast( grid() ); g->setDayWidth( 30 ); // TODO: extend QLocale/KGantt to support formats for hourly time display // see bug #349030 // removed custom code here connect( model(), &NodeItemModel::nodeInserted, this, &MyKGanttView::slotNodeInserted ); } GanttItemModel *MyKGanttView::model() const { return static_cast( NodeGanttViewBase::model() ); } void MyKGanttView::setProject( Project *proj ) { clearDependencies(); if ( project() ) { disconnect( project(), &Project::relationToBeModified, this, &MyKGanttView::removeDependency); disconnect( project(), &Project::relationModified, this, &MyKGanttView::addDependency); disconnect( project(), &Project::relationAdded, this, &MyKGanttView::addDependency ); disconnect( project(), &Project::relationToBeRemoved, this, &MyKGanttView::removeDependency ); disconnect( project(), &Project::projectCalculated, this, &MyKGanttView::slotProjectCalculated ); } NodeGanttViewBase::setProject( proj ); if ( proj ) { connect( project(), &Project::relationToBeModified, this, &MyKGanttView::removeDependency); connect( project(), &Project::relationModified, this, &MyKGanttView::addDependency); connect( proj, &Project::relationAdded, this, &MyKGanttView::addDependency ); connect( proj, &Project::relationToBeRemoved, this, &MyKGanttView::removeDependency ); connect( proj, &Project::projectCalculated, this, &MyKGanttView::slotProjectCalculated ); } createDependencies(); } void MyKGanttView::slotProjectCalculated( ScheduleManager *sm ) { if ( m_manager == sm ) { setScheduleManager( sm ); } } void MyKGanttView::setScheduleManager( ScheduleManager *sm ) { clearDependencies(); m_manager = sm; KGantt::DateTimeGrid *g = static_cast( grid() ); if ( sm && project() ) { QDateTime start = project()->startTime( sm->scheduleId() ); if ( start.isValid() && g->startDateTime() != start ) { g->setStartDateTime( start ); } } if ( ! g->startDateTime().isValid() ) { g->setStartDateTime( QDateTime::currentDateTime() ); } model()->setScheduleManager( sm ); createDependencies(); } void MyKGanttView::slotNodeInserted( Node *node ) { foreach( Relation *r, node->dependChildNodes() ) { addDependency( r ); } foreach( Relation *r, node->dependParentNodes() ) { addDependency( r ); } } void MyKGanttView::addDependency( Relation *rel ) { QModelIndex par = sfModel()->mapFromSource( model()->index( rel->parent() ) ); QModelIndex ch = sfModel()->mapFromSource( model()->index( rel->child() ) ); // debugPlan<<"addDependency() "<( rel->type() )/*NOTE!!*/ ); if ( ! constraintModel()->hasConstraint( con ) ) { constraintModel()->addConstraint( con ); } } } void MyKGanttView::removeDependency( Relation *rel ) { QModelIndex par = sfModel()->mapFromSource( model()->index( rel->parent() ) ); QModelIndex ch = sfModel()->mapFromSource( model()->index( rel->child() ) ); KGantt::Constraint con( par, ch, KGantt::Constraint::TypeSoft, static_cast( rel->type() )/*NOTE!!*/ ); constraintModel()->removeConstraint( con ); } void MyKGanttView::clearDependencies() { constraintModel()->clear(); // Remove old deps from view // NOTE: This should be handled by KGantt graphicsView()->updateScene(); } void MyKGanttView::createDependencies() { clearDependencies(); if ( project() == 0 || m_manager == 0 ) { return; } foreach ( Node* n, project()->allNodes() ) { foreach ( Relation *r, n->dependChildNodes() ) { addDependency( r ); } } } //------------------------------------------ GanttView::GanttView(KoPart *part, KoDocument *doc, QWidget *parent, bool readWrite) : ViewBase(part, doc, parent), m_readWrite( readWrite ), m_project( 0 ) { debugPlan <<" ---------------- KPlato: Creating GanttView ----------------"; QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); m_splitter = new QSplitter( this ); l->addWidget( m_splitter ); m_splitter->setOrientation( Qt::Vertical ); m_gantt = new MyKGanttView( m_splitter ); connect(this, &ViewBase::expandAll, m_gantt->treeView(), &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_gantt->treeView(), &TreeViewBase::slotCollapse); setupGui(); updateReadWrite( readWrite ); //connect( m_gantt->constraintModel(), SIGNAL(constraintAdded(Constraint)), this, SLOT(update()) ); debugPlan <constraintModel(); connect( m_gantt->treeView(), &TreeViewBase::contextMenuRequested, this, &GanttView::slotContextMenuRequested ); connect( m_gantt->treeView(), &TreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Gantt View" "" "Displays scheduled tasks in a Gantt diagram." " The chart area can be zoomed in and out with a slider" " positioned in the upper left corner of the time scale." " You need to hoover over it with the mouse for it to show." "" "This view supports configuration and printing using the context menu of the tree view." "More..." "", Help::page("Manual/Task_Gantt_View"))); } KoPrintJob *GanttView::createPrintJob() { return new GanttPrintingDialog( this, m_gantt ); } void GanttView::setZoom( double ) { //debugPlan <<"setting gantt zoom:" << zoom; //m_gantt->setZoomFactor(zoom,true); NO!!! setZoomFactor() is something else } void GanttView::setupGui() { // create context menu actions actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); // FIXME: Dependencies depend on these methods being called in the correct order connect(actionShowProject, &QAction::triggered, m_gantt, &MyKGanttView::clearDependencies); connect(actionShowProject, &QAction::triggered, m_gantt->model(), &NodeItemModel::setShowProject); connect(actionShowProject, &QAction::triggered, m_gantt, &MyKGanttView::createDependencies); addContextAction( actionShowProject ); actionShowUnscheduled = new KToggleAction( i18n( "Show Unscheduled Tasks" ), this ); connect(actionShowUnscheduled, &QAction::triggered, m_gantt, &MyKGanttView::setShowUnscheduledTasks); addContextAction(actionShowUnscheduled); createOptionActions(ViewBase::OptionAll); } void GanttView::slotOptions() { debugPlan; GanttViewSettingsDialog *dlg = new GanttViewSettingsDialog( m_gantt, m_gantt->delegate(), this, sender()->objectName() == "print options" ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void GanttView::slotOptionsFinished( int result ) { GanttViewSettingsDialog *dlg = qobject_cast( sender() ); if ( dlg && result == QDialog::Accepted ) { m_gantt->graphicsView()->updateScene(); } ViewBase::slotOptionsFinished( result ); } void GanttView::clear() { // m_gantt->clear(); } void GanttView::setShowResources( bool on ) { m_gantt->delegate()->showResources = on; } void GanttView::setShowTaskName( bool on ) { m_gantt->delegate()->showTaskName = on; } void GanttView::setShowProgress( bool on ) { m_gantt->delegate()->showProgress = on; } void GanttView::setShowPositiveFloat( bool on ) { m_gantt->delegate()->showPositiveFloat = on; } void GanttView::setShowCriticalTasks( bool on ) { m_gantt->delegate()->showCriticalTasks = on; } void GanttView::setShowCriticalPath( bool on ) { m_gantt->delegate()->showCriticalPath = on; } void GanttView::setShowNoInformation( bool on ) { m_gantt->delegate()->showNoInformation = on; } void GanttView::setShowAppointments( bool on ) { m_gantt->delegate()->showAppointments = on; } void GanttView::setShowTaskLinks( bool on ) { m_gantt->delegate()->showTaskLinks = on; } void GanttView::setProject( Project *project ) { m_gantt->setProject( project ); } void GanttView::setScheduleManager( ScheduleManager *sm ) { if (!sm && scheduleManager()) { // we should only get here if the only schedule manager is scheduled, // or when last schedule manager is deleted m_domdoc.clear(); QDomElement element = m_domdoc.createElement("expanded"); m_domdoc.appendChild(element); m_gantt->treeView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); bool expand = sm && scheduleManager() && sm != scheduleManager(); QDomDocument doc; if (expand) { QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_gantt->treeView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_gantt->setScheduleManager( sm ); if (expand) { m_gantt->treeView()->doExpand(doc); } else if (tryexpand) { m_gantt->treeView()->doExpand(m_domdoc); } } void GanttView::draw( Project &project ) { setProject( &project ); } void GanttView::drawChanges( Project &project ) { if ( m_project != &project ) { setProject( &project ); } } Node *GanttView::currentNode() const { QModelIndex idx = m_gantt->treeView()->selectionModel()->currentIndex(); return m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); } void GanttView::slotContextMenuRequested( const QModelIndex &idx, const QPoint &pos ) { debugPlan; QString name; Node *node = m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); + qInfo()<type() ) { + case Node::Type_Project: + name = "taskview_project_popup"; + break; case Node::Type_Task: name = "taskview_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node"; m_gantt->treeView()->setContextMenuIndex(idx); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); debugPlan<<"No menu"; return; } emit requestPopupMenu( name, pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); } bool GanttView::loadContext( const KoXmlElement &settings ) { debugPlan; ViewBase::loadContext( settings ); bool show = (bool)(settings.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); m_gantt->model()->setShowProject( show ); // why is this not called by the action? show = (bool)(settings.attribute( "show-unscheduled", "1" ).toInt() ); actionShowUnscheduled->setChecked(show); m_gantt->setShowUnscheduledTasks(show); return m_gantt->loadContext( settings ); } void GanttView::saveContext( QDomElement &settings ) const { debugPlan; ViewBase::saveContext( settings ); settings.setAttribute( "show-project", QString::number(actionShowProject->isChecked()) ); settings.setAttribute( "show-unscheduled", QString::number(actionShowUnscheduled->isChecked()) ); m_gantt->saveContext( settings ); } void GanttView::updateReadWrite( bool on ) { // TODO: KGanttView needs read/write mode m_readWrite = on; } //---- MilestoneGanttViewSettingsDialog::MilestoneGanttViewSettingsDialog( GanttViewBase *gantt, ViewBase *view, bool selectPrint ) : ItemViewSettupDialog( view, gantt->treeView(), true, view ), m_gantt( gantt ) { QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_printingoptions = new GanttPrintingOptionsWidget( this ); m_printingoptions->setOptions( gantt->printingOptions() ); tab->addTab( m_printingoptions, m_printingoptions->windowTitle() ); KPageWidgetItem *page = insertWidget( -1, tab, i18n( "Printing" ), i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, SIGNAL(accepted()), this, SLOT(slotOk()) ); } void MilestoneGanttViewSettingsDialog::slotOk() { debugPlan; m_gantt->setPrintingOptions( m_printingoptions->options()); ItemViewSettupDialog::slotOk(); } //------------------------ MilestoneKGanttView::MilestoneKGanttView( QWidget *parent ) : NodeGanttViewBase( parent ), m_manager( 0 ) { debugPlan<<"------------------- create MilestoneKGanttView -----------------------"; MilestoneItemModel *mm = new MilestoneItemModel( this ); setItemModel( mm ); treeView()->createItemDelegates( mm ); sfModel()->setFilterRole ( Qt::EditRole ); sfModel()->setFilterFixedString( QString::number( Node::Type_Milestone ) ); sfModel()->setFilterKeyColumn( NodeModel::NodeType ); QList show; show << NodeModel::NodeWBSCode << NodeModel::NodeName << NodeModel::NodeStartTime; treeView()->setDefaultColumns( show ); for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { treeView()->hideColumn( i ); } } treeView()->header()->moveSection(NodeModel::NodeWBSCode, show.indexOf(NodeModel::NodeWBSCode)); treeView()->setRootIsDecorated ( false ); KGantt::ProxyModel *m = static_cast( ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); // To provide correct format m->setRole( KGantt::StartTimeRole, Qt::EditRole ); // To provide correct format m->setRole( KGantt::EndTimeRole, Qt::EditRole ); // To provide correct format m->removeColumn( Qt::DisplayRole ); m->setColumn( KGantt::ItemTypeRole, NodeModel::NodeType ); m->setColumn( KGantt::StartTimeRole, NodeModel::NodeStartTime ); m->setColumn( KGantt::EndTimeRole, NodeModel::NodeEndTime ); m->setColumn( KGantt::TaskCompletionRole, NodeModel::NodeCompleted ); KGantt::DateTimeGrid *g = static_cast( grid() ); g->setDayWidth( 30 ); // TODO: extend QLocale/KGantt to support formats for hourly time display // see bug #349030 // removed custom code here // TODO: add to context treeView()->sortByColumn(NodeModel::NodeWBSCode, Qt::AscendingOrder); treeView()->setSortingEnabled(true); Help::add(this, xi18nc("@info:whatsthis", "Milestone Gantt View" "" "Displays scheduled milestones in a Gantt diagram." " The chart area can be zoomed in and out with a slider" " positioned in the upper left corner of the time scale." " You need to hoover over it with the mouse for it to show." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Milestone_Gantt_View"))); } MilestoneItemModel *MilestoneKGanttView::model() const { return static_cast( NodeGanttViewBase::model() ); } void MilestoneKGanttView::setProject( Project *proj ) { if ( project() ) { disconnect( project(), &Project::projectCalculated, this, &MilestoneKGanttView::slotProjectCalculated ); } NodeGanttViewBase::setProject( proj ); if ( proj ) { connect( proj, &Project::projectCalculated, this, &MilestoneKGanttView::slotProjectCalculated ); } } void MilestoneKGanttView::slotProjectCalculated( ScheduleManager *sm ) { if ( m_manager == sm ) { setScheduleManager( sm ); } } void MilestoneKGanttView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<setScheduleManager( 0 ); m_manager = sm; KGantt::DateTimeGrid *g = static_cast( grid() ); if ( sm && m_project ) { QDateTime start; foreach ( const Node *n, model()->mileStones() ) { QDateTime nt = n->startTime( sm->scheduleId() ); if ( ! nt.isValid() ) { continue; } if ( ! start.isValid() || start > nt ) { start = nt; debugPlan<name()<startTime( sm->scheduleId() ); } if ( g->startDateTime() != start ) { g->setStartDateTime( start ); } } if ( ! g->startDateTime().isValid() ) { g->setStartDateTime( QDateTime::currentDateTime() ); } model()->setScheduleManager( sm ); } //------------------------------------------ MilestoneGanttView::MilestoneGanttView(KoPart *part, KoDocument *doc, QWidget *parent, bool readWrite) : ViewBase(part, doc, parent), m_readWrite( readWrite ), m_project( 0 ) { debugPlan <<" ---------------- Plan: Creating Milesone GanttView ----------------"; QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); m_splitter = new QSplitter( this ); l->addWidget( m_splitter ); m_splitter->setOrientation( Qt::Vertical ); setupGui(); m_gantt = new MilestoneKGanttView( m_splitter ); m_showTaskName = false; // FIXME m_showProgress = false; //FIXME m_showPositiveFloat = false; //FIXME m_showCriticalTasks = false; //FIXME m_showNoInformation = false; //FIXME updateReadWrite( readWrite ); connect( m_gantt->treeView(), &TreeViewBase::contextMenuRequested, this, &MilestoneGanttView::slotContextMenuRequested ); connect( m_gantt->treeView(), &TreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); } void MilestoneGanttView::setZoom( double ) { //debugPlan <<"setting gantt zoom:" << zoom; //m_gantt->setZoomFactor(zoom,true); NO!!! setZoomFactor() is something else } void MilestoneGanttView::show() { } void MilestoneGanttView::clear() { } void MilestoneGanttView::setProject( Project *project ) { m_gantt->setProject( project ); } void MilestoneGanttView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<setScheduleManager( sm ); } void MilestoneGanttView::draw( Project &project ) { setProject( &project ); } void MilestoneGanttView::drawChanges( Project &project ) { if ( m_project != &project ) { setProject( &project ); } } Node *MilestoneGanttView::currentNode() const { QModelIndex idx = m_gantt->treeView()->selectionModel()->currentIndex(); return m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); } void MilestoneGanttView::setupGui() { createOptionActions(ViewBase::OptionAll); } void MilestoneGanttView::slotContextMenuRequested( const QModelIndex &idx, const QPoint &pos ) { debugPlan; QString name; Node *node = m_gantt->model()->node( m_gantt->sfModel()->mapToSource( idx ) ); if ( node ) { switch ( node->type() ) { case Node::Type_Task: name = "taskview_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node"; m_gantt->treeView()->setContextMenuIndex(idx); if ( name.isEmpty() ) { debugPlan<<"No menu"; slotHeaderContextMenuRequested( pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); } void MilestoneGanttView::slotOptions() { debugPlan; MilestoneGanttViewSettingsDialog *dlg = new MilestoneGanttViewSettingsDialog( m_gantt, this, sender()->objectName() == "print options" ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool MilestoneGanttView::loadContext( const KoXmlElement &settings ) { debugPlan; ViewBase::loadContext( settings ); return m_gantt->loadContext( settings ); } void MilestoneGanttView::saveContext( QDomElement &settings ) const { debugPlan; ViewBase::saveContext( settings ); return m_gantt->saveContext( settings ); } void MilestoneGanttView::updateReadWrite( bool on ) { m_readWrite = on; } KoPrintJob *MilestoneGanttView::createPrintJob() { return new GanttPrintingDialog( this, m_gantt ); } //-------------------- ResourceAppointmentsGanttViewSettingsDialog::ResourceAppointmentsGanttViewSettingsDialog( GanttViewBase *gantt, ViewBase *view, bool selectPrint ) : ItemViewSettupDialog( view, gantt->treeView(), true, view ) , m_gantt(gantt) { QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_printingoptions = new GanttPrintingOptionsWidget( this ); m_printingoptions->setOptions( gantt->printingOptions() ); tab->addTab( m_printingoptions, m_printingoptions->windowTitle() ); KPageWidgetItem *page = insertWidget( -1, tab, i18n( "Printing" ), i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, SIGNAL(accepted()), this, SLOT(slotOk()) ); } void ResourceAppointmentsGanttViewSettingsDialog::slotOk() { debugPlan; m_gantt->setPrintingOptions( m_printingoptions->options()); ItemViewSettupDialog::slotOk(); } //------------------------------------------ ResourceAppointmentsGanttView::ResourceAppointmentsGanttView(KoPart *part, KoDocument *doc, QWidget *parent, bool readWrite) : ViewBase(part, doc, parent), m_project( 0 ), m_model( new ResourceAppointmentsGanttModel( this ) ) { debugPlan <<" ---------------- KPlato: Creating ResourceAppointmentsGanttView ----------------"; m_gantt = new GanttViewBase( this ); m_gantt->graphicsView()->setItemDelegate( new ResourceGanttItemDelegate( m_gantt ) ); GanttTreeView *tv = new GanttTreeView( m_gantt ); tv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); tv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tv->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); // needed since qt 4.2 m_gantt->setLeftView( tv ); connect(this, &ViewBase::expandAll, tv, &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, tv, &TreeViewBase::slotCollapse); m_rowController = new KGantt::TreeViewRowController( tv, m_gantt->ganttProxyModel() ); m_gantt->setRowController( m_rowController ); tv->header()->setStretchLastSection( true ); tv->setTreePosition(-1); KGantt::ProxyModel *m = static_cast( m_gantt->ganttProxyModel() ); m->setRole( KGantt::ItemTypeRole, KGantt::ItemTypeRole ); m->setRole( KGantt::StartTimeRole, KGantt::StartTimeRole ); m->setRole( KGantt::EndTimeRole, KGantt::EndTimeRole ); m->setRole( KGantt::TaskCompletionRole, KGantt::TaskCompletionRole ); m_gantt->setModel( m_model ); QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); l->addWidget( m_gantt ); setupGui(); updateReadWrite( readWrite ); connect( m_gantt->leftView(), SIGNAL(contextMenuRequested(QModelIndex,QPoint,QModelIndexList)), SLOT(slotContextMenuRequested(QModelIndex,QPoint)) ); connect( m_gantt->leftView(), SIGNAL(headerContextMenuRequested(QPoint)), SLOT(slotHeaderContextMenuRequested(QPoint)) ); Help::add(this, xi18nc("@info:whatsthis", "Resource Assignments (Gantt)" "" "Displays the scheduled resource - task assignments in a Gantt diagram." " The chart area can be zoomed in and out with a slider" " positioned in the upper left corner of the time scale." " You need to hoover over it with the mouse for it to show." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Resource_Assignment_Gantt_View"))); } ResourceAppointmentsGanttView::~ResourceAppointmentsGanttView() { delete m_rowController; } void ResourceAppointmentsGanttView::setZoom( double ) { //debugPlan <<"setting gantt zoom:" << zoom; //m_gantt->setZoomFactor(zoom,true); NO!!! setZoomFactor() is something else } Project *ResourceAppointmentsGanttView::project() const { return m_model->project(); } void ResourceAppointmentsGanttView::setProject( Project *project ) { m_model->setProject( project ); } void ResourceAppointmentsGanttView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); bool expand = sm && scheduleManager() && sm != scheduleManager(); QDomDocument doc; if (expand) { QDomElement element = doc.createElement("expanded"); doc.appendChild(element); treeView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_model->setScheduleManager( sm ); if (expand) { treeView()->doExpand(doc); } else if (tryexpand) { treeView()->doExpand(m_domdoc); } } void ResourceAppointmentsGanttView::setupGui() { createOptionActions(ViewBase::OptionAll); } Node *ResourceAppointmentsGanttView::currentNode() const { QModelIndex idx = treeView()->selectionModel()->currentIndex(); return m_model->node( idx ); } void ResourceAppointmentsGanttView::slotContextMenuRequested( const QModelIndex &idx, const QPoint &pos ) { debugPlan<node( idx ); if ( n ) { name = "taskview_popup"; } } m_gantt->treeView()->setContextMenuIndex(idx); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_gantt->treeView()->setContextMenuIndex(QModelIndex()); } void ResourceAppointmentsGanttView::slotOptions() { debugPlan; ItemViewSettupDialog *dlg = new ResourceAppointmentsGanttViewSettingsDialog(m_gantt, this, sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool ResourceAppointmentsGanttView::loadContext( const KoXmlElement &settings ) { debugPlan; ViewBase::loadContext( settings ); m_gantt->loadContext( settings ); return treeView()->loadContext( m_model->columnMap(), settings ); } void ResourceAppointmentsGanttView::saveContext( QDomElement &settings ) const { debugPlan; ViewBase::saveContext( settings ); m_gantt->saveContext( settings ); treeView()->saveContext( m_model->columnMap(), settings ); } void ResourceAppointmentsGanttView::updateReadWrite( bool on ) { m_readWrite = on; } KoPrintJob *ResourceAppointmentsGanttView::createPrintJob() { return new GanttPrintingDialog( this, m_gantt ); } } //KPlato namespace #include "moc_kptganttview.cpp" diff --git a/src/libs/ui/kptmainprojectpanel.cpp b/src/libs/ui/kptmainprojectpanel.cpp index fbd7139d..20f77953 100644 --- a/src/libs/ui/kptmainprojectpanel.cpp +++ b/src/libs/ui/kptmainprojectpanel.cpp @@ -1,311 +1,321 @@ /* 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_description = new TaskDescriptionPanel( p, tabWidget->widget(1) ); + m_documents = new DocumentsPanel(p, tabWidget->widget(1)); + tabWidget->widget(1)->layout()->addWidget(m_documents); + + m_description = new TaskDescriptionPanel(p, tabWidget->widget(2)); m_description->namefield->hide(); m_description->namelabel->hide(); - tabWidget->widget(1)->layout()->addWidget(m_description); + tabWidget->widget(2)->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); } 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 ); + } 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 a4d103a4..899a7bbc 100644 --- a/src/libs/ui/kptmainprojectpanel.h +++ b/src/libs/ui/kptmainprojectpanel.h @@ -1,79 +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: + 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 d819a0e1..b2eaf041 100644 --- a/src/libs/ui/kptmainprojectpanelbase.ui +++ b/src/libs/ui/kptmainprojectpanelbase.ui @@ -1,407 +1,409 @@ Dag Andersen <danders@get2net.dk> KPlato::MainProjectPanelBase 0 0 489 520 - - - 0 - - - 0 - - - 0 - - - 0 - + - 0 + 1 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 + + + + + Description 0 0 0 0 KLineEdit QLineEdit
klineedit.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/kpttaskdescriptiondialog.cpp b/src/libs/ui/kpttaskdescriptiondialog.cpp index 2745afd3..01f47c6e 100644 --- a/src/libs/ui/kpttaskdescriptiondialog.cpp +++ b/src/libs/ui/kpttaskdescriptiondialog.cpp @@ -1,177 +1,177 @@ /* This file is part of the KDE project Copyright (C) 2009 Dag Andersen #include #include namespace KPlato { TaskDescriptionPanel::TaskDescriptionPanel(Node &node, QWidget *p, bool readOnly ) : TaskDescriptionPanelImpl( node, p ) { initDescription( readOnly ); setStartValues( node ); descriptionfield->setFocus(); } void TaskDescriptionPanel::setStartValues( Node &node ) { namefield->setText(node.name()); descriptionfield->setTextOrHtml( node.description() ); } MacroCommand *TaskDescriptionPanel::buildCommand() { KUndo2MagicString s = kundo2_i18n("Modify task description"); if ( m_node.type() == Node::Type_Milestone ) { s = kundo2_i18n("Modify milestone description"); } else if ( m_node.type() == Node::Type_Summarytask ) { s = kundo2_i18n("Modify summary task description"); } else if ( m_node.type() == Node::Type_Project ) { s = kundo2_i18n("Modify project description"); } MacroCommand *cmd = new MacroCommand(s); bool modified = false; if ( m_node.description() != descriptionfield->textOrHtml() ) { cmd->addCommand(new NodeModifyDescriptionCmd(m_node, descriptionfield->textOrHtml())); modified = true; } if (!modified) { delete cmd; return 0; } return cmd; } bool TaskDescriptionPanel::ok() { return true; } void TaskDescriptionPanel::initDescription( bool readOnly ) { toolbar->setVisible( ! readOnly ); toolbar->setToolButtonStyle( Qt::ToolButtonIconOnly ); KActionCollection *collection = new KActionCollection( this ); //krazy:exclude=tipsandthis descriptionfield->setRichTextSupport( KRichTextWidget::SupportBold | KRichTextWidget::SupportItalic | KRichTextWidget::SupportUnderline | KRichTextWidget::SupportStrikeOut | KRichTextWidget::SupportChangeListStyle | KRichTextWidget::SupportAlignment | KRichTextWidget::SupportFormatPainting ); collection->addActions(descriptionfield->createActions()); toolbar->addAction( collection->action( "format_text_bold" ) ); toolbar->addAction( collection->action( "format_text_italic" ) ); toolbar->addAction( collection->action( "format_text_underline" ) ); toolbar->addAction( collection->action( "format_text_strikeout" ) ); toolbar->addSeparator(); toolbar->addAction( collection->action( "format_list_style" ) ); toolbar->addSeparator(); toolbar->addAction( collection->action( "format_align_left" ) ); toolbar->addAction( collection->action( "format_align_center" ) ); toolbar->addAction( collection->action( "format_align_right" ) ); toolbar->addAction( collection->action( "format_align_justify" ) ); toolbar->addSeparator(); // toolbar->addAction( collection->action( "format_painter" ) ); descriptionfield->append( "" ); descriptionfield->setReadOnly( readOnly ); descriptionfield->setOverwriteMode( false ); descriptionfield->setLineWrapMode( KTextEdit::WidgetWidth ); descriptionfield->setTabChangesFocus( true ); } //----------------------------- TaskDescriptionPanelImpl::TaskDescriptionPanelImpl( Node &node, QWidget *p ) : QWidget(p), m_node(node) { setupUi(this); CONNECT(descriptionfield, &QTextEdit::textChanged, this, &TaskDescriptionPanelImpl::slotChanged); } TaskDescriptionPanelImpl::~TaskDescriptionPanelImpl() { DISCONNECT; } void TaskDescriptionPanelImpl::slotChanged() { emit textChanged( descriptionfield->textOrHtml() != m_node.description() ); } //----------------------------- -TaskDescriptionDialog::TaskDescriptionDialog( Task &task, QWidget *p, bool readOnly ) +TaskDescriptionDialog::TaskDescriptionDialog( Node &node, QWidget *p, bool readOnly ) : KoDialog(p) { - setCaption( i18n( "Task Description" ) ); + setCaption(node.type() == Node::Type_Project ? i18n( "Project Description" ) : i18n( "Task Description" ) ); if ( readOnly ) { setButtons( Close ); } else { setButtons( Ok|Cancel ); setDefaultButton( Ok ); } showButtonSeparator( true ); - m_descriptionTab = new TaskDescriptionPanel( task, this, readOnly ); + m_descriptionTab = new TaskDescriptionPanel( node, this, readOnly ); setMainWidget(m_descriptionTab); enableButtonOk(false); connect( m_descriptionTab, &TaskDescriptionPanelImpl::textChanged, this, &KoDialog::enableButtonOk ); } MacroCommand *TaskDescriptionDialog::buildCommand() { return m_descriptionTab->buildCommand(); } void TaskDescriptionDialog::slotButtonClicked( int button ) { if (button == KoDialog::Ok) { if ( ! m_descriptionTab->ok() ) { return; } accept(); } else { KoDialog::slotButtonClicked( button ); } } } //KPlato namespace diff --git a/src/libs/ui/kpttaskdescriptiondialog.h b/src/libs/ui/kpttaskdescriptiondialog.h index 5300f89f..b0835302 100644 --- a/src/libs/ui/kpttaskdescriptiondialog.h +++ b/src/libs/ui/kpttaskdescriptiondialog.h @@ -1,97 +1,97 @@ /* This file is part of the KDE project Copyright (C) 2009 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 KPTTASKDESCRIPTIONDIALOG_H #define KPTTASKDESCRIPTIONDIALOG_H #include "planui_export.h" #include "PlanMacros.h" #include "ui_kpttaskdescriptionpanelbase.h" #include namespace KPlato { class TaskDescriptionPanel; class Task; class Node; class MacroCommand; class TaskDescriptionPanelImpl : public QWidget, public Ui_TaskDescriptionPanelBase { Q_OBJECT public: TaskDescriptionPanelImpl( Node &node, QWidget *parent ); ~TaskDescriptionPanelImpl(); public Q_SLOTS: virtual void slotChanged(); Q_SIGNALS: void textChanged( bool ); protected: Node &m_node; private: OBJECTCONNECTIONS; }; class TaskDescriptionPanel : public TaskDescriptionPanelImpl { Q_OBJECT public: explicit TaskDescriptionPanel( Node &node, QWidget *parent = 0, bool readOnly = false ); MacroCommand *buildCommand(); bool ok(); void setStartValues( Node &node ); protected: void initDescription( bool readOnly ); }; class PLANUI_EXPORT TaskDescriptionDialog : public KoDialog { Q_OBJECT public: /** * The constructor for the task description dialog. * @param task the node to show * @param parent parent widget * @param readOnly determines whether the data are read-only */ - explicit TaskDescriptionDialog( Task &task, QWidget *parent = 0, bool readOnly = false ); + explicit TaskDescriptionDialog( Node &node, QWidget *parent = 0, bool readOnly = false ); MacroCommand *buildCommand(); protected Q_SLOTS: void slotButtonClicked( int button ); protected: TaskDescriptionPanel *m_descriptionTab; }; } //KPlato namespace #endif // KPTTASKDESCRIPTIONDIALOG_H diff --git a/src/libs/ui/kpttaskeditor.cpp b/src/libs/ui/kpttaskeditor.cpp index 774f0e42..298677e5 100644 --- a/src/libs/ui/kpttaskeditor.cpp +++ b/src/libs/ui/kpttaskeditor.cpp @@ -1,1729 +1,1732 @@ /* 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 "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::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->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::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 ); } 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 ); // 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::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(); } } // namespace KPlato